Skip to content

Commit

Permalink
v2.0.0b16
Browse files Browse the repository at this point in the history
  • Loading branch information
Yiğit Topcu committed Aug 9, 2024
1 parent 86b830c commit bf0c613
Show file tree
Hide file tree
Showing 37 changed files with 2,160 additions and 1,063 deletions.
29 changes: 15 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
[![version](https://img.shields.io/github/manifest-json/v/Tasshack/dreame-vacuum/dev?filename=custom_components%2Fdreame_vacuum%2Fmanifest.json&color=slateblue&label=version)](https://github.com/Tasshack/dreame-vacuum/releases)
![GitHub all releases](https://img.shields.io/github/downloads/Tasshack/dreame-vacuum/total)
[![HACS](https://img.shields.io/badge/HACS-Default-orange.svg?logo=HomeAssistantCommunityStore&logoColor=white)](https://github.com/hacs/integration)
[![Community Forum](https://img.shields.io/static/v1.svg?label=Community&message=Forum&color=41bdf5&logo=HomeAssistant&logoColor=white)](https://community.home-assistant.io/t/custom-component-dreame-vacuum/473026)
[![Ko-Fi](https://img.shields.io/static/v1.svg?label=%20&message=Ko-Fi&color=F16061&logo=ko-fi&logoColor=white)](https://www.ko-fi.com/Tasshack)
[![PayPal.Me](https://img.shields.io/static/v1.svg?label=%20&message=PayPal.Me&logo=paypal)](https://paypal.me/Tasshackk)
[![Version](https://img.shields.io/github/manifest-json/v/Tasshack/dreame-vacuum/dev?filename=custom_components%2Fdreame_vacuum%2Fmanifest.json&color=slateblue&label=Version&style=for-the-badge)](https://github.com/Tasshack/dreame-vacuum/releases)
![Downloads](https://img.shields.io/github/downloads/Tasshack/dreame-vacuum/total?label=Downloads&style=for-the-badge)
![Stars](https://img.shields.io/github/stars/Tasshack/dreame-vacuum?label=Stars&color=darkgoldenrod&style=for-the-badge)
[![HACS](https://img.shields.io/badge/HACS-Default-orange.svg?logo=HomeAssistantCommunityStore&logoColor=white&style=for-the-badge)](https://github.com/hacs/integration)
[![Community Forum](https://img.shields.io/static/v1.svg?label=Community&message=Forum&color=41bdf5&logo=HomeAssistant&logoColor=white&style=for-the-badge)](https://community.home-assistant.io/t/custom-component-dreame-vacuum/473026)
[![Ko-Fi](https://img.shields.io/static/v1.svg?label=%20&message=Ko-Fi&color=F16061&logo=ko-fi&logoColor=white&style=for-the-badge)](https://www.ko-fi.com/Tasshack)
[![PayPal.Me](https://img.shields.io/static/v1.svg?label=%20&message=PayPal.Me&logo=paypal&color=blue&style=for-the-badge)](https://paypal.me/Tasshackk)

![Logo](https://raw.githubusercontent.com/Tasshack/dreame-vacuum/dev/docs/media/logo.png)

Expand Down Expand Up @@ -68,6 +69,11 @@ wget -O - https://raw.githubusercontent.com/Tasshack/dreame-vacuum/dev/install |
## How To Use
Integration is compatible with all available Lovelace vacuum cards but if you want to use zone cleaning feature you can prefer the Xiaomi Vacuum Card.

#### With Dreame Vacuum Card

*Work in progress... <a href="https://github.com/Tasshack/dreame-vacuum/issues/466" target="_blank">#466</a>*


#### With [Xiaomi Vacuum Map Card](https://github.com/PiotrMachowski/lovelace-xiaomi-vacuum-map-card)
> Template for room and zone cleaning.
<a href="https://my.home-assistant.io/redirect/developer_template/" target="_blank"><img src="https://my.home-assistant.io/badges/developer_template.svg" alt="Open your Home Assistant instance and show your template developer tools." /></a>
Expand Down Expand Up @@ -255,24 +261,19 @@ attributes:

```

#### With Dreame Vacuum Card

*Work in progress*

## To Do

- Integrated custom lovelace map card
- Mopping effect settings
- Shortcut editing
- Schedule editing
- Furniture editing
- DnD editing
- Live camera streaming
- Backend translations


## Contributing
Integrations is currently only available on English language and if want you use it on our language it would be very helpful to you to translate files on *translations* folder and share with us.

To submit your changes please fork this repository and open a pull request.
To submit your changes please fork `dev` branch of this repository and open a pull request.

## Thanks To

Expand Down
14 changes: 11 additions & 3 deletions custom_components/dreame_vacuum/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,15 +152,23 @@ class DreameVacuumButtonEntityDescription(DreameVacuumEntityDescription, ButtonE
exists_fn=lambda description, device: device.capability.lidar_navigation,
),
DreameVacuumButtonEntityDescription(
name_fn=lambda value, device: "Self-Clean Pause" if device.status.washing else "Self-Clean",
name_fn=lambda value, device: "Self-Clean Resume" if (device.status.washing_paused or device.status.returning_to_wash_paused) else "Self-Clean Pause" if device.status.washing else "Self-Clean",
key="self_clean",
icon_fn=lambda value, device: (
"mdi:dishwasher-off"
if device.status.washing or not device.status.washing_available
if not device.status.washing_paused and (device.status.washing or not device.status.washing_available)
else "mdi:dishwasher"
),
action_fn=lambda device: device.toggle_washing(),
exists_fn=lambda description, device: device.capability.self_wash_base,
available_fn=lambda device: (
device.status.washing_available
or device.status.washing
or device.status.returning_to_wash_paused
or device.status.washing_paused
)
and not device.status.draining
and not device.status.self_repairing
),
DreameVacuumButtonEntityDescription(
name_fn=lambda value, device: "Stop Drying" if device.status.drying else "Start Drying",
Expand Down Expand Up @@ -447,5 +455,5 @@ async def async_press(self, **kwargs: Any) -> None:
await self._try_command(
"Unable to call %s",
self.device.backup_map,
self.device.get_map(self.map_index).map_id,
self.device.get_map().map_id,
)
76 changes: 56 additions & 20 deletions custom_components/dreame_vacuum/camera.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from __future__ import annotations

import collections
import voluptuous as vol
from enum import IntEnum, IntFlag
from enum import IntEnum
import time
import asyncio
import traceback
Expand All @@ -13,6 +12,7 @@
from functools import partial
from aiohttp import web

from homeassistant.components.http import HomeAssistantView
from homeassistant.components.camera import (
Camera,
CameraEntityDescription,
Expand Down Expand Up @@ -53,6 +53,7 @@
ATTR_RECOVERY_MAP_PICTURE,
ATTR_RECOVERY_MAP_FILE,
ATTR_WIFI_MAP_PICTURE,
ATTR_COLOR_SCHEME,
)
from .dreame.map import (
DreameVacuumMapRenderer,
Expand Down Expand Up @@ -293,6 +294,37 @@ async def handle(self, request: web.Request, camera: Camera) -> web.Response:
raise web.HTTPNotFound()


class CameraResourcesView(HomeAssistantView):
"""Camera view to serve the map data resources."""

url = "/api/camera_resources_proxy/{entity_id}"
name = "api:camera:resources"

requires_auth = False

def __init__(self, component) -> None:
"""Initialize camera view."""
self.component = component

async def get(self, request: web.Request, entity_id: str) -> web.StreamResponse:
"""Serve resources data."""
if (camera := self.component.get_entity(entity_id)) is None or camera.map_data_json or camera.map_index != 0 or not camera.device:
raise web.HTTPNotFound

icon_set = request.query.get("icon_set")
response = web.Response(
body=gzip.compress(
bytes(
camera.resources(icon_set),
"utf-8",
)
),
content_type=JSON_CONTENT_TYPE,
)
response.headers["Content-Encoding"] = "gzip"
return response


async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
Expand Down Expand Up @@ -336,13 +368,15 @@ async def async_setup_entry(
coordinator.async_add_listener(update_map_cameras)
update_map_cameras()

hass.http.register_view(CameraDataView(hass.data["camera"]))
hass.http.register_view(CameraObstacleView(hass.data["camera"]))
hass.http.register_view(CameraObstacleHistoryView(hass.data["camera"]))
hass.http.register_view(CameraHistoryView(hass.data["camera"]))
hass.http.register_view(CameraRecoveryView(hass.data["camera"]))
hass.http.register_view(CameraWifiView(hass.data["camera"]))

camera = hass.data["camera"]
hass.http.register_view(CameraDataView(camera))
hass.http.register_view(CameraObstacleView(camera))
hass.http.register_view(CameraObstacleHistoryView(camera))
hass.http.register_view(CameraHistoryView(camera))
hass.http.register_view(CameraRecoveryView(camera))
hass.http.register_view(CameraWifiView(camera))
hass.http.register_view(CameraResourcesView(camera))


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return True
Expand Down Expand Up @@ -458,6 +492,7 @@ def __init__(
self._device_active = None
self._error = None
self._proxy_renderer = None
self._color_scheme = color_scheme

if description.map_type == DreameVacuumMapType.JSON_MAP_DATA:
self._renderer = DreameVacuumMapDataJsonRenderer()
Expand Down Expand Up @@ -505,8 +540,7 @@ def __init__(
self._attr_unique_id = f"{self.device.mac}_map_{'wifi_' if self.wifi_map else ''}{description.key}"
self.entity_id = f"camera.{self.device.name.lower().replace(' ','_')}_{'wifi_' if self.wifi_map else ''}{description.key.lower()}"

if self.map_index == 0 or self.map_data_json:
self.update()
self.update()

def _set_map_name(self, wifi_map) -> None:
name = (
Expand Down Expand Up @@ -550,6 +584,7 @@ def _handle_coordinator_update(self) -> None:
or self._device_active != self.device.status.active
or self._error != self.device.status.error
or self._last_updated is None
or self.map_index > 0
):
self.update()
elif self._error != self.device.status.error or self._device_active != self.device.status.active:
Expand Down Expand Up @@ -726,9 +761,9 @@ async def recovery_map(self, index, info_text, data_string, include_resources):
if not self.map_data_json and not self.wifi_map:
if self.map_index == 0:
selected_map = self.device.status.selected_map
map_data = self.device.recovery_map(selected_map.map_id, index) if selected_map else None
map_data = await self.hass.async_add_executor_job(self.device.recovery_map, selected_map.map_id, index) if selected_map else None
else:
map_data = self.device.recovery_map(self._map_id, index)
map_data = await self.hass.async_add_executor_job(self.device.recovery_map, self._map_id, index)
if map_data:
map_data = self.device.get_map_for_render(map_data)
if data_string:
Expand Down Expand Up @@ -760,7 +795,7 @@ async def wifi_map_data(self, data_string, include_resources):
1,
)

def map_data_string(self, include_resources) -> str | None:
def map_data_string(self, include_resources) -> str:
if not self.map_data_json and self._map_data:
if self.map_index == 0 and self.device:
self._last_map_request = time.time()
Expand All @@ -772,6 +807,11 @@ def map_data_string(self, include_resources) -> str | None:
self.device.status.station_status,
)
return "{}"

def resources(self, icon_set=None) -> str:
if self.device:
return self._renderer.get_resources(self.device.capability, True, icon_set)
return "{}"

async def _update_image(self, map_data, robot_status, station_status) -> None:
try:
Expand Down Expand Up @@ -814,11 +854,6 @@ def _get_proxy_obstacle_image(self, data, obstacle, box, crop, cache_key, max_it
self._proxy_images[cache_key][item_key] = image
return image

@property
def resources(self):
if self.map_index == 0 and not self.map_data_json:
return self._renderer.get_resources(self.device.capability)

@property
def wifi_map(self) -> bool:
return bool(self.entity_description.map_type == DreameVacuumMapType.WIFI_MAP)
Expand Down Expand Up @@ -896,7 +931,8 @@ def extra_state_attributes(self) -> Dict[str, Any]:

token = self.access_tokens[-1]
if self.map_index == 0:

attributes[ATTR_COLOR_SCHEME] = self._color_scheme

def get_key(index, history):
return f"{index}: {time.strftime('%m/%d %H:%M', time.localtime(history.date.timestamp()))} - {'Second ' if history.second_cleaning else ''}{STATUS_CODE_TO_NAME.get(history.status, STATE_UNKNOWN).replace('_', ' ').title()} {'(Completed)' if history.completed else '(Interrupted)'}"

Expand Down
Loading

0 comments on commit bf0c613

Please sign in to comment.