python-roborock 7.8.1__py3-none-any.whl → 7.9.0__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: python-roborock
3
- Version: 7.8.1
3
+ Version: 7.9.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/
@@ -11,7 +11,7 @@ roborock/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  roborock/roborock_message.py,sha256=VWsytnm7nQqOMBj7oYbiqPPqwE9qClDSI03CFuHXsds,11822
12
12
  roborock/roborock_typing.py,sha256=xppavdf57-Cu6elj3MP403uT75Tjw5Zs6bLGPLQmZf4,15483
13
13
  roborock/util.py,sha256=4zibjn2MgPrLCiha66LUXZj907LOXUtNzugbbb0NIlo,1634
14
- roborock/web_api.py,sha256=ypW9e11U0jNXnyIQO9UN0DnIdz4c6AxtJlUJmjuEl-o,40514
14
+ roborock/web_api.py,sha256=-igt3Y4-kOoWX4mKkjt-P-7t7uHxNGkOm11-_W2ge0c,42065
15
15
  roborock/data/__init__.py,sha256=jSZHOrd-LfVN-TdBkjNW334ef99rGBz4-hPT_7KvoLs,278
16
16
  roborock/data/code_mappings.py,sha256=j-ZZRtQQdIMJQAgEwDAxWKreNhD6U36oIhBKr_nLbis,9783
17
17
  roborock/data/containers.py,sha256=ePz1tmhNY1QpgiVlyc8j6PH5kjU6fkZYtBByCFj2wzU,17896
@@ -30,7 +30,7 @@ roborock/data/mower/mower_containers.py,sha256=PYNVhbGCafkchenBlBmG_fUnIC-9WfxF8
30
30
  roborock/data/v1/__init__.py,sha256=Lx_D6baZrCMxp57D0NcdiA8rssJ5NnInZwUwrT79XOI,91
31
31
  roborock/data/v1/v1_clean_modes.py,sha256=ZUhMqrM9MwnXFl3btZj9_LEAOmhNIl80krW-AiuWouk,13866
32
32
  roborock/data/v1/v1_code_mappings.py,sha256=hToQbd38mM3Dw8ONLyuDa2scqmNs0gNgOyqVgm-Gg7Y,12948
33
- roborock/data/v1/v1_containers.py,sha256=UBScIG6VKziEAC0FaQRHnfrGWDY06nITY8NkFJEzhiE,17256
33
+ roborock/data/v1/v1_containers.py,sha256=6erBnuQicFsdAsUaWIEAghzOxUU-xGuwqJBhcy_snZ4,17939
34
34
  roborock/data/zeo/__init__.py,sha256=VxG5qG4M24DGOq2YAaxownWp6bEfj9DEOgOMHmTpcqY,63
35
35
  roborock/data/zeo/zeo_code_mappings.py,sha256=mfm2xoI8RllPZfyZE0HmUhUhLECwRD0llkMNWfaEHvw,8057
36
36
  roborock/data/zeo/zeo_containers.py,sha256=huDeisutagKz1rzsLZr_tGAietec_qOd0KB3i09YGgo,4339
@@ -72,7 +72,7 @@ roborock/devices/traits/b01/q7/__init__.py,sha256=tbsZSe2VojCN7uRdHPm74on8WrexJe
72
72
  roborock/devices/traits/b01/q7/clean_summary.py,sha256=M43iJVeYjrkBV6ZxXdHtn4Rd5Mx-KAK7WLU5fABiN8I,2742
73
73
  roborock/devices/traits/b01/q7/map.py,sha256=NfPQAEwNCUsb0SYGMxmGeq4q4rEHqDJK0zTD5cE-ESs,1299
74
74
  roborock/devices/traits/b01/q7/map_content.py,sha256=O4UMnmX0-K-jxhOaknUZpWJSwxUuaTd2_D9_AP-elGY,4920
