Skip to content

Commit

Permalink
v2.0.0b10
Browse files Browse the repository at this point in the history
  • Loading branch information
Yiğit Topcu committed Dec 15, 2023
1 parent e3c1f0d commit 2a20251
Show file tree
Hide file tree
Showing 39 changed files with 2,980 additions and 1,155 deletions.
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
[![version](https://img.shields.io/github/manifest-json/v/Tasshack/dreame-vacuum?filename=custom_components%2Fdreame_vacuum%2Fmanifest.json&color=slateblue)](https://github.com/Tasshack/dreame-vacuum/releases/latest)
[![version](https://img.shields.io/github/manifest-json/v/Tasshack/dreame-vacuum/dev?filename=custom_components%2Fdreame_vacuum%2Fmanifest.json&color=slateblue)](https://github.com/Tasshack/dreame-vacuum/releases)
![GitHub all releases](https://img.shields.io/github/downloads/Tasshack/dreame-vacuum/total)
![GitHub issues](https://img.shields.io/github/issues/Tasshack/dreame-vacuum)
[![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)

![dreame Logo](https://cdn.shopify.com/s/files/1/0302/5276/1220/files/rsz_logo_-01_400x_2ecfe8c0-2756-4bd1-a3f4-593b1f73e335_284x.jpg "dreame Logo")
![Dreame Logo](https://raw.githubusercontent.com/Tasshack/dreame-vacuum/dev/docs/media/logo.png "Dreame")

# Dreame vacuum integration for Home Assistant

Expand Down
21 changes: 17 additions & 4 deletions custom_components/dreame_vacuum/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,7 @@ class DreameVacuumButtonEntityDescription(
action_key=DreameVacuumAction.RESET_SENSOR,
icon="mdi:radar",
entity_category=EntityCategory.DIAGNOSTIC,
exists_fn=lambda description, device: bool(
DreameVacuumEntityDescription().exists_fn(description, device)
and device.status.sensor_dirty_life is not None
),
exists_fn=lambda description, device: device.capability.sensor_cleaning,
),
DreameVacuumButtonEntityDescription(
action_key=DreameVacuumAction.RESET_MOP_PAD,
Expand Down Expand Up @@ -190,6 +187,14 @@ class DreameVacuumButtonEntityDescription(
exists_fn=lambda description, device: device.capability.self_wash_base
and device.capability.drainage,
),
DreameVacuumButtonEntityDescription(
key="empty_water_tank",
icon="mdi:waves-arrow-up",
entity_category=EntityCategory.DIAGNOSTIC,
action_fn=lambda device: device.start_draining(True),
exists_fn=lambda description, device: device.capability.self_wash_base
and device.capability.empty_water_tank,
),
DreameVacuumButtonEntityDescription(
key="base_station_self_repair",
icon_fn=lambda value, device: "mdi:wrench-clock"
Expand All @@ -199,6 +204,14 @@ class DreameVacuumButtonEntityDescription(
action_fn=lambda device: device.start_self_repairing(),
exists_fn=lambda description, device: device.capability.self_wash_base,
),
DreameVacuumButtonEntityDescription(
key="start_recleaning",
name="Start Re-Cleaning",
icon="mdi:refresh-circle",
entity_category=None,
action_fn=lambda device: device.start_recleaning(),
exists_fn=lambda description, device: device.capability.second_cleaning and device.capability.map,
),
)


Expand Down
45 changes: 14 additions & 31 deletions custom_components/dreame_vacuum/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,6 @@ class CameraDataView(CameraView):
async def handle(self, request: web.Request, camera: Camera) -> web.Response:
"""Serve camera data."""
if not camera.map_data_json:
if not camera.available:
raise web.HTTPServiceUnavailable()

resources = request.query.get("resources")
return web.Response(
body=camera.map_data_string(
Expand All @@ -136,9 +133,6 @@ class CameraObstacleView(CameraView):
async def handle(self, request: web.Request, camera: Camera) -> web.Response:
"""Serve camera obstacle image."""
if camera.map_index == 0:
if not camera.available:
raise web.HTTPServiceUnavailable()

crop = request.query.get("crop")
box = request.query.get("box")
file = request.query.get("file")
Expand Down Expand Up @@ -171,9 +165,6 @@ class CameraObstacleHistoryView(CameraView):
async def handle(self, request: web.Request, camera: Camera) -> web.Response:
"""Serve camera obstacle image."""
if camera.map_index == 0:
if not camera.available:
raise web.HTTPServiceUnavailable()

crop = request.query.get("crop")
box = request.query.get("box")
file = request.query.get("file")
Expand Down Expand Up @@ -210,20 +201,19 @@ class CameraHistoryView(CameraView):
async def handle(self, request: web.Request, camera: Camera) -> web.Response:
"""Serve camera cleaning history or cruising data."""
if not camera.map_data_json and camera.map_index == 0:
if not camera.available:
raise web.HTTPServiceUnavailable()

data = request.query.get("data")
data = data and (data == True or data == "true" or data == "1")
cruising = request.query.get("cruising")
resources = request.query.get("resources")
dirty = request.query.get("dirty")
info = request.query.get("info")
result = await camera.history_map_image(
request.query.get("index", 1),
not info or (info and (info == True or info == "true" or info == "1")),
cruising
and (cruising == True or cruising == "true" or cruising == "1"),
data,
dirty and (dirty == True or dirty == "true" or dirty == "1"),
data
and resources
and (resources == True or resources == "true" or resources == "1"),
Expand All @@ -245,9 +235,6 @@ class CameraRecoveryView(CameraView):
async def handle(self, request: web.Request, camera: Camera) -> web.Response:
"""Serve camera recovery map data."""
if not camera.map_data_json:
if not camera.available:
raise web.HTTPServiceUnavailable()

index = request.query.get("index", 1)
file = request.query.get("file")
file = file and (file == True or file == "true" or file == "1")
Expand Down Expand Up @@ -293,9 +280,6 @@ class CameraWifiView(CameraView):
async def handle(self, request: web.Request, camera: Camera) -> web.Response:
"""Serve camera wifi map data."""
if not camera.map_data_json:
if not camera.available:
raise web.HTTPServiceUnavailable()

data = request.query.get("data")
data = data and (data == True or data == "true" or data == "1")
resources = request.query.get("resources")
Expand Down Expand Up @@ -481,7 +465,6 @@ def __init__(
self._error = None
self._proxy_renderer = None

self._available = self.device.device_connected and self.device.cloud_connected
if description.map_type == DreameVacuumMapType.JSON_MAP_DATA:
self._renderer = DreameVacuumMapDataJsonRenderer()
self.content_type = JSON_CONTENT_TYPE
Expand Down Expand Up @@ -544,12 +527,11 @@ def _set_map_name(self, wifi_map) -> None:
@callback
def _handle_coordinator_update(self) -> None:
"""Fetch state from the device."""
self._available = self.device.cloud_connected
self._last_map_request = 0
map_data = self._map_data
if (
map_data
and self.available
and self.device.cloud_connected
and (self.map_index > 0 or self.device.status.located)
):
if self.map_index > 0:
Expand Down Expand Up @@ -591,7 +573,7 @@ async def async_camera_image(
now = time.time()
if now - self._last_map_request >= self.frame_interval:
self._last_map_request = now
if self.map_index == 0:
if self.map_index == 0 and self.device:
self.device.update_map()
self.update()
self._should_poll = True
Expand Down Expand Up @@ -664,7 +646,7 @@ def update(self) -> None:
map_data = self._map_data
if (
map_data
and self.available
and self.device.cloud_connected
and (self.map_index > 0 or self.device.status.located)
):
if (
Expand Down Expand Up @@ -736,14 +718,14 @@ async def obstacle_history_image(
return (None, None)

async def history_map_image(
self, index, info_text, cruising, data_string, include_resources
self, index, info_text, cruising, data_string, dirty_map, include_resources
):
if self.map_index == 0 and not self.map_data_json:
map_data = await self.hass.async_add_executor_job(
self.device.history_map, index, cruising
)
if map_data:
map_data = self.device.get_map_for_render(map_data)
map_data = self.device.get_map_for_render(map_data) if cruising or not dirty_map or map_data.cleaning_map_data is None else map_data.cleaning_map_data
if data_string:
return self._renderer.get_data_string(
map_data,
Expand All @@ -752,7 +734,7 @@ async def history_map_image(
else None,
)
return self._get_proxy_image(
index, map_data, info_text, "cruising" if cruising else "cleaning"
index, map_data, info_text, "cruising" if cruising else "dirty" if dirty_map else "cleaning"
)

async def recovery_map_file(self, index):
Expand Down Expand Up @@ -906,7 +888,7 @@ def _map_data(self) -> Any:

@property
def _default_map_image(self) -> Any:
if self._image and not self.available:
if self._image and not self.device.cloud_connected:
return self._renderer.disconnected_map_image
return self._renderer.default_map_image

Expand All @@ -926,7 +908,7 @@ def state(self) -> str:
@property
def available(self) -> bool:
"""Return True if entity is available."""
return self._available
return True

@property
def entity_picture(self) -> str:
Expand All @@ -937,9 +919,11 @@ def entity_picture(self) -> str:
@property
def extra_state_attributes(self) -> Dict[str, Any]:
if not self.map_data_json:
attributes = None
map_data = self._map_data
if (
map_data
and self.device.cloud_connected
and not map_data.empty_map
and (self.map_index > 0 or self.device.status.located)
):
Expand All @@ -952,7 +936,7 @@ def extra_state_attributes(self) -> Dict[str, Any]:
if self._calibration_points
else self._renderer.calibration_points
)
elif self.available:
elif self.device.cloud_connected:
attributes = {
ATTR_CALIBRATION: self._renderer.default_calibration_points
}
Expand All @@ -962,9 +946,8 @@ def extra_state_attributes(self) -> Dict[str, Any]:

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

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

if self.device.status._cleaning_history is not None:
cleaning_history = {}
Expand Down
4 changes: 2 additions & 2 deletions custom_components/dreame_vacuum/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@
CONSUMABLE_MAIN_BRUSH = "main_brush"
CONSUMABLE_SIDE_BRUSH = "side_brush"
CONSUMABLE_FILTER = "filter"
CONSUMABLE_SECONDARY_FILTER = "secondary_filter"
CONSUMABLE_TANK_FILTER = "tank_filter"
CONSUMABLE_SENSOR = "sensor"
CONSUMABLE_MOP_PAD = "mop_pad"
CONSUMABLE_SILVER_ION = "silver_ion"
Expand All @@ -158,7 +158,7 @@
NOTIFICATION_ID_REPLACE_MAIN_BRUSH: Final = "replace_main_brush"
NOTIFICATION_ID_REPLACE_SIDE_BRUSH: Final = "replace_side_brush"
NOTIFICATION_ID_REPLACE_FILTER: Final = "replace_filter"
NOTIFICATION_ID_REPLACE_SECONDARY_FILTER: Final = "replace_secondary_filter"
NOTIFICATION_ID_REPLACE_TANK_FILTER: Final = "replace_tank_filter"
NOTIFICATION_ID_CLEAN_SENSOR: Final = "clean_sensor"
NOTIFICATION_ID_REPLACE_MOP: Final = "replace_mop"
NOTIFICATION_ID_SILVER_ION: Final = "silver_ion"
Expand Down
21 changes: 11 additions & 10 deletions custom_components/dreame_vacuum/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
NOTIFICATION_ID_REPLACE_MAIN_BRUSH,
NOTIFICATION_ID_REPLACE_SIDE_BRUSH,
NOTIFICATION_ID_REPLACE_FILTER,
NOTIFICATION_ID_REPLACE_SECONDARY_FILTER,
NOTIFICATION_ID_REPLACE_TANK_FILTER,
NOTIFICATION_ID_CLEAN_SENSOR,
NOTIFICATION_ID_REPLACE_MOP,
NOTIFICATION_ID_SILVER_ION,
Expand Down Expand Up @@ -75,7 +75,7 @@
CONSUMABLE_MAIN_BRUSH,
CONSUMABLE_SIDE_BRUSH,
CONSUMABLE_FILTER,
CONSUMABLE_SECONDARY_FILTER,
CONSUMABLE_TANK_FILTER,
CONSUMABLE_SENSOR,
CONSUMABLE_MOP_PAD,
CONSUMABLE_SILVER_ION,
Expand Down Expand Up @@ -354,15 +354,16 @@ def _check_consumables(self):
DreameVacuumProperty.FILTER_LEFT,
)
self._check_consumable(
CONSUMABLE_SECONDARY_FILTER,
NOTIFICATION_ID_REPLACE_SECONDARY_FILTER,
DreameVacuumProperty.SECONDARY_FILTER_LEFT,
)
self._check_consumable(
CONSUMABLE_SENSOR,
NOTIFICATION_ID_CLEAN_SENSOR,
DreameVacuumProperty.SENSOR_DIRTY_LEFT,
CONSUMABLE_TANK_FILTER,
NOTIFICATION_ID_REPLACE_TANK_FILTER,
DreameVacuumProperty.TANK_FILTER_LEFT,
)
if self.device.capability.sensor_cleaning:
self._check_consumable(
CONSUMABLE_SENSOR,
NOTIFICATION_ID_CLEAN_SENSOR,
DreameVacuumProperty.SENSOR_DIRTY_LEFT,
)
self._check_consumable(
CONSUMABLE_MOP_PAD,
NOTIFICATION_ID_REPLACE_MOP,
Expand Down
3 changes: 3 additions & 0 deletions custom_components/dreame_vacuum/dreame/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
DreameVacuumMopPadSwing,
DreameVacuumSecondCleaning,
DreameVacuumCleaningRoute,
DreameVacuumCustomMoppingRoute,
DreameVacuumSelfCleanFrequency,
DreameVacuumAutoEmptyMode,
DreameVacuumCleanGenius,
Expand All @@ -48,6 +49,8 @@
ACTION_TO_NAME,
SUCTION_LEVEL_QUIET,
STATUS_CODE_TO_NAME,
CUSTOM_MOPPING_ROUTE_TO_NAME,
CLEANING_ROUTE_TO_NAME,
)
from .device import DreameVacuumDevice
from .protocol import DreameVacuumProtocol
Expand Down
Loading

0 comments on commit 2a20251

Please sign in to comment.