Skip to content

Commit 1a1565b

Browse files
feat: adding server timer and retry command compatibility
1 parent b721df9 commit 1a1565b

File tree

6 files changed

+61
-8
lines changed

6 files changed

+61
-8
lines changed

roborock/api.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
RoborockBase,
3636
RoomMapping,
3737
S7MaxVStatus,
38+
ServerTimer,
3839
SmartWashParams,
3940
Status,
4041
UserData,
@@ -136,6 +137,13 @@ async def update_value(self, params):
136137
await self._async_value()
137138
return response
138139

140+
async def add_value(self, params):
141+
if self.attribute.add_command is None:
142+
raise RoborockException(f"{self.attribute.attribute} have no add command")
143+
response = await self.api._send_command(self.attribute.add_command, params)
144+
await self._async_value()
145+
return response
146+
139147
async def close_value(self):
140148
if self.attribute.close_command is None:
141149
raise RoborockException(f"{self.attribute.attribute} have no close command")
@@ -483,6 +491,15 @@ async def get_sound_volume(self) -> int | None:
483491
"""Gets current volume level."""
484492
return await self.cache[CacheableAttribute.sound_volume].async_value()
485493

494+
async def get_server_timer(self) -> list[ServerTimer]:
495+
"""Gets current server timer."""
496+
server_timers = await self.cache[CacheableAttribute.server_timer].async_value()
497+
if server_timers:
498+
if isinstance(server_timers[0], list):
499+
return [ServerTimer(*server_timer) for server_timer in server_timers]
500+
return [ServerTimer(*server_timers)]
501+
return []
502+
486503
def add_listener(self, listener: Callable):
487504
self._listeners.append(listener)
488505

roborock/command_cache.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class CacheableAttribute(str, Enum):
3838
class RoborockAttribute:
3939
attribute: str
4040
get_command: RoborockCommand
41+
add_command: Optional[RoborockCommand] = None
4142
set_command: Optional[RoborockCommand] = None
4243
close_command: Optional[RoborockCommand] = None
4344

