python-roborock 4.2.1__tar.gz → 4.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_roborock-4.2.1 → python_roborock-4.3.0}/PKG-INFO +1 -1
- {python_roborock-4.2.1 → python_roborock-4.3.0}/pyproject.toml +1 -1
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/data/containers.py +7 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/data/v1/v1_containers.py +35 -7
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/device.py +8 -6
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/device_manager.py +10 -5
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/__init__.py +7 -1
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/device_features.py +30 -6
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/diagnostics.py +25 -5
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/web_api.py +2 -2
- {python_roborock-4.2.1 → python_roborock-4.3.0}/.gitignore +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/LICENSE +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/README.md +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/__init__.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/broadcast_protocol.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/callbacks.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/cli.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/const.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/data/__init__.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/data/b01_q10/__init__.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/data/b01_q10/b01_q10_code_mappings.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/data/b01_q10/b01_q10_containers.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/data/b01_q7/__init__.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/data/b01_q7/b01_q7_code_mappings.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/data/b01_q7/b01_q7_containers.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/data/code_mappings.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/data/dyad/__init__.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/data/dyad/dyad_code_mappings.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/data/dyad/dyad_containers.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/data/v1/__init__.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/data/v1/v1_clean_modes.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/data/v1/v1_code_mappings.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/data/zeo/__init__.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/data/zeo/zeo_code_mappings.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/data/zeo/zeo_containers.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/device_features.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/README.md +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/__init__.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/cache.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/file_cache.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/rpc/__init__.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/rpc/a01_channel.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/rpc/b01_q10_channel.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/rpc/b01_q7_channel.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/rpc/v1_channel.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/__init__.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/a01/__init__.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/b01/__init__.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/b01/q10/__init__.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/b01/q10/command.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/b01/q7/__init__.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/traits_mixin.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/child_lock.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/clean_summary.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/command.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/common.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/consumeable.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/do_not_disturb.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/dust_collection_mode.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/flow_led_status.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/home.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/led_status.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/map_content.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/maps.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/network_info.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/rooms.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/routines.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/smart_wash_params.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/status.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/valley_electricity_timer.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/volume.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/wash_towel_mode.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/transport/__init__.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/transport/channel.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/transport/local_channel.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/transport/mqtt_channel.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/exceptions.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/map/__init__.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/map/map_parser.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/mqtt/__init__.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/mqtt/health_manager.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/mqtt/roborock_session.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/mqtt/session.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/protocol.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/protocols/__init__.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/protocols/a01_protocol.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/protocols/b01_q10_protocol.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/protocols/b01_q7_protocol.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/protocols/v1_protocol.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/py.typed +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/roborock_message.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/roborock_typing.py +0 -0
- {python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/util.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-roborock
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.3.0
|
|
4
4
|
Summary: A package to control Roborock vacuums.
|
|
5
5
|
Project-URL: Repository, https://github.com/humbertogontijo/python-roborock
|
|
6
6
|
Project-URL: Documentation, https://python-roborock.readthedocs.io/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "python-roborock"
|
|
3
|
-
version = "4.
|
|
3
|
+
version = "4.3.0"
|
|
4
4
|
description = "A package to control Roborock vacuums."
|
|
5
5
|
authors = [{ name = "humbertogontijo", email = "humbertogontijo@users.noreply.github.com" }, {name="Lash-L"}, {name="allenporter"}]
|
|
6
6
|
requires-python = ">=3.11, <4"
|
|
@@ -232,6 +232,13 @@ class HomeDataProduct(RoborockBase):
|
|
|
232
232
|
"""Return a string with key product information for logging purposes."""
|
|
233
233
|
return f"{self.name} (model={self.model}, category={self.category})"
|
|
234
234
|
|
|
235
|
+
@cached_property
|
|
236
|
+
def supported_schema_codes(self) -> set[str]:
|
|
237
|
+
"""Return a set of fields that are supported by the device."""
|
|
238
|
+
if self.schema is None:
|
|
239
|
+
return set()
|
|
240
|
+
return {schema.code for schema in self.schema if schema.code is not None}
|
|
241
|
+
|
|
235
242
|
|
|
236
243
|
@dataclass
|
|
237
244
|
class HomeDataDevice(RoborockBase):
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import logging
|
|
3
|
-
from dataclasses import dataclass
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from enum import StrEnum
|
|
4
5
|
from typing import Any
|
|
5
6
|
|
|
6
7
|
from roborock.const import (
|
|
@@ -91,12 +92,39 @@ from .v1_code_mappings import (
|
|
|
91
92
|
_LOGGER = logging.getLogger(__name__)
|
|
92
93
|
|
|
93
94
|
|
|
95
|
+
class FieldNameBase(StrEnum):
|
|
96
|
+
"""A base enum class that represents a field name in a RoborockBase dataclass."""
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class StatusField(FieldNameBase):
|
|
100
|
+
"""An enum that represents a field in the `Status` class.
|
|
101
|
+
|
|
102
|
+
This is used with `roborock.devices.traits.v1.status.DeviceFeaturesTrait`
|
|
103
|
+
to understand if a feature is supported by the device using `is_field_supported`.
|
|
104
|
+
|
|
105
|
+
The enum values are names of fields in the `Status` class. Each field is
|
|
106
|
+
annotated with `requires_schema_code` metadata to map the field to a schema
|
|
107
|
+
code in the product schema, which may have a different name than the field/attribute name.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
STATE = "state"
|
|
111
|
+
BATTERY = "battery"
|
|
112
|
+
FAN_POWER = "fan_power"
|
|
113
|
+
WATER_BOX_MODE = "water_box_mode"
|
|
114
|
+
CHARGE_STATUS = "charge_status"
|
|
115
|
+
DRY_STATUS = "dry_status"
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _requires_schema_code(requires_schema_code: str, default=None) -> Any:
|
|
119
|
+
return field(metadata={"requires_schema_code": requires_schema_code}, default=default)
|
|
120
|
+
|
|
121
|
+
|
|
94
122
|
@dataclass
|
|
95
123
|
class Status(RoborockBase):
|
|
96
124
|
msg_ver: int | None = None
|
|
97
125
|
msg_seq: int | None = None
|
|
98
|
-
state: RoborockStateCode | None = None
|
|
99
|
-
battery: int | None = None
|
|
126
|
+
state: RoborockStateCode | None = _requires_schema_code("state", default=None)
|
|
127
|
+
battery: int | None = _requires_schema_code("battery", default=None)
|
|
100
128
|
clean_time: int | None = None
|
|
101
129
|
clean_area: int | None = None
|
|
102
130
|
error_code: RoborockErrorCode | None = None
|
|
@@ -109,12 +137,12 @@ class Status(RoborockBase):
|
|
|
109
137
|
back_type: int | None = None
|
|
110
138
|
wash_phase: int | None = None
|
|
111
139
|
wash_ready: int | None = None
|
|
112
|
-
fan_power: RoborockFanPowerCode | None = None
|
|
140
|
+
fan_power: RoborockFanPowerCode | None = _requires_schema_code("fan_power", default=None)
|
|
113
141
|
dnd_enabled: int | None = None
|
|
114
142
|
map_status: int | None = None
|
|
115
143
|
is_locating: int | None = None
|
|
116
144
|
lock_status: int | None = None
|
|
117
|
-
water_box_mode: RoborockMopIntensityCode | None = None
|
|
145
|
+
water_box_mode: RoborockMopIntensityCode | None = _requires_schema_code("water_box_mode", default=None)
|
|
118
146
|
water_box_carriage_status: int | None = None
|
|
119
147
|
mop_forbidden_enable: int | None = None
|
|
120
148
|
camera_status: int | None = None
|
|
@@ -132,13 +160,13 @@ class Status(RoborockBase):
|
|
|
132
160
|
collision_avoid_status: int | None = None
|
|
133
161
|
switch_map_mode: int | None = None
|
|
134
162
|
dock_error_status: RoborockDockErrorCode | None = None
|
|
135
|
-
charge_status: int | None = None
|
|
163
|
+
charge_status: int | None = _requires_schema_code("charge_status", default=None)
|
|
136
164
|
unsave_map_reason: int | None = None
|
|
137
165
|
unsave_map_flag: int | None = None
|
|
138
166
|
wash_status: int | None = None
|
|
139
167
|
distance_off: int | None = None
|
|
140
168
|
in_warmup: int | None = None
|
|
141
|
-
dry_status: int | None = None
|
|
169
|
+
dry_status: int | None = _requires_schema_code("drying_status", default=None)
|
|
142
170
|
rdt: int | None = None
|
|
143
171
|
clean_percent: int | None = None
|
|
144
172
|
rss: int | None = None
|
|
@@ -226,9 +226,11 @@ class RoborockDevice(ABC, TraitsMixin):
|
|
|
226
226
|
"""Return diagnostics information about the device."""
|
|
227
227
|
extra: dict[str, Any] = {}
|
|
228
228
|
if self.v1_properties:
|
|
229
|
-
extra["traits"] =
|
|
230
|
-
return
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
229
|
+
extra["traits"] = self.v1_properties.as_dict()
|
|
230
|
+
return redact_device_data(
|
|
231
|
+
{
|
|
232
|
+
"device": self.device_info.as_dict(),
|
|
233
|
+
"product": self.product.as_dict(),
|
|
234
|
+
**extra,
|
|
235
|
+
}
|
|
236
|
+
)
|
|
@@ -16,7 +16,7 @@ from roborock.data import (
|
|
|
16
16
|
UserData,
|
|
17
17
|
)
|
|
18
18
|
from roborock.devices.device import DeviceReadyCallback, RoborockDevice
|
|
19
|
-
from roborock.diagnostics import Diagnostics
|
|
19
|
+
from roborock.diagnostics import Diagnostics, redact_device_data
|
|
20
20
|
from roborock.exceptions import RoborockException
|
|
21
21
|
from roborock.map.map_parser import MapParserConfig
|
|
22
22
|
from roborock.mqtt.roborock_session import create_lazy_mqtt_session
|
|
@@ -76,6 +76,7 @@ class DeviceManager:
|
|
|
76
76
|
self._devices: dict[str, RoborockDevice] = {}
|
|
77
77
|
self._mqtt_session = mqtt_session
|
|
78
78
|
self._diagnostics = diagnostics
|
|
79
|
+
self._home_data: HomeData | None = None
|
|
79
80
|
|
|
80
81
|
async def discover_devices(self, prefer_cache: bool = True) -> list[RoborockDevice]:
|
|
81
82
|
"""Discover all devices for the logged-in user."""
|
|
@@ -91,9 +92,9 @@ class DeviceManager:
|
|
|
91
92
|
raise
|
|
92
93
|
_LOGGER.debug("Failed to fetch home data, using cached data: %s", ex)
|
|
93
94
|
await self._cache.set(cache_data)
|
|
94
|
-
|
|
95
|
+
self._home_data = cache_data.home_data
|
|
95
96
|
|
|
96
|
-
device_products =
|
|
97
|
+
device_products = self._home_data.device_products
|
|
97
98
|
_LOGGER.debug("Discovered %d devices", len(device_products))
|
|
98
99
|
|
|
99
100
|
# These are connected serially to avoid overwhelming the MQTT broker
|
|
@@ -106,7 +107,7 @@ class DeviceManager:
|
|
|
106
107
|
if duid in self._devices:
|
|
107
108
|
continue
|
|
108
109
|
try:
|
|
109
|
-
new_device = self._device_creator(
|
|
110
|
+
new_device = self._device_creator(self._home_data, device, product)
|
|
110
111
|
except UnsupportedDeviceError:
|
|
111
112
|
_LOGGER.info("Skipping unsupported device %s %s", product.summary_info(), device.summary_info())
|
|
112
113
|
unsupported_devices_counter.increment(device.pv or "unknown")
|
|
@@ -136,7 +137,11 @@ class DeviceManager:
|
|
|
136
137
|
|
|
137
138
|
def diagnostic_data(self) -> Mapping[str, Any]:
|
|
138
139
|
"""Return diagnostics information about the device manager."""
|
|
139
|
-
return
|
|
140
|
+
return {
|
|
141
|
+
"home_data": redact_device_data(self._home_data.as_dict()) if self._home_data else None,
|
|
142
|
+
"devices": [device.diagnostic_data() for device in self._devices.values()],
|
|
143
|
+
"diagnostics": self._diagnostics.as_dict(),
|
|
144
|
+
}
|
|
140
145
|
|
|
141
146
|
|
|
142
147
|
@dataclass
|
|
@@ -44,6 +44,12 @@ optional traits:
|
|
|
44
44
|
available features.
|
|
45
45
|
- `requires_dock_type` - If set, this is a function that accepts a `RoborockDockTypeCode`
|
|
46
46
|
and returns a boolean indicating whether the trait is supported for that dock type.
|
|
47
|
+
|
|
48
|
+
Additionally, DeviceFeaturesTrait has a method `is_field_supported` that is used to
|
|
49
|
+
check individual trait field values. This is a more fine grained version to allow
|
|
50
|
+
optional fields in a dataclass, vs the above feature checks that apply to an entire
|
|
51
|
+
trait. The `requires_schema_code` field metadata attribute is a string of the schema
|
|
52
|
+
code in HomeDataProduct Schema that is required for the field to be supported.
|
|
47
53
|
"""
|
|
48
54
|
|
|
49
55
|
import logging
|
|
@@ -189,7 +195,7 @@ class PropertiesApi(Trait):
|
|
|
189
195
|
self.maps = MapsTrait(self.status)
|
|
190
196
|
self.map_content = MapContentTrait(map_parser_config)
|
|
191
197
|
self.home = HomeTrait(self.status, self.maps, self.map_content, self.rooms, self._device_cache)
|
|
192
|
-
self.device_features = DeviceFeaturesTrait(product
|
|
198
|
+
self.device_features = DeviceFeaturesTrait(product, self._device_cache)
|
|
193
199
|
self.network_info = NetworkInfoTrait(device_uid, self._device_cache)
|
|
194
200
|
self.routines = RoutinesTrait(device_uid, web_api)
|
|
195
201
|
|
{python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/device_features.py
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
from dataclasses import fields
|
|
1
|
+
from dataclasses import Field, fields
|
|
2
2
|
|
|
3
|
-
from roborock.data import AppInitStatus,
|
|
3
|
+
from roborock.data import AppInitStatus, HomeDataProduct, RoborockBase
|
|
4
|
+
from roborock.data.v1.v1_containers import FieldNameBase
|
|
4
5
|
from roborock.device_features import DeviceFeatures
|
|
5
6
|
from roborock.devices.cache import DeviceCache
|
|
6
7
|
from roborock.devices.traits.v1 import common
|
|
@@ -8,19 +9,42 @@ from roborock.roborock_typing import RoborockCommand
|
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class DeviceFeaturesTrait(DeviceFeatures, common.V1TraitMixin):
|
|
11
|
-
"""Trait for managing
|
|
12
|
+
"""Trait for managing supported features on Roborock devices."""
|
|
12
13
|
|
|
13
14
|
command = RoborockCommand.APP_GET_INIT_STATUS
|
|
14
15
|
|
|
15
|
-
def __init__(self,
|
|
16
|
-
"""Initialize
|
|
17
|
-
self.
|
|
16
|
+
def __init__(self, product: HomeDataProduct, device_cache: DeviceCache) -> None: # pylint: disable=super-init-not-called
|
|
17
|
+
"""Initialize DeviceFeaturesTrait."""
|
|
18
|
+
self._product = product
|
|
19
|
+
self._nickname = product.product_nickname
|
|
18
20
|
self._device_cache = device_cache
|
|
19
21
|
# All fields of DeviceFeatures are required. Initialize them to False
|
|
20
22
|
# so we have some known state.
|
|
21
23
|
for field in fields(self):
|
|
22
24
|
setattr(self, field.name, False)
|
|
23
25
|
|
|
26
|
+
def is_field_supported(self, cls: type[RoborockBase], field_name: FieldNameBase) -> bool:
|
|
27
|
+
"""Determines if the specified field is supported by this device.
|
|
28
|
+
|
|
29
|
+
We use dataclass attributes on the field to specify the schema code that is required
|
|
30
|
+
for the field to be supported and it is compared against the list of
|
|
31
|
+
supported schema codes for the device returned in the product information.
|
|
32
|
+
"""
|
|
33
|
+
dataclass_field: Field | None = None
|
|
34
|
+
for field in fields(cls):
|
|
35
|
+
if field.name == field_name:
|
|
36
|
+
dataclass_field = field
|
|
37
|
+
break
|
|
38
|
+
if dataclass_field is None:
|
|
39
|
+
raise ValueError(f"Field {field_name} not found in {cls}")
|
|
40
|
+
|
|
41
|
+
requires_schema_code = dataclass_field.metadata.get("requires_schema_code", None)
|
|
42
|
+
if requires_schema_code is None:
|
|
43
|
+
# We assume the field is supported
|
|
44
|
+
return True
|
|
45
|
+
# If the field requires a protocol that is not supported, we return False
|
|
46
|
+
return requires_schema_code in self._product.supported_schema_codes
|
|
47
|
+
|
|
24
48
|
async def refresh(self) -> None:
|
|
25
49
|
"""Refresh the contents of this trait.
|
|
26
50
|
|
|
@@ -101,30 +101,50 @@ REDACT_KEYS = {
|
|
|
101
101
|
"imageContent",
|
|
102
102
|
"mapData",
|
|
103
103
|
"rawApiResponse",
|
|
104
|
+
# Home data
|
|
105
|
+
"id", # We want to redact home_data.id but keep some other ids, see below
|
|
106
|
+
"name",
|
|
107
|
+
"productId",
|
|
108
|
+
"ipAddress",
|
|
109
|
+
"wifiName",
|
|
110
|
+
"lat",
|
|
111
|
+
"long",
|
|
112
|
+
}
|
|
113
|
+
KEEP_KEYS = {
|
|
114
|
+
# Product information not unique per user
|
|
115
|
+
"product.id",
|
|
116
|
+
"product.schema.id",
|
|
117
|
+
"product.schema.name",
|
|
118
|
+
# Room ids are likely unique per user, but don't seem too sensitive and are
|
|
119
|
+
# useful for debugging
|
|
120
|
+
"rooms.id",
|
|
104
121
|
}
|
|
105
122
|
DEVICE_UID = "duid"
|
|
106
123
|
REDACTED = "**REDACTED**"
|
|
107
124
|
|
|
108
125
|
|
|
109
|
-
def redact_device_data(data: T) -> T | dict[str, Any]:
|
|
126
|
+
def redact_device_data(data: T, path: str = "") -> T | dict[str, Any]:
|
|
110
127
|
"""Redact sensitive data in a dict."""
|
|
111
128
|
if not isinstance(data, (Mapping, list)):
|
|
112
129
|
return data
|
|
113
130
|
|
|
114
131
|
if isinstance(data, list):
|
|
115
|
-
return cast(T, [redact_device_data(item) for item in data])
|
|
132
|
+
return cast(T, [redact_device_data(item, path) for item in data])
|
|
116
133
|
|
|
117
134
|
redacted = {**data}
|
|
118
135
|
|
|
119
136
|
for key, value in redacted.items():
|
|
120
|
-
|
|
137
|
+
curr_path = f"{path}.{key}" if path else key
|
|
138
|
+
if key in KEEP_KEYS or curr_path in KEEP_KEYS:
|
|
139
|
+
continue
|
|
140
|
+
if key in REDACT_KEYS or curr_path in REDACT_KEYS:
|
|
121
141
|
redacted[key] = REDACTED
|
|
122
142
|
elif key == DEVICE_UID and isinstance(value, str):
|
|
123
143
|
redacted[key] = redact_device_uid(value)
|
|
124
144
|
elif isinstance(value, dict):
|
|
125
|
-
redacted[key] = redact_device_data(value)
|
|
145
|
+
redacted[key] = redact_device_data(value, curr_path)
|
|
126
146
|
elif isinstance(value, list):
|
|
127
|
-
redacted[key] = [redact_device_data(item) for item in value]
|
|
147
|
+
redacted[key] = [redact_device_data(item, curr_path) for item in value]
|
|
128
148
|
|
|
129
149
|
return redacted
|
|
130
150
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/data/b01_q10/b01_q10_code_mappings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/data/b01_q7/b01_q7_code_mappings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/do_not_disturb.py
RENAMED
|
File without changes
|
{python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/dust_collection_mode.py
RENAMED
|
File without changes
|
{python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/flow_led_status.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/smart_wash_params.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-4.2.1 → python_roborock-4.3.0}/roborock/devices/traits/v1/wash_towel_mode.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|