From 36a6b15a641f70f3a3b6feb92a982c85c3294462 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sun, 22 Mar 2026 11:44:35 -0700 Subject: [PATCH 1/3] chore: Migrate to `typing.Self` and remove `__future__` annotations. --- roborock/broadcast_protocol.py | 2 -- roborock/data/code_mappings.py | 14 ++++++-------- roborock/data/v1/v1_code_mappings.py | 4 +++- roborock/devices/rpc/b01_q10_channel.py | 2 -- roborock/devices/traits/b01/q7/clean_summary.py | 2 -- roborock/devices/traits/v1/network_info.py | 2 -- roborock/diagnostics.py | 4 +--- roborock/exceptions.py | 2 -- roborock/protocol.py | 2 -- roborock/roborock_message.py | 5 ++--- roborock/roborock_typing.py | 5 ++--- roborock/util.py | 2 -- roborock/web_api.py | 2 -- 13 files changed, 14 insertions(+), 34 deletions(-) diff --git a/roborock/broadcast_protocol.py b/roborock/broadcast_protocol.py index a08bca94..e226a9cf 100644 --- a/roborock/broadcast_protocol.py +++ b/roborock/broadcast_protocol.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import asyncio import hashlib import json diff --git a/roborock/data/code_mappings.py b/roborock/data/code_mappings.py index 47cab2b8..d1f36b83 100644 --- a/roborock/data/code_mappings.py +++ b/roborock/data/code_mappings.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import logging from collections import namedtuple from enum import Enum, IntEnum, StrEnum @@ -17,7 +15,7 @@ def name(self) -> str: return super().name.lower() @classmethod - def _missing_(cls: type[RoborockEnum], key) -> RoborockEnum: + def _missing_(cls: type[Self], key) -> Self: if hasattr(cls, "unknown"): warning = f"Missing {cls.__name__} code: {key} - defaulting to 'unknown'" if warning not in completed_warnings: @@ -32,23 +30,23 @@ def _missing_(cls: type[RoborockEnum], key) -> RoborockEnum: return default_value @classmethod - def as_dict(cls: type[RoborockEnum]): + def as_dict(cls: type[Self]): return {i.name: i.value for i in cls if i.name != "missing"} @classmethod - def as_enum_dict(cls: type[RoborockEnum]): + def as_enum_dict(cls: type[Self]): return {i.value: i for i in cls if i.name != "missing"} @classmethod - def values(cls: type[RoborockEnum]) -> list[int]: + def values(cls: type[Self]) -> list[int]: return list(cls.as_dict().values()) @classmethod - def keys(cls: type[RoborockEnum]) -> list[str]: + def keys(cls: type[Self]) -> list[str]: return list(cls.as_dict().keys()) @classmethod - def items(cls: type[RoborockEnum]): + def items(cls: type[Self]): return cls.as_dict().items() diff --git a/roborock/data/v1/v1_code_mappings.py b/roborock/data/v1/v1_code_mappings.py index c0bab14b..2ad3a0b3 100644 --- a/roborock/data/v1/v1_code_mappings.py +++ b/roborock/data/v1/v1_code_mappings.py @@ -1,3 +1,5 @@ +from typing import Self + from ..code_mappings import RoborockEnum @@ -91,7 +93,7 @@ class RoborockStartType(RoborockEnum): class RoborockDssCodes(RoborockEnum): @classmethod - def _missing_(cls: type[RoborockEnum], key) -> RoborockEnum: + def _missing_(cls: type[Self], key) -> Self: # If the calculated value is not provided, then it should be viewed as okay. # As the math will sometimes result in you getting numbers that don't matter. return cls.okay # type: ignore diff --git a/roborock/devices/rpc/b01_q10_channel.py b/roborock/devices/rpc/b01_q10_channel.py index d27b148b..1e0510ba 100644 --- a/roborock/devices/rpc/b01_q10_channel.py +++ b/roborock/devices/rpc/b01_q10_channel.py @@ -1,7 +1,5 @@ """Thin wrapper around the MQTT channel for Roborock B01 Q10 devices.""" -from __future__ import annotations - import logging from collections.abc import AsyncGenerator from typing import Any diff --git a/roborock/devices/traits/b01/q7/clean_summary.py b/roborock/devices/traits/b01/q7/clean_summary.py index b49040f3..65fea0e8 100644 --- a/roborock/devices/traits/b01/q7/clean_summary.py +++ b/roborock/devices/traits/b01/q7/clean_summary.py @@ -4,8 +4,6 @@ and a `record_list` whose items contain a JSON string in `detail`. """ -from __future__ import annotations - import logging from roborock import CleanRecordDetail, CleanRecordList, CleanRecordSummary diff --git a/roborock/devices/traits/v1/network_info.py b/roborock/devices/traits/v1/network_info.py index bba8bc65..2bdca57a 100644 --- a/roborock/devices/traits/v1/network_info.py +++ b/roborock/devices/traits/v1/network_info.py @@ -1,7 +1,5 @@ """Trait for device network information.""" -from __future__ import annotations - import logging from roborock.data import NetworkInfo diff --git a/roborock/diagnostics.py b/roborock/diagnostics.py index f455f607..9f2276f6 100644 --- a/roborock/diagnostics.py +++ b/roborock/diagnostics.py @@ -9,8 +9,6 @@ DeviceManager. """ -from __future__ import annotations - import time from collections import Counter from collections.abc import Generator, Mapping @@ -49,7 +47,7 @@ def as_dict(self) -> Mapping[str, Any]: data[k] = v return data - def subkey(self, key: str) -> Diagnostics: + def subkey(self, key: str) -> "Diagnostics": """Return sub-Diagnostics object with the specified subkey. This will create a new Diagnostics object if one does not already exist diff --git a/roborock/exceptions.py b/roborock/exceptions.py index 3c5b8295..55e72b60 100644 --- a/roborock/exceptions.py +++ b/roborock/exceptions.py @@ -1,7 +1,5 @@ """Roborock exceptions.""" -from __future__ import annotations - class RoborockException(Exception): """Class for Roborock exceptions.""" diff --git a/roborock/protocol.py b/roborock/protocol.py index 828a432a..fc3af842 100644 --- a/roborock/protocol.py +++ b/roborock/protocol.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import binascii import gzip import hashlib diff --git a/roborock/roborock_message.py b/roborock/roborock_message.py index fbe9becf..7b04318e 100644 --- a/roborock/roborock_message.py +++ b/roborock/roborock_message.py @@ -1,7 +1,6 @@ -from __future__ import annotations - from dataclasses import dataclass, field from enum import StrEnum +from typing import Self from roborock import RoborockEnum from roborock.util import get_next_int, get_timestamp @@ -37,7 +36,7 @@ class RoborockDataProtocol(RoborockEnum): OFFLINE_STATUS = 135 @classmethod - def _missing_(cls: type[RoborockEnum], key) -> RoborockEnum: + def _missing_(cls: type[Self], key) -> Self: raise ValueError("%s not a valid key for Data Protocol", key) diff --git a/roborock/roborock_typing.py b/roborock/roborock_typing.py index 418f350c..71251324 100644 --- a/roborock/roborock_typing.py +++ b/roborock/roborock_typing.py @@ -1,7 +1,6 @@ -from __future__ import annotations - from dataclasses import dataclass, field from enum import Enum, StrEnum +from typing import Self from .data import ( CleanRecord, @@ -368,7 +367,7 @@ def __post_init__(self) -> None: ): self.dust_collection_mode_name = self.dock_summary.dust_collection_mode.mode.name - def update(self, device_prop: DeviceProp) -> None: + def update(self, device_prop: Self) -> None: if device_prop.status: self.status = device_prop.status if device_prop.clean_summary: diff --git a/roborock/util.py b/roborock/util.py index 8679511e..481759ec 100644 --- a/roborock/util.py +++ b/roborock/util.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import logging import math import time diff --git a/roborock/web_api.py b/roborock/web_api.py index 141cbeb5..a76d14c5 100644 --- a/roborock/web_api.py +++ b/roborock/web_api.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import base64 import hashlib import hmac From 88132bcbc9cac2cb8574555ece6ba37e13c3f575 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sun, 22 Mar 2026 11:56:37 -0700 Subject: [PATCH 2/3] chore: Use `typing.Self` for class-referencing type hints and dynamic instantiation --- roborock/device_features.py | 6 ++---- roborock/diagnostics.py | 8 ++++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/roborock/device_features.py b/roborock/device_features.py index b8ecf483..ef3ea19f 100644 --- a/roborock/device_features.py +++ b/roborock/device_features.py @@ -1,8 +1,6 @@ -from __future__ import annotations - from dataclasses import dataclass, field, fields from enum import IntEnum, StrEnum -from typing import Any +from typing import Any, Self from roborock.data.code_mappings import RoborockProductNickname from roborock.data.containers import RoborockBase @@ -566,7 +564,7 @@ def from_feature_flags( new_feature_info_str: str, feature_info: list[int], product_nickname: RoborockProductNickname | None, - ) -> DeviceFeatures: + ) -> Self: """Creates a DeviceFeatures instance from raw feature flags. :param new_feature_info: A int from get_init_status (sometimes can be found in homedata, but it is not always) :param new_feature_info_str: A hex string from get_init_status or home_data. diff --git a/roborock/diagnostics.py b/roborock/diagnostics.py index 9f2276f6..c238209c 100644 --- a/roborock/diagnostics.py +++ b/roborock/diagnostics.py @@ -13,7 +13,7 @@ from collections import Counter from collections.abc import Generator, Mapping from contextlib import contextmanager -from typing import Any, TypeVar, cast +from typing import Any, Self, TypeVar, cast class Diagnostics: @@ -26,7 +26,7 @@ class Diagnostics: def __init__(self) -> None: """Initialize Diagnostics.""" self._counter: Counter = Counter() - self._subkeys: dict[str, Diagnostics] = {} + self._subkeys: dict[str, Self] = {} def increment(self, key: str, count: int = 1) -> None: """Increment a counter for the specified key/event.""" @@ -47,7 +47,7 @@ def as_dict(self) -> Mapping[str, Any]: data[k] = v return data - def subkey(self, key: str) -> "Diagnostics": + def subkey(self, key: str) -> Self: """Return sub-Diagnostics object with the specified subkey. This will create a new Diagnostics object if one does not already exist @@ -61,7 +61,7 @@ def subkey(self, key: str) -> "Diagnostics": The Diagnostics object for the specified subkey. """ if key not in self._subkeys: - self._subkeys[key] = Diagnostics() + self._subkeys[key] = type(self)() return self._subkeys[key] @contextmanager From ba2fdf4a4b4b64419da11d8d29e0f8fd2b06569a Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sun, 22 Mar 2026 11:58:01 -0700 Subject: [PATCH 3/3] chore: Update roborock/roborock_message.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- roborock/roborock_message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roborock/roborock_message.py b/roborock/roborock_message.py index 7b04318e..7c7f65a6 100644 --- a/roborock/roborock_message.py +++ b/roborock/roborock_message.py @@ -37,7 +37,7 @@ class RoborockDataProtocol(RoborockEnum): @classmethod def _missing_(cls: type[Self], key) -> Self: - raise ValueError("%s not a valid key for Data Protocol", key) + raise ValueError(f"{key} not a valid key for Data Protocol") class RoborockDyadDataProtocol(RoborockEnum):