@@ -126,7 +127,9 @@ def create_cache_map():
126127
CacheableAttribute.server_timer: RoborockAttribute(
127128
attribute="server_timer",
128129
get_command=RoborockCommand.GET_SERVER_TIMER,
129-
set_command=RoborockCommand.SET_SERVER_TIMER,
130+
add_command=RoborockCommand.SET_SERVER_TIMER,
131+
set_command=RoborockCommand.UPD_SERVER_TIMER,
132+
close_command=RoborockCommand.DEL_SERVER_TIMER,
130133
),
131134
CacheableAttribute.smart_wash_params: RoborockAttribute(
132135
attribute="smart_wash_params",

roborock/containers.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import re
66
from dataclasses import asdict, dataclass
77
from enum import Enum
8-
from typing import Any, Optional, Type
8+
from typing import Any, NamedTuple, Optional, Type
99

1010
from dacite import Config, from_dict
1111

@@ -513,3 +513,9 @@ class FlowLedStatus(RoborockBase):
513513
class BroadcastMessage(RoborockBase):
514514
duid: str
515515
ip: str
516+
517+
518+
class ServerTimer(NamedTuple):
519+
id: str
520+
status: str
521+
dontknow: int

roborock/local_api.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from .api import COMMANDS_SECURED, QUEUE_TIMEOUT, RoborockClient
1212
from .exceptions import CommandVacuumError, RoborockConnectionException, RoborockException
1313
from .protocol import MessageParser
14-
from .roborock_message import RoborockMessage, RoborockMessageProtocol
14+
from .roborock_message import MessageRetry, RoborockMessage, RoborockMessageProtocol
1515
from .roborock_typing import RoborockCommand
1616

1717
_LOGGER = logging.getLogger(__name__)
@@ -85,10 +85,11 @@ def build_roborock_message(self, method: RoborockCommand, params: Optional[list
8585
secured = True if method in COMMANDS_SECURED else False
8686
request_id, timestamp, payload = self._get_payload(method, params, secured)
8787
request_protocol = RoborockMessageProtocol.GENERAL_REQUEST
88+
message_retry: Optional[MessageRetry] = None
89+
if method == RoborockCommand.RETRY_REQUEST and isinstance(params, dict):
90+
message_retry = MessageRetry(method=params["method"], retry_id=params["retry_id"])
8891
return RoborockMessage(
89-
timestamp=timestamp,
90-
protocol=request_protocol,
91-
payload=payload,
92+
timestamp=timestamp, protocol=request_protocol, payload=payload, message_retry=message_retry
9293
)
9394

9495
async def hello(self):
@@ -161,4 +162,9 @@ async def send_message(self, roborock_message: RoborockMessage):
161162
raise CommandVacuumError(method, err) from err
162163
if roborock_message.protocol == RoborockMessageProtocol.GENERAL_REQUEST:
163164
_LOGGER.debug(f"id={request_id} Response from method {roborock_message.get_method()}: {response}")
165+
if response == "retry":
166+
retry_id = roborock_message.get_retry_id()
167+
return self.send_command(
168+
RoborockCommand.RETRY_REQUEST, {"retry_id": retry_id, "retry_count": 8, "method": method}
169+
)
164170
return response

roborock/roborock_message.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ class RoborockDataProtocol(RoborockEnum):
5555
]
5656

5757

58+
@dataclass
59+
class MessageRetry:
60+
method: str
61+
retry_id: int
62+
63+
5864
@dataclass
5965
class RoborockMessage:
6066
protocol: RoborockMessageProtocol
@@ -63,6 +69,7 @@ class RoborockMessage:
6369
version: bytes = b"1.0"
6470
random: int = randint(10000, 99999)
6571
timestamp: int = math.floor(time.time())
72+
message_retry: Optional[MessageRetry] = None
6673

6774
def get_request_id(self) -> int | None:
6875
if self.payload:
@@ -73,7 +80,14 @@ def get_request_id(self) -> int | None:
7380
return data_point_response.get("id")
7481
return None
7582

83+
def get_retry_id(self) -> int | None:
84+
if self.message_retry:
85+
return self.message_retry.retry_id
86+
return self.get_request_id()
87+
7688
def get_method(self) -> str | None:
89+
if self.message_retry:
90+
return self.message_retry.method
7791
protocol = self.protocol
7892
if self.payload and protocol in [4, 5, 101, 102]:
7993
payload = json.loads(self.payload.decode())

roborock/roborock_typing.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ class RoborockCommand(str, Enum):
125125
SWITCH_WATER_MARK = "switch_water_mark"
126126
TEST_SOUND_VOLUME = "test_sound_volume"
127127
UPD_SERVER_TIMER = "upd_server_timer"
128+
DEL_SERVER_TIMER = "del_server_timer"
128129

129130

130131
@dataclass
@@ -274,7 +275,12 @@ class CommandInfo:
274275
RoborockCommand.SET_IDENTIFY_GROUND_MATERIAL_STATUS: CommandInfo(params={"status": 1}),
275276
RoborockCommand.SET_LED_STATUS: CommandInfo(params=[1]),
276277
RoborockCommand.SET_MOP_MODE: CommandInfo(params=None),
277-
RoborockCommand.SET_SERVER_TIMER: CommandInfo(params=None),
278+
RoborockCommand.SET_SERVER_TIMER: CommandInfo(
279+
params={
280+
"data": [["1687793948482", ["39 12 * * 0,1,2,3,4,5,6", ["start_clean", 106, "0", -1]]]],
281+
"need_retry": 1,
282+
}
283+
),
278284
RoborockCommand.SET_SMART_WASH_PARAMS: CommandInfo(params=None),
279285
RoborockCommand.SET_TIMEZONE: CommandInfo(params=["America/Sao_Paulo"]),
280286
RoborockCommand.SET_VALLEY_ELECTRICITY_TIMER: CommandInfo(params=[0, 0, 8, 0]),
@@ -286,7 +292,8 @@ class CommandInfo:
286292
RoborockCommand.STOP_CAMERA_PREVIEW: CommandInfo(params={"client_id": "443f8636"}),
287293
RoborockCommand.SWITCH_WATER_MARK: CommandInfo(params={"waterMark": "OFF"}),
288294
RoborockCommand.TEST_SOUND_VOLUME: CommandInfo(params=None),
289-
RoborockCommand.UPD_SERVER_TIMER: CommandInfo(params=None),
295+
RoborockCommand.UPD_SERVER_TIMER: CommandInfo(params=[["1687793948482", "off"]]),
296+
RoborockCommand.DEL_SERVER_TIMER: CommandInfo(params=["1687793948482"]),
290297
}
291298

292299

0 commit comments

Comments
 (0)