From a27c4d22e7dd21cf5ae454886c743d28db130803 Mon Sep 17 00:00:00 2001 From: Luke Date: Sat, 9 Sep 2023 15:42:13 -0400 Subject: [PATCH 1/8] feat: add datetime parsing in cleanrecord --- roborock/containers.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/roborock/containers.py b/roborock/containers.py index 67b9233..963fa35 100644 --- a/roborock/containers.py +++ b/roborock/containers.py @@ -408,7 +408,9 @@ def __post_init__(self) -> None: @dataclass class CleanRecord(RoborockBase): begin: Optional[int] = None + begin_datetime: datetime.datetime | None = None end: Optional[int] = None + end_datetime: datetime.datetime | None = None duration: Optional[int] = None area: Optional[int] = None square_meter_area: Optional[float] = None @@ -424,6 +426,8 @@ class CleanRecord(RoborockBase): def __post_init__(self) -> None: self.square_meter_area = round(self.area / 1000000, 1) if self.area is not None else None + self.begin_datetime = datetime.datetime.fromtimestamp(self.begin).astimezone(datetime.UTC) if self.begin else None + self.end_datetime = datetime.datetime.fromtimestamp(self.end).astimezone(datetime.UTC) if self.end else None @dataclass From 97f1922433acabc97e6e6c9eb06b331f5cb2b0a6 Mon Sep 17 00:00:00 2001 From: Luke Date: Sat, 9 Sep 2023 16:42:57 -0400 Subject: [PATCH 2/8] chore: lint --- roborock/containers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/roborock/containers.py b/roborock/containers.py index 963fa35..42ec9e5 100644 --- a/roborock/containers.py +++ b/roborock/containers.py @@ -426,7 +426,9 @@ class CleanRecord(RoborockBase): def __post_init__(self) -> None: self.square_meter_area = round(self.area / 1000000, 1) if self.area is not None else None - self.begin_datetime = datetime.datetime.fromtimestamp(self.begin).astimezone(datetime.UTC) if self.begin else None + self.begin_datetime = ( + datetime.datetime.fromtimestamp(self.begin).astimezone(datetime.UTC) if self.begin else None + ) self.end_datetime = datetime.datetime.fromtimestamp(self.end).astimezone(datetime.UTC) if self.end else None From d296a90f59a3590d4cdbc52c34ba50009310a90f Mon Sep 17 00:00:00 2001 From: Luke Date: Sat, 9 Sep 2023 16:46:39 -0400 Subject: [PATCH 3/8] chore: lint --- roborock/containers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/roborock/containers.py b/roborock/containers.py index 50bdd57..aa70cf9 100644 --- a/roborock/containers.py +++ b/roborock/containers.py @@ -407,9 +407,9 @@ def __post_init__(self) -> None: @dataclass class CleanRecord(RoborockBase): - begin: Optional[int] = None + begin: int | None = None begin_datetime: datetime.datetime | None = None - end: Optional[int] = None + end: int | None = None end_datetime: datetime.datetime | None = None duration: int | None = None area: int | None = None From 4ab6dce0aeee0abaddc473459fcb6c2bc85a971c Mon Sep 17 00:00:00 2001 From: Luke Date: Sat, 9 Sep 2023 16:51:02 -0400 Subject: [PATCH 4/8] fix: timezone for non-3.11 --- roborock/containers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/roborock/containers.py b/roborock/containers.py index aa70cf9..3dd8e55 100644 --- a/roborock/containers.py +++ b/roborock/containers.py @@ -6,7 +6,7 @@ from dataclasses import asdict, dataclass from enum import Enum from typing import Any, NamedTuple - +from datetime import timezone from dacite import Config, from_dict from .code_mappings import ( @@ -427,9 +427,9 @@ class CleanRecord(RoborockBase): def __post_init__(self) -> None: self.square_meter_area = round(self.area / 1000000, 1) if self.area is not None else None self.begin_datetime = ( - datetime.datetime.fromtimestamp(self.begin).astimezone(datetime.UTC) if self.begin else None + datetime.datetime.fromtimestamp(self.begin).astimezone(timezone.utc) if self.begin else None ) - self.end_datetime = datetime.datetime.fromtimestamp(self.end).astimezone(datetime.UTC) if self.end else None + self.end_datetime = datetime.datetime.fromtimestamp(self.end).astimezone(timezone.utc) if self.end else None @dataclass From 8e5a63d7551982e586e53c8764944b097a56f641 Mon Sep 17 00:00:00 2001 From: Luke Date: Sat, 9 Sep 2023 16:51:22 -0400 Subject: [PATCH 5/8] chore: lint --- roborock/containers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/roborock/containers.py b/roborock/containers.py index 3dd8e55..0082181 100644 --- a/roborock/containers.py +++ b/roborock/containers.py @@ -4,9 +4,10 @@ import logging import re from dataclasses import asdict, dataclass +from datetime import timezone from enum import Enum from typing import Any, NamedTuple -from datetime import timezone + from dacite import Config, from_dict from .code_mappings import ( From 1f7d46c5e63f7dc7b553c33ddf2f6ea8cdcc0750 Mon Sep 17 00:00:00 2001 From: Luke Date: Sat, 9 Sep 2023 19:28:44 -0400 Subject: [PATCH 6/8] feat: add is_available for ha and here in future --- roborock/api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/roborock/api.py b/roborock/api.py index 747e3cb..e5d45d7 100644 --- a/roborock/api.py +++ b/roborock/api.py @@ -185,6 +185,7 @@ def __init__(self, endpoint: str, device_info: DeviceData) -> None: device_cache[device_info.device.duid] = cache self.cache: dict[CacheableAttribute, AttributeCache] = cache self._listeners: list[Callable[[str, CacheableAttribute, RoborockBase], None]] = [] + self.is_available: bool = False def __del__(self) -> None: self.release() From c3ce248ca0743cfd635692cf57b4b0e8475e1023 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 11 Sep 2023 15:30:36 -0400 Subject: [PATCH 7/8] fix: add timeout as a variable and set a longer default timeout for cloud --- roborock/api.py | 8 ++++---- roborock/cloud_api.py | 4 ++-- roborock/local_api.py | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/roborock/api.py b/roborock/api.py index e5d45d7..3b396db 100644 --- a/roborock/api.py +++ b/roborock/api.py @@ -70,7 +70,6 @@ _LOGGER = logging.getLogger(__name__) KEEPALIVE = 60 -QUEUE_TIMEOUT = 4 COMMANDS_SECURED = [ RoborockCommand.GET_MAP_V1, RoborockCommand.GET_MULTI_MAP, @@ -166,7 +165,7 @@ async def refresh_value(self): class RoborockClient: - def __init__(self, endpoint: str, device_info: DeviceData) -> None: + def __init__(self, endpoint: str, device_info: DeviceData, queue_timeout: int = 4) -> None: self.event_loop = get_running_loop_or_create_one() self.device_info = device_info self._endpoint = endpoint @@ -186,6 +185,7 @@ def __init__(self, endpoint: str, device_info: DeviceData) -> None: self.cache: dict[CacheableAttribute, AttributeCache] = cache self._listeners: list[Callable[[str, CacheableAttribute, RoborockBase], None]] = [] self.is_available: bool = False + self.queue_timeout = queue_timeout def __del__(self) -> None: self.release() @@ -323,12 +323,12 @@ async def validate_connection(self) -> None: async def _wait_response(self, request_id: int, queue: RoborockFuture) -> tuple[Any, VacuumError | None]: try: - (response, err) = await queue.async_get(QUEUE_TIMEOUT) + (response, err) = await queue.async_get(self.queue_timeout) if response == "unknown_method": raise UnknownMethodError("Unknown method") return response, err except (asyncio.TimeoutError, asyncio.CancelledError): - raise RoborockTimeout(f"id={request_id} Timeout after {QUEUE_TIMEOUT} seconds") from None + raise RoborockTimeout(f"id={request_id} Timeout after {self.queue_timeout} seconds") from None finally: self._waiting_queue.pop(request_id, None) diff --git a/roborock/cloud_api.py b/roborock/cloud_api.py index fbcc5ff..51e3e5e 100644 --- a/roborock/cloud_api.py +++ b/roborock/cloud_api.py @@ -29,12 +29,12 @@ class RoborockMqttClient(RoborockClient, mqtt.Client): _thread: threading.Thread _client_id: str - def __init__(self, user_data: UserData, device_info: DeviceData) -> None: + def __init__(self, user_data: UserData, device_info: DeviceData, queue_timeout: int = 10) -> None: rriot = user_data.rriot if rriot is None: raise RoborockException("Got no rriot data from user_data") endpoint = base64.b64encode(Utils.md5(rriot.k.encode())[8:14]).decode() - RoborockClient.__init__(self, endpoint, device_info) + RoborockClient.__init__(self, endpoint, device_info, queue_timeout) mqtt.Client.__init__(self, protocol=mqtt.MQTTv5) self._logger = RoborockLoggerAdapter(device_info.device.name, _LOGGER) self._mqtt_user = rriot.u diff --git a/roborock/local_api.py b/roborock/local_api.py index 32b9239..a692659 100644 --- a/roborock/local_api.py +++ b/roborock/local_api.py @@ -18,10 +18,10 @@ class RoborockLocalClient(RoborockClient, asyncio.Protocol): - def __init__(self, device_data: DeviceData): + def __init__(self, device_data: DeviceData, queue_timeout: int = 4): if device_data.host is None: raise RoborockException("Host is required") - super().__init__("abc", device_data) + super().__init__("abc", device_data, queue_timeout) self.host = device_data.host self._batch_structs: list[RoborockMessage] = [] self._executing = False From f4e82a320a52f5d4c57fffe45f2aadded3296b80 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 11 Sep 2023 15:33:04 -0400 Subject: [PATCH 8/8] chore: lint --- roborock/local_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/roborock/local_api.py b/roborock/local_api.py index a692659..73dcd3a 100644 --- a/roborock/local_api.py +++ b/roborock/local_api.py @@ -7,7 +7,7 @@ import async_timeout from . import DeviceData -from .api import COMMANDS_SECURED, QUEUE_TIMEOUT, RoborockClient +from .api import COMMANDS_SECURED, RoborockClient from .exceptions import CommandVacuumError, RoborockConnectionException, RoborockException from .protocol import MessageParser from .roborock_message import MessageRetry, RoborockMessage, RoborockMessageProtocol @@ -58,7 +58,7 @@ async def async_connect(self) -> None: try: if not self.is_connected(): self.sync_disconnect() - async with async_timeout.timeout(QUEUE_TIMEOUT): + async with async_timeout.timeout(self.queue_timeout): self._logger.info(f"Connecting to {self.host}") self.transport, _ = await self.event_loop.create_connection( # type: ignore lambda: self, self.host, 58867