Skip to content

Commit 9cc3dc1

Browse files
committed
feat: add the ability to update supported_features via cli
1 parent 29b1fd5 commit 9cc3dc1

File tree

2 files changed

+241
-1
lines changed

2 files changed

+241
-1
lines changed

SUPPORTED_FEATURES.md

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
| Feature | roborock.vacuum.a15 | roborock.vacuum.a87 |
2+
|---|---|---|
3+
| Product Nickname | RoborockProductNickname.TANOSS | RoborockProductNickname.PEARLPLUS |
4+
| Protocol Version | 1.0 | 1.0 |
5+
| New Feature Info | 636084721975295 | 2247397454282751 |
6+
| New Feature Info Str | 0000000000002000 | 000A177F7EFEFFFF |
7+
| Feature Info | [111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 122, 123, 124, 125] | [111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125] |
8+
| `is_activate_video_charging_and_standby_supported` | | |
9+
| `is_analysis_supported` | X | X |
10+
| `is_any_state_transit_goto_supported` | X | X |
11+
| `is_auto_collection_2_supported` | | |
12+
| `is_auto_delivery_field_in_global_status_supported` | | X |
13+
| `is_auto_tear_down_mop_supported` | | |
14+
| `is_avoid_collision_mode_supported` | | X |
15+
| `is_avoid_collision_supported` | | X |
16+
| `is_back_charge_auto_wash_supported` | | X |
17+
| `is_careful_slow_mop_supported` | X | X |
18+
| `is_carpet_custom_clean_supported` | | X |
19+
| `is_carpet_deep_clean_supported` | | X |
20+
| `is_carpet_long_haired_supported` | | |
21+
| `is_carpet_pressure_use_origin_paras_supported` | | |
22+
| `is_carpet_show_on_map` | | X |
23+
| `is_carpet_supported` | X | X |
24+
| `is_ces2022_supported` | | |
25+
| `is_clean_count_setting_supported` | | X |
26+
| `is_clean_direct_status_supported` | | |
27+
| `is_clean_history_time_line_supported` | | |
28+
| `is_clean_route_deep_slow_plus_supported` | | |
29+
| `is_clean_route_fast_mode_supported` | | X |
30+
| `is_collect_dust_mode_supported` | X | X |
31+
| `is_corner_clean_mode_supported` | | |
32+
| `is_corner_mop_stretch_supported` | | X |
33+
| `is_current_map_restore_enabled` | X | X |
34+
| `is_custom_clean_mode_count_supported` | | X |
35+
| `is_custom_mode_supported` | X | X |
36+
| `is_custom_water_box_distance_supported` | | X |
37+
| `is_dirty_object_detect_supported` | | |
38+
| `is_dirty_replenish_clean_supported` | | X |
39+
| `is_dry_interval_timer_supported` | | |
40+
| `is_dss_believable` | | X |
41+
| `is_dust_collection_setting_supported` | X | X |
42+
| `is_dynamically_add_clean_zones_supported` | | X |
43+
| `is_dynamically_skip_clean_zone_supported` | | X |
44+
| `is_egg_dance_mode_supported` | | |
45+
| `is_egg_mode_supported_from_new_features` | | |
46+
| `is_exact_custom_mode_supported` | | |
47+
| `is_exhibition_function_supported` | | |
48+
| `is_floor_dir_clean_any_time_supported` | | X |
49+
| `is_flow_led_setting_supported` | X | |
50+
| `is_fw_filter_obstacle_supported` | X | X |
51+
| `is_gap_deep_clean_supported` | | |
52+
| `is_goto_pure_clean_path_supported` | | X |
53+
| `is_hot_wash_towel_supported` | | X |
54+
| `is_identify_room_supported` | | |
55+
| `is_ignore_unknown_map_object_supported` | X | X |
56+
| `is_lds_lifting_supported` | | |
57+
| `is_led_status_switch_supported` | X | X |
58+
| `is_left_water_drain_supported` | | X |
59+
| `is_main_brush_up_down_supported_from_str` | | X |
60+
| `is_map_beautify_internal_debug_supported` | X | X |
61+
| `is_map_carpet_add_support` | | X |
62+
| `is_map_eraser_supported` | | |
63+
| `is_matter_supported` | | |
64+
| `is_max_zone_opened_supported` | | |
65+
| `is_midway_back_to_dock_supported` | | |
66+
| `is_min_battery_15_to_clean_task_supported` | | |
67+
| `is_mop_forbidden_supported` | X | |
68+
| `is_mop_path_supported` | X | X |
69+
| `is_mop_shake_water_max_supported` | | |
70+
| `is_multi_floor_supported` | X | X |
71+
| `is_multi_map_segment_timer_supported` | X | X |
72+
| `is_new_ai_recognition_supported` | | |
73+
| `is_new_data_for_clean_history` | X | X |
74+
| `is_new_data_for_clean_history_detail` | X | X |
75+
| `is_new_endpoint_supported` | | X |
76+
| `is_no_need_carpet_press_set_supported` | | |
77+
| `is_object_detect_check_supported` | | |
78+
| `is_offline_map_supported` | | X |
79+
| `is_optimize_battery_supported` | | |
80+
| `is_order_clean_supported` | X | X |
81+
| `is_pet_snapshot_supported` | | |
82+
| `is_pet_supplies_deep_clean_supported` | | |
83+
| `is_pumping_water_supported` | | |
84+
| `is_re_segment_supported` | X | X |
85+
| `is_record_allowed` | X | X |
86+
| `is_remote_supported` | X | X |
87+
| `is_right_brush_stretch_supported` | | |
88+
| `is_room_name_supported` | X | X |
89+
| `is_rpc_retry_supported` | | X |
90+
| `is_rubber_brush_carpet_supported` | | |
91+
| `is_set_child_supported` | X | X |
92+
| `is_setting_carpet_first_supported` | | X |
93+
| `is_shake_mop_set_supported` | X | X |
94+
| `is_show_clean_finish_reason_supported` | X | X |
95+
| `is_show_general_obstacle_supported` | | |
96+
| `is_show_obstacle_photo_supported` | | |
97+
| `is_small_side_mop_supported` | | |
98+
| `is_smart_clean_mode_set_supported` | | |
99+
| `is_soft_clean_mode_supported` | | |
100+
| `is_super_deep_wash_supported` | | X |
101+
| `is_support_backup_map` | X | X |
102+
| `is_support_clean_estimate` | | X |
103+
| `is_support_cliff_zone` | | X |
104+
| `is_support_custom_carpet` | | |
105+
| `is_support_custom_dnd` | | X |
106+
| `is_support_custom_door_sill` | | X |
107+
| `is_support_custom_mode_in_cleaning` | | X |
108+
| `is_support_fetch_timer_summary` | X | X |
109+
| `is_support_floor_direction` | | X |
110+
| `is_support_floor_edit` | | X |
111+
| `is_support_furniture` | | X |
112+
| `is_support_incremental_map` | | X |
113+
| `is_support_main_brush_up_down_supported` | | |
114+
| `is_support_mop_back_pwm_set` | | |
115+
| `is_support_quick_map_builder` | X | X |
116+
| `is_support_remote_control_in_call` | | |
117+
| `is_support_room_tag` | | X |
118+
| `is_support_set_switch_map_mode` | | X |
119+
| `is_support_set_volume_in_call` | | X |
120+
| `is_support_side_brush_up_down_supported` | | |
121+
| `is_support_smart_door_sill` | | X |
122+
| `is_support_smart_global_clean_with_custom_mode` | | X |
123+
| `is_support_smart_scene` | | X |
124+
| `is_support_stuck_zone` | | X |
125+
| `is_support_voice_control_debug` | | |
126+
| `is_supported_download_test_voice` | | X |
127+
| `is_supported_drying` | | X |
128+
| `is_supported_valley_electricity` | | X |
129+
| `is_two_key_real_time_video_supported` | | X |
130+
| `is_two_key_rtv_in_charging_supported` | | X |
131+
| `is_unsave_map_reason_supported` | X | X |
132+
| `is_uvc_sterilize_supported` | | |
133+
| `is_video_monitor_supported` | X | X |
134+
| `is_video_patrol_supported` | | |
135+
| `is_video_setting_supported` | X | X |
136+
| `is_voice_control_led_supported` | | |
137+
| `is_voice_control_supported` | | X |
138+
| `is_wash_then_charge_cmd_supported` | | X |
139+
| `is_water_leak_check_supported` | | |
140+
| `is_water_up_down_drain_supported` | | X |
141+
| `is_wifi_manage_supported` | | X |
142+
| `is_workday_holiday_supported` | | |