75
- roborock/devices/traits/v1/__init__.py,sha256=gzp5b2aJCugHF_9jqEZG7ycsv_xCMZa8ymhtm3Nqofg,16986
75
+ roborock/devices/traits/v1/__init__.py,sha256=dXSulH8cKqgPSua_4XhLRVk7PyCQxcsW7-aXLRBI370,17098
76
76
  roborock/devices/traits/v1/child_lock.py,sha256=NeaPqcEoBRXvEzKZGonkyINkwCBOK3xa4zSWvJW66kA,1161
77
77
  roborock/devices/traits/v1/clean_summary.py,sha256=gwAwJMuJewrdSxmYEaD5mywYrqfz62__NB7sL23u98A,4641
78
78
  roborock/devices/traits/v1/command.py,sha256=2yNyCF_EDPkLE_vK6IN5VB8ZVypwrrwKTByCdQzJs3c,1494
@@ -86,6 +86,7 @@ roborock/devices/traits/v1/home.py,sha256=UCLGCkhIowmVSjNv_aC76MortpKn8v0E1jlQA1
86
86
  roborock/devices/traits/v1/led_status.py,sha256=qUY6GgC-RSXCAKlL2D0_FBO-1xwi2eU3fkMFg-pLQCs,1754
87
87
  roborock/devices/traits/v1/map_content.py,sha256=w_34H6ItiYm98QFOmEDCP0AvTBWAFVuB-uz5rmgBRwI,3175
88
88
  roborock/devices/traits/v1/maps.py,sha256=JdOjJCeJ6iAH85pNHKHbVGlD-zOrha9h6XAh7IDeQr8,3468
89
+ roborock/devices/traits/v1/mop_dryer.py,sha256=O_kesBuk_oxtcTE3jFD3Qm2lH939kQatib2aEjvPwCY,1979
89
90
  roborock/devices/traits/v1/network_info.py,sha256=CX9-ye2Zb_8lAWPGvegXWSOguxMY2K0xO-2OmWmE67o,2291
90
91
  roborock/devices/traits/v1/obstacle_photos.py,sha256=Aul1sOxJ6AxLWqI5AbeHHSzxfUPzQfQDCPIvZ3oXb_4,4179
91
92
  roborock/devices/traits/v1/rooms.py,sha256=SEAs3sHZxDmiiSWc9Korm838zu8ub_04CsmJVlT4F48,6237
@@ -126,8 +127,8 @@ roborock/testing/channel.py,sha256=vjulT5MHBe8lAq6XITUqcxJVcCILWx7ZEcw5imTx0Tk,7
126
127
  roborock/testing/cloud.py,sha256=L2L0KlZDYaMuoV19KUQUoq5EAUDeNQ1lJ8Ew5YaOgjo,11946
127
128
  roborock/testing/simulator.py,sha256=g8ZUWVSjgAzuRRxWOCeOq-7deodyEItwUpqEw9RzCmQ,4347
128
129
  roborock/testing/v1_simulator.py,sha256=_LX_vsBMcB2Uu2QDt8QnQymgtVtbnwD5Xa7tmhiPAnY,14439
129
- python_roborock-7.8.1.dist-info/METADATA,sha256=77EsLWO11LPYHIV2lWaCCpDc_DBbcn8yruBM9dXohNA,5278
130
- python_roborock-7.8.1.dist-info/WHEEL,sha256=THafob7ofN-NsuMN7Mg4qZyHaQI7KkD-QlcQatYhXPo,87
131
- python_roborock-7.8.1.dist-info/entry_points.txt,sha256=EvC1nMqi9ZXKgZnqlNXA33v_3nzgPNjnM0mzWlrehnY,47
132
- python_roborock-7.8.1.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
133
- python_roborock-7.8.1.dist-info/RECORD,,
130
+ python_roborock-7.9.0.dist-info/METADATA,sha256=KZWHzuRvz4cVEnK2e9-EMTPQ7kboPnd42_4bgkbnxfQ,5278
131
+ python_roborock-7.9.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
132
+ python_roborock-7.9.0.dist-info/entry_points.txt,sha256=EvC1nMqi9ZXKgZnqlNXA33v_3nzgPNjnM0mzWlrehnY,47
133
+ python_roborock-7.9.0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
134
+ python_roborock-7.9.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.32.3
2
+ Generator: hatchling 1.32.4
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -459,6 +459,28 @@ class SmartWashParams(RoborockBase):
459
459
  wash_interval: int | None = None
