Skip to content

Commit 22720ae

Browse files
authored
feat: add information from product api (#158)
* fix: add information from product api * feat: add dyad protocol
1 parent 5197df1 commit 22720ae

File tree

5 files changed

+163
-1
lines changed

5 files changed

+163
-1
lines changed

roborock/api.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
ModelStatus,
3636
MultiMapsList,
3737
NetworkInfo,
38+
ProductResponse,
3839
RoborockBase,
3940
RoomMapping,
4041
RRiot,
@@ -831,3 +832,22 @@ async def get_rooms(self, user_data: UserData, home_id: int | None = None) -> li
831832
return output_list
832833
else:
833834
raise RoborockException("home_response result was an unexpected type")
835+
836+
async def get_products(self, user_data: UserData) -> ProductResponse:
837+
"""Gets all products and their schemas, good for determining status codes and model numbers."""
838+
base_url = await self._get_base_url()
839+
header_clientid = self._get_header_client_id()
840+
product_request = PreparedRequest(base_url, {"header_clientid": header_clientid})
841+
product_response = await product_request.request(
842+
"get",
843+
"/api/v3/product",
844+
headers={"Authorization": user_data.token},
845+
)
846+
if product_response is None:
847+
raise RoborockException("home_id_response is None")
848+
if product_response.get("code") != 200:
849+
raise RoborockException(f"{product_response.get('msg')} - response code: {product_response.get('code')}")
850+
result = product_response.get("data")
851+
if isinstance(result, dict):
852+
return ProductResponse.from_dict(result)
853+
raise RoborockException("product result was an unexpected type")

roborock/code_mappings.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,23 @@ class RoborockStateCode(RoborockEnum):
6565
emptying_the_bin = 22 # on s7+
6666
washing_the_mop = 23 # on a46
6767
going_to_wash_the_mop = 26 # on a46
68+
in_call = 28
69+
mapping = 29
6870
charging_complete = 100
6971
device_offline = 101
7072

7173

74+
class RoborockDyadStateCode(RoborockEnum):
75+
washing = 1
76+
ready = 2
77+
charging = 3
78+
mop_washing = 4
79+
drying = 10
80+
reserving = 12
81+
mop_washing_paused = 13
82+
dusting_mode = 14
83+
84+
7285
class RoborockErrorCode(RoborockEnum):
7386
none = 0
7487
lidar_blocked = 1

roborock/const.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@
1818
ROBOROCK_T7S = "roborock.vacuum.a14"
1919
ROBOROCK_T7SPLUS = "roborock.vacuum.a23"
2020
ROBOROCK_S7_MAXV = "roborock.vacuum.a27"
21+
ROBOROCK_S7_MAXV_ULTRA = "roborock.vacuum.a65"
2122
ROBOROCK_S7_PRO_ULTRA = "roborock.vacuum.a62"
2223
ROBOROCK_Q5 = "roborock.vacuum.a34"
23-
ROBOROCK_Q7 = "roborock.vacuum.a37" # CHECK THIS
24+
ROBOROCK_Q5_PRO = "roborock.vacuum.a72"
25+
ROBOROCK_Q7 = "roborock.vacuum.a40"
2426
ROBOROCK_Q7_MAX = "roborock.vacuum.a38"
2527
ROBOROCK_Q7PLUS = "roborock.vacuum.a40"
28+
ROBOROCK_Q8_MAX = "roborock.vacuum.a73"
2629
ROBOROCK_G10S_PRO = "roborock.vacuum.a26"
2730
ROBOROCK_G10S = "roborock.vacuum.a46"
2831
ROBOROCK_G10 = "roborock.vacuum.a29"
@@ -36,6 +39,10 @@
3639
ROBOROCK_S8 = "roborock.vacuum.a51"
3740
ROBOROCK_P10 = "roborock.vacuum.a75"
3841

42+
ROBOROCK_DYAD_AIR = "roborock.wetdryvac.a107"
43+
ROBOROCK_DYAD_PRO_COMBO = "roborock.wetdryvac.a83"
44+
ROBOROCK_DYAD_PRO = "roborock.wetdryvac.a56"
45+
3946
SUPPORTED_VACUUMS = (
4047
[ # These are the devices that show up when you add a device - more could be supported and just not show up
4148
ROBOROCK_G10,

roborock/containers.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import datetime
4+
import json
45
import logging
56
import re
67
from dataclasses import asdict, dataclass, field
@@ -589,3 +590,79 @@ class ServerTimer(NamedTuple):
589590
id: str
590591
status: str
591592
dontknow: int
593+
594+
595+
@dataclass
596+
class RoborockProductStateValue(RoborockBase):
597+
value: list
598+
desc: dict
599+
600+
601+
@dataclass
602+
class RoborockProductState(RoborockBase):
603+
dps: int
604+
desc: dict
605+
value: list[RoborockProductStateValue]
606+
607+
608+
@dataclass
609+
class RoborockProductSpec(RoborockBase):
610+
state: RoborockProductState
611+
battery: dict | None = None
612+
dry_countdown: dict | None = None
613+
extra: dict | None = None
614+
offpeak: dict | None = None
615+
countdown: dict | None = None
616+
mode: dict | None = None
617+
ota_nfo: dict | None = None
618+
pause: dict | None = None
619+
program: dict | None = None
620+
shutdown: dict | None = None
621+
washing_left: dict | None = None
622+
623+
624+
@dataclass
625+
class RoborockProduct(RoborockBase):
626+
id: int
627+
name: str
628+
model: str
629+
packagename: str
630+
ssid: str
631+
picurl: str
632+
cardpicurl: str
633+
medium_cardpicurl: str
634+
resetwifipicurl: str
635+
resetwifitext: dict
636+
tuyaid: str
637+
status: int
638+
rriotid: str
639+
cardspec: str
640+
pictures: list
641+
nc_mode: str
642+
scope: None
643+
product_tags: list
644+
agreements: list
645+
plugin_pic_url: None
646+
products_specification: RoborockProductSpec | None = None
647+
648+
def __post_init__(self):
649+
if self.cardspec:
650+
self.products_specification = RoborockProductSpec.from_dict(json.loads(self.cardspec).get("data"))
651+
652+
653+
@dataclass
654+
class RoborockProductCategory(RoborockBase):
655+
id: int
656+
display_name: str
657+
icon_url: str
658+
659+
660+
@dataclass
661+
class RoborockCategoryDetail(RoborockBase):
662+
category: RoborockProductCategory
663+
product_list: list[RoborockProduct]
664+
665+
666+
@dataclass
667+
class ProductResponse(RoborockBase):
668+
category_detail_list: list[RoborockCategoryDetail]

roborock/roborock_message.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,51 @@ def _missing_(cls: type[RoborockEnum], key) -> RoborockEnum:
4242
raise ValueError("%s not a valid key for Data Protocol", key)
4343

4444

45+
class RoborockDyadDataProtocol(RoborockEnum):
46+
DRYING_STATUS = 134
47+
START = 200
48+
STATUS = 201
49+
SELF_CLEAN_MODE = 202
50+
SELF_CLEAN_LEVEL = 203
51+
WARM_LEVEL = 204
52+
CLEAN_MODE = 205
53+
SUCTION = 206
54+
WATER_LEVEL = 207
55+
BRUSH_SPEED = 208
56+
POWER = 209
57+
COUNTDOWN_TIME = 210
58+
AUTO_SELF_CLEAN_SET = 212
59+
AUTO_DRY = 213
60+
MESH_LEF = 214
61+
BRUSH_LEFT = 215
62+
ERROR = 216
63+
MESH_RESET = 218
64+
BRUSH_RESET = 219
65+
VOLUME_SET = 221
66+
STAND_LOCK_AUTO_RUN = 222
67+
AUTO_SELF_CLEAN_SET_MODE = 223
68+
AUTO_DRY_MODE = 224
69+
SILENT_DRY_DURATION = 225
70+
SILENT_MODE = 226
71+
SILENT_MODE_START_TIME = 227
72+
SILENT_MODE_END_TIME = 228
73+
RECENT_RUN_TIMe = 229
74+
TOTAL_RUN_TIME = 230
75+
FEATURE_INFO = 235
76+
RECOVER_SETTINGS = 236
77+
DRY_COUNTDOWN = 237
78+
ID_QUERY = 10000
79+
F_C = 10001
80+
SCHEDULE_TASK = 10002
81+
SND_SWITCH = 10003
82+
SND_STATE = 10004
83+
PRODUCT_INFO = 10005
84+
PRIVACY_INFO = 10006
85+
OTA_NFO = 10007
86+
RPC_REQUEST = 10101
87+
RPC_RESPONSE = 10102
88+
89+
4590
ROBOROCK_DATA_STATUS_PROTOCOL = [
4691
RoborockDataProtocol.ERROR_CODE,
4792
RoborockDataProtocol.STATE,

0 commit comments

Comments
 (0)