roborock/cli.py

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import asyncio
44
import json
55
import logging
6+
from dataclasses import asdict
67
from pathlib import Path
78
from typing import Any
89

@@ -11,7 +12,7 @@
1112
from pyshark.capture.live_capture import LiveCapture, UnknownInterfaceException # type: ignore
1213
from pyshark.packet.packet import Packet # type: ignore
1314

14-
from roborock import RoborockException
15+
from roborock import SHORT_MODEL_TO_ENUM, DeviceFeatures, RoborockCommand, RoborockException
1516
from roborock.containers import DeviceData, HomeData, HomeDataProduct, LoginData
1617
from roborock.devices.device_manager import create_device_manager, create_home_data_api
1718
from roborock.protocol import MessageParser
@@ -300,6 +301,102 @@ def on_package(packet: Packet):
300301
)
301302

302303

304+
@click.command()
305+
@click.pass_context
306+
@run_sync()
307+
async def update_docs(ctx):
308+
"""
309+
Generates or updates a markdown file with features for all devices.
310+
"""
311+
MARKDOWN_FILE = Path("../SUPPORTED_FEATURES.md")
312+
313+
def write_markdown_table(product_data: dict[str, dict[str, str]], all_features: set[str]):
314+
"""Writes the data into a markdown table (products as columns)."""
315+
sorted_products = sorted(product_data.keys())
316+
sorted_features = sorted(list(all_features))
317+
318+
header = ["Feature"] + sorted_products
319+
320+
with open(MARKDOWN_FILE, "w", encoding="utf-8") as f:
321+
f.write("| " + " | ".join(header) + " |\n")
322+
f.write("|" + "---|" * len(header) + "\n")
323+
324+
# Create data for special rows
325+
special_rows = [
326+
"Product Nickname",
327+
"Protocol Version",
328+
"New Feature Info",
329+
"New Feature Info Str",
330+
"Feature Info",
331+
]
332+
for row in special_rows:
333+
row_values = [str(product_data[p].get(row, "")) for p in sorted_products]
334+
f.write("| " + " | ".join([row] + row_values) + " |\n")
335+
for feature in sorted_features:
336+
feature_row = [f"`{feature}`"]
337+
for product in sorted_products:
338+
feature_row.append(product_data[product].get(feature, ""))
339+
f.write("| " + " | ".join(feature_row) + " |\n")
340+
341+
product_features_map = {}
342+
all_feature_names = set()
343+
344+
context: RoborockContext = ctx.obj
345+
login_data = context.login_data()
346+
if not login_data.home_data:
347+
await _discover(ctx)
348+
login_data = context.login_data()
349+
home_data = login_data.home_data
350+
351+
all_devices = home_data.devices + home_data.received_devices
352+
click.echo(f"Found {len(all_devices)} devices. Fetching current data via MQTT...")
353+
354+
for device in all_devices:
355+
click.echo(f" - Processing {device.name} ({device.duid})")
356+
product_info = home_data.product_map[device.product_id]
357+
device_data = DeviceData(device, product_info.model)
358+
mqtt_client = RoborockMqttClientV1(login_data.user_data, device_data)
359+
try:
360+
init_status_result = await mqtt_client.send_command(
361+
RoborockCommand.APP_GET_INIT_STATUS,
362+
)
363+
product_nickname = SHORT_MODEL_TO_ENUM.get(product_info.model.split(".")[-1])
364+
device_features = DeviceFeatures.from_feature_flags(
365+
new_feature_info=init_status_result.get("new_feature_info"),
366+
new_feature_info_str=init_status_result.get("new_feature_info_str"),
367+
feature_info=init_status_result.get("feature_info"),
368+
product_nickname=product_nickname,
369+
)
370+
features_dict = asdict(device_features)
371+
372+
current_product_data = {
373+
"Protocol Version": device.pv,
374+
"Product Nickname": product_nickname,
375+
"New Feature Info": init_status_result.get("new_feature_info"),
376+
"New Feature Info Str": init_status_result.get("new_feature_info_str"),
377+
"Feature Info": init_status_result.get("feature_info"),
378+
}
379+
380+
for feature, is_supported in features_dict.items():
381+
all_feature_names.add(feature)
382+
current_product_data[feature] = "X" if is_supported else ""
383+
384+
product_features_map[product_info.model] = current_product_data
385+
386+
except Exception as e:
387+
click.echo(f" - Error processing device {device.name}: {e}")
388+
finally:
389+
await mqtt_client.async_release()
390+
391+
if not product_features_map:
392+
click.echo("No device data could be gathered. File not updated.")
393+
return
394+
395+
click.echo(f"Writing updated data to {MARKDOWN_FILE}...")
396+
write_markdown_table(product_features_map, all_feature_names)
397+
click.echo("Done.")
398+
399+
303400
cli.add_command(login)
304401
cli.add_command(discover)
305402
cli.add_command(list_devices)
@@ -309,6 +406,7 @@ def on_package(packet: Packet):
309406
cli.add_command(command)
310407
cli.add_command(parser)
311408
cli.add_command(session)
409+
cli.add_command(update_docs)
312410

313411

314412
def main():

0 commit comments

Comments
 (0)