460
460
 
461
461
 
462
+ @dataclass
463
+ class MopDryerProfile(RoborockBase):
464
+ """Dryer parameters for one state of the auto mop-drying setting."""
465
+
466
+ cliff_on: int | None = None
467
+ cliff_off: int | None = None
468
+ count: int | None = None
469
+ dry_time: int | None = None
470
+ """Drying duration in seconds. Only present in the ``on`` profile."""
471
+ dry_heating_film_time: int | None = None
472
+ """Heating element run time in seconds. Only present in the ``on`` profile."""
473
+
474
+
475
+ @dataclass
476
+ class MopDryerSetting(RoborockBase):
477
+ """Auto mop-drying setting, as returned by APP_GET_DRYER_SETTING."""
478
+
479
+ status: int | None = None
480
+ on: MopDryerProfile | None = None
481
+ off: MopDryerProfile | None = None
482
+
483
+
462
484
  @dataclass
463
485
  class DustCollectionMode(RoborockBase):
464
486
  mode: RoborockDockDustCollectionModeCode | None = None
@@ -82,6 +82,7 @@ from . import (
82
82
  led_status,
83
83
  map_content,
84
84
  maps,
85
+ mop_dryer,
85
86
  network_info,
86
87
  obstacle_photos,
87
88
  rooms,
@@ -105,6 +106,7 @@ from .home import HomeTrait
105
106
  from .led_status import LedStatusTrait
106
107
  from .map_content import MapContentTrait
107
108
  from .maps import MapsTrait
109
+ from .mop_dryer import MopDryerTrait
108
110
  from .network_info import NetworkInfoTrait
109
111
  from .obstacle_photos import ObstaclePhotoTrait
110
112
  from .rooms import RoomsTrait
@@ -132,6 +134,7 @@ __all__ = [
132
134
  "led_status",
133
135
  "map_content",
134
136
  "maps",
137
+ "mop_dryer",
135
138
  "network_info",
136
139
  "obstacle_photos",
137
140
  "rooms",
@@ -175,6 +178,7 @@ class PropertiesApi(Trait):
175
178
  wash_towel_mode: WashTowelModeTrait | None = None
176
179
  smart_wash_params: SmartWashParamsTrait | None = None
177
180
  obstacle_photos: ObstaclePhotoTrait | None = None
181
+ mop_dryer: MopDryerTrait | None = None
178
182
 
179
183
  def __init__(
180
184
  self,
@@ -0,0 +1,51 @@
1
+ """Trait for the dock mop dryer."""
2
+
3
+ from roborock.data import MopDryerSetting
4
+ from roborock.device_features import RoborockDockFeatures
5
+ from roborock.devices.traits.v1 import common
6
+ from roborock.roborock_typing import RoborockCommand
7
+
8
+ _STATUS_PARAM = "status"
9
+
10
+
11
+ def _supports_mop_dryer(dock_features: RoborockDockFeatures) -> bool:
12
+ return dock_features.is_dryable
13
+
14
+
15
+ class MopDryerTrait(MopDryerSetting, common.V1TraitMixin, common.RoborockSwitchBase):
16
+ """Trait for the dock mop dryer.
17
+
18
+ The switch controls the auto mop-drying setting, i.e. whether the dock
19
+ dries the mop after washing. ``start_dry`` and ``stop_dry`` control a
20
+ drying cycle directly. Whether a cycle is currently running is reported
21
+ as ``dry_status`` on the device status.
22
+ """
23
+
24
+ command = RoborockCommand.APP_GET_DRYER_SETTING
25
+ converter = common.DefaultConverter(MopDryerSetting)
26
+ requires_dock_features = _supports_mop_dryer
27
+
28
+ @property
29
+ def is_on(self) -> bool:
30
+ """Return whether auto mop drying is enabled."""
31
+ return self.status == 1
32
+
33
+ async def enable(self) -> None:
34
+ """Enable auto mop drying."""
35
+ await self.rpc_channel.send_command(RoborockCommand.APP_SET_DRYER_SETTING, params={_STATUS_PARAM: 1})
36
+ # Optimistic update to avoid an extra refresh
37
+ self.status = 1
38
+
39
+ async def disable(self) -> None:
40
+ """Disable auto mop drying."""
41
+ await self.rpc_channel.send_command(RoborockCommand.APP_SET_DRYER_SETTING, params={_STATUS_PARAM: 0})
42
+ # Optimistic update to avoid an extra refresh
43
+ self.status = 0
44
+
45
+ async def start_dry(self) -> None:
46
+ """Start a mop drying cycle."""
47
+ await self.rpc_channel.send_command(RoborockCommand.APP_SET_DRYER_STATUS, params={_STATUS_PARAM: 1})
48
+
49
+ async def stop_dry(self) -> None:
50
+ """Stop the running mop drying cycle."""
51
+ await self.rpc_channel.send_command(RoborockCommand.APP_SET_DRYER_STATUS, params={_STATUS_PARAM: 0})
roborock/web_api.py CHANGED
@@ -38,6 +38,10 @@ BASE_URLS = [
38
38
  "https://ruiot.roborock.com",
39
39
  ]
40
40
 
41
+ # Fallback user agreement version, used only if the latest version cannot be fetched.
42
+ DEFAULT_AGREEMENT_MAJOR_VERSION = 14
43
+ DEFAULT_AGREEMENT_MINOR_VERSION = 0
44
+
41
45
 
42
46
  @dataclass
43
47
  class IotLoginInfo:
@@ -292,6 +296,34 @@ class RoborockApiClient:
292
296
 
293
297
  return code_response["data"]["k"]
294
298
 
299
+ async def _get_agreement_version(self, country: str) -> dict[str, int]:
300
+ """Get the latest user agreement version for the given country.
301
+
302
+ The login endpoint rejects a stale agreement version with code 3006. The
303
+ current version differs per server and per country, so it must be looked
304
+ up rather than hardcoded. Falls back to the previously hardcoded values
305
+ if the lookup fails so that login is never blocked by this request.
306
+ """
307
+ try:
308
+ base_url = await self.base_url
309
+ agreement_request = PreparedRequest(base_url, self.session, {"header_clientlang": "en"})
310
+ response = await agreement_request.request(
311
+ "get",
312
+ "/api/v3/app/agreement/latest",
313
+ params={"country": country},
314
+ )
315
+ if response is not None and response.get("code") == 200:
316
+ data = response.get("data") or {}
317
+ major = data.get("majorVersion")
318
+ minor = data.get("minorVersion")
319
+ if isinstance(major, int) and isinstance(minor, int):
320
+ _LOGGER.debug("Using user agreement version %s.%s for %s", major, minor, country)
321
+ return {"majorVersion": major, "minorVersion": minor}
322
+ _LOGGER.debug("Unexpected agreement version response: %s", response)
323
+ except RoborockException as err:
324
+ _LOGGER.debug("Could not fetch latest user agreement version: %s", err)
325
+ return {"majorVersion": DEFAULT_AGREEMENT_MAJOR_VERSION, "minorVersion": DEFAULT_AGREEMENT_MINOR_VERSION}
326
+
295
327
  async def code_login_v4(
296
328
  self, code: int | str, country: str | None = None, country_code: int | None = None
297
329
  ) -> UserData:
@@ -334,10 +366,7 @@ class RoborockApiClient:
334
366
  "countryCode": country_code,
335
367
  "email": self._username,
336
368
  "code": code,
337
- # Major and minor version are the user agreement version, we will need to see if this needs to be
338
- # dynamic https://usiot.roborock.com/api/v3/app/agreement/latest?country=US
339
- "majorVersion": 14,
340
- "minorVersion": 0,
369
+ **await self._get_agreement_version(country),
341
370
  },
342
371
  )
343
372
  if login_response is None: