python-roborock 5.4.1__tar.gz → 5.5.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.
Files changed (103) hide show
  1. {python_roborock-5.4.1 → python_roborock-5.5.0}/PKG-INFO +1 -1
  2. {python_roborock-5.4.1 → python_roborock-5.5.0}/pyproject.toml +1 -1
  3. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/data/containers.py +16 -1
  4. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/data/v1/v1_containers.py +25 -28
  5. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/v1/__init__.py +2 -2
  6. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/v1/device_features.py +15 -16
  7. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/v1/home.py +6 -2
  8. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/roborock_message.py +1 -1
  9. {python_roborock-5.4.1 → python_roborock-5.5.0}/.gitignore +0 -0
  10. {python_roborock-5.4.1 → python_roborock-5.5.0}/LICENSE +0 -0
  11. {python_roborock-5.4.1 → python_roborock-5.5.0}/README.md +0 -0
  12. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/__init__.py +0 -0
  13. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/broadcast_protocol.py +0 -0
  14. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/callbacks.py +0 -0
  15. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/cli.py +0 -0
  16. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/const.py +0 -0
  17. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/data/__init__.py +0 -0
  18. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/data/b01_q10/__init__.py +0 -0
  19. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/data/b01_q10/b01_q10_code_mappings.py +0 -0
  20. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/data/b01_q10/b01_q10_containers.py +0 -0
  21. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/data/b01_q7/__init__.py +0 -0
  22. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/data/b01_q7/b01_q7_code_mappings.py +0 -0
  23. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/data/b01_q7/b01_q7_containers.py +0 -0
  24. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/data/code_mappings.py +0 -0
  25. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/data/dyad/__init__.py +0 -0
  26. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/data/dyad/dyad_code_mappings.py +0 -0
  27. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/data/dyad/dyad_containers.py +0 -0
  28. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/data/v1/__init__.py +0 -0
  29. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/data/v1/v1_clean_modes.py +0 -0
  30. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/data/v1/v1_code_mappings.py +0 -0
  31. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/data/zeo/__init__.py +0 -0
  32. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/data/zeo/zeo_code_mappings.py +0 -0
  33. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/data/zeo/zeo_containers.py +0 -0
  34. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/device_features.py +0 -0
  35. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/README.md +0 -0
  36. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/__init__.py +0 -0
  37. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/cache.py +0 -0
  38. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/device.py +0 -0
  39. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/device_manager.py +0 -0
  40. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/file_cache.py +0 -0
  41. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/rpc/__init__.py +0 -0
  42. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/rpc/a01_channel.py +0 -0
  43. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/rpc/b01_q10_channel.py +0 -0
  44. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/rpc/b01_q7_channel.py +0 -0
  45. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/rpc/v1_channel.py +0 -0
  46. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/__init__.py +0 -0
  47. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/a01/__init__.py +0 -0
  48. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/b01/__init__.py +0 -0
  49. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/b01/q10/__init__.py +0 -0
  50. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/b01/q10/command.py +0 -0
  51. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/b01/q10/common.py +0 -0
  52. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/b01/q10/status.py +0 -0
  53. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/b01/q10/vacuum.py +0 -0
  54. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/b01/q7/__init__.py +0 -0
  55. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/b01/q7/clean_summary.py +0 -0
  56. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/b01/q7/map.py +0 -0
  57. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/b01/q7/map_content.py +0 -0
  58. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/traits_mixin.py +0 -0
  59. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/v1/child_lock.py +0 -0
  60. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/v1/clean_summary.py +0 -0
  61. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/v1/command.py +0 -0
  62. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/v1/common.py +0 -0
  63. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/v1/consumeable.py +0 -0
  64. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/v1/do_not_disturb.py +0 -0
  65. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/v1/dust_collection_mode.py +0 -0
  66. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/v1/flow_led_status.py +0 -0
  67. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/v1/led_status.py +0 -0
  68. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/v1/map_content.py +0 -0
  69. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/v1/maps.py +0 -0
  70. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/v1/network_info.py +0 -0
  71. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/v1/rooms.py +0 -0
  72. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/v1/routines.py +0 -0
  73. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/v1/smart_wash_params.py +0 -0
  74. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/v1/status.py +0 -0
  75. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/v1/valley_electricity_timer.py +0 -0
  76. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/v1/volume.py +0 -0
  77. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/traits/v1/wash_towel_mode.py +0 -0
  78. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/transport/__init__.py +0 -0
  79. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/transport/channel.py +0 -0
  80. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/transport/local_channel.py +0 -0
  81. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/devices/transport/mqtt_channel.py +0 -0
  82. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/diagnostics.py +0 -0
  83. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/exceptions.py +0 -0
  84. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/map/__init__.py +0 -0
  85. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/map/b01_map_parser.py +0 -0
  86. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/map/map_parser.py +0 -0
  87. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/map/proto/__init__.py +0 -0
  88. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/map/proto/b01_scmap.proto +0 -0
  89. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/map/proto/b01_scmap_pb2.py +0 -0
  90. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/mqtt/__init__.py +0 -0
  91. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/mqtt/health_manager.py +0 -0
  92. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/mqtt/roborock_session.py +0 -0
  93. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/mqtt/session.py +0 -0
  94. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/protocol.py +0 -0
  95. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/protocols/__init__.py +0 -0
  96. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/protocols/a01_protocol.py +0 -0
  97. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/protocols/b01_q10_protocol.py +0 -0
  98. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/protocols/b01_q7_protocol.py +0 -0
  99. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/protocols/v1_protocol.py +0 -0
  100. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/py.typed +0 -0
  101. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/roborock_typing.py +0 -0
  102. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/util.py +0 -0
  103. {python_roborock-5.4.1 → python_roborock-5.5.0}/roborock/web_api.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-roborock
