Skip to content

Commit 5498596

Browse files
authored
feat: update listeners (#140)
* fix: change some typing * fix: include poetry lock * fix: linting * fix: linting * fix: add typing * fix: linting * fix: bugs * fix: linting * fix: none typing * fix: none typing * fix: weird merge things * fix: rework listeners and cache a bit more * chore: linting * chore: typo * chore: self listener model * chore: linting * fix: override missing for data protocol
1 parent 6a2ea94 commit 5498596

File tree

4 files changed

+50
-14
lines changed

4 files changed

+50
-14
lines changed

roborock/api.py

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import asyncio
66
import base64
7+
import dataclasses
78
import hashlib
89
import hmac
910
import json
@@ -169,7 +170,15 @@ async def refresh_value(self):
169170
await self._async_value()
170171

171172

173+
@dataclasses.dataclass
174+
class ListenerModel:
175+
protocol_handlers: dict[RoborockDataProtocol, list[Callable[[Status | Consumable], None]]]
176+
cache: dict[CacheableAttribute, AttributeCache]
177+
178+
172179
class RoborockClient:
180+
_listeners: dict[str, ListenerModel] = {}
181+
173182
def __init__(self, endpoint: str, device_info: DeviceData, queue_timeout: int = 4) -> None:
174183
self.event_loop = get_running_loop_or_create_one()
175184
self.device_info = device_info
@@ -184,10 +193,12 @@ def __init__(self, endpoint: str, device_info: DeviceData, queue_timeout: int =
184193
self.cache: dict[CacheableAttribute, AttributeCache] = {
185194
cacheable_attribute: AttributeCache(attr, self) for cacheable_attribute, attr in get_cache_map().items()
186195
}
187-
self._listeners: list[Callable[[str, CacheableAttribute, RoborockBase], None]] = []
188196
self.is_available: bool = True
189197
self.queue_timeout = queue_timeout
190198
self._status_type: type[Status] = ModelStatus.get(self.device_info.model, S7MaxVStatus)
199+
if device_info.device.duid not in self._listeners:
200+
self._listeners[device_info.device.duid] = ListenerModel({}, self.cache)
201+
self.listener_model = self._listeners[device_info.device.duid]
191202

192203
def __del__(self) -> None:
193204
self.release()
@@ -260,32 +271,36 @@ def on_message_received(self, messages: list[RoborockMessage]) -> None:
260271
data_protocol = RoborockDataProtocol(int(data_point_number))
261272
self._logger.debug(f"Got device update for {data_protocol.name}: {data_point}")
262273
if data_protocol in ROBOROCK_DATA_STATUS_PROTOCOL:
263-
if self.cache[CacheableAttribute.status].value is None:
274+
if data_protocol not in self.listener_model.protocol_handlers:
264275
self._logger.debug(
265276
f"Got status update({data_protocol.name}) before get_status was called."
266277
)
267278
return
268-
value = self.cache[CacheableAttribute.status].value
279+
value = self.listener_model.cache[CacheableAttribute.status].value
269280
value[data_protocol.name] = data_point
270281
status = self._status_type.from_dict(value)
271-
for listener in self._listeners:
272-
listener(self.device_info.device.duid, CacheableAttribute.status, status)
282+
for listener in self.listener_model.protocol_handlers.get(data_protocol, []):
283+
listener(status)
273284
elif data_protocol in ROBOROCK_DATA_CONSUMABLE_PROTOCOL:
274-
if self.cache[CacheableAttribute.consumable].value is None:
285+
if data_protocol not in self.listener_model.protocol_handlers:
275286
self._logger.debug(
276287
f"Got consumable update({data_protocol.name})"
277288
+ "before get_consumable was called."
278289
)
279290
return
280-
value = self.cache[CacheableAttribute.consumable].value
291+
value = self.listener_model.cache[CacheableAttribute.consumable].value
281292
value[data_protocol.name] = data_point
282293
consumable = Consumable.from_dict(value)
283-
for listener in self._listeners:
284-
listener(
285-
self.device_info.device.duid, CacheableAttribute.consumable, consumable
286-
)
294+
for listener in self.listener_model.protocol_handlers.get(data_protocol, []):
295+
listener(consumable)
287296
return
288297
except ValueError:
298+
self._logger.warning(
299+
f"Got listener data for {data_point_number}, data: {data_point}. "
300+
f"This lets us update data quicker, please open an issue "
301+
f"at https://github.com/humbertogontijo/python-roborock/issues"
302+
)
303+
289304
pass
290305
dps = {data_point_number: data_point}
291306
self._logger.debug(f"Got unknown data point {dps}")
@@ -552,8 +567,13 @@ async def get_server_timer(self) -> list[ServerTimer]:
552567
return [ServerTimer(*server_timers)]
553568
return []
554569

555-
def add_listener(self, listener: Callable):
556-
self._listeners.append(listener)
570+
def add_listener(
571+
self, protocol: RoborockDataProtocol, listener: Callable, cache: dict[CacheableAttribute, AttributeCache]
572+
):
573+
self.listener_model.cache = cache
574+
if protocol not in self.listener_model.protocol_handlers:
575+
self.listener_model.protocol_handlers[protocol] = []
576+
self.listener_model.protocol_handlers[protocol].append(listener)
557577

558578
async def get_from_cache(self, key: CacheableAttribute) -> AttributeCache | None:
559579
val = self.cache.get(key)

roborock/code_mappings.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,17 @@ class RoborockMopIntensityS5Max(RoborockMopIntensityCode):
249249
custom_water_flow = 207
250250

251251

252+
class RoborockMopIntensityS6MaxV(RoborockMopIntensityCode):
253+
"""Describes the mop intensity of the vacuum cleaner."""
254+
255+
off = 200
256+
low = 201
257+
medium = 202
258+
high = 203
259+
custom = 204
260+
custom_water_flow = 207
261+
262+
252263
class RoborockDockErrorCode(RoborockEnum):
253264
"""Describes the error code of the dock."""
254265

roborock/containers.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
RoborockMopIntensityCode,
2626
RoborockMopIntensityP10,
2727
RoborockMopIntensityS5Max,
28+
RoborockMopIntensityS6MaxV,
2829
RoborockMopIntensityS7,
2930
RoborockMopIntensityV2,
3031
RoborockMopModeCode,
@@ -357,7 +358,7 @@ class Q7MaxStatus(Status):
357358
@dataclass
358359
class S6MaxVStatus(Status):
359360
fan_power: RoborockFanSpeedS7MaxV | None = None
360-
water_box_mode: RoborockMopIntensityS7 | None = None
361+
water_box_mode: RoborockMopIntensityS6MaxV | None = None
361362

362363

363364
@dataclass

roborock/roborock_message.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ class RoborockDataProtocol(RoborockEnum):
3737
CHARGE_STATUS = 133
3838
DRYING_STATUS = 134
3939

40+
@classmethod
41+
def _missing_(cls: type[RoborockEnum], key) -> RoborockEnum:
42+
raise ValueError("%s not a valid key for Data Protocol", key)
43+
4044

4145
ROBOROCK_DATA_STATUS_PROTOCOL = [
4246
RoborockDataProtocol.ERROR_CODE,

0 commit comments

Comments
 (0)