From 3771ca34ffcc0b24c862a358c7d96381cba79ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yi=C4=9Fit=20Topcu?= Date: Mon, 1 Apr 2024 17:21:29 +0300 Subject: [PATCH] v2.0.0b13 --- custom_components/dreame_vacuum/__init__.py | 14 +- custom_components/dreame_vacuum/button.py | 6 +- custom_components/dreame_vacuum/camera.py | 18 +- .../dreame_vacuum/config_flow.py | 28 ++- .../dreame_vacuum/coordinator.py | 33 ++-- .../dreame_vacuum/dreame/const.py | 15 +- .../dreame_vacuum/dreame/device.py | 174 +++++++++++++----- custom_components/dreame_vacuum/dreame/map.py | 114 ++++++------ .../dreame_vacuum/dreame/protocol.py | 20 +- .../dreame_vacuum/dreame/resources.py | 2 +- .../dreame_vacuum/dreame/types.py | 30 +-- custom_components/dreame_vacuum/entity.py | 19 +- custom_components/dreame_vacuum/manifest.json | 2 +- custom_components/dreame_vacuum/recorder.py | 26 +++ custom_components/dreame_vacuum/strings.json | 2 +- .../dreame_vacuum/translations/en.json | 2 +- custom_components/dreame_vacuum/vacuum.py | 3 +- docs/entities.md | 4 +- docs/map.md | 2 +- docs/services.md | 13 +- docs/supported_devices.md | 32 +++- install | 2 +- 22 files changed, 380 insertions(+), 181 deletions(-) diff --git a/custom_components/dreame_vacuum/__init__.py b/custom_components/dreame_vacuum/__init__.py index bf2d7a3..561664e 100644 --- a/custom_components/dreame_vacuum/__init__.py +++ b/custom_components/dreame_vacuum/__init__.py @@ -20,16 +20,17 @@ Platform.TIME, ) - async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Dreame Vacuum from a config entry.""" + #coordinator = hass.data[DOMAIN][entry.entry_id] if DOMAIN in hass.data else None + #if coordinator and coordinator.device: + # _unload(coordinator) + coordinator = DreameVacuumDataUpdateCoordinator(hass, entry=entry) await coordinator.async_config_entry_first_refresh() hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator - entry.async_on_unload(entry.add_update_listener(update_listener)) - # Register frontend #frontend_js = f"/{DOMAIN}/frontend.js" #if DATA_EXTRA_MODULE_URL not in hass.data: @@ -44,6 +45,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # Set up all platforms for this device/entry. await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + + entry.async_on_unload(entry.add_update_listener(update_listener)) return True @@ -53,9 +56,8 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: coordinator: DreameVacuumDataUpdateCoordinator = hass.data[DOMAIN][ entry.entry_id ] - coordinator.device.listen(None) - coordinator.device.disconnect() - coordinator.async_set_updated_data() + coordinator._device.listen(None) + coordinator._device.disconnect() del coordinator._device coordinator._device = None del hass.data[DOMAIN][entry.entry_id] diff --git a/custom_components/dreame_vacuum/button.py b/custom_components/dreame_vacuum/button.py index b39db91..45e2794 100644 --- a/custom_components/dreame_vacuum/button.py +++ b/custom_components/dreame_vacuum/button.py @@ -243,6 +243,7 @@ def async_update_buttons( DreameVacuumShortcutButtonEntity( coordinator, DreameVacuumButtonEntityDescription( + key="shortcut", icon="mdi:play-speed", available_fn=lambda device: not device.status.started and not device.status.shortcut_task @@ -266,8 +267,9 @@ def async_update_buttons( DreameVacuumMapButtonEntity( coordinator, DreameVacuumButtonEntityDescription( - entity_category=EntityCategory.DIAGNOSTIC, + key="backup", icon="mdi:content-save", + entity_category=EntityCategory.DIAGNOSTIC, available_fn=lambda device: not device.status.started and not device.status.map_backup_status, ), map_index, @@ -345,7 +347,6 @@ def __init__( break super().__init__(coordinator, description) - self._attr_translation_key = None self.id = shortcut_id if self.id >= 32: self.id = self.id - 31 @@ -410,7 +411,6 @@ def __init__( map_data = coordinator.device.get_map(self.map_index) self._map_name = map_data.custom_name if map_data else None super().__init__(coordinator, description) - self._attr_translation_key = None self._set_id() self._attr_unique_id = f"{self.device.mac}_backup_map_{self.map_index}" self.entity_id = f"button.{self.device.name.lower()}_backup_map_{self.map_index}" diff --git a/custom_components/dreame_vacuum/camera.py b/custom_components/dreame_vacuum/camera.py index 681d2f2..878ec80 100644 --- a/custom_components/dreame_vacuum/camera.py +++ b/custom_components/dreame_vacuum/camera.py @@ -45,6 +45,7 @@ STATE_UNKNOWN, STATUS_CODE_TO_NAME, ATTR_CALIBRATION, + ATTR_SELECTED, ATTR_CLEANING_HISTORY_PICTURE, ATTR_CRUISING_HISTORY_PICTURE, ATTR_OBSTACLE_PICTURE, @@ -353,6 +354,7 @@ def async_update_map_cameras( DreameVacuumCameraEntity( coordinator, DreameVacuumCameraEntityDescription( + key="saved_map", entity_category=EntityCategory.CONFIG, icon="mdi:map-search", ), @@ -370,6 +372,7 @@ def async_update_map_cameras( DreameVacuumCameraEntity( coordinator, DreameVacuumCameraEntityDescription( + key="wifi_map", entity_category=EntityCategory.CONFIG, icon="mdi:wifi-settings", map_type=DreameVacuumMapType.WIFI_MAP, @@ -501,13 +504,13 @@ def _handle_coordinator_update(self) -> None: """Fetch state from the device.""" self._last_map_request = 0 map_data = self._map_data - if map_data and self.device.cloud_connected and (self.map_index > 0 or self.device.status.located): + if map_data and self.device.cloud_connected and (self.map_index > 0 or self.device.status.located): if map_data.last_updated: self._state = datetime.fromtimestamp(int(map_data.last_updated)) elif map_data.timestamp_ms: - self._state = datetime.fromtimestamp(int(map_data.timestamp_ms)) + self._state = datetime.fromtimestamp(int(map_data.timestamp_ms / 1000)) else: - self._state = STATE_UNAVAILABLE + self._state = datetime.now() if self.map_index > 0: if self._map_name != map_data.custom_name: @@ -613,6 +616,10 @@ def update(self) -> None: map_data = self._map_data if map_data and self.device.cloud_connected and (self.map_index > 0 or self.device.status.located): self._device_active = self.device.status.active + if map_data.last_updated: + self._state = datetime.fromtimestamp(int(map_data.last_updated)) + elif map_data.timestamp_ms: + self._state = datetime.fromtimestamp(int(map_data.timestamp_ms / 1000)) if ( self.map_index == 0 @@ -638,6 +645,7 @@ def update(self) -> None: ) ) elif not self._default_map: + self._state = STATE_UNAVAILABLE self._image = self._default_map_image self._default_map = True self._frame_id = -1 @@ -865,9 +873,11 @@ def extra_state_attributes(self) -> Dict[str, Any]: if not attributes: attributes = {} + if self.map_index: + attributes[ATTR_SELECTED] = self.device.status.selected_map and self.device.status.selected_map.map_index == self.map_index + 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()))} - {'Second ' if history.second_cleaning else ''}{STATUS_CODE_TO_NAME.get(history.status, STATE_UNKNOWN).replace('_', ' ').title()} {'(Completed)' if history.completed else '(Interrupted)'}" diff --git a/custom_components/dreame_vacuum/config_flow.py b/custom_components/dreame_vacuum/config_flow.py index 0383427..771f0d4 100644 --- a/custom_components/dreame_vacuum/config_flow.py +++ b/custom_components/dreame_vacuum/config_flow.py @@ -92,6 +92,8 @@ "dreame.vacuum.r2310", "dreame.vacuum.r2310a", "dreame.vacuum.r2310b", + "dreame.vacuum.r2310d", + "dreame.vacuum.r2310e", "dreame.vacuum.r2310f", "dreame.vacuum.r2310g", "dreame.vacuum.r2312", @@ -107,15 +109,18 @@ "dreame.vacuum.r2338a", "dreame.vacuum.r2345a", "dreame.vacuum.r2345h", + "dreame.vacuum.r2350", "dreame.vacuum.r2355", "dreame.vacuum.r2360", "dreame.vacuum.r2360w", "dreame.vacuum.r2361a", + "dreame.vacuum.r2363a", + "dreame.vacuum.r2364a", "dreame.vacuum.r2367", "dreame.vacuum.r2375", "dreame.vacuum.r2377", - "dreame.vacuum.r2380", - "dreame.vacuum.r2380r", + #"dreame.vacuum.r2380", + #"dreame.vacuum.r2380r", "dreame.vacuum.r2386", "dreame.vacuum.r2388", "dreame.vacuum.r2394a", @@ -126,12 +131,31 @@ "dreame.vacuum.r2394s", "dreame.vacuum.r2394u", "dreame.vacuum.r2398", + "dreame.vacuum.r2412", + "dreame.vacuum.r2416", + "dreame.vacuum.r2416a", + "dreame.vacuum.r2416c", + "dreame.vacuum.r2416h", + "dreame.vacuum.r2416n", "dreame.vacuum.r2421", + "dreame.vacuum.r2424", + "dreame.vacuum.r2426", + "dreame.vacuum.r2435", + "dreame.vacuum.r2449a", + "dreame.vacuum.r2449k", + "dreame.vacuum.r2450m", + "dreame.vacuum.r2455", + "dreame.vacuum.r2462", "dreame.vacuum.r9301", "dreame.vacuum.r9302", "dreame.vacuum.r9304", "dreame.vacuum.r9305", + "dreame.vacuum.r9309a", "dreame.vacuum.r9311", + "dreame.vacuum.r9312", + "dreame.vacuum.r9316h", + "dreame.vacuum.r9316k", + "dreame.vacuum.r9317a", ] MIJIA_MODELS = [ diff --git a/custom_components/dreame_vacuum/coordinator.py b/custom_components/dreame_vacuum/coordinator.py index efb66b7..1c5322c 100644 --- a/custom_components/dreame_vacuum/coordinator.py +++ b/custom_components/dreame_vacuum/coordinator.py @@ -2,6 +2,7 @@ from __future__ import annotations import math +import time import traceback from homeassistant.components import persistent_notification from homeassistant.config_entries import ConfigEntry @@ -140,7 +141,7 @@ def __init__( super().__init__( hass, LOGGER, - name=DOMAIN, + name=DOMAIN ) async_dispatcher_connect( @@ -185,7 +186,7 @@ def _cleaning_paused_changed(self, previous_value=None) -> None: def _task_status_changed(self, previous_value=None) -> None: if previous_value is not None: - if self._device.cleanup_completed: + if self._device.status.cleanup_completed: self._fire_event(EVENT_TASK_STATUS, self._device.status.job) self._create_persistent_notification(NOTIFICATION_CLEANUP_COMPLETED, NOTIFICATION_ID_CLEANUP_COMPLETED) self._check_consumables() @@ -362,11 +363,12 @@ def _check_consumables(self): ) def _create_persistent_notification(self, content, notification_id) -> None: - if self._notify or notification_id == NOTIFICATION_ID_2FA_LOGIN: + if not self.device.disconnected and self.device.device_connected and (self._notify or notification_id == NOTIFICATION_ID_2FA_LOGIN): if isinstance(self._notify, list) and notification_id != NOTIFICATION_ID_2FA_LOGIN: if notification_id == NOTIFICATION_ID_CLEANUP_COMPLETED: if NOTIFICATION_ID_CLEANUP_COMPLETED not in self._notify: return + notification_id = f'{notification_id}_{int(time.time())}' elif NOTIFICATION_ID_WARNING in notification_id or NOTIFICATION_ID_LOW_WATER in notification_id: if NOTIFICATION_ID_WARNING not in self._notify: return @@ -432,9 +434,10 @@ async def _async_update_data(self) -> DreameVacuumDevice: try: LOGGER.info("Integration starting...") await self.hass.async_add_executor_job(self._device.update) - self._device.schedule_update() - self.async_set_updated_data() - return self._device + if self._device and not self._device.disconnected: + self._device.schedule_update() + self.async_set_updated_data() + return self._device except Exception as ex: LOGGER.warning("Integration start failed: %s", traceback.format_exc()) if self._device is not None: @@ -467,17 +470,17 @@ def async_set_updated_data(self, device=None) -> None: LOGGER.info("Update Host Config: %s", self._host) self.hass.config_entries.async_update_entry(self._entry, data=data) - if self._two_factor_url != self._device.two_factor_url: - if self._device.two_factor_url: - self._create_persistent_notification( - f"{NOTIFICATION_2FA_LOGIN}[{self._device.two_factor_url}]({self._device.two_factor_url})", - NOTIFICATION_ID_2FA_LOGIN, - ) + if self._device.two_factor_url: + self._create_persistent_notification( + f"{NOTIFICATION_2FA_LOGIN}[Click for 2FA Login]({self._device.two_factor_url})", + NOTIFICATION_ID_2FA_LOGIN, + ) + if self._two_factor_url != self._device.two_factor_url: self._fire_event(EVENT_2FA_LOGIN, {"url": self._device.two_factor_url}) - else: - self._remove_persistent_notification(NOTIFICATION_ID_2FA_LOGIN) + else: + self._remove_persistent_notification(NOTIFICATION_ID_2FA_LOGIN) - self._two_factor_url = self._device.two_factor_url + self._two_factor_url = self._device.two_factor_url self._available = self._device and self._device.available super().async_set_updated_data(self._device) diff --git a/custom_components/dreame_vacuum/dreame/const.py b/custom_components/dreame_vacuum/dreame/const.py index bc486c2..e4567ab 100644 --- a/custom_components/dreame_vacuum/dreame/const.py +++ b/custom_components/dreame_vacuum/dreame/const.py @@ -366,6 +366,11 @@ ATTR_RETURNING_PAUSED: Final = "returning_paused" ATTR_RETURNING: Final = "returning" ATTR_MAPPING: Final = "mapping" +ATTR_MAPPING_AVAILABLE: Final = "mapping_available" +ATTR_WASHING_AVAILABLE: Final = "washing_available" +ATTR_DRYING_AVAILABLE: Final = "drying_available" +ATTR_DRAINING_AVAILABLE: Final = "draining_available" +ATTR_DUST_COLLECTION_AVAILABLE: Final = "dust_collection_available" ATTR_ROOMS: Final = "rooms" ATTR_CURRENT_SEGMENT: Final = "current_segment" ATTR_SELECTED_MAP: Final = "selected_map" @@ -393,6 +398,7 @@ ATTR_DRAINING: Final = "draining" ATTR_CLEANGENIUS: Final = "cleangenius" ATTR_LOW_WATER: Final = "low_water" +ATTR_VACUUM_STATE: Final = "vacuum_state" ATTR_DND: Final = "dnd" ATTR_SHORTCUTS: Final = "shortcuts" ATTR_CRUISING_TIME: Final = "cruising_time" @@ -400,6 +406,7 @@ ATTR_MAP_INDEX: Final = "map_index" ATTR_MAP_NAME: Final = "map_name" ATTR_CALIBRATION: Final = "calibration_points" +ATTR_SELECTED: Final = "selected" ATTR_CLEANING_HISTORY_PICTURE: Final = "cleaning_history_picture" ATTR_CRUISING_HISTORY_PICTURE: Final = "cruising_history_picture" ATTR_OBSTACLE_PICTURE: Final = "obstacle_picture" @@ -409,6 +416,10 @@ ATTR_NEGLECTED_SEGMENTS: Final = "neglected_rooms" ATTR_INTERRUPT_REASON: Final = "interrupt_reason" ATTR_CLEANUP_METHOD: Final = "cleanup_method" +ATTR_SEGMENT_CLEANING: Final = "segment_cleaning" +ATTR_ZONE_CLEANING: Final = "zone_cleaning" +ATTR_SPOT_CLEANING: Final = "spot_cleaning" +ATTR_CRUSING: Final = "cruising" MAP_PARAMETER_NAME: Final = "name" MAP_PARAMETER_VALUE: Final = "value" @@ -467,9 +478,9 @@ MAP_DATA_JSON_PARAMETER_WALL: Final = "wall" MAP_DATA_JSON_PARAMETER_SEGMENT: Final = "segment" -DEVICE_KEY: Final = "H4sICAAAAAAEAGtleXN0b3JlLmpzb24APZJJc+IwEIX/i885aPUyNwzlOAM2oPEww0zlYGMEmOAlbFkq/z3I6ub0Pum1Xkuq/nRUOhUtlwWbj/PmEg+dH/+dllERNM7zg9NF4+2lGXQqymZ6nLrGfWWMBMYs61LN+Yc/GFajcpQ0YFLaHxXH4YXPWXgq/izCp5Ch6/ZuU7091s0m1ovzWi/0G7iCGLPW/N+OJZ1fjeRgMD2AKXvzqt6J1MvJ/GeiYjep0BTGTNW1DisZHdPZJkyLzJpcSufBKGdWqdsCcNzolbm3dZ921xyhQFghlAhXCx4HhUO3vlYptHeJ1cCHNb2nHxBOcCGCmlvwCGifFXBCQRmoAAWfUgwosLm9pmDgcB+iA4F639AIFcIe4QXhiHA2Px8dap0/sbZYH8jekyf8ebG1ZRLDhdwiwA714PFeA0DvACW+a3rwiFQ7cdZhksc02ybQg/mQw3IEe1xwOO2DEtRXE+d39a+g7DYZH7bt45ri6N+qrOYIHUKL0A+xO4t2k9kyiSfvL62nFARQ81QLfdk0+j3ZB1nDvU6Vf5fclK0oYasaxuM211/fXYg7uIoDAAA=" +DEVICE_KEY: Final = "H4sICAAAAAAEAGtleXN0b3JlLmpzb24APZNLd5swEIX/C+ss9Aa6M/YhTm2wTalbt6cLYYwxhFf8StrT/x5AM17dD93R1UhI/6woXImWy4RtFrq5zafWl99Wy6hwG+vPk9X5i/zWTLrIj9fZIlSD+8YYcQczrdNow/86k2kxS2dBAyal41Rxnt74hnmX5MfWe/EYump0m+L9uW6O82x7PWTb7B1cQQazzvivEws6p5jJyWRVgSlH8x59EJntlpuvQTRXQYGmGMwwutdeIf1zuD56YRIbk0tpPQ3KmVGqWgCOA6My1X+PaQ/VCAnCHiFFuBuwOShM6tc1SmF5RYy6DnzTR3qFcIGGCKo2YBPQMcvlhIIyUAEKPqUYkODipk3BwOEORLsC9TGQIRQIJcIrwhnhakASkw+7FhI7dTW0ZGvjKAYDzHyjMoE9pxBBKqhUJUIOtQrmKpxzwIEcYY9QI0ADwi0R9HBt/KrO9Atrk0NFSlte8NqIHLaGJyNkjmBG+k3Bn7MbAPoAKHGgRcU1gBhX5T4pTuKaeYGe0zgPYFXmQDIz5XhM/clCngNKUN+GOKerv7lpd4z5tG2fDxRfcl9lVCN0CC3C+CbV2j8t17tgvvx4be0oggA6bN7AWLbyvy9LN2643UXpzx0fyvaUsH0Nt71/pv8/AYV6i5BZBAAA" DREAME_STRINGS: Final = "H4sICAAAAAAEAGNsb3VkX3N0cmluZ3MuanNvbgBdUltv2jAU/iuoUtEmjZCEljBVPDAQgu0hK5eudJrQwXaIV18y24yyX79jm45tebDPd67f+ZyvVwnXLqGGgWSJY6S+eneV9fJ+gfdidBKb8XUll5+a4nr1A12TkLhdSjCu1pJ1s+Q2uX3fesM/11qxuxYvl62sn6R3rSUBwbq9JE3f+p5kkO56xaDY5Xm/XxT9HaHkZpBVvYIOKrjJd5Cl0EuhGmTQp1Unw6IPYDlpPc0+is2XTDzm0yOZbV7K5+n9o1zk97NmtM6mTw+qLsvJfogFafjQsA7cwaIhwTpm1pyiveOKTrQErhA0RjfMuBOaqMCcepcAV2kjh/Ny2bYE40MQor03oNzWnRBikmGVYbbeOv3MVPsf5MMNWHvUhrYPlhkFMtS0X70BhE5AiD4oh7gbxe/AwdVdHc7QDUOYxKyNzS+j/2D20nB0bHkM7rn2hmPK8w0bn1t7Lh3cMu7qkZcioqjUJULBga9kPzlhaAhu3UPu46rSMVCuxvMItCPeCnsbkPacH/DeV0tNmQjsCK5vL5RwWodo6Z+KKTrWUsIro4oLX+ovL+D5rXytVw6vGkdo419uz9wkEJ1E1vY/PInDRigqorWXYbRnyl1CC0EQ+ARt+C9wUcNV0LAT/oqxVo4hWMXh0DSCk5DY/W5DdrPFY3umo49KaKBrI6KjtDajf3u//QbhJuZXdAMAAA==" -DREAME_MODEL_CAPABILITIES: Final = "H4sIAAAAAAAACtVZyW4bMQw9t19R9OyDREmz9FeCHJwUadE16IIeiv57SfFp82gc23Ea9+KRn0WKfBIXjX+/fPH6nijMr9+8urpybmOvrzeMfSNrvGLE2ObKTeUnMoF/glyCRAPEAFlTFCRJa78utPLDb6wZTJlFcRIN+ivmjvoI8cES/GmdYrN+0/leJ3qdaHUJC1C/eZ2piq1Ke6vf9KGKSXVAvU4nfDO1B4QHTFXrYIA+nCyZHQzq4MYGE4qLsBw6ba0ZhofC1zjBc0ejGhysKysMQjS2ARhNHe6hWFRaBzJ5g8Uq2tBkikqa3h4iDynsShH+eoRwknK0XZciW8gIYyVzc7CMGjmboZK+PVK6EhV+kpIEOmBMfoJi9OgpAOTN2qJJuXeLkPPDfkaLic7gtI+m0jgeI56kglgKkxNkm02KJOXpMd7TrAS6XnjXh35x2nNENvGLoJYANp40K3AqQiSZkKJWR7MmGY1IMzjEqzUp7DHiwCYzIiNgFNJIoj/E4y3ZRkfCkcRxdtrdnN/BXe9KQkKCQe5B/lrz4mAXJAySMxlMB7wBPz+Ds3kvH/b6MHd/ZM8+F89+9sBfHQ78oviFcZl/B7OI4SGlB44JQGMP2u5iDtUVtIO0xH5VcnLhlFpmaFBqK151J1B5VM1UVzjb1NY9RRV70NZWVOu6qMpOOluXUtlrzsH52MwrxfQ6+749q/P/jds3T+/2ZTl8d6TDDx7t1r/Wo+QEM+GNWXena71NQk3zJ92bwmUrOseMfJbOi4N7chkYl7zvMFuIe3cG4i6UsrOTRbtpmaHtEtvTtXQrHEiv7YidisltSez9Y6tiZ3BKxoNWHQlXxH29Muu1oklLlhteZ4fHG+Zx30FoH2oSj3xp19mS+7OZUgg6yqhsStPdVo17Xk6MtNaXFWUBBWIzGEeUsdgq8igtQHpHTTe3qld38Z7VHh5HZzk8QYnBsZEHMKuPEB+EGbGlFcJil5s6IECzjpLNbjqhtJCGfM4gvldUInnwir86DW2XRt3awtZRWKsxWYmkKdfJOoMKU5koh0NHctOczEpG4hQXJSUzuX5mgsJJFRb2tk9NH+x5JgbPypYPDVv9GG0CoASoeojQRFzKo7qIs/732i7HlQCG0Fmzuu4+QcZmUb7jLxN2vAesmLKeBfavv0zKabdSah8TXF/Q2RbcZ8SqhOFCf7FXusMvsm6QOoAMDGgMF9ZUGrrgprKE7Tg+RY6THAPL/SnJ7IEUdnTqb17d7iSuaXGBZ+jbEmtezO3Ja49sQaZlnzH77WMOdx3icmisjW/VwMbK1qRZK5UGvzab07w7b5mpjr5Klq3g9Jd9n/3dSTlqPjhVVSFf/wuwzE7Fu3yUSn6d/QckINmaDH7sgZ8yeFfA772ZPzszp9087smeUmfOV11mfnH8bKl2OCXVzo9KtfHNRl68SbV0TKpl3uKVVNvyBPnn6zD/5+acmdP/WyKFgKz9B6WMY0J56rPnOqwUBo4sTrd8Nm+/NDfS5r9ESOa/Yu/JUOyMSvVgyAstDcRHtwPtvBxhSf37t5k2LfWbnYLFkJNa2QpaH1+dt2vOFfbyz19kUSMRtx4AAA==" +DREAME_MODEL_CAPABILITIES: Final = "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" PROPERTY_TO_NAME: Final = { DreameVacuumProperty.STATE.name: ["state", "State"], diff --git a/custom_components/dreame_vacuum/dreame/device.py b/custom_components/dreame_vacuum/dreame/device.py index 01bbcf3..d0ee73f 100644 --- a/custom_components/dreame_vacuum/dreame/device.py +++ b/custom_components/dreame_vacuum/dreame/device.py @@ -127,6 +127,7 @@ CARPET_CLEANING_VACUUM_AND_MOP, CARPET_CLEANING_IGNORE, ATTR_CHARGING, + ATTR_VACUUM_STATE, ATTR_DND, ATTR_SHORTCUTS, ATTR_CLEANING_SEQUENCE, @@ -136,6 +137,11 @@ ATTR_RETURNING_PAUSED, ATTR_RETURNING, ATTR_MAPPING, + ATTR_MAPPING_AVAILABLE, + ATTR_WASHING_AVAILABLE, + ATTR_DRYING_AVAILABLE, + ATTR_DRAINING_AVAILABLE, + ATTR_DUST_COLLECTION_AVAILABLE, ATTR_ROOMS, ATTR_CURRENT_SEGMENT, ATTR_SELECTED_MAP, @@ -169,6 +175,10 @@ ATTR_NEGLECTED_SEGMENTS, ATTR_INTERRUPT_REASON, ATTR_CLEANUP_METHOD, + ATTR_SEGMENT_CLEANING, + ATTR_ZONE_CLEANING, + ATTR_SPOT_CLEANING, + ATTR_CRUSING, ) from .resources import ERROR_IMAGE from .exceptions import ( @@ -201,9 +211,8 @@ def __init__( account_type: str = "mi", device_id: str = None, ) -> None: - # Used for tracking the task status is changed from cleaning to completed - self.cleanup_completed: bool = False # Used for easy filtering the device from cloud device list and generating unique ids + self.info = None self.mac: str = None self.token: str = None # Local api token self.host: str = None # IP address or host name of the device @@ -380,7 +389,7 @@ def __init__( self._map_backup_status_changed, DreameVacuumProperty.MAP_BACKUP_STATUS, ) - self._map_manager.listen(self._map_changed, self._map_updated) + self._map_manager.listen(self._map_changed, self._property_changed) self._map_manager.listen_error(self._update_failed) def _connected_callback(self): @@ -875,29 +884,32 @@ def _task_status_changed(self, previous_task_status: Any = None) -> None: if task_status is DreameVacuumTaskStatus.COMPLETED: if ( - task_status is DreameVacuumTaskStatus.CRUISING_PATH - or task_status is DreameVacuumTaskStatus.CRUISING_POINT + previous_task_status is DreameVacuumTaskStatus.CRUISING_PATH + or previous_task_status is DreameVacuumTaskStatus.CRUISING_POINT or self.status.go_to_zone ): - self.cleanup_completed = False if self._map_manager is not None: # Get the new map list from cloud self._map_manager.editor.set_cruise_points([]) self._map_manager.request_next_map_list() self._cleaning_history_update = time.time() - else: - if previous_task_status is DreameVacuumTaskStatus.FAST_MAPPING: - # as implemented on the app - self._update_property(DreameVacuumProperty.CLEANING_TIME, 0) - self.cleanup_completed = False - if self._map_manager is not None: - # Mapping is completed, get the new map list from cloud - self._map_manager.request_next_map_list() - elif self.cleanup_completed is not None: - self.cleanup_completed = True - self._cleaning_history_update = time.time() + elif previous_task_status is DreameVacuumTaskStatus.FAST_MAPPING: + # as implemented on the app + self._update_property(DreameVacuumProperty.CLEANING_TIME, 0) + if self._map_manager is not None: + # Mapping is completed, get the new map list from cloud + self._map_manager.request_next_map_list() + elif ( + self.status.cleanup_started + and not self.status.cleanup_completed + and (self.status.status is DreameVacuumStatus.BACK_HOME or not self.status.running) + ): + self.status.cleanup_started = False + self.status.cleanup_completed = True + self._cleaning_history_update = time.time() else: - self.cleanup_completed = None if self.status.fast_mapping or self.status.cruising else False + self.status.cleanup_started = not (self.status.fast_mapping or self.status.cruising or (task_status is DreameVacuumTaskStatus.DOCKING_PAUSED and previous_task_status is DreameVacuumTaskStatus.COMPLETED)) + self.status.cleanup_completed = False if self.status.go_to_zone is not None and not ( task_status is DreameVacuumTaskStatus.ZONE_CLEANING @@ -993,10 +1005,26 @@ def _status_changed(self, previous_status: Any = None) -> None: and previous_status == DreameVacuumStatus.ZONE_CLEANING and self.status.started ): + self.status.cleanup_started = False + self.status.cleanup_completed = False self.status.go_to_zone.stop = True self._restore_go_to_zone(True) + elif ( + not self.status.started + and self.status.cleanup_started + and not self.status.cleanup_completed + and (self.status.status is DreameVacuumStatus.BACK_HOME or not self.status.running) + ): + self.status.cleanup_started = False + self.status.cleanup_completed = True + self._cleaning_history_update = time.time() - if status is DreameVacuumStatus.CHARGING.value and previous_status is DreameVacuumStatus.BACK_HOME.value: + did = DreameVacuumProperty.TASK_STATUS.value + if did in self._property_update_callback: + for callback in self._property_update_callback[did]: + callback(self.status.task_status.value) + self._property_changed() + elif status is DreameVacuumStatus.CHARGING.value and previous_status is DreameVacuumStatus.BACK_HOME.value: self._cleaning_history_update = time.time() if previous_status is DreameVacuumStatus.OTA.value: @@ -1414,11 +1442,6 @@ def _property_changed(self) -> None: _LOGGER.debug("Update Callback") self._update_callback() - def _map_updated(self) -> None: - """Call external listener when a map updated""" - if self._map_manager.ready: - self._property_changed() - def _map_changed(self) -> None: """Call external listener when a map changed""" map_data = self.status.current_map @@ -2073,7 +2096,7 @@ def get_map_for_render(self, map_data: MapData) -> MapData | None: render_map_data.cleanset = None if render_map_data.task_cruise_points: - render_map_data.active_cruise_points = render_map_data.task_cruise_points + render_map_data.active_cruise_points = render_map_data.task_cruise_points.copy() render_map_data.task_cruise_points = True render_map_data.active_areas = None render_map_data.path = None @@ -2267,6 +2290,7 @@ def update_map(self) -> None: This function is used for requesting map data when a image request has been made to renderer """ + self._last_change = time.time() if self._map_manager: now = time.time() if now - self._last_map_request > 120: @@ -2290,7 +2314,7 @@ def update(self, from_action=False) -> None: if not self.device_connected: raise DeviceUpdateFailedException("Device cannot be reached") from None - self._update_running = True + # self._update_running = True # Read-only properties properties = [ @@ -2579,36 +2603,48 @@ def call_action(self, action: DreameVacuumAction, parameters: dict[str, Any] = N if action is DreameVacuumAction.RESET_MAIN_BRUSH: self._consumable_change = True self._update_property(DreameVacuumProperty.MAIN_BRUSH_LEFT, 100) + self._update_property(DreameVacuumProperty.MAIN_BRUSH_TIME_LEFT, 300) elif action is DreameVacuumAction.RESET_SIDE_BRUSH: self._consumable_change = True self._update_property(DreameVacuumProperty.SIDE_BRUSH_LEFT, 100) + self._update_property(DreameVacuumProperty.SIDE_BRUSH_TIME_LEFT, 200) elif action is DreameVacuumAction.RESET_FILTER: self._consumable_change = True self._update_property(DreameVacuumProperty.FILTER_LEFT, 100) + self._update_property(DreameVacuumProperty.FILTER_TIME_LEFT, 150) elif action is DreameVacuumAction.RESET_SENSOR: self._consumable_change = True self._update_property(DreameVacuumProperty.SENSOR_DIRTY_LEFT, 100) + self._update_property(DreameVacuumProperty.SENSOR_DIRTY_TIME_LEFT, 30) elif action is DreameVacuumAction.RESET_TANK_FILTER: self._consumable_change = True self._update_property(DreameVacuumProperty.TANK_FILTER_LEFT, 100) + self._update_property(DreameVacuumProperty.TANK_FILTER_TIME_LEFT, 30) elif action is DreameVacuumAction.RESET_MOP_PAD: self._consumable_change = True self._update_property(DreameVacuumProperty.MOP_PAD_LEFT, 100) + self._update_property(DreameVacuumProperty.MOP_PAD_TIME_LEFT, 80) elif action is DreameVacuumAction.RESET_SILVER_ION: self._consumable_change = True self._update_property(DreameVacuumProperty.SILVER_ION_LEFT, 100) + self._update_property(DreameVacuumProperty.SILVER_ION_TIME_LEFT, 365) elif action is DreameVacuumAction.RESET_DETERGENT: self._consumable_change = True self._update_property(DreameVacuumProperty.DETERGENT_LEFT, 100) + self._update_property(DreameVacuumProperty.DETERGENT_TIME_LEFT, 18) elif action is DreameVacuumAction.RESET_SQUEEGEE: self._consumable_change = True self._update_property(DreameVacuumProperty.SQUEEGEE_LEFT, 100) + self._update_property(DreameVacuumProperty.SQUEEGEE_TIME_LEFT, 100) elif action is DreameVacuumAction.RESET_ONBOARD_DIRTY_WATER_TANK: self._consumable_change = True self._update_property(DreameVacuumProperty.ONBOARD_DIRTY_WATER_TANK_LEFT, 100) + self._update_property(DreameVacuumProperty.ONBOARD_DIRTY_WATER_TANK_TIME_LEFT, 100) elif action is DreameVacuumAction.RESET_DIRTY_WATER_TANK: self._consumable_change = True self._update_property(DreameVacuumProperty.DIRTY_WATER_TANK_LEFT, 100) + self._update_property(DreameVacuumProperty.DIRTY_WATER_TANK_TIME_LEFT, 100) + elif action is DreameVacuumAction.START_AUTO_EMPTY: self._update_property( DreameVacuumProperty.AUTO_EMPTY_STATUS, @@ -2629,7 +2665,7 @@ def call_action(self, action: DreameVacuumAction, parameters: dict[str, Any] = N return # Schedule update for retrieving new properties after action sent - self.schedule_update(3, bool(not map_action)) + self.schedule_update(5, bool(not map_action and self._protocol.dreame_cloud)) if result and result.get("code") == 0: _LOGGER.info("Send action %s %s", action.name, parameters) self._last_change = time.time() @@ -2640,11 +2676,11 @@ def call_action(self, action: DreameVacuumAction, parameters: dict[str, Any] = N return result - def send_command(self, command: str, parameters: dict[str, Any]) -> dict[str, Any] | None: + def send_command(self, command: str, parameters: dict[str, Any] = None) -> dict[str, Any] | None: """Send a raw command to the device. This is mostly useful when trying out commands which are not implemented by a given device instance. (Not likely)""" - if command == "" or parameters is None: + if command == "": raise InvalidActionException(f"Invalid Command: ({command}).") self.schedule_update(10, True) @@ -3071,7 +3107,7 @@ def clean_zone( if self.status.draining or self.status.self_repairing: raise InvalidActionException(f"Cannot start cleaning while draining or self repairing/testing") - if not isinstance(zones, list): + if not isinstance(zones, list) or not zones: raise InvalidActionException(f"Invalid zone coordinates: %s", zones) if not isinstance(zones[0], list): @@ -3273,7 +3309,7 @@ def clean_spot( if self.status.draining or self.status.self_repairing: raise InvalidActionException(f"Cannot start cleaning while draining or self repairing/testing") - if not isinstance(points, list): + if not isinstance(points, list) or not points: raise InvalidActionException(f"Invalid point coordinates: %s", points) if not isinstance(points[0], list): @@ -3702,7 +3738,9 @@ def clear_low_water_warning(self) -> dict[str, Any] | None: if self.status.low_water: return self.set_property(DreameVacuumProperty.LOW_WATER_WARNING, 1) - def remote_control_move_step(self, rotation: int = 0, velocity: int = 0, prompt: bool | None = None) -> dict[str, Any] | None: + def remote_control_move_step( + self, rotation: int = 0, velocity: int = 0, prompt: bool | None = None + ) -> dict[str, Any] | None: """Send remote control command to device.""" if self.status.fast_mapping: raise InvalidActionException("Cannot remote control vacuum while fast mapping") @@ -3713,7 +3751,15 @@ def remote_control_move_step(self, rotation: int = 0, velocity: int = 0, prompt: payload = '{"spdv":%(velocity)d,"spdw":%(rotation)d,"audio":"%(audio)s","random":%(random)d}' % { "velocity": velocity, "rotation": rotation, - "audio": "true" if prompt == True else "false" if prompt == False or self._remote_control or self.status.status is DreameVacuumStatus.SLEEPING else "true", + "audio": ( + "true" + if prompt == True + else ( + "false" + if prompt == False or self._remote_control or self.status.status is DreameVacuumStatus.SLEEPING + else "true" + ) + ), "random": randrange(65535), } self._remote_control = True @@ -4666,10 +4712,11 @@ def set_custom_cleaning( current_map = self.status.current_map if current_map: + segments = self.status.segments index = 0 for k in segment_id: id = int(k) - if id not in segments: + if not segments or id not in segments: raise InvalidActionException("Invalid Segment ID: %s", id) self._map_manager.editor.set_segment_suction_level(id, int(suction_level[index]), False) self._map_manager.editor.set_segment_water_volume(id, int(water_volume[index]), False) @@ -4699,7 +4746,6 @@ def set_custom_cleaning( elif not has_cleaning_mode and custom_cleaning_mode: raise InvalidActionException("Cleaning mode is required") - segments = self.status.segments if segments: count = len(segments.items()) if ( @@ -5046,6 +5092,8 @@ def __init__(self, device): self.self_clean_value = None self.ai_policy_accepted = False self.go_to_zone: GoToZoneSettings = None + self.cleanup_completed: bool = False + self.cleanup_started: bool = False self.stream_status = None self.stream_session = None @@ -5309,7 +5357,12 @@ def carpet_cleaning_name(self) -> str: def state(self) -> DreameVacuumState: """Return state of the device.""" value = self._get_property(DreameVacuumProperty.STATE) - if int(value) > 18 and not self._capability.new_state and value in DreameVacuumStateOld._value2member_map_: + if ( + value is not None + and int(value) > 18 + and not self._capability.new_state + and value in DreameVacuumStateOld._value2member_map_ + ): value = DreameVacuumState[DreameVacuumStateOld(value).name].value if value is not None and value in DreameVacuumState._value2member_map_: @@ -6049,7 +6102,7 @@ def auto_add_detergent(self) -> bool: @property def cleaning_paused(self) -> bool: """Returns true when device battery is too low for resuming its task and needs to be charged before continuing.""" - return bool(self._get_property(DreameVacuumProperty.CLEANING_PAUSED) > 0) + return bool(self._get_property(DreameVacuumProperty.CLEANING_PAUSED)) @property def charging(self) -> bool: @@ -6097,7 +6150,21 @@ def started(self) -> bool: """Returns true when device has an active task. Used for preventing updates on settings that relates to currently performing task. """ - return bool(self.task_status is not DreameVacuumTaskStatus.COMPLETED or self.cleaning_paused) + status = self.status + return bool( + (self.task_status is not DreameVacuumTaskStatus.COMPLETED + and self.task_status is not DreameVacuumTaskStatus.DOCKING_PAUSED) + or self.cleaning_paused + or status is DreameVacuumStatus.CLEANING + or status is DreameVacuumStatus.SEGMENT_CLEANING + or status is DreameVacuumStatus.ZONE_CLEANING + or status is DreameVacuumStatus.SPOT_CLEANING + or status is DreameVacuumStatus.PART_CLEANING + or status is DreameVacuumStatus.FAST_MAPPING + or status is DreameVacuumStatus.CRUISING_PATH + or status is DreameVacuumStatus.CRUISING_POINT + or status is DreameVacuumStatus.SHORTCUT + ) @property def paused(self) -> bool: @@ -6794,7 +6861,7 @@ def job(self) -> dict[str, Any] | None: self.water_tank_or_mop_installed ) - if self._device.cleanup_completed: + if self.cleanup_completed: attributes.update( { ATTR_CLEANED_AREA: self._get_property(DreameVacuumProperty.CLEANED_AREA), @@ -6832,6 +6899,7 @@ def attributes(self) -> dict[str, Any] | None: DreameVacuumProperty.CLEANING_MODE, DreameVacuumProperty.TIGHT_MOPPING, DreameVacuumProperty.ERROR, + DreameVacuumProperty.LOW_WATER_WARNING, DreameVacuumProperty.CLEANING_TIME, DreameVacuumProperty.CLEANED_AREA, DreameVacuumProperty.VOICE_PACKET_ID, @@ -6862,8 +6930,12 @@ def attributes(self) -> dict[str, Any] | None: DreameVacuumProperty.CUSTOMIZED_CLEANING, DreameVacuumProperty.SERIAL_NUMBER, DreameVacuumProperty.NATION_MATCHED, - # DreameVacuumProperty.TOTAL_RUNTIME, - # DreameVacuumProperty.TOTAL_CRUISE_TIME, + DreameVacuumProperty.TOTAL_RUNTIME, + DreameVacuumProperty.TOTAL_CRUISE_TIME, + DreameVacuumProperty.DRYING_PROGRESS, + DreameVacuumProperty.CLEANING_PROGRESS, + DreameVacuumProperty.INTELLIGENT_RECOGNITION, + DreameVacuumProperty.MULTI_FLOOR_MAP, ] if not self._capability.disable_sensor_cleaning: @@ -6913,6 +6985,8 @@ def attributes(self) -> dict[str, Any] | None: if prop is DreameVacuumProperty.ERROR: value = self.error_name.replace("_", " ").capitalize() + elif prop is DreameVacuumProperty.LOW_WATER_WARNING: + value = self.low_water_warning_name.replace("_", " ").capitalize() elif prop is DreameVacuumProperty.STATUS: value = self.status_name.replace("_", " ").capitalize() elif prop is DreameVacuumProperty.WATER_VOLUME: @@ -6933,6 +7007,8 @@ def attributes(self) -> dict[str, Any] | None: value = bool(value == 1) attributes[prop_name] = value + attributes[ATTR_VACUUM_STATE] = self.state_name.lower() + if self._capability.dnd_task and self.dnd_tasks is not None: attributes[ATTR_DND] = {} for dnd_task in self.dnd_tasks: @@ -6956,8 +7032,17 @@ def attributes(self) -> dict[str, Any] | None: attributes[ATTR_PAUSED] = self.paused attributes[ATTR_RUNNING] = self.running attributes[ATTR_RETURNING_PAUSED] = self.returning_paused - attributes[ATTR_RETURNING] = self.returning - attributes[ATTR_MAPPING] = self.fast_mapping + attributes[ATTR_RETURNING] = self.returning + attributes[ATTR_SEGMENT_CLEANING] = self.segment_cleaning + attributes[ATTR_ZONE_CLEANING] = self.zone_cleaning + attributes[ATTR_SPOT_CLEANING] = self.spot_cleaning + attributes[ATTR_CRUSING] = self.cruising + + if self._capability.lidar_navigation: + attributes[ATTR_MAPPING] = self.fast_mapping + attributes[ATTR_MAPPING_AVAILABLE] = self.mapping_available + if self._capability.auto_empty_base: + attributes[ATTR_DUST_COLLECTION_AVAILABLE] = self.dust_collection_available if self._capability.self_wash_base: attributes[ATTR_WASHING] = self.washing @@ -6967,6 +7052,9 @@ def attributes(self) -> dict[str, Any] | None: attributes[ATTR_LOW_WATER] = bool(self.low_water_warning) else: attributes[ATTR_DRAINING] = self.draining + attributes[ATTR_WASHING_AVAILABLE] = self.washing_available + attributes[ATTR_DRYING_AVAILABLE] = self.drying_available + attributes[ATTR_DRAINING_AVAILABLE] = self.water_draining_available if self._capability.cleangenius: attributes[ATTR_CLEANGENIUS] = bool(self.cleangenius_cleaning) diff --git a/custom_components/dreame_vacuum/dreame/map.py b/custom_components/dreame_vacuum/dreame/map.py index b264e17..43277be 100644 --- a/custom_components/dreame_vacuum/dreame/map.py +++ b/custom_components/dreame_vacuum/dreame/map.py @@ -76,6 +76,7 @@ MAP_COLOR_SCHEME_LIST, MAP_ICON_SET_LIST, SEGMENT_TYPE_CODE_TO_NAME, + SEGMENT_TYPE_CODE_TO_HA_ICON, FURNITURE_TYPE_TO_DIMENSIONS, FURNITURE_V2_TYPE_TO_DIMENSIONS, ALine, @@ -464,8 +465,7 @@ def _update_task(self) -> None: start = time.time() self.update() - if not self._disconnected: - self.schedule_update(max(self._update_interval - (time.time() - start), 1)) + self.schedule_update(max(self._update_interval - (time.time() - start), 1)) def _queue_partial_map(self, map_data) -> None: if map_data.map_id != self._latest_map_id: @@ -572,7 +572,7 @@ def _get_interim_file_data(self, object_name: str = "", timestamp=None) -> str | url = self._get_file_url(object_name) if url: - _LOGGER.debug("Request map data from cloud %s", url) + _LOGGER.info("Request map data from cloud %s", url) response = self._protocol.cloud.get_file(url) if response is not None: return response @@ -810,7 +810,7 @@ def _add_map_data(self, partial_map: MapDataPartial) -> None: if map_data is None: self._add_next_map_data() return True - + if map_data.empty_map: if self._map_data is None or not self._map_data.empty_map: self._init_data() @@ -946,7 +946,7 @@ def _add_map_data(self, partial_map: MapDataPartial) -> None: self._current_frame_id = map_data.frame_id self._current_map_id = map_data.map_id self._current_timestamp_ms = map_data.timestamp_ms - + if changed: _LOGGER.info("Decode I map %d %d", map_data.map_id, map_data.frame_id) self._map_data.last_updated = time.time() @@ -1033,6 +1033,7 @@ def handle_properties(self, properties): if object_name or raw_map_data: partial_map_data = None timestamp = int(time.time() * 1000) + if raw_map_data: partial_map_data = [self._decode_map_partial(raw_map_data, timestamp)] self._add_cloud_map_data(partial_map_data, object_name, timestamp) @@ -1188,7 +1189,7 @@ def schedule_update(self, wait: float = None) -> None: self._update_timer.cancel() del self._update_timer self._update_timer = None - if wait >= 0: + if wait >= 0 and not self._disconnected: self._update_timer = Timer(wait, self._update_task) self._update_timer.start() @@ -2914,7 +2915,6 @@ def decode_map_data_from_partial( map_data.wifi_map = True carpet_pixels = [] - has_wall = False map_data.empty_map = ( map_data.frame_type == MapFrameType.I.value or map_data.frame_type == MapFrameType.W.value ) @@ -2951,17 +2951,14 @@ def decode_map_data_from_partial( segment_id = pixel >> 2 if 0 < segment_id < 64: if segment_id == 63: - has_wall = True map_data.pixel_type[x, y] = MapPixelType.WALL.value elif segment_id == 62: - has_wall = True map_data.pixel_type[x, y] = MapPixelType.FLOOR.value elif segment_id == 61: map_data.pixel_type[x, y] = MapPixelType.UNKNOWN.value else: map_data.pixel_type[x, y] = segment_id else: - has_wall = True if (pixel & 0x40) == 64: carpet_pixels.append((x, y)) segment_id = pixel & 0x3F @@ -2974,7 +2971,6 @@ def decode_map_data_from_partial( for x in range(width): pixel = map_data.data[(width * y) + x] if pixel > 0: - has_wall = True if (pixel & 0x40) == 64: carpet_pixels.append((x, y)) segment_id = pixel & 0x3F @@ -2985,21 +2981,20 @@ def decode_map_data_from_partial( elif segment_id == 2: map_data.empty_map = False map_data.pixel_type[x, y] = MapPixelType.WALL.value - elif vslam_map and not map_data.saved_map and not map_data.recovery_map: + elif ( + vslam_map and not map_data.saved_map and not map_data.recovery_map + ) or map_data.saved_map_status == 2: for y in range(height): for x in range(width): segment_id = map_data.data[(width * y) + x] & 0x3F if segment_id > 0: - has_wall = True - if segment_id == 1: - map_data.empty_map = False - map_data.pixel_type[x, y] = MapPixelType.NEW_SEGMENT.value - elif segment_id == 3: - map_data.empty_map = False - map_data.pixel_type[x, y] = MapPixelType.NEW_SEGMENT_UNKNOWN.value - elif segment_id == 2: - map_data.empty_map = False + map_data.empty_map = False + if segment_id == 2: map_data.pixel_type[x, y] = MapPixelType.WALL.value + else: + map_data.pixel_type[x, y] = MapPixelType.NEW_SEGMENT.value + if segment_id == 3: + carpet_pixels.append((x, y)) else: for y in range(height): for x in range(width): @@ -3008,7 +3003,6 @@ def decode_map_data_from_partial( map_data.empty_map = False segment_id = pixel & 0x3F if pixel >> 7: - has_wall = True map_data.pixel_type[x, y] = ( MapPixelType.HIDDEN_WALL.value if map_data.hidden_segments @@ -3054,9 +3048,6 @@ def decode_map_data_from_partial( ) segments[k].set_name() - if not has_wall and len(segments) > 2: - has_wall = True - map_data.segments = segments if map_data.wifi_map: @@ -3099,7 +3090,7 @@ def decode_map_data_from_partial( if ( restored_map or map_data.recovery_map - or (map_data.saved_map_status == 2 and (map_data.empty_map or not has_wall)) + or (map_data.saved_map_status == 2 and (map_data.empty_map or (not map_data.frame_map and not vslam_map))) ): map_data.segments = copy.deepcopy(saved_map_data.segments) if saved_map_data.floor_material is not None: @@ -3107,7 +3098,7 @@ def decode_map_data_from_partial( if map_data.hidden_segments is None and saved_map_data.hidden_segments is not None: map_data.hidden_segments = copy.deepcopy(saved_map_data.hidden_segments) - if not has_wall: + if map_data.saved_map_status == 2 and not map_data.frame_map: left = min(map_data.dimensions.left, saved_map_data.dimensions.left) top = min(map_data.dimensions.top, saved_map_data.dimensions.top) width = int( @@ -3143,6 +3134,7 @@ def decode_map_data_from_partial( nim = ni + map_data.dimensions.width njm = nj + map_data.dimensions.height pixel_type = np.zeros((width, height), np.uint8) + for j in range(height): for i in range(width): if j >= sj and i >= si and j < sjm and i < sim: @@ -3163,16 +3155,18 @@ def decode_map_data_from_partial( pixel_type[i, j] = segment_id elif j >= nj and i >= ni and j < njm and i < nim: clean_value = int(map_data.pixel_type[(i - ni), ((j - nj))]) - if clean_value == 2: - pixel_type[i, j] = 255 - elif clean_value == 1: + if clean_value == 255: + pixel_type[i, j] = clean_value + elif clean_value == 253: pixel_type[i, j] = segment_id if segment_id else 254 map_data.pixel_type = pixel_type map_data.dimensions = MapImageDimensions( top, left, height, width, map_data.dimensions.grid_size ) - map_data.carpet_pixels = DreameVacuumMapDecoder.get_carpets(map_data, saved_map_data) + + if map_data.restored_map: + map_data.carpet_pixels = DreameVacuumMapDecoder.get_carpets(map_data, saved_map_data) else: # map_data.data = saved_map_data.data map_data.pixel_type = saved_map_data.pixel_type @@ -3220,11 +3214,11 @@ def decode_map_data_from_partial( ): map_data.charger_position = saved_map_data.charger_position - #map_data.walls_info = saved_map_data.walls_info - #map_data.walls_info_new = saved_map_data.walls_info_new - #map_data.ai_outborders_ar_origin = saved_map_data.ai_outborders_ar_origin - #map_data.ai_furniture_ar_origin = saved_map_data.ai_furniture_ar_origin - #map_data.ai_furniture_ar_origin_v2 = saved_map_data.ai_furniture_ar_origin_v2 + # map_data.walls_info = saved_map_data.walls_info + # map_data.walls_info_new = saved_map_data.walls_info_new + # map_data.ai_outborders_ar_origin = saved_map_data.ai_outborders_ar_origin + # map_data.ai_furniture_ar_origin = saved_map_data.ai_furniture_ar_origin + # map_data.ai_furniture_ar_origin_v2 = saved_map_data.ai_furniture_ar_origin_v2 if map_data.saved_map_status == 2: map_data.no_go_areas = saved_map_data.no_go_areas @@ -3579,19 +3573,18 @@ def decode_map_data_from_partial( ) if map_data.cleaning_map_data: map_data.cleaned_segments = map_data.cleaning_map_data.cleaned_segments - - - #map_data.ai_outborders_user = data_json.get("ai_outborders_user") - #map_data.ai_outborders = data_json.get("ai_outborders") - #map_data.ai_outborders_new = data_json.get("ai_outborders_new") - #map_data.ai_outborders_2d = data_json.get("ai_outborders_2d") - #map_data.ai_outborders_ar_origin = data_json.get("ai_outborders_ar_origin") - #map_data.ai_furniture_ar_origin = data_json.get("ai_furniture_ar_origin") - #map_data.ai_furniture_ar_origin_v2 = data_json.get("ai_furniture_ar_origin_v2") - #map_data.ai_furniture_warning = data_json.get("ai_furniture_warning") - #if "walls_info" in data_json: + + # map_data.ai_outborders_user = data_json.get("ai_outborders_user") + # map_data.ai_outborders = data_json.get("ai_outborders") + # map_data.ai_outborders_new = data_json.get("ai_outborders_new") + # map_data.ai_outborders_2d = data_json.get("ai_outborders_2d") + # map_data.ai_outborders_ar_origin = data_json.get("ai_outborders_ar_origin") + # map_data.ai_furniture_ar_origin = data_json.get("ai_furniture_ar_origin") + # map_data.ai_furniture_ar_origin_v2 = data_json.get("ai_furniture_ar_origin_v2") + # map_data.ai_furniture_warning = data_json.get("ai_furniture_warning") + # if "walls_info" in data_json: # map_data.walls_info = data_json["walls_info"] - #if "walls_info_new" in data_json: + # if "walls_info_new" in data_json: # map_data.walls_info = data_json["walls_info_new"] if vslam_map and not map_data.saved_map: @@ -5188,15 +5181,20 @@ def get_data_string( if map_data.router_position else None ), - #ai_outborders_user=map_data.ai_outborders_user, - #ai_outborders=map_data.ai_outborders, - #ai_outborders_new=map_data.ai_outborders_new, - #ai_outborders_2d=map_data.ai_outborders_2d, - #ai_furniture_warning=map_data.ai_furniture_warning, - #walls_info=map_data.walls_info, - #walls_info_new=map_data.walls_info_new, + # ai_outborders_user=map_data.ai_outborders_user, + # ai_outborders=map_data.ai_outborders, + # ai_outborders_new=map_data.ai_outborders_new, + # ai_outborders_2d=map_data.ai_outborders_2d, + # ai_furniture_warning=map_data.ai_furniture_warning, + # walls_info=map_data.walls_info, + # walls_info_new=map_data.walls_info_new, startup_method=map_data.startup_method.name.lower() if map_data.startup_method is not None else None, cleanup_method=map_data.cleanup_method.name.lower() if map_data.cleanup_method is not None else None, + second_cleaning=map_data.second_cleaning, + mop_wash_count=map_data.mop_wash_count, + dust_collection_count=map_data.dust_collection_count, + multiple_cleaning_time=map_data.multiple_cleaning_time, + dos=map_data.dos, cleaned_area=map_data.cleaned_area, cleaning_time=map_data.cleaning_time, work_status=map_data.work_status, @@ -5249,7 +5247,7 @@ def get_data_string( ), active_points=[[point.x0, point.y0] for point in map_data.active_points] if map_data.active_points else [], active_cruise_points=( - [[point.x0, point.y0, point.type, point.completed] for point in map_data.active_cruise_points] + [[point.x, point.y, point.type, point.completed] for point in map_data.active_cruise_points.values()] if map_data.active_cruise_points else [] ), @@ -6092,7 +6090,7 @@ def render_map( else: lines = [header_text] - if map_data.history_map: + if map_data.history_map and not map_data.task_cruise_points: header_text = "" if map_data.mop_wash_count: header_text = f"Self-Cleaned" @@ -8734,7 +8732,7 @@ def get_resources(self, capability) -> MapRendererResources: cleaning_direction=MAP_ROBOT_CLEANING_DIRECTION_IMAGE, selected_segment=MAP_ICON_SELECTED_SEGMENT, cruise_point_background=MAP_ICON_CRUISE_POINT_DREAME, - segment={k: {"name": SEGMENT_TYPE_CODE_TO_NAME.get(k), "icon": v} for k, v in icon_set.items()}, + segment={k: {"name": SEGMENT_TYPE_CODE_TO_NAME.get(k), "icon": v, "mdi": SEGMENT_TYPE_CODE_TO_HA_ICON.get(k, 'mdi:home-outline')} for k, v in icon_set.items()}, default_map_image=DEFAULT_MAP_IMAGE, font=base64.b64encode(self._light_font_file).decode("utf-8"), rotate=MAP_ICON_ROTATE, diff --git a/custom_components/dreame_vacuum/dreame/protocol.py b/custom_components/dreame_vacuum/dreame/protocol.py index 5a20e8f..fb96b76 100644 --- a/custom_components/dreame_vacuum/dreame/protocol.py +++ b/custom_components/dreame_vacuum/dreame/protocol.py @@ -290,19 +290,19 @@ def login(self) -> bool: timeout=10, ) if response.status_code == 200: - response = json.loads(response.text) - if self._strings[18] in response: - self._key = response.get(self._strings[18]) - self._secondary_key = response.get(self._strings[19]) - self._key_expire = time.time() + response.get(self._strings[20]) - 120 + data = json.loads(response.text) + if self._strings[18] in data: + self._key = data.get(self._strings[18]) + self._secondary_key = data.get(self._strings[19]) + self._key_expire = time.time() + data.get(self._strings[20]) - 120 self._logged_in = True - self._uuid = response.get("uid") - self._location = response.get(self._strings[21], self._location) - self._ti = response.get(self._strings[22], self._ti) + self._uuid = data.get("uid") + self._location = data.get(self._strings[21], self._location) + self._ti = data.get(self._strings[22], self._ti) else: try: - response = json.loads(response.text) - if "error_description" in response and "refresh token" in response["error_description"]: + data = json.loads(response.text) + if "error_description" in data and "refresh token" in data["error_description"]: self._secondary_key = None return self.login() except: diff --git a/custom_components/dreame_vacuum/dreame/resources.py b/custom_components/dreame_vacuum/dreame/resources.py index e6e9483..1c0e819 100644 --- a/custom_components/dreame_vacuum/dreame/resources.py +++ b/custom_components/dreame_vacuum/dreame/resources.py @@ -338,7 +338,7 @@ 12: "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", 13: "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", 14: "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", - 15: "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", + 15: "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", 16: "iVBORw0KGgoAAAANSUhEUgAAAMYAAAFICAMAAAA8iHFzAAACeVBMVEXf39/e39/h4eHGxsbX19fd3d21tbXc3Nzb29vY2NjZ2dkAAADU1NTa2tri4uLj4+OztLTIyMjW1ta0tbS2travr6/FxcWurq7T09PNzc3R0dHPz8/Ly8u4uLiYmJiZmZm8vbybm5vS0tK9vr6wsLDExMSxsbHQ0NDMzMy3t7fOz86/wcCampq7u7vX19fBwcGfoJ+ysrLGx8ednZ2tra26u7qzs7O5ubnf39/k5OSXmJempqa/v7/Bv76lpqXKysqrq6upqamoqKi2t7d0dHTAv8DZ2djAwMC/v7/AvbvCwsKurq3Dw8PY2NehoqFsbGxjY2Jzc3NcXVyrrKzT1NTX1tZkZGSjpKR6e3tubm5xcXGenp5fX198fX1nZ2fJycl2dnZTU1NWVlazs7NhYWFlZmVqa2p/f394eXnFw8LBwcHa29vCw8PX2NjIyclpaWl+fn5aWlq1tbRQUFC7uLZYWFipp6fZ2trAwsFwcHDBwsJLS0urqaqys7JISEi2tramo6NGR0fFxcWop6e3uLeqqam5urnh4uLOzs64tbXb2tqmpaWkpKSpqavRzs6+vr7g4ODc29umpqa1tbWQkJDW19jDw8TW1tbAwcDV1NTLycnV1dW0srKzs7Osq6vV1NTR0dHd3d2SkpLLy8q7u7vOzs7DxMS9vb21tbXc3NzJycm8urrV09Pa2trPzc3X1ta2trbW19ezs7Pe397Dw8OsqarY2NitrK6FhYXMzMzKysm/v7/f39++vr6ysbHRzs+ysK+8vLvR0dBAQUGzsrK1traysrHd3d3j4+Pe396ztLTh4eGys7Pc3NzZ2dm7vLu1tbXX19bGx8eXVONfAAAAx3RSTlPy8vLy8vLy8vLy8gDy8vLz8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy/vLyw/Ly8vLy8vLy8sPy8vLtLfLy8vLy8vLu8vLyLfLy8vLy8vLy8vLyw/Ly8vLy8vLy8vLy8vL28vLy8vIt7vP98vLy8vL38i3yUfL98vLyVfLy+FXy/kj6Ovz5aAqyQykfF/r3oDES8u3t65J6Id+giUwt/vfy4d6+YEbm18qrnI1MPO7QyLu1aUEG8unV0cXCuYhy9vLy1M/Pz13moQAAJa9JREFUeNrc2s+rElEUB/A540w6ppUW4k8QJUaNEgSxoHARmUXuau26H7YKat3GahEvoqJa1KZF0A8iCIIgaNMTqUV/Uefce8c7eh0dLXPoa/Z8Oj/ux3PPHd/jaXv/i8wy7r648/bBvY/HA5uP9x68vfP+4ULGi6fHYcyyG9SMKfrrt588GS8e6Lvj4XD/PhF9X7CiY9iDneF4PPz4bj7jqbY7ZpsGbfhS4cpwPHxwX2Xcv7e7q+sBVsxANIS8fj/LePZld6gHneGCaBh9bH6aZtz/QqVQFft9ZtnplQ0mu7Cd5x2R370hzAHvpxj3dnckQpxWHM7XzR2X3vd7sfCAMhJCCnK8fuZiPB3vdyvcx/YVZQTTg/H/Pizew+3QeEb3JOOFPpYzanoUc894gg44Z+yKQHlVzYkTJ/CunAef8y6Ii7Hz/c6E8WAsEbIGntUggRjDzNhpTOJ5sRGFXqDgV/EAv9L24hWx1/KKCANzaCKjD/cF49POUNOkYkk1ZB2E+YQIjm3lyF38TDQKEeguHU+JwYtBDFKIMfrsDeVlz6F6Z7W2d7W4LMddxngWGbvaQiyGEuwKAGhKQIQe0nfuV5SN5LP4/6JjgXouMe9nx7bz4x1j3BnzxRbBHEDHM81wOByRiW87EUo4bJrAQC766A0xnDklqqBBmI05+rfz58fs4L8OHSliai7H6Pl9ZNx/PWY+dtHTTDQ4O1nudKxV0uks3x7PQRtaq6RnWAamFzVlNXauvkPGe3PIFBQt3kGv5TeGMbA2nh7/MjBEkskYJS4b8MdTZNwZ7TgMM2r0rAX5+wMfLD6cwdOzDIkgRrqUThsgGNgcyPjqtIZuiq3V96LXM0T4g15v8ix/5PB79EqPbcJDz2Gco7HnxWZ8Q/YN3tkO4kSTYxizIQQpslnbjgEpiPEKGW8chkkbGZSBVBg+Iokbj6hFyc7lcrUYKYhx7K629x4y0LFPM2IYLnEqjoxgRTCyuVwmU6kkCWECLlWcgTc9evHixDFAQ2+AVQ4Yw1HYuVol3+5X4qgwzdHzZ9reS2ONEo5h23AHzY9B4AjuzsjVMvl+I5TKmFSO0ZNT2t5jnGFkS+RwplUAEaRwipGptBOhwxcuxDSsxvdHlx1GuJRFhyyHETxGUhSDphRTnK+fbcRhimHY9rcpR29gBC1OMagx+qg42+3Wc1PVMLM5285ms9JhbSa93sp7sJ2EgxWDNUYKFeVyNxUFFyNaq+UQQg7OGFgd/5/YKJvZmnawOhj0IAUZpSw1BinK5WKx3LXB/DFhJCsZdORsZKQZw8IPkgEJHwiCLMawqTFSF5gCHY0oMs5xhlnKZzJYEBsdJWJgLeKRYAUl6CBGHhX1LiGOHi12bZxUtOCOQIvk2pWK40jHsBikCAcnDIKOZCxtZ/KNw/VumSmaR4uN6Pcnl4lhQrTSzqMjwxzE6JDCDFAYJW4Z2OKZduh8lxCoaDWL57M/nrzgDKPdRgebV5wRjZMCMCYGth0OwXIYSWKk6mxGNZvNQqt4tv/TqUasTw4xr0rEuEoK+SsA0LYQfmpJQYeVvJittVNn2YRqtVqFQrN74aXDSF/v97mDM3pRLAaAFqAAcEaaMUhBiGqhVa7/shkDzGwiMXEQw6I5NavQ2W2deO240uGoHBMGIUhRrbaK9V+feTVMO5Fw6jFh8GLoOt7dAt37pnu+6rEjRtnH8wgAnGFjb3SPcgWmUKzfDnFGuNZIUD361OeMcTMS5oqARMMQYxBjjHITEaggxtGz1x5zRiSDDF6QfCbHWjxgDF0wDLz8ZfqHy47iEGfwy1+kEiIHa5BMLitWqoApkBGJIiNXSVwoCgSm2kTGOV6NfKghHO1KDi8cePnDFg8gI4mMfOP80QIheFrda1c4I55PNTDMUakRA5sjcAwzLC4bjToyECAZpwQj1KBwhp3GHo+yFVf8Anv7ceYU6/DQ2Wb1tGSUr53hLR4lBgUZ+Rq//sV5OfYFw8Eb3KI5lemnui0qhspoE4OCDFpxxYcqIEYQIGxKYTGIUXEz9uzZU0AGTarvZrSPDB6HwWbVrOPfauTZqBhh8fm2VrmeKiNjD95IQYwrN1g1LGSEQi4G9rgoh6YHoBysMeLRQZIz8LJxmgkO0b1QvvWYM4zEhHGdGBcdBnNsQLFiXVHBisEZCWQQQKRQvMWrAcjgEYw0Z4hppW+7NVhjXI16M3hvqAxacW9yxvav5RovhsPIzzCqWI1zrMWJIRyJPH4aIYaohgmwTYdUYGsYMYdRmFcNMznDuOhibP+nJ1JgMRxGZYpxABlHkMF7I+VmlDjjaiQc3j4DULGAcfBgFRk4qS6NzGRjuhoBYoBkYG8oDMwh0RvESDkO96TaCAMA/i6jKhmJzTNkYCWywshPTypkHBErlVoNY5YBAOu+pTCvGiC/F1/5KyoDh7CwGpIR88FQBrMcAs7w5qhg5nmJYx7xKmMsqsZBYoiVSmHEphni6DOOlX5bNreAoL4rICggn/dgIIAiGd+XMcRolswXWNLOAGt1jhfjgOM4tCbDoyzgq2fXWNnU3pAMLMqho4IBMQJ49wbIybz+EH28FWrAYVxVGAfdDN7i3gyuWJ+hBlY4BFDUaqgM8ZlqSYsvOjusSAKPHecuA8CLMcNQe+MyX6lCs4zk9KRS4/M5lQQrNpA3Q65UJwWjsTKDhrP5AKHmM0ghVyrGSPpYqeQlCv7dBxMAT4bIgakW99UbQJnDgLVlrvVDDbAsZVSpxVWGuuBu4Hqw+A97JUKbw7jumlQHqRqsNy6pK1VamVQyAEsZ/s34aG0GOdj/yJC94Z/hf1axUSyaSapk6YKbE4wDLsYV9tPfzFXcVq7iyts0NSjPz+IYj4VZhflnKNcNZNxAhtIbJQ+GLPXCSMb6LaJeN+Jen6mqxRUY4N8BmI1fN+SkOoSXPzap/DMofqqxTveDuoPzFwoLGDipqMWJAdMMW2G4p6p63qW18bU9zGdoCkO2OEUyvv+m5WxWpYiBKMxBcFw40BAZRDduRNy4EAY3gjvBRxHRrW/iK4lvpXFiV6ZPqk6SbgN3BnXsm++m/qtyT4bBKl5EhLUbAoNNAEYSR8PQ+cb7v178VAyunQZhkIJ4oSpTTXlOixl8DApGaowPLFRsV4HdWq0PxzDY4D7MGBaMNFTcwdhoNzZbwf+KFtsYT9LDguGo+Ae2VFt9JQU5vtjGGGdyf6noxrXEVDLCNYhJDAygoO3+vpNu3DAsNDx1YACZYlwH0PwLCEUPMJ5zLr5mfzpQxybZQLhfHFngaWM8rIKR95Rv1N0mFqrRwIgJMB6MREJl7u+Nm2+cDEMt9Eo/MMa+YpyparjJN6xqeBnM/rQTDg/NSrztMJ9quISRltXgat2Y0Gf9ASA+kZKraIzXxVIxxuMhDOxCg9AlgDHMUlUYcSkamB3Enffv1TcUxU8Tqr6Kun3JhbnaAkD/aEJ1Jku1qngODf8OW3SdhtOtACWFU54deTGkYVQxFWP8qJLYS4wBz/BweoQp4VOdWMO43Bvcchp/MS5KqOgsxDYO7Yufat2oMfLL9ZY2/erDYAqNAWDKCABxX/zdJon9ZEIlMLqToOEaj9uVM0vFBZ6lnIYlsVzg6cLATFUEoxEjgr64+Y15DC0iOuzFnmELc391aHgJMIbEnTDq1iEGzVbUNEt/MVyhekUYwT6VtRRBlWgKSox0Vxkxv+GUoicDDsA/MDf8BZRuGEZdNTT351RGBn2X5rY3NI2WPo20WExFJQUfA8Gm9jSb0JHDrhg23fZwMQw7jfv2foDRFIRJJ8eDLtJSGUYiDF1RF7s7rBOAAOPc0I2UFip++gaXhcj+IDAmF+nGl0aEm9eyqrh2f2QphVjpkogWPFbxj3Qa6VYB/dcYQBvjSyVU4C1r183UMSs92s3FLTRM1t9gg0sYtj8MSzjUJ42VAgYV4aZkKn7S3abyyK4t4iitWDEekaUijN7GwLwWo18duOkbWar7XPwnQoxYFbD/Rx7WFKFHX640+hJj0Ob3QvRMKcSDSEvBKCqel45wQcVNbaj2a4g7pRAH6vk2qY+hK+vH1UY0Rlp7f3qcmGZWhNvaycAYsqHcPU+FcfnWnHAxdNqUVkt1bWF8oNOY8MoHqoqHceNIWcXNb4xg9KrmtFChH2NZ7DROj3WEi+prN4dzx2FCqJY/HFddUjCMA9MJOfqCBsbZnYq+YXAwwip+/AI6p0C3GM9pDncdfVHXUPYPttCJKufjCZVh5PWvot6+TfM0wICIkGbVhQND5TfyyhhFxUk3Whg4xDphQIkIIw7UNQas+Tu3yfmPBpOfGaIqKXCdanthDlzoa/pE4LD9AxIj5SLPsixrf4PqVM6NAd3PP96f+6P2BcNKCroxcIBiAMOAQJRvJBOqjMGzhoQxNQeFPX4S1YUB6ja1pxQMwxGqIFXukjbk1SNc9PQ7jK8bDJpS0OU2TjSA0UY+wBwaRAx3L2kVKjVriP8WgEgh1Ve0bIKnBCOGwboxv3EYAOD6fXFjYKJO1cRoofRqRmDNkFc4r+5jFFPlYzxTGObVRWMAdBIgjLsPs5vVvT/LNwba+xg+GIDDcP1B4yQMmsOlirpuDACYG9gLOv7wQoG4+JkKBt37ExiAXVdCZ8Nstf9UnIO+uIXgNKykYLOGoaWiR08cjfQbmKpTpVtM9dl0Y8LgonrTfX6B7BpchXE1jLnQEIC96RvTIh0eKkUv/7x4nf2JmRF9GVSLCCa7adym+fbyhVmqpbZUbycaynovHHSrmYuBhrJdQykYZWakwqAkNuZAB4s5yramQ2L4FXXLxR8oDOyMB4EwugfMkocYr56zitMN5RgDGN47NXbCfBcgUHEa+aUqKeiRyZl7yPZfOx8AMmkAnCG9grEOW+jQkNRRVdhUuAdVsCMM/5fXPOGL1nrWcIRDBhvK20Rpk13ttfa+wJAmyORhUO05ENziuhhL2v4SgqeZIsSwp7o3cXsxTERt2yASGtLbjNqnG0Z+WSvqv3k7f94oYiCKM0q0SwqkE4dOKUC6Nm0kdGU6pBTp6OkThSoSfMUU+U5gbZw539jvjQcvlggSgbA/7fg8f58vzyCGPiirS/Dyi/kR4F+gLb5ZMMocLs9TCfH8ZOFohKoAJHKK7/MUpm5xlYSg1SbsI6JGHCntn5fKuDOyrOPuttnRh3t0ylqiBqJimVEmUT6AIbwrejnFn9xGlRYILPpdJWJQJcaHWg637OB5SRg71k8VTiwrH1en6+9SmIpW+zOG0X4TAh7GxY4ty/aM5D7cZY2aGJDMKeH59yY1wFg4dGJAh4LGDD4M7osWQaf4Js+LezE0Igg0SAbLO9WM+meAwZu7peJ8mt9pR5V0cYiAINb0jCiGtr4wDPA4wFJMgMdAFaMxvqgYZIuDx6NE/Bv0D0i6rZ4ZuQYYazTcEvPj8YZiOGZiw0t6/5J0Jz+vXo3qZWZB7DrSedzSWgXlfTH2/vVVSJZj8MVBpWE06VczogRGlTGmvxh2QvkGVJtGy1KJCGB3YyRnxD8vzuI6UFCi4NKNsdXRXtrAqhjVkFksFwdBcgom6877cLVHfXbLpbBRQ9zIaSdyeOs9wzjqUZ/dbyPsFjZa2rmlolZ7PTcUg5zieBja56xYy7F12N6wyaSiKQZebgpuXt6ZWDWqDmULs2RVpQ67xR9LnREtKHtdQ5LbGS3fr1+KuvhdgZE4NKPufhsg5ce2bkjSSlC8cfi0yRc+ZAyn6ovwCd4ABoKBZZrNtsTw66hzgvF+Iiwo69j7k3d63yk9EKUxI5IcI3fwfKl0fl43Y3G8m2F/LiUU6w+wVPSyw7USu2QN4aQZTyi1OAA3ir6BURkMf2HA+exh2RfDgTFUvKbEmB0YAmYuAmNw/Ro8vwqMqfBwAQa/YmdVNSGwN1SEIFHk6M9i2DwVf0RfCivujDycYGyyIKAJYpHOSP7VAJCw2+hQtqhipPeQMRqXuhgM4IeD432M7n1L83M6jv6cQSyMJ2SFjcN7RvKomSqwijfB41fU4XYnvYpIdkSrNCoe/cHyzBjx3gCG1sXvtdq02/Wn22SAJYVnm8ze4Bjl8FeFQxiGxJBw8jNRnEZ/O8/9G/884y5DMK7eMGwQizAEaCiuepRr0azo/CzVie+ffBj+XS0BIvuOIcaP4m2g7jYizu/PbQ7e4vo27C1az/N338SA9c39FTIJUtChIE1+MtdQZ9j++4VHDCN9OdIZ4Ri1pP3qPrsAdeKcp5oOhQD5rqkzEhIBiu1orE9lMSY9Nzw6I9oC1eOVcwyur/mmFvbNeLi6N3SYlGAULV2cY1ylWdWJKYa/oNwpPNxRw7VaRQVGo/a3VUUk0/k5TBICe5V23qASEmh328VddWBOY3EaNjHVBkYKaKRV97AY5/n6qRJjc1AM2MEj7Y9Zcad5EAhu8MmCgOfZGdFPqgJj9jQi4XIXW/H58sadZtqINDX7qUC8wbdAVLFABBx/YHpfMT6e8REtl3XHJ/8kdKfZgvHbPaH8bkzRTPq7QBFG2aUwO6VLTwr8pp971BIXBsiMgHSbjcIzgBKGHCdh6t91jLSWc+OQjz9P0Qw+YezyHe4YeHvUb53X3eriE+z17/rxGMZPxUiJde017DzFpbZhI2JI9qeCU1wx7J1mqmyBjAqYLsWAbP7jD2PcRsRrcG908POXnxuP1ZZJc41I6BRfW/wTaylstSvaKwmx9pLWRBE5xc0WD2KIjMLon1BOGAcT/ZFPKndzd5CjO6OevNyUGWEYM47+1l8Cg9gpYRwpW5wRjLecmwSLZEHZe97cnVMK8FKX+USCjhYs40FInQO1vigGEq95UAwdvjfjuCNtjqfbLMZ2qmLcWAxsI4hjHAYX6HieiZAsKG2svbhcij9sIlt5xQXl4BPHxm5xi/G+ohUN5VzGL4vBnZFdMipnd5uMzNmCJmlsVEnWNwexZm9cAgwB0vbDzE3/xwbGVKiFqQA5wpCyciyKsXpt37HFr0wJE2PwNd6zqmPszb1N2k4MMFCkFEeKH397TX4eGdXcxNBTPL/jqMMUwS23+MUf5s7gNXEgCuNIRDZSwdoSsq4LksUaC6U0sPa0eEt3i97cs/ctevW0f9/+W/t9zpu8pGNCG5T0O8RprBN+vPdmXiaZGeehGVVMRiAHQ3dDKd0O74zGaMkhG1JIj2CUzm1KRgZj2+31rDWqrnVm0RplGB2R8+xPrTHwiSHWqJ3E1n+9WwsaG9uKx/t1MU7b+VVMn6h4Li4YBacKqjFaFRT1ky6NgeOzoFvHMcZ2LQW7XMqvIkaQw9i/GeN04zoK7mKkK8Uwa/CQRDDcRN3FqOdT9e9ij2P4bYuxBMbBHNnG3GW9+GCbw1CD11j4pM7ioKoSjFlHMOhVupBs4r3GWBVjo1WBceoWtli5NriCERMDFNrg6jSUTIlgtIsYOJxN1Q9HndiIE8XQdw1lFqaLob2460NnB1OKYr8RwanyGDjM2G+IU70No1U5nVq/q0+pv3OdyifGFLEhGBLiNlH/V4LxycV4c6tVn6MUwxcMWsMsXtOXVe01NnIYsYNR+TIUVI5UdzY/C/KpGNv2hcWYGYy+YNjViQsYpU7VKp3truWyf6xYWtnNCfQ7ymKsBjkMMlwJhvbiJRg63HZKZdUpcql0ZAQ+RYzPo+QZ7+GCApKbWPflbsXYw6v07q8puRiL5PlOMCCz/4a+M3JQEGQYaW5SLCgaVG7DhzUxPGDYlopeZRfLZKJuB6qAcUuM/M14g9ItlB0MGW/L7b0vGBQxpnkMSGprSMU8/RUGjtmTWAcjyjDA0bgK97BfgHH//frQUkEG408lxgs5pKoGpaFhMAJiaPc3Mxi/M4wAEgwMjbCpMvaouIBeiUUt6HlKStS7CSj6lMWIN8ETMShpcO2r9jkMbzMCxtqYQzhQWXN66UFd9uGm94t3wdOjYnQ0pypimP2niLGnW1G9RrXvgsIY45AZTn4+8h6WEqeyLRUISEGM3Uhyw20Kjp6B6DYrn7aQ0ECCC4x+JrVGC9YQyZhCdEFzpN1uD5KafBzTtCtKKf4pZ/SsfJU7JR8i/YGWXtWhJR8CQ9tQ0KcWyZIze63yyYhiaIwPDAcq85vUqk0NSLGOYIx4492PZ1f9+VwwrmZjpobiVKJJkGxiehXN0fapdqMiASFgi3AIY4wug6e7a1BYjI5g2FUmDxTEQHBMI3IMpJ5mRQRSRNEUkbHxlt8eO/35g4OhTgWMiXdJrxqizSUIa/kACsMwGoIiXlwG9+OvoChiSGx4QgEhOGiOIezxMRSSARCggEt5S3R+87/AsBzAMN0fMSzFcuLtFrHhCMP1OmxaETQkxC1ssfMmNMbDzc0NOBTjh2D8Z+ZMXpcI4zBetNgKbSYt1gyaZSjjLmSUmjBilpZgi2ULaRGRRQRpFNQpOgRRRHWJIupSh6BDEJ06dOrSX9TzvMuMk7bS9smZeX3HmXk/ft9l5vVHG5RGvV4XrWMzPf4DVgMlgVicTW2oVeMrYBGOJK/AQ2rox6b5S5VGvVars3Ws3USRjRt5in+Ce1korNmMSGxau33prA01Ox7NRcKA4ch5NRac3SBjAY0awnEWHhDZvOZfs5msYiDWbj+SogWqVCTs9/vDTrUKGvrpbzaiIWNhWVYNrXzpke1r126Cyj9mE1lLh6WpWSvrlm3kl0f8AQCPJOOh2gajITUYC8u2bau+Ac+yS88eObJ9O2T+KdvJkbNLU5So2SEjPxcWmUwGGsrD1WDbUMGohkJVy6rXeauYSi39PZz99UNTYNYsfscjSGSjS8KBTAvAgxpJoXEJGnxs2pXaABiMkMC2avU6hpD/AFmwkV01jGxwbtKfaRVLpWJRe1yBRuiSjkaqDmBRDRlGHP9C1aptWaNRTWP9CUZfZtS8u7nfBixVNh+lBBxK5TI9dupqFQzpPwtbfJYWI6uKuGWz+Xw+G48bhhESGP+WeDyezQcxvxYJtKhQAGUERIQDHtQQlQoaR2oiGCEjng8GV6xYsSMaDQaDeRLURMGOHTtWCLBBGiDrS/g5xYqxXDLt88x3056DeCVOgmCkgIMw2AbooasVNS6dZjSEBi0YDP4xYi4JZG+Wy12JCMLADwJfojP900D2DxIA3Cjc3J2iNZQpsE+zTXjsFB65PDTkT5jztrP2VxmM6BKUdmcm0yLFIs9ACpJtQJ1pjMJ4WoCtF3e397PbvLhFdc6l8naPIz0QDngoDVYq36ZRTVhk0Y4ydHcOTfxNOh13PX2/2AePQsGpVvmqioZvziqLVUp2acUCyt5Jm5r0NMyJdz9B5+u75OXGr2pOHNFJqHC0GI5c1r6k/hOCOWtgwSqF/iBT3pdeL4lJcFKuuPFg/gzf/zRPj5dafeMY5SF6q0A4CQ01bsxZXbOrbBgMxraEuWXLOgVkuJ5CTCxe8MEfJhbTGwWv5E1OHiD90iocJVSrcC5us1JRY+aiOsaHPDrncKa821xPDSyTbCHOeYHcukZMu7vVwo0TXGnr5k18Ofo63GpcK3p4qtVcwz4jmjjaxoJZIRmMAIMhS0sVeUZXgZhpXbvERqeJ3KpF49RKZ+e044koKxP4IteNF8GV4X5osJVLjcDcqqOBgcPgkJELt8q70+tViTVahgms3xw90Oj3+4MBlu5g0O32ut12D7R77XaziZegzcSJEyew4osZzCK9XhMH4DCeAqdqNBoH8BoeJHsqlVNvnvCKLl4PNxyoVf4dUoPDn8+3hsHAeF9CMNbLGuU5ixMS8+TxY5oLDpcVV8G1a9f05hrROWo3E+QC4VmOg4sXL54/v3fvoUN7D4ETh05tWa+Lr7YaoWE64dgZzlNDNnHfnGWW6GxlMFQodVBd0C7MPRcu7nU4L7hIjpNjLrqYXLscl/AAFhyw3Agag9XrKpqNJzGn+Nww7dVQ4Qgk47Z1Ro/ivsWz8sHl4UyJLcOj4SCzTfPgsfN75ZWxdpFWsJkK83XZiTZgnWPxZRUb9AGravvAXWgIgS9RjTyhwtGaazgaPvyKsYaPJK3C7k5svNRTNA4IDV4eYN0DSJATsJmABXaRziy/bh6y8GxtbCFkgGhAw2Mx2TgYjnIxEKzaI1YqPjbhV6P51twrYsxYLz48oSBzlQYtdBUYCLoS4QTgKOE7Nm9NTyCO66vGLRjqFUz6gzY0eLVpFtSgh9TIxavWyOmp4LExmPSX9iVM1ZE7B2Glk0KjcQzfaVN2Mw3BAYfG9zjgMhyKfolUgEggAyqNfq9x1xTf2qQLLByNQikQNWxo3MPPNDc/CI3FK5OB0r6Oqceo6VADwWh3ByyTLEmFHAUVD0c92Ux7OOVBZVagMmz0uwNqTLkrWAcJV6NcymX5wH1960NqiF+efcuMndQgsa8BjT41etCABAxEgfaTw1/nHJdz55yV2ujd+yWQgQfi0e9176bXT0VppNnGC+VM0Kha0Hh6a8bC29QQrTyKtsF7yW/cu3XMwYXze080tYerIYoLThKsyUR6PFujRLQGgjFodp+kYw4svINJxF3utnJrR9ywR7X69bd3Zix8KTXAplxpG27S0zSRMhNKiXT38nG08Wa72+3LaoWI0EVBJaw0KOH3OIVjZdVDKA5Aotc81H3SMTUTN9NpWrB9z41jNqoGjXcLZyx88OGx0MCvlalIpljYJ5+VOoq0S4ex7F47dvwiOk729wOnqxmyxao2q7deDu7RufJDMsXc4ZC9g+ij4NBunzh0vvkkYU6tFGnAZycMfYEVWQPdFCZDPr2GxrOPQoMei9euwBNsqVDQT40JuXZAKIfvr/FOAi5iDFPDmKYpEFt4ciG6E1b7NM5BJzh+qgGUtwKXm7HdKO40EoADXysiLTihdv0+NB7Nf4yGwXAsRvuIR5KYXtyJ53A9E0H2KdA7bDl1oM9xij2m/m4rfJEKkF98RYLGoxOTiAN4PBbWKBUcvLmLb3E6aj4hsCSPyTRbWMw6cwMat1598FFDeiyqzcU0SBIunJQolhTuxELR3xJv8SpgjaTaITfMBfIdF/3CIiiJd2UNkuO7SwQTGfwCx6cm1ASEmqIK54IIRVVNbl4fPYfGwhfUmKfwLVhrYI4nImdzMLlCSp8bO5fWJqIwDC8q3qaCliheQYxG0xAk0phlFNEWdecPcOFKE1yIaBcJSjWlwYZAAhptUir2RgtSilJUvIFaEBOT/iLf91wzOY3p05mTSZzOnKfv+Sb2dEqpY1GzJkRsDonHC7J1cF6TRwTs8Q202MUe6wZfvaiUsEoxaQeD/aHY9jCmA5UFNTY+lanB4qBGinHg4/CpaDwQDkMmG0MsJwV2MorTP5w/amfo2jW8NISGc0NYAB7whOCLcYOt3BYPehOfKvbEFg4BeDCeR56M5+VyMiQmCPnX/cKYBYwe54iiBjiwUfGoUZ5a38E8tMnOgeQwp0A55RgIcJW/YYfZOxCjGkCjBAFbNt2Q/ha7rz0CCZGYIHteIG5wIeiJnMqMx2kBDdS3KPD0gtDwvtZhAA2yh2wbvJqANEUAPJSK8aCI0eiFDbI7ViNGlIJyCABlkRAW0BBjSmhsfPakxqupdeWg2XWiLxgVHgATq1pk716aZNs1/L1lu1UcDytiHIAwkEnoSfKgmvGnRzqdERpgov7ar7F7V/+Z68eVh4kEGoAa1kP2vyeigFAsoIcINDigOiWYhIqCGrRgGhhTy57WWJuryzGlJEj/4LlhBAKkiIoDmPowYWzS6Q4FOpBOFZGgQoWhstjuJEEJbSE1OKQuT0oNcvvFOusbSA0ycPjm1WCUpa4HFgtdxQG6a+ivuxZw2UzDn4WQIHCgRKeF0kiPvfeUBnnbWse1Vnk8fEiNXQMnzvQNH49DA4g4lAZQcdDC1RjqgdXwZUF0GBxRQgK4FgyDGrCoeUaDTMBDStgbwQYODu67Cg+h4feghYpDs2UNxOTXcMOwGjqMuN9ClkZ6rOppDeNRRx4pn8e2/tPJAyNSJAzs1Sq7ybXKrQ10WeHUhcYvwSxMeUOCGAv9gzxaiDeNdBoWWkMzP1BnGtqDwOPEoXOoEF3n1iPmevzvaksPn4AbBC1CPouAa2E1aFHzrIZhYaqeMhqSKxxYyVOoEKUBhAfiIB0i//Po8rZhkzBZgHaLhM8CCAtMJBS/eY4GeTXXqO/ZrTVMgZw+eu4sPcL2sss4BCYR0sNF974jBysh68IUxiUm4deAAxgGo+nZJ56jIclXWgiEEkZjNzwOH7p7aiSK/5nwJ6Wqzs+DrHkfbEsFq0uX9zpXghbbgY3CWhgNZBEZXS54joZhYalRT0HDjCqY4BejBpN9vGSZRCgCFaJEnFhsf32b7X1noxToYCTsJYoORoIW1EAUo7N4u3A1LPmXLyAiFa7I21dZ6EdwF29QVAjzEIlYD6JNtohRj0lwHFsVBBK6uG0WI6IyRiPFKqPorkEKlQ+NeiOlBhW4gjwOijv1g3FYQIMm9CBaJORgzJxt2X3joBRsVeAcYUTfNqCISKIYiSSWcx5wNByRibmDMGk0UineIM67kvtRIMm+6yMYWBxVKhB8BwIPo6K7FXK0xDP7GvbwpaAldBIECioMWdvfi0UoRIo/ayUP9NYg9+crc2+OzLQarYak1ZqZmdkYS0eK4+ApeUTuPbqnuWXhtotvDyyaR1gkTwXjgr9+IuPhxdXqt9yaZ3E1XNYKuYX5WqWy/O7d0tLSx49vwPM70fixxLF7PGPzd5MLGskffEiaWP7wKRqsWLjBRj3n3mZX/ju3uNlswujW3kuBlZWVxcXF2dkfv1ZXv3ypVmu195ncZN6zuBq9yefL5UJhEpRKpRx4Bp4opqcftJN5LMgQbGPNsLErW99nTAMe6BnJgRLAuQqFcjnPrvfmH/6z3VkmchMJAAAAAElFTkSuQmCC", 17: "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", 18: "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", diff --git a/custom_components/dreame_vacuum/dreame/types.py b/custom_components/dreame_vacuum/dreame/types.py index a2ca20c..6969aa8 100644 --- a/custom_components/dreame_vacuum/dreame/types.py +++ b/custom_components/dreame_vacuum/dreame/types.py @@ -107,7 +107,6 @@ ATTR_Y1: Final = "y1" ATTR_Y2: Final = "y2" ATTR_Y3: Final = "y3" -ATTR_CALIBRATION: Final = "calibration_points" ATTR_CHARGER: Final = "charger_position" ATTR_IS_EMPTY: Final = "is_empty" ATTR_NO_GO_AREAS: Final = "no_go_areas" @@ -2102,18 +2101,18 @@ def __init__( def set_segment(self, map_data): if map_data and map_data.segments and map_data.pixel_type is not None: - obstacle_pixel = map_data.pixel_type[ - int((self.x - map_data.dimensions.left) / map_data.dimensions.grid_size), - int((self.y - map_data.dimensions.top) / map_data.dimensions.grid_size), - ] - - if obstacle_pixel not in map_data.segments: - for k, v in map_data.segments.items(): - if v.check_point(self.x, self.y, map_data.dimensions.grid_size * 4): - self.segment = v.name - break - else: - self.segment = map_data.segments[obstacle_pixel].name + x = int((self.x - map_data.dimensions.left) / map_data.dimensions.grid_size) + y = int((self.y - map_data.dimensions.top) / map_data.dimensions.grid_size) + if x >= 0 and x < map_data.dimensions.width and y >= 0 and y < map_data.dimensions.height: + obstacle_pixel = map_data.pixel_type[x,y] + + if obstacle_pixel not in map_data.segments: + for k, v in map_data.segments.items(): + if v.check_point(self.x, self.y, map_data.dimensions.grid_size * 4): + self.segment = v.name + break + else: + self.segment = map_data.segments[obstacle_pixel].name def as_dict(self) -> Dict[str, Any]: attributes = super().as_dict() @@ -3558,6 +3557,11 @@ class MapRendererData: ai_outborders: list[list[int]] | None = None ai_outborders_new: list[list[int]] | None = None ai_outborders_2d: list[list[int]] | None = None + second_cleaning: int | None = None + mop_wash_count: int | None = None + dust_collection_count: int | None = None + multiple_cleaning_time: int | None = None + dos: int | None = None ai_furniture_warning: int | None = None walls_info: Any | None = None walls_info_new: Any | None = None diff --git a/custom_components/dreame_vacuum/entity.py b/custom_components/dreame_vacuum/entity.py index 05fc80c..985e9c5 100644 --- a/custom_components/dreame_vacuum/entity.py +++ b/custom_components/dreame_vacuum/entity.py @@ -169,15 +169,16 @@ async def _try_command(self, mask_error, func, *args, **kwargs) -> bool: @property def device_info(self) -> DeviceInfo: """Return device information about this Dreame Vacuum device.""" - return DeviceInfo( - connections={(CONNECTION_NETWORK_MAC, self.device.mac)}, - identifiers={(DOMAIN, self.device.mac)}, - name=self.device.name, - manufacturer=self.device.info.manufacturer, - model=self.device.info.model, - sw_version=self.device.info.firmware_version, - hw_version=self.device.info.hardware_version, - ) + if self.device.info: + return DeviceInfo( + connections={(CONNECTION_NETWORK_MAC, self.device.mac)}, + identifiers={(DOMAIN, self.device.mac)}, + name=self.device.name, + manufacturer=self.device.info.manufacturer, + model=self.device.info.model, + sw_version=self.device.info.firmware_version, + hw_version=self.device.info.hardware_version, + ) @property def available(self) -> bool: diff --git a/custom_components/dreame_vacuum/manifest.json b/custom_components/dreame_vacuum/manifest.json index 79e75e6..a1646c6 100644 --- a/custom_components/dreame_vacuum/manifest.json +++ b/custom_components/dreame_vacuum/manifest.json @@ -19,5 +19,5 @@ "tzlocal", "paho-mqtt" ], - "version": "v2.0.0b11" + "version": "v2.0.0b13" } \ No newline at end of file diff --git a/custom_components/dreame_vacuum/recorder.py b/custom_components/dreame_vacuum/recorder.py index 21b86d4..df3d2df 100644 --- a/custom_components/dreame_vacuum/recorder.py +++ b/custom_components/dreame_vacuum/recorder.py @@ -20,12 +20,23 @@ ATTR_SELF_CLEAN_TIME, ATTR_MOP_PAD, ATTR_CALIBRATION, + ATTR_SELECTED, ATTR_CLEANING_HISTORY_PICTURE, ATTR_CRUISING_HISTORY_PICTURE, ATTR_OBSTACLE_PICTURE, ATTR_RECOVERY_MAP_PICTURE, ATTR_RECOVERY_MAP_FILE, ATTR_WIFI_MAP_PICTURE, + ATTR_VACUUM_STATE, + ATTR_MAPPING_AVAILABLE, + ATTR_WASHING_AVAILABLE, + ATTR_DRYING_AVAILABLE, + ATTR_DRAINING_AVAILABLE, + ATTR_DUST_COLLECTION_AVAILABLE, + ATTR_SEGMENT_CLEANING, + ATTR_ZONE_CLEANING, + ATTR_SPOT_CLEANING, + ATTR_CRUSING, ) from .dreame.types import ( @@ -41,6 +52,7 @@ "entity_picture", ATTR_ROOMS, ATTR_CALIBRATION, + ATTR_SELECTED, ATTR_CLEANING_HISTORY_PICTURE, ATTR_CRUISING_HISTORY_PICTURE, ATTR_OBSTACLE_PICTURE, @@ -68,6 +80,16 @@ ATTR_SELF_CLEAN_AREA, ATTR_SELF_CLEAN_TIME, ATTR_MOP_PAD, + ATTR_VACUUM_STATE, + ATTR_MAPPING_AVAILABLE, + ATTR_WASHING_AVAILABLE, + ATTR_DRYING_AVAILABLE, + ATTR_DRAINING_AVAILABLE, + ATTR_DUST_COLLECTION_AVAILABLE, + ATTR_SEGMENT_CLEANING, + ATTR_ZONE_CLEANING, + ATTR_SPOT_CLEANING, + ATTR_CRUSING, "fan_speed_list", "fan_speed", "battery_level", @@ -112,6 +134,10 @@ DreameVacuumProperty.NATION_MATCHED.name.lower(), DreameVacuumProperty.TOTAL_RUNTIME.name.lower(), DreameVacuumProperty.TOTAL_CRUISE_TIME.name.lower(), + DreameVacuumProperty.DRYING_PROGRESS.name.lower(), + DreameVacuumProperty.CLEANING_PROGRESS.name.lower(), + DreameVacuumProperty.INTELLIGENT_RECOGNITION.name.lower(), + DreameVacuumProperty.MULTI_FLOOR_MAP.name.lower(), } diff --git a/custom_components/dreame_vacuum/strings.json b/custom_components/dreame_vacuum/strings.json index 9d77782..a20b626 100644 --- a/custom_components/dreame_vacuum/strings.json +++ b/custom_components/dreame_vacuum/strings.json @@ -858,7 +858,7 @@ } }, "vacuum_set_restricted_zone": { - "name": "Set Restriced Zone", + "name": "Set Restricted Zone", "description": "Define virtual walls, restricted zones, and/or no mop zones.", "fields": { "walls": { diff --git a/custom_components/dreame_vacuum/translations/en.json b/custom_components/dreame_vacuum/translations/en.json index f647a61..1172e32 100644 --- a/custom_components/dreame_vacuum/translations/en.json +++ b/custom_components/dreame_vacuum/translations/en.json @@ -859,7 +859,7 @@ } }, "vacuum_set_restricted_zone": { - "name": "Set Restriced Zone", + "name": "Set Restricted Zone", "description": "Define virtual walls, restricted zones, and/or no mop zones.", "fields": { "walls": { diff --git a/custom_components/dreame_vacuum/vacuum.py b/custom_components/dreame_vacuum/vacuum.py index 0b4625c..8235ef7 100644 --- a/custom_components/dreame_vacuum/vacuum.py +++ b/custom_components/dreame_vacuum/vacuum.py @@ -668,6 +668,7 @@ def _set_attrs(self): self._attr_supported_features = SUPPORT_DREAME if ( not ( + self.device.status and self.device.status.started and ( self.device.status.customized_cleaning @@ -1004,7 +1005,7 @@ async def async_install_voice_pack(self, lang_id, url, md5, size, **kwargs) -> N size, ) - async def async_send_command(self, command: str, params, **kwargs) -> None: + async def async_send_command(self, command: str, params = None, **kwargs) -> None: """Send a command to a vacuum cleaner.""" await self._try_command("Unable to call send_command: %s", self.device.send_command, command, params) diff --git a/docs/entities.md b/docs/entities.md index 76eb06e..8b20c12 100644 --- a/docs/entities.md +++ b/docs/entities.md @@ -6,7 +6,7 @@ - Some entities may not be available on devices with older firmware versions like *customized_cleaning* and *cleaning_mode* that are also not available on valetudo. - Most of the entities including the vacuum entity has dynamic icons for their state and can be overridden from entity settings. - Most of the sensor and all select entities returns their current raw integer value on `raw_value`, `map_id` or `segment_id` attributes for ease of use on automations and services. -- All entities has dynamic refresh rate determined by its change range and device state. Integration only inform Home Assistant when a device property has changed trough listeners. This is more like a *local_push* type of approach instead of *local_pull* but please note that it may take time entity to reflect the changes when you edit related setting from the official App. +- All entities has dynamic refresh rate determined by its change range and device state. Integration only inform Home Assistant when a device property has changed through listeners. This is more like a *local_push* type of approach instead of *local_pull* but please note that it may take time entity to reflect the changes when you edit related setting from the official App. - Some entities has custom availability rules for another specific entity or state. E.g. *tight_mopping* entity will become *unavailable* when water tank or mop pad is not attached. (All off the rules extracted from the official App) - Exposed cloud connected entities for all available settings that are stored not on the device but on specific map data itself. E.g. *map_rotation* - Generated entities have the following naming schema: @@ -86,7 +86,7 @@ | `task_status` | Task status of the robot | | `water_tank` | Water tank status of the robot | Available on vacuums with water tank | `mop_pad` | Water mop pad status of the robot | Available on vacuums with self-wash base -| `dust_collection` | Dust collection is available, not available or not preformed due to do not disturb settings | Available on vacuums with auto-empty station +| `dust_collection` | Dust collection is available, not available or not performed due to do not disturb settings | Available on vacuums with auto-empty station | `auto_empty_status` | Status of auto empty dock | Available on vacuums with auto-empty station | `self_wash_base_status` | Status of self-wash base | Available on vacuums with self-wash base | `error` | Fault code description of robot | Error reporting diff --git a/docs/map.md b/docs/map.md index da2def4..82e7152 100644 --- a/docs/map.md +++ b/docs/map.md @@ -88,7 +88,7 @@ Automatically generated saved and live map entities for map editing and automati -**For more info about map entities** +**For more info about map entities** ### Dynamic room entities for selected map diff --git a/docs/services.md b/docs/services.md index 1fc9791..29899ad 100644 --- a/docs/services.md +++ b/docs/services.md @@ -215,13 +215,22 @@ Start selected spot cleaning with optional customized cleaning parameters. - 3 target: entity_id: vacuum.vacuum - ```tity_id: vacuum.vacuum ``` -### `dreame_vacuum.vacuum_go_to` +### `dreame_vacuum.vacuum_goto` TODO +- Go to at [819, -2235] and stop + ```yaml + service: dreame_vacuum.vacuum_goto + data: + x: 819 + y: -2235 + target: + entity_id: vacuum.vacuum + ``` + ### `dreame_vacuum.vacuum_follow_path` TODO diff --git a/docs/supported_devices.md b/docs/supported_devices.md index 1edd960..ba0c67b 100644 --- a/docs/supported_devices.md +++ b/docs/supported_devices.md @@ -1,7 +1,7 @@ # Supported Devices ## Dreame -| Name | Model | +| Name | Model | |-------------------------------------|--------| | C9 | dreame.vacuum.r2260 | | D10 Plus | dreame.vacuum.r2205 | @@ -19,10 +19,13 @@ | L10 Prime | dreame.vacuum.r2251a | | L10 Pro | dreame.vacuum.p2029 | | L10 Ultra | dreame.vacuum.r2257o | -| L10s Prime | dreame.vacuum.r2232d | -| L10s Prime | dreame.vacuum.r2232c | +| L10s Plus | dreame.vacuum.r2363a | | L10s Prime | dreame.vacuum.r2232b | +| L10s Prime | dreame.vacuum.r2232c | +| L10s Prime | dreame.vacuum.r2232d | | L10s Pro | dreame.vacuum.r2216o | +| L10s Pro Gen 2 | dreame.vacuum.r2364a | +| L10s Pro Ultra | dreame.vacuum.r9309a | | L10s Pro Ultra Heat | dreame.vacuum.r2338a | | L10s Pro Ultra Heat | dreame.vacuum.r2377 | | L10s Ultra | dreame.vacuum.r2228d | @@ -43,9 +46,11 @@ | L20 Ultra Complete | dreame.vacuum.r2394j | | L20 Ultra Complete | dreame.vacuum.r2394k | | L20 Ultra Complete | dreame.vacuum.r2394u | +| L30 Pro Ultra | dreame.vacuum.r9317a | | L30 Ultra | dreame.vacuum.r2361a | | Master One | dreame.vacuum.r2310a | | Master Pro | dreame.vacuum.r2310 | +| P10s Pro | dreame.vacuum.r2462 | | S10 | dreame.vacuum.r2228 | | S10 Plus | dreame.vacuum.r2246 | | S10 Pro | dreame.vacuum.r2233 | @@ -54,6 +59,7 @@ | S10 Pro Max (Hot Water) | dreame.vacuum.r2360w | | S10 Pro Plus | dreame.vacuum.r2247 | | S10 Pro Plus (Mop Pad Swing) | dreame.vacuum.r9305 | +| S10 Pro Ultra (Double Roller Brush) | dreame.vacuum.r9312 | | S10 Pro Ultra (Mop Pad Swing) | dreame.vacuum.r9304 | | S10 Pro Ultra (Ultra-Thin embedded) | dreame.vacuum.r2310b | | S20 | dreame.vacuum.r2316 | @@ -65,6 +71,9 @@ | S20 Pro (Ultra-Thin embedded) | dreame.vacuum.r2310f | | S20 Pro Plus | dreame.vacuum.r2332 | | S20 Pro Plus (Hot Water) | dreame.vacuum.r2360 | +| S30 Pro | dreame.vacuum.r2412 | +| S30 Pro Ultra | dreame.vacuum.r2424 | +| S30 Pro Ultra (Ultra-Thin embedded) | dreame.vacuum.r2310d | | ~~TROUVER M1~~ | ~~dreame.vacuum.r2380r~~ | | W10 | dreame.vacuum.p2027 | | W10 Pro | dreame.vacuum.r2104 | @@ -80,13 +89,24 @@ | X20 Pro Plus | dreame.vacuum.r2253 | | X20 Pro Plus | dreame.vacuum.r2273a | | X30 | dreame.vacuum.r2375 | +| X30 Master | dreame.vacuum.r2450m | | X30 Pro | dreame.vacuum.r9301 | | X30 Pro (Ultra-Thin embedded) | dreame.vacuum.r2310g | +| X30 Ultra | dreame.vacuum.r9316k | +| X30 Ultra | dreame.vacuum.r9316h | +| X40 | dreame.vacuum.r2426 | +| X40 Pro | dreame.vacuum.r2416 | +| X40 Pro (Ultra-Thin embedded) | dreame.vacuum.r2310e | +| X40 Ultra | dreame.vacuum.r2416h | +| X40 Ultra | dreame.vacuum.r2416c | +| X40 Ultra | dreame.vacuum.r2416n | +| X40 Ultra | dreame.vacuum.r2416a | +| X40 Ultra Complete | dreame.vacuum.r2449k | +| X40 Ultra Complete | dreame.vacuum.r2449a | | Z10 Pro | dreame.vacuum.p2028 | ## Mijia | Name | Model | -| | | |------------------------------------|--------| | 1S | dreame.vacuum.r2254 | | 1T | dreame.vacuum.p2041 | @@ -114,7 +134,10 @@ | Name | Model | |-------------------------|--------| | 10 Robot Vacuum and Mop | dreame.vacuum.r2388 | +| G20 | dreame.vacuum.r2350 | | G20 Master | dreame.vacuum.r2212 | +| G30 | dreame.vacuum.r2435 | +| G30 Pro | dreame.vacuum.r2455 | | L600 | dreame.vacuum.p2157 | | M1 | dreame.vacuum.r2380 | | Z500 | dreame.vacuum.p2156o | @@ -123,7 +146,6 @@ # Unsupported Devices -## Mijia | Name | Model | |-------------------------------------------|--------| | 1C | dreame.vacuum.ma1808 | diff --git a/install b/install index 52a898e..ffc911b 100644 --- a/install +++ b/install @@ -27,7 +27,7 @@ if [ -n "$path" ]; then mkdir "$path/custom_components" fi cd "$path/custom_components" - wget "https://github.com/Tasshack/dreame-vacuum/releases/download/v2.0.0b11/dreame_vacuum.zip" + wget "https://github.com/Tasshack/dreame-vacuum/releases/download/v2.0.0b13/dreame_vacuum.zip" if [ -d "$path/custom_components/dreame_vacuum" ]; then rm -R "$path/custom_components/dreame_vacuum" fi