3
- Version: 5.4.1
3
+ Version: 5.5.0
4
4
  Summary: A package to control Roborock vacuums.
5
5
  Project-URL: Repository, https://github.com/python-roborock/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 = "5.4.1"
3
+ version = "5.5.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"
@@ -251,11 +251,26 @@ class HomeDataProduct(RoborockBase):
251
251
 
252
252
  @cached_property
253
253
  def supported_schema_codes(self) -> set[str]:
254
- """Return a set of fields that are supported by the device."""
254
+ """Return a set of schema codes that are supported by the device.
255
+
256
+ These correspond with string field names like "state" or "error_code" that
257
+ correspond to RoborockDataProtocol or RoborockB01Protocol code values.
258
+ """
255
259
  if self.schema is None:
256
260
  return set()
257
261
  return {schema.code for schema in self.schema if schema.code is not None}
258
262
 
263
+ @cached_property
264
+ def supported_schema_ids(self) -> set[int]:
265
+ """Return a set of schema IDs (DPS integers) that are supported by the device.
266
+
267
+ These correspond to RoborockMessageProtocol and RoborockDataProtocol or
268
+ RoborockB01Protocol enum number values (depends on the device protocol versions).
269
+ """
270
+ if self.schema is None:
271
+ return set()
272
+ return {int(schema.id) for schema in self.schema if schema.id is not None}
273
+
259
274
 
260
275
  @dataclass
261
276
  class HomeDataDevice(RoborockBase):
@@ -37,6 +37,7 @@ from roborock.const import (
37
37
  ROBOROCK_G20S_Ultra,
38
38
  )
39
39
  from roborock.exceptions import RoborockException
40
+ from roborock.roborock_message import RoborockDataProtocol
40
41
 
41
42
  from ..containers import NamedRoomMapping, RoborockBase, RoborockBaseTimer, _attr_repr
42
43
  from .v1_clean_modes import WashTowelModes
@@ -102,9 +103,8 @@ class StatusField(FieldNameBase):
102
103
  This is used with `roborock.devices.traits.v1.status.DeviceFeaturesTrait`
103
104
  to understand if a feature is supported by the device using `is_field_supported`.
104
105
 
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.
106
+ The enum values are names of fields in the `Status` class. Each field is annotated
107
+ with a metadata value to determine if the field is supported by the device.
108
108
  """
109
109
 
110
110
  STATE = "state"
@@ -116,21 +116,17 @@ class StatusField(FieldNameBase):
116
116
  ERROR_CODE = "error_code"
117
117
 
118
118
 
119
- def _requires_schema_code(requires_schema_code: str, default=None) -> Any:
120
- return field(metadata={"requires_schema_code": requires_schema_code}, default=default)
121
-
122
-
123
119
  @dataclass
124
120
  class Status(RoborockBase):
125
121
  """This status will be deprecated in favor of StatusV2."""
126
122
 
127
123
  msg_ver: int | None = None
128
124
  msg_seq: int | None = None
129
- state: RoborockStateCode | None = _requires_schema_code("state")
130
- battery: int | None = _requires_schema_code("battery")
125
+ state: RoborockStateCode | None = field(default=None, metadata={"dps": RoborockDataProtocol.STATE})
126
+ battery: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.BATTERY})
131
127
  clean_time: int | None = None
132
128
  clean_area: int | None = None
133
- error_code: RoborockErrorCode | None = _requires_schema_code("error_code")
129
+ error_code: RoborockErrorCode | None = field(default=None, metadata={"dps": RoborockDataProtocol.ERROR_CODE})
134
130
  map_present: int | None = None
135
131
  in_cleaning: RoborockInCleaning | None = None
136
132
  in_returning: int | None = None
@@ -140,12 +136,14 @@ class Status(RoborockBase):
140
136
  back_type: int | None = None
141
137
  wash_phase: int | None = None
142
138
  wash_ready: int | None = None
143
- fan_power: RoborockFanPowerCode | None = _requires_schema_code("fan_power")
139
+ fan_power: RoborockFanPowerCode | None = field(default=None, metadata={"dps": RoborockDataProtocol.FAN_POWER})
144
140
  dnd_enabled: int | None = None
145
141
  map_status: int | None = None
146
142
  is_locating: int | None = None
147
143
  lock_status: int | None = None
148
- water_box_mode: RoborockMopIntensityCode | None = _requires_schema_code("water_box_mode")
144
+ water_box_mode: RoborockMopIntensityCode | None = field(
145
+ default=None, metadata={"dps": RoborockDataProtocol.WATER_BOX_MODE}
146
+ )
149
147
  water_box_carriage_status: int | None = None
150
148
  mop_forbidden_enable: int | None = None
151
149
  camera_status: int | None = None
@@ -163,13 +161,13 @@ class Status(RoborockBase):
163
161
  collision_avoid_status: int | None = None
164
162
  switch_map_mode: int | None = None
165
163
  dock_error_status: RoborockDockErrorCode | None = None
166
- charge_status: int | None = _requires_schema_code("charge_status")
164
+ charge_status: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.CHARGE_STATUS})
167
165
  unsave_map_reason: int | None = None
168
166
  unsave_map_flag: int | None = None
169
167
  wash_status: int | None = None
170
168
  distance_off: int | None = None
171
169
  in_warmup: int | None = None
172
- dry_status: int | None = _requires_schema_code("drying_status")
170
+ dry_status: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.DRYING_STATUS})
173
171
  rdt: int | None = None
174
172
  clean_percent: int | None = None
175
173
  rss: int | None = None
@@ -294,11 +292,11 @@ class StatusV2(RoborockBase):
294
292
 
295
293
  msg_ver: int | None = None
296
294
  msg_seq: int | None = None
297
- state: RoborockStateCode | None = _requires_schema_code("state")
298
- battery: int | None = _requires_schema_code("battery")
295
+ state: RoborockStateCode | None = field(default=None, metadata={"dps": RoborockDataProtocol.STATE})
296
+ battery: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.BATTERY})
299
297
  clean_time: int | None = None
300
298
  clean_area: int | None = None
301
- error_code: RoborockErrorCode | None = _requires_schema_code("error_code")
299
+ error_code: RoborockErrorCode | None = field(default=None, metadata={"dps": RoborockDataProtocol.ERROR_CODE})
302
300
  map_present: int | None = None
303
301
  in_cleaning: RoborockInCleaning | None = None
304
302
  in_returning: int | None = None
@@ -308,12 +306,12 @@ class StatusV2(RoborockBase):
308
306
  back_type: int | None = None
309
307
  wash_phase: int | None = None
310
308
  wash_ready: int | None = None
311
- fan_power: int | None = _requires_schema_code("fan_power")
309
+ fan_power: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.FAN_POWER})
312
310
  dnd_enabled: int | None = None
313
311
  map_status: int | None = None
314
312
  is_locating: int | None = None
315
313
  lock_status: int | None = None
316
- water_box_mode: int | None = _requires_schema_code("water_box_mode")
314
+ water_box_mode: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.WATER_BOX_MODE})
317
315
  water_box_carriage_status: int | None = None
318
316
  mop_forbidden_enable: int | None = None
319
317
  camera_status: int | None = None
@@ -330,14 +328,14 @@ class StatusV2(RoborockBase):
330
328
  debug_mode: int | None = None
331
329
  collision_avoid_status: int | None = None
332
330
  switch_map_mode: int | None = None
333
- dock_error_status: RoborockDockErrorCode | None = _requires_schema_code("dock_error_status")
334
- charge_status: int | None = _requires_schema_code("charge_status")
331
+ dock_error_status: RoborockDockErrorCode | None = None
332
+ charge_status: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.CHARGE_STATUS})
335
333
  unsave_map_reason: int | None = None
336
334
  unsave_map_flag: int | None = None
337
335
  wash_status: int | None = None
338
336
  distance_off: int | None = None
339
337
  in_warmup: int | None = None
340
- dry_status: int | None = _requires_schema_code("drying_status")
338
+ dry_status: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.DRYING_STATUS})
341
339
  rdt: int | None = None
342
340
  clean_percent: int | None = None
343
341
  rss: int | None = None
@@ -631,9 +629,8 @@ class ConsumableField(FieldNameBase):
631
629
  This is used with `roborock.devices.traits.v1.status.DeviceFeaturesTrait`
632
630
  to understand if a feature is supported by the device using `is_field_supported`.
633
631
 
634
- The enum values are names of fields in the `Consumable` class. Each field is
635
- annotated with `requires_schema_code` metadata to map the field to a schema
636
- code in the product schema, which may have a different name than the field/attribute name.
632
+ The enum values are names of fields in the `Consumable` class. Each field is annotated
633
+ with a metadata value to determine if the field is supported by the device.
637
634
  """
638
635
 
639
636
  MAIN_BRUSH_WORK_TIME = "main_brush_work_time"
@@ -643,9 +640,9 @@ class ConsumableField(FieldNameBase):
643
640
 
644
641
  @dataclass
645
642
  class Consumable(RoborockBase):
646
- main_brush_work_time: int | None = field(metadata={"requires_schema_code": "main_brush_life"}, default=None)
647
- side_brush_work_time: int | None = field(metadata={"requires_schema_code": "side_brush_life"}, default=None)
648
- filter_work_time: int | None = field(metadata={"requires_schema_code": "filter_life"}, default=None)
643
+ main_brush_work_time: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.MAIN_BRUSH_WORK_TIME})
644
+ side_brush_work_time: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.SIDE_BRUSH_WORK_TIME})
645
+ filter_work_time: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.FILTER_WORK_TIME})
649
646
  filter_element_work_time: int | None = None
650
647
  sensor_dirty_time: int | None = None
651
648
  strainer_work_times: int | None = None
@@ -48,8 +48,8 @@ optional traits:
48
48
  Additionally, DeviceFeaturesTrait has a method `is_field_supported` that is used to
49
49
  check individual trait field values. This is a more fine grained version to allow
50
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.
51
+ trait. The `dps` field metadata attribute references a schema code in
52
+ HomeDataProduct Schema that is required for the field to be supported.
53
53
  """
54
54
 
55
55
  import logging
@@ -45,27 +45,26 @@ class DeviceFeaturesTrait(DeviceFeatures, common.V1TraitMixin):
45
45
  for field in fields(self):
46
46
  setattr(self, field.name, False)
47
47
 
48
+ @staticmethod
49
+ def _get_dataclass_field(cls: type[RoborockBase], field_name: FieldNameBase) -> Field:
50
+ """Look up a dataclass field by its FieldNameBase name."""
51
+ for f in fields(cls):
52
+ if f.name == field_name:
53
+ return f
54
+ raise ValueError(f"Field {field_name!r} not found in {cls}")
55
+
48
56
  def is_field_supported(self, cls: type[RoborockBase], field_name: FieldNameBase) -> bool:
49
57
  """Determines if the specified field is supported by this device.
50
58
 
51
- We use dataclass attributes on the field to specify the schema code that is required
52
- for the field to be supported and it is compared against the list of
53
- supported schema codes for the device returned in the product information.
59
+ We use the `dps` dataclass field metadata to get the `RoborockDataProtocol`
60
+ integer ID and check it against the set of supported schema IDs for the
61
+ device returned in the product information.
54
62
  """
55
- dataclass_field: Field | None = None
56
- for field in fields(cls):
57
- if field.name == field_name:
58
- dataclass_field = field
59
- break
60
- if dataclass_field is None:
61
- raise ValueError(f"Field {field_name} not found in {cls}")
62
-
63
- requires_schema_code = dataclass_field.metadata.get("requires_schema_code", None)
64
- if requires_schema_code is None:
65
- # We assume the field is supported
63
+ dataclass_field = self._get_dataclass_field(cls, field_name)
64
+ if (dps := dataclass_field.metadata.get("dps")) is None:
65
+ # No DPS metadata — field is assumed always supported
66
66
  return True
67
- # If the field requires a protocol that is not supported, we return False
68
- return requires_schema_code in self._product.supported_schema_codes
67
+ return int(dps) in self._product.supported_schema_ids
69
68
 
70
69
  async def refresh(self) -> None:
71
70
  """Refresh the contents of this trait.
@@ -107,7 +107,10 @@ class HomeTrait(RoborockBase, common.V1TraitMixin):
107
107
 
108
108
  await self._maps_trait.refresh()
109
109
  if self._maps_trait.current_map_info is None:
110
- raise RoborockException("Cannot perform home discovery without current map info")
110
+ _LOGGER.debug("Cannot perform home discovery without current map info")
111
+ self._discovery_completed = True
112
+ await self._update_home_cache({}, {})
113
+ return
111
114
 
112
115
  home_map_info, home_map_content = await self._build_home_map_info()
113
116
  _LOGGER.debug("Home discovery complete, caching data for %d maps", len(home_map_info))
@@ -198,7 +201,8 @@ class HomeTrait(RoborockBase, common.V1TraitMixin):
198
201
  if (current_map_info := self._maps_trait.current_map_info) is None or (
199
202
  map_flag := self._maps_trait.current_map
200
203
  ) is None:
201
- raise RoborockException("Cannot refresh home data without current map info")
204
+ _LOGGER.debug("Cannot refresh home data without current map info")
205
+ return
202
206
 
203
207
  # Refresh the map content to ensure we have the latest image and object positions
204
208
  new_map_content = await self._refresh_map_content()
@@ -2,7 +2,7 @@ from dataclasses import dataclass, field
2
2
  from enum import StrEnum
3
3
  from typing import Self
4
4
 
5
- from roborock import RoborockEnum
5
+ from roborock.data.code_mappings import RoborockEnum
6
6
  from roborock.util import get_next_int, get_timestamp
7
7
 
8
8
 
File without changes