deebot-client 7.2.0__tar.gz → 7.3.0__tar.gz
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.
- {deebot_client-7.2.0 → deebot_client-7.3.0}/.pre-commit-config.yaml +2 -2
- {deebot_client-7.2.0 → deebot_client-7.3.0}/PKG-INFO +1 -1
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/capabilities.py +2 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/command.py +48 -26
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/__init__.py +3 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/clean.py +1 -1
- deebot_client-7.3.0/deebot_client/commands/json/cut_direction.py +43 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/error.py +12 -7
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/pos.py +5 -1
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/water_info.py +20 -4
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/xml/pos.py +4 -1
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/const.py +44 -1
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/device.py +24 -12
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/event_bus.py +2 -1
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/events/__init__.py +13 -1
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/events/map.py +1 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/events/water_info.py +9 -0
- deebot_client-7.3.0/deebot_client/hardware/deebot/2ap5uq.py +139 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/5xu9h3.py +6 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/77atlz.py +6 -0
- deebot_client-7.3.0/deebot_client/hardware/deebot/9s1s80.py +226 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/e6ofmn.py +0 -1
- deebot_client-7.3.0/deebot_client/hardware/deebot/guzput.py +139 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/itk04l.py +6 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/lf3bn4.py +0 -1
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/map.py +47 -94
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client.egg-info/PKG-INFO +1 -1
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client.egg-info/SOURCES.txt +6 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/requirements-test.txt +5 -4
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/__init__.py +17 -7
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_common.py +1 -1
- deebot_client-7.3.0/tests/commands/json/test_cut_direction.py +20 -0
- deebot_client-7.3.0/tests/commands/json/test_error.py +35 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_life_span.py +48 -0
- deebot_client-7.3.0/tests/commands/json/test_pos.py +22 -0
- deebot_client-7.3.0/tests/commands/json/test_water_info.py +89 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/xml/test_pos.py +1 -1
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/fixtures/base.py +2 -2
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/hardware/test_init.py +7 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/test_command.py +2 -1
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/test_device.py +3 -2
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/test_map.py +104 -53
- deebot_client-7.2.0/tests/commands/json/test_error.py +0 -29
- deebot_client-7.2.0/tests/commands/json/test_water_info.py +0 -47
- {deebot_client-7.2.0 → deebot_client-7.3.0}/.codecov.yml +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/.coveragerc +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/.devcontainer.json +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/.gitattributes +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/.github/FUNDING.yml +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/.github/dependabot.yml +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/.github/release-drafter.yml +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/.github/workflows/ci.yml +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/.github/workflows/codeql-analysis.yml +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/.github/workflows/python-publish.yml +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/.github/workflows/release-drafter.yml +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/.gitignore +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/.prettierrc.js +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/.yamllint +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/LICENSE.txt +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/README.md +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/__init__.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/api_client.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/authentication.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/__init__.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/advanced_mode.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/battery.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/border_switch.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/carpet.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/charge.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/charge_state.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/child_lock.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/clean_count.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/clean_logs.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/clean_preference.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/clear_map.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/common.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/const.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/continuous_cleaning.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/cross_map_border_warning.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/custom.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/efficiency.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/fan_speed.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/life_span.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/map.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/moveup_warning.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/multimap_state.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/network.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/ota.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/play_sound.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/relocation.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/safe_protect.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/stats.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/sweep_mode.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/true_detect.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/voice_assistant_state.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/volume.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/work_mode.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/xml/__init__.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/xml/charge_state.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/xml/common.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/xml/error.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/xml/fan_speed.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/events/base.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/events/efficiency_mode.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/events/fan_speed.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/events/network.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/events/work_mode.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/exceptions.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/__init__.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/2o4lnm.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/55aiho.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/626v6g.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/85nbtp.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/9ku8nu.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/__init__.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/clojes.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/fallback.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/lx3j7m.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/p1jij8.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/p95mgv.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/paeygf.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/rss8xk.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/umwv6z.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/vi829v.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/x5d34r.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/yna5xi.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/zjavof.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/logging_filter.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/message.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/messages/__init__.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/messages/json/__init__.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/messages/json/battery.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/messages/json/map.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/messages/json/stats.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/models.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/mqtt_client.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/py.typed +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/util/__init__.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/util/continents.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/util/countries.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client.egg-info/dependency_links.txt +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client.egg-info/requires.txt +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client.egg-info/top_level.txt +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/mypy.ini +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/pyproject.toml +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/pytest.ini +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/requirements-dev.txt +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/requirements.txt +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/scripts/check_getLogger.sh +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/scripts/run-in-env.sh +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/setup.cfg +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/__init__.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/__init__.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_advanced_mode.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_battery.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_border_switch.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_carpet.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_charge.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_charge_state.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_child_lock.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_clean.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_clean_count.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_clean_log.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_clean_preference.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_continuous_cleaning.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_cross_map_border_warning.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_custom.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_efficiency.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_fan_speed.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_map.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_moveup_warning.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_mulitmap_state.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_network.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_ota.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_safe_protect.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_sweep_mode.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_true_detect.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_voice_assistant_state.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_volume.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_work_mode.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/xml/__init__.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/xml/test_charge_state.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/xml/test_error.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/xml/test_fan_speed.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/common.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/conftest.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/events/__init__.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/fixtures/__init__.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/fixtures/mqtt_server.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/hardware/__init__.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/helpers/__init__.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/helpers/tasks.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/messages/__init__.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/messages/json/__init__.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/messages/json/test_battery.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/messages/json/test_map.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/messages/json/test_stats.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/messages/test_get_messages.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/messages/test_messages.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/mqtt_util.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/test_authentication.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/test_event_bus.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/test_mqtt_client.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/test_mqtt_client_reconnect.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/util/__init__.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/util/test_continents.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/util/test_countries.py +0 -0
- {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/util/test_init.py +0 -0
|
@@ -11,7 +11,7 @@ default_language_version:
|
|
|
11
11
|
|
|
12
12
|
repos:
|
|
13
13
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
14
|
-
rev: v0.4.
|
|
14
|
+
rev: v0.4.5
|
|
15
15
|
hooks:
|
|
16
16
|
- id: ruff
|
|
17
17
|
args:
|
|
@@ -25,7 +25,7 @@ repos:
|
|
|
25
25
|
args:
|
|
26
26
|
- --py312-plus
|
|
27
27
|
- repo: https://github.com/codespell-project/codespell
|
|
28
|
-
rev: v2.
|
|
28
|
+
rev: v2.3.0
|
|
29
29
|
hooks:
|
|
30
30
|
- id: codespell
|
|
31
31
|
args:
|
|
@@ -21,6 +21,7 @@ from deebot_client.events import (
|
|
|
21
21
|
ContinuousCleaningEvent,
|
|
22
22
|
CrossMapBorderWarningEvent,
|
|
23
23
|
CustomCommandEvent,
|
|
24
|
+
CutDirectionEvent,
|
|
24
25
|
ErrorEvent,
|
|
25
26
|
Event,
|
|
26
27
|
FanSpeedEvent,
|
|
@@ -189,6 +190,7 @@ class CapabilitySettings:
|
|
|
189
190
|
)
|
|
190
191
|
border_switch: CapabilitySetEnable[BorderSwitchEvent] | None = None
|
|
191
192
|
child_lock: CapabilitySetEnable[ChildLockEvent] | None = None
|
|
193
|
+
cut_direction: CapabilitySet[CutDirectionEvent, int] | None = None
|
|
192
194
|
moveup_warning: CapabilitySetEnable[MoveUpWarningEvent] | None = None
|
|
193
195
|
cross_map_border_warning: CapabilitySetEnable[CrossMapBorderWarningEvent] | None = (
|
|
194
196
|
None
|
|
@@ -8,7 +8,10 @@ from dataclasses import dataclass, field
|
|
|
8
8
|
from typing import TYPE_CHECKING, Any, final
|
|
9
9
|
|
|
10
10
|
from deebot_client.events import AvailabilityEvent
|
|
11
|
-
from deebot_client.exceptions import
|
|
11
|
+
from deebot_client.exceptions import (
|
|
12
|
+
ApiTimeoutError,
|
|
13
|
+
DeebotError,
|
|
14
|
+
)
|
|
12
15
|
|
|
13
16
|
from .const import PATH_API_IOT_DEVMANAGER, REQUEST_HEADERS, DataType
|
|
14
17
|
from .logging_filter import get_logger
|
|
@@ -41,6 +44,22 @@ class CommandResult(HandlingResult):
|
|
|
41
44
|
return CommandResult(HandlingState.ANALYSE)
|
|
42
45
|
|
|
43
46
|
|
|
47
|
+
@dataclass(frozen=True)
|
|
48
|
+
class DeviceCommandResult:
|
|
49
|
+
"""Device command result object.
|
|
50
|
+
|
|
51
|
+
Returns
|
|
52
|
+
-------
|
|
53
|
+
device_reached (bool): True if the command was targeting the bot, and it responded in time. False otherwise.
|
|
54
|
+
This value is not indicating if the command was executed successfully.
|
|
55
|
+
raw_response (dict[str, Any]): The command response data.
|
|
56
|
+
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
device_reached: bool
|
|
60
|
+
raw_response: dict[str, Any] = field(default_factory=dict)
|
|
61
|
+
|
|
62
|
+
|
|
44
63
|
class Command(ABC):
|
|
45
64
|
"""Abstract command object."""
|
|
46
65
|
|
|
@@ -73,17 +92,12 @@ class Command(ABC):
|
|
|
73
92
|
authenticator: Authenticator,
|
|
74
93
|
device_info: ApiDeviceInfo,
|
|
75
94
|
event_bus: EventBus,
|
|
76
|
-
) ->
|
|
77
|
-
"""Execute command.
|
|
78
|
-
|
|
79
|
-
Returns
|
|
80
|
-
-------
|
|
81
|
-
bot_reached (bool): True if the command was targeting the bot, and it responded in time. False otherwise.
|
|
82
|
-
This value is not indicating if the command was executed successfully.
|
|
83
|
-
|
|
84
|
-
"""
|
|
95
|
+
) -> DeviceCommandResult:
|
|
96
|
+
"""Execute command."""
|
|
85
97
|
try:
|
|
86
|
-
result = await self._execute(
|
|
98
|
+
result, response = await self._execute(
|
|
99
|
+
authenticator, device_info, event_bus
|
|
100
|
+
)
|
|
87
101
|
if result.state == HandlingState.SUCCESS:
|
|
88
102
|
# Execute command which are requested by the handler
|
|
89
103
|
async with asyncio.TaskGroup() as tg:
|
|
@@ -94,21 +108,24 @@ class Command(ABC):
|
|
|
94
108
|
)
|
|
95
109
|
)
|
|
96
110
|
|
|
97
|
-
return
|
|
111
|
+
return DeviceCommandResult(
|
|
112
|
+
device_reached=self._targets_bot, raw_response=response
|
|
113
|
+
)
|
|
114
|
+
|
|
98
115
|
except Exception: # pylint: disable=broad-except
|
|
99
116
|
_LOGGER.warning(
|
|
100
117
|
"Could not execute command %s",
|
|
101
118
|
self.name,
|
|
102
119
|
exc_info=True,
|
|
103
120
|
)
|
|
104
|
-
return False
|
|
121
|
+
return DeviceCommandResult(device_reached=False)
|
|
105
122
|
|
|
106
123
|
async def _execute(
|
|
107
124
|
self,
|
|
108
125
|
authenticator: Authenticator,
|
|
109
126
|
device_info: ApiDeviceInfo,
|
|
110
127
|
event_bus: EventBus,
|
|
111
|
-
) -> CommandResult:
|
|
128
|
+
) -> tuple[CommandResult, dict[str, Any]]:
|
|
112
129
|
"""Execute command."""
|
|
113
130
|
try:
|
|
114
131
|
response = await self._execute_api_request(authenticator, device_info)
|
|
@@ -117,21 +134,24 @@ class Command(ABC):
|
|
|
117
134
|
"Could not execute command %s: Timeout reached",
|
|
118
135
|
self.name,
|
|
119
136
|
)
|
|
120
|
-
return CommandResult(HandlingState.ERROR)
|
|
137
|
+
return CommandResult(HandlingState.ERROR), {}
|
|
121
138
|
|
|
122
139
|
result = self.__handle_response(event_bus, response)
|
|
123
140
|
if result.state == HandlingState.ANALYSE:
|
|
124
141
|
_LOGGER.debug(
|
|
125
142
|
"ANALYSE: Could not handle command: %s with %s", self.name, response
|
|
126
143
|
)
|
|
127
|
-
return
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
144
|
+
return (
|
|
145
|
+
CommandResult(
|
|
146
|
+
HandlingState.ANALYSE_LOGGED,
|
|
147
|
+
result.args,
|
|
148
|
+
result.requested_commands,
|
|
149
|
+
),
|
|
150
|
+
response,
|
|
131
151
|
)
|
|
132
152
|
if result.state == HandlingState.ERROR:
|
|
133
153
|
_LOGGER.warning("Could not parse %s: %s", self.name, response)
|
|
134
|
-
return result
|
|
154
|
+
return result, response
|
|
135
155
|
|
|
136
156
|
async def _execute_api_request(
|
|
137
157
|
self, authenticator: Authenticator, device_info: ApiDeviceInfo
|
|
@@ -260,6 +280,7 @@ class InitParam:
|
|
|
260
280
|
|
|
261
281
|
type_: type
|
|
262
282
|
name: str | None = None
|
|
283
|
+
optional: bool = field(default=False, kw_only=True)
|
|
263
284
|
|
|
264
285
|
|
|
265
286
|
class CommandMqttP2P(Command, ABC):
|
|
@@ -283,7 +304,12 @@ class CommandMqttP2P(Command, ABC):
|
|
|
283
304
|
# Remove field
|
|
284
305
|
data.pop(name, None)
|
|
285
306
|
else:
|
|
286
|
-
|
|
307
|
+
try:
|
|
308
|
+
values[param.name or name] = _pop_or_raise(name, param.type_, data)
|
|
309
|
+
except KeyError as err:
|
|
310
|
+
if not param.optional:
|
|
311
|
+
msg = f'"{name}" is missing in {data}'
|
|
312
|
+
raise DeebotError(msg) from err
|
|
287
313
|
|
|
288
314
|
if data:
|
|
289
315
|
_LOGGER.debug("Following data will be ignored: %s", data)
|
|
@@ -292,11 +318,7 @@ class CommandMqttP2P(Command, ABC):
|
|
|
292
318
|
|
|
293
319
|
|
|
294
320
|
def _pop_or_raise(name: str, type_: type, data: dict[str, Any]) -> Any:
|
|
295
|
-
|
|
296
|
-
value = data.pop(name)
|
|
297
|
-
except KeyError as err:
|
|
298
|
-
msg = f'"{name}" is missing in {data}'
|
|
299
|
-
raise DeebotError(msg) from err
|
|
321
|
+
value = data.pop(name)
|
|
300
322
|
try:
|
|
301
323
|
return type_(value)
|
|
302
324
|
except ValueError as err:
|
|
@@ -20,6 +20,7 @@ from .clean_preference import GetCleanPreference, SetCleanPreference
|
|
|
20
20
|
from .clear_map import ClearMap
|
|
21
21
|
from .continuous_cleaning import GetContinuousCleaning, SetContinuousCleaning
|
|
22
22
|
from .cross_map_border_warning import GetCrossMapBorderWarning, SetCrossMapBorderWarning
|
|
23
|
+
from .cut_direction import GetCutDirection, SetCutDirection
|
|
23
24
|
from .efficiency import GetEfficiencyMode, SetEfficiencyMode
|
|
24
25
|
from .error import GetError
|
|
25
26
|
from .fan_speed import GetFanSpeed, SetFanSpeed
|
|
@@ -77,6 +78,8 @@ __all__ = [
|
|
|
77
78
|
"GetCleanLogs",
|
|
78
79
|
"GetContinuousCleaning",
|
|
79
80
|
"SetContinuousCleaning",
|
|
81
|
+
"SetCutDirection",
|
|
82
|
+
"GetCutDirection",
|
|
80
83
|
"GetCrossMapBorderWarning",
|
|
81
84
|
"SetCrossMapBorderWarning",
|
|
82
85
|
"GetEfficiencyMode",
|
|
@@ -32,7 +32,7 @@ class Clean(ExecuteCommand):
|
|
|
32
32
|
authenticator: Authenticator,
|
|
33
33
|
device_info: ApiDeviceInfo,
|
|
34
34
|
event_bus: EventBus,
|
|
35
|
-
) -> CommandResult:
|
|
35
|
+
) -> tuple[CommandResult, dict[str, Any]]:
|
|
36
36
|
"""Execute command."""
|
|
37
37
|
state = event_bus.get_last_event(StateEvent)
|
|
38
38
|
if state and isinstance(self._args, dict):
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Cut direction command module."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from types import MappingProxyType
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
|
+
|
|
8
|
+
from deebot_client.command import InitParam
|
|
9
|
+
from deebot_client.events import CutDirectionEvent
|
|
10
|
+
from deebot_client.message import HandlingResult
|
|
11
|
+
|
|
12
|
+
from .common import JsonGetCommand, JsonSetCommand
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from deebot_client.event_bus import EventBus
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class GetCutDirection(JsonGetCommand):
|
|
19
|
+
"""Get cut direction command."""
|
|
20
|
+
|
|
21
|
+
name = "getCutDirection"
|
|
22
|
+
|
|
23
|
+
@classmethod
|
|
24
|
+
def _handle_body_data_dict(
|
|
25
|
+
cls, event_bus: EventBus, data: dict[str, Any]
|
|
26
|
+
) -> HandlingResult:
|
|
27
|
+
"""Handle message->body->data and notify the correct event subscribers.
|
|
28
|
+
|
|
29
|
+
:return: A message response
|
|
30
|
+
"""
|
|
31
|
+
event_bus.notify(CutDirectionEvent(angle=data["angle"]))
|
|
32
|
+
return HandlingResult.success()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class SetCutDirection(JsonSetCommand):
|
|
36
|
+
"""Set cut direction command."""
|
|
37
|
+
|
|
38
|
+
name = "setCutDirection"
|
|
39
|
+
get_command = GetCutDirection
|
|
40
|
+
_mqtt_params = MappingProxyType({"angle": InitParam(int)})
|
|
41
|
+
|
|
42
|
+
def __init__(self, angle: int) -> None:
|
|
43
|
+
super().__init__({"angle": angle})
|
|
@@ -28,16 +28,21 @@ class GetError(JsonCommandWithMessageHandling, MessageBodyDataDict):
|
|
|
28
28
|
|
|
29
29
|
:return: A message response
|
|
30
30
|
"""
|
|
31
|
-
codes = data.get("code"
|
|
31
|
+
codes = data.get("code")
|
|
32
|
+
if not isinstance(codes, list):
|
|
33
|
+
return HandlingResult.analyse()
|
|
34
|
+
|
|
35
|
+
error: int | None = 0
|
|
36
|
+
|
|
32
37
|
if codes:
|
|
33
38
|
# the last error code
|
|
34
39
|
error = codes[-1]
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
if error is not None:
|
|
42
|
+
description = ERROR_CODES.get(error)
|
|
43
|
+
if error != 0:
|
|
44
|
+
event_bus.notify(StateEvent(State.ERROR))
|
|
45
|
+
event_bus.notify(ErrorEvent(error, description))
|
|
46
|
+
return HandlingResult.success()
|
|
42
47
|
|
|
43
48
|
return HandlingResult.analyse()
|
|
@@ -40,13 +40,17 @@ class GetPos(JsonCommandWithMessageHandling, MessageBodyDataDict):
|
|
|
40
40
|
type=PositionType(type_str),
|
|
41
41
|
x=data_positions["x"],
|
|
42
42
|
y=data_positions["y"],
|
|
43
|
+
a=data_positions.get("a", 0),
|
|
43
44
|
)
|
|
44
45
|
)
|
|
45
46
|
else:
|
|
46
47
|
positions.extend(
|
|
47
48
|
[
|
|
48
49
|
Position(
|
|
49
|
-
type=PositionType(type_str),
|
|
50
|
+
type=PositionType(type_str),
|
|
51
|
+
x=entry["x"],
|
|
52
|
+
y=entry["y"],
|
|
53
|
+
a=entry.get("a", 0),
|
|
50
54
|
)
|
|
51
55
|
for entry in data_positions
|
|
52
56
|
]
|
|
@@ -6,7 +6,7 @@ from types import MappingProxyType
|
|
|
6
6
|
from typing import TYPE_CHECKING, Any
|
|
7
7
|
|
|
8
8
|
from deebot_client.command import InitParam
|
|
9
|
-
from deebot_client.events import WaterAmount, WaterInfoEvent
|
|
9
|
+
from deebot_client.events import SweepType, WaterAmount, WaterInfoEvent
|
|
10
10
|
from deebot_client.message import HandlingResult
|
|
11
11
|
from deebot_client.util import get_enum
|
|
12
12
|
|
|
@@ -33,8 +33,15 @@ class GetWaterInfo(JsonGetCommand):
|
|
|
33
33
|
if mop_attached is not None:
|
|
34
34
|
mop_attached = bool(mop_attached)
|
|
35
35
|
|
|
36
|
+
if sweep_type := data.get("sweepType"):
|
|
37
|
+
sweep_type = SweepType(int(sweep_type))
|
|
38
|
+
|
|
36
39
|
event_bus.notify(
|
|
37
|
-
WaterInfoEvent(
|
|
40
|
+
WaterInfoEvent(
|
|
41
|
+
WaterAmount(int(data["amount"])),
|
|
42
|
+
sweep_type,
|
|
43
|
+
mop_attached=mop_attached,
|
|
44
|
+
)
|
|
38
45
|
)
|
|
39
46
|
return HandlingResult.success()
|
|
40
47
|
|
|
@@ -48,10 +55,19 @@ class SetWaterInfo(JsonSetCommand):
|
|
|
48
55
|
{
|
|
49
56
|
"amount": InitParam(WaterAmount),
|
|
50
57
|
"enable": None, # Remove it as we don't can set it (App includes it)
|
|
58
|
+
"sweepType": InitParam(SweepType, "sweep_type", optional=True),
|
|
51
59
|
}
|
|
52
60
|
)
|
|
53
61
|
|
|
54
|
-
def __init__(
|
|
62
|
+
def __init__(
|
|
63
|
+
self, amount: WaterAmount | str, sweep_type: SweepType | str | None = None
|
|
64
|
+
) -> None:
|
|
65
|
+
params = {}
|
|
55
66
|
if isinstance(amount, str):
|
|
56
67
|
amount = get_enum(WaterAmount, amount)
|
|
57
|
-
|
|
68
|
+
params["amount"] = amount.value
|
|
69
|
+
if sweep_type:
|
|
70
|
+
if isinstance(sweep_type, str):
|
|
71
|
+
sweep_type = get_enum(SweepType, sweep_type)
|
|
72
|
+
params["sweepType"] = sweep_type.value
|
|
73
|
+
super().__init__(params)
|
|
@@ -31,7 +31,10 @@ class GetPos(XmlCommandWithMessageHandling):
|
|
|
31
31
|
|
|
32
32
|
if p := xml.attrib.get("p"):
|
|
33
33
|
p_x, p_y = p.split(",", 2)
|
|
34
|
-
|
|
34
|
+
p_a = xml.attrib.get("a", 0)
|
|
35
|
+
position = Position(
|
|
36
|
+
type=PositionType.DEEBOT, x=int(p_x), y=int(p_y), a=int(p_a)
|
|
37
|
+
)
|
|
35
38
|
event_bus.notify(PositionsEvent(positions=[position]))
|
|
36
39
|
return HandlingResult.success()
|
|
37
40
|
|
|
@@ -46,7 +46,9 @@ ERROR_CODES = {
|
|
|
46
46
|
-2: "Internal error",
|
|
47
47
|
-1: "Host not reachable or communication malfunction",
|
|
48
48
|
0: "NoError: Robot is operational",
|
|
49
|
+
1: "Request error",
|
|
49
50
|
3: "RequestOAuthError: Authentication error",
|
|
51
|
+
5: "Access denied",
|
|
50
52
|
7: "log data is not found",
|
|
51
53
|
100: "NoError: Robot is operational",
|
|
52
54
|
101: "BatteryLow: Low battery",
|
|
@@ -69,6 +71,11 @@ ERROR_CODES = {
|
|
|
69
71
|
118: "StrainerBlock:",
|
|
70
72
|
119: "FanError:",
|
|
71
73
|
120: "WaterBoxError:",
|
|
74
|
+
121: "PlateJammed:",
|
|
75
|
+
125: "Reservoir malfunction",
|
|
76
|
+
126: "Reservoir is not installed",
|
|
77
|
+
128: "Mopping Pad Plates are not both installed",
|
|
78
|
+
129: "Mopping Pad Plate is tangled",
|
|
72
79
|
201: "AirFilterUninstall:",
|
|
73
80
|
202: "UltrasonicComponentAbnormal",
|
|
74
81
|
203: "SmallWheelError",
|
|
@@ -76,9 +83,45 @@ ERROR_CODES = {
|
|
|
76
83
|
205: "IonSterilizeExhausted",
|
|
77
84
|
206: "IonSterilizeAbnormal",
|
|
78
85
|
207: "IonSterilizeFault",
|
|
79
|
-
|
|
86
|
+
209: "D-ToF Sensor malfunction",
|
|
87
|
+
301: "FreshWaterBox empty",
|
|
88
|
+
302: "WasteWaterBox full",
|
|
89
|
+
303: "FreshWaterBox missing",
|
|
90
|
+
304: "WasteWaterBox missing",
|
|
91
|
+
305: "Dirty Water Tank is full",
|
|
92
|
+
306: "Cleaning Sink Filter is not installed",
|
|
93
|
+
307: "Auto-Clean Station malfunction",
|
|
94
|
+
308: "Communication Malfunction",
|
|
95
|
+
310: "Lid open",
|
|
96
|
+
311: "Replace the Dust Bag",
|
|
97
|
+
312: "TODO: unknown",
|
|
98
|
+
313: "TODO: unknown",
|
|
99
|
+
314: "Water Supply Module or Silver Ion Sterilization module is not installed",
|
|
100
|
+
315: "Water Supply Module is not installed",
|
|
101
|
+
316: "Clean silk is full",
|
|
102
|
+
317: "Clean Water Tank refill malfunction",
|
|
103
|
+
318: "Dirty Water Tank is full",
|
|
104
|
+
319: "Cleaning solution is running low",
|
|
80
105
|
404: "Recipient unavailable",
|
|
81
106
|
500: "Request Timeout",
|
|
82
107
|
601: "ERROR_ClosedAIVISideAbnormal",
|
|
83
108
|
602: "ClosedAIVIRollAbnormal",
|
|
109
|
+
1007: "Mop plugged",
|
|
110
|
+
1021: "Cleaning complete",
|
|
111
|
+
1024: "Low battery, returning to charge",
|
|
112
|
+
1027: "Unable de find the charging dock, returning to starting position",
|
|
113
|
+
1052: "Time to change the mop",
|
|
114
|
+
1053: "Task interrupted, returning to charge",
|
|
115
|
+
1061: "Resuming cleaning after position retrieved",
|
|
116
|
+
1062: "Unable de locate, starting new map",
|
|
117
|
+
1068: "Failed to find position, returning to charge",
|
|
118
|
+
1071: "Position updated",
|
|
119
|
+
1094: "AirDrying mop",
|
|
120
|
+
1088: "Failed to find position",
|
|
121
|
+
2036: "Obstacle detected",
|
|
122
|
+
2079: "TODO: unknown",
|
|
123
|
+
4200: "Robot not reachable",
|
|
124
|
+
20003: "Task type not supported",
|
|
125
|
+
20011: "Handle deal message fail",
|
|
126
|
+
20012: "Get point count out of range",
|
|
84
127
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
+
from collections.abc import Callable, Coroutine
|
|
6
7
|
from contextlib import suppress
|
|
7
8
|
from datetime import datetime
|
|
8
9
|
import json
|
|
@@ -12,6 +13,7 @@ from deebot_client.events.network import NetworkInfoEvent
|
|
|
12
13
|
from deebot_client.mqtt_client import MqttClient, SubscriberInfo
|
|
13
14
|
from deebot_client.util import cancel
|
|
14
15
|
|
|
16
|
+
from .command import Command
|
|
15
17
|
from .event_bus import EventBus
|
|
16
18
|
from .events import (
|
|
17
19
|
AvailabilityEvent,
|
|
@@ -30,15 +32,16 @@ from .messages import get_message
|
|
|
30
32
|
from .models import DeviceCapabilities, DeviceInfo, State
|
|
31
33
|
|
|
32
34
|
if TYPE_CHECKING:
|
|
33
|
-
from collections.abc import Callable
|
|
34
|
-
|
|
35
35
|
from .authentication import Authenticator
|
|
36
|
-
from .command import
|
|
36
|
+
from .command import DeviceCommandResult
|
|
37
37
|
|
|
38
38
|
_LOGGER = get_logger(__name__)
|
|
39
39
|
_AVAILABLE_CHECK_INTERVAL = 60
|
|
40
40
|
|
|
41
41
|
|
|
42
|
+
DeviceCommandExecute = Callable[[Command], Coroutine[Any, Any, dict[str, Any]]]
|
|
43
|
+
|
|
44
|
+
|
|
42
45
|
class Device(Generic[DeviceCapabilities]):
|
|
43
46
|
"""Device representation."""
|
|
44
47
|
|
|
@@ -107,9 +110,15 @@ class Device(Generic[DeviceCapabilities]):
|
|
|
107
110
|
|
|
108
111
|
self.events.subscribe(NetworkInfoEvent, on_network)
|
|
109
112
|
|
|
110
|
-
async def execute_command(self, command: Command) ->
|
|
111
|
-
"""Execute given command.
|
|
112
|
-
|
|
113
|
+
async def execute_command(self, command: Command) -> dict[str, Any]:
|
|
114
|
+
"""Execute given command.
|
|
115
|
+
|
|
116
|
+
Returns
|
|
117
|
+
-------
|
|
118
|
+
command_response (dict[str, Any]) The command raw response.
|
|
119
|
+
|
|
120
|
+
"""
|
|
121
|
+
return (await self._execute_command(command)).raw_response
|
|
113
122
|
|
|
114
123
|
async def initialize(self, client: MqttClient) -> None:
|
|
115
124
|
"""Initialize vacumm bot, which includes MQTT-subscription and starting the available check."""
|
|
@@ -147,7 +156,7 @@ class Device(Generic[DeviceCapabilities]):
|
|
|
147
156
|
tasks.add(asyncio.create_task(self._execute_command(command)))
|
|
148
157
|
|
|
149
158
|
result = await asyncio.gather(*tasks)
|
|
150
|
-
self._set_available(available=all(result))
|
|
159
|
+
self._set_available(available=all(r.device_reached for r in result))
|
|
151
160
|
except Exception: # pylint: disable=broad-exception-caught
|
|
152
161
|
_LOGGER.debug(
|
|
153
162
|
"An exception occurred during the available check",
|
|
@@ -156,16 +165,19 @@ class Device(Generic[DeviceCapabilities]):
|
|
|
156
165
|
await cancel(tasks)
|
|
157
166
|
await asyncio.sleep(_AVAILABLE_CHECK_INTERVAL)
|
|
158
167
|
|
|
159
|
-
async def _execute_command(
|
|
168
|
+
async def _execute_command(
|
|
169
|
+
self,
|
|
170
|
+
command: Command,
|
|
171
|
+
) -> DeviceCommandResult:
|
|
160
172
|
"""Execute given command."""
|
|
161
173
|
async with self._semaphore:
|
|
162
|
-
|
|
174
|
+
result = await command.execute(
|
|
163
175
|
self._authenticator, self.device_info, self.events
|
|
164
|
-
)
|
|
176
|
+
)
|
|
177
|
+
if result.device_reached:
|
|
165
178
|
self._set_available(available=True)
|
|
166
|
-
return True
|
|
167
179
|
|
|
168
|
-
|
|
180
|
+
return result
|
|
169
181
|
|
|
170
182
|
def _set_available(self, *, available: bool) -> None:
|
|
171
183
|
"""Set available."""
|
|
@@ -16,6 +16,7 @@ if TYPE_CHECKING:
|
|
|
16
16
|
from collections.abc import Callable, Coroutine
|
|
17
17
|
|
|
18
18
|
from .command import Command
|
|
19
|
+
from .device import DeviceCommandExecute
|
|
19
20
|
|
|
20
21
|
_LOGGER = get_logger(__name__)
|
|
21
22
|
|
|
@@ -63,7 +64,7 @@ class EventBus:
|
|
|
63
64
|
|
|
64
65
|
def __init__(
|
|
65
66
|
self,
|
|
66
|
-
execute_command:
|
|
67
|
+
execute_command: DeviceCommandExecute,
|
|
67
68
|
get_refresh_commands: Callable[[type[Event]], list[Command]],
|
|
68
69
|
) -> None:
|
|
69
70
|
self._event_processing_dict: dict[type[Event], _EventProcessingData[Any]] = {}
|
|
@@ -24,7 +24,7 @@ from .map import (
|
|
|
24
24
|
PositionType,
|
|
25
25
|
)
|
|
26
26
|
from .network import NetworkInfoEvent
|
|
27
|
-
from .water_info import WaterAmount, WaterInfoEvent
|
|
27
|
+
from .water_info import SweepType, WaterAmount, WaterInfoEvent
|
|
28
28
|
from .work_mode import WorkMode, WorkModeEvent
|
|
29
29
|
|
|
30
30
|
if TYPE_CHECKING:
|
|
@@ -52,6 +52,7 @@ __all__ = [
|
|
|
52
52
|
"PositionType",
|
|
53
53
|
"PositionsEvent",
|
|
54
54
|
"SweepModeEvent",
|
|
55
|
+
"SweepType",
|
|
55
56
|
"WaterAmount",
|
|
56
57
|
"WaterInfoEvent",
|
|
57
58
|
"WorkMode",
|
|
@@ -135,6 +136,10 @@ class LifeSpan(str, Enum):
|
|
|
135
136
|
HUMIDIFY_MAINTENANCE = "wbCare"
|
|
136
137
|
BLADE = "blade"
|
|
137
138
|
LENS_BRUSH = "lensBrush"
|
|
139
|
+
DUST_BAG = "dustBag"
|
|
140
|
+
CLEANING_FLUID = "autoWater_cleaningFluid"
|
|
141
|
+
STRAINER = "strainer"
|
|
142
|
+
HAND_FILTER = "handFilter"
|
|
138
143
|
|
|
139
144
|
|
|
140
145
|
@dataclass(frozen=True)
|
|
@@ -283,3 +288,10 @@ class MoveUpWarningEvent(EnableEvent):
|
|
|
283
288
|
@dataclass(frozen=True)
|
|
284
289
|
class SafeProtectEvent(EnableEvent):
|
|
285
290
|
"""Safe protect event."""
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
@dataclass(frozen=True)
|
|
294
|
+
class CutDirectionEvent(Event):
|
|
295
|
+
"""Cut direction event representation."""
|
|
296
|
+
|
|
297
|
+
angle: int
|
|
@@ -18,10 +18,19 @@ class WaterAmount(IntEnum):
|
|
|
18
18
|
ULTRAHIGH = 4
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
@unique
|
|
22
|
+
class SweepType(IntEnum):
|
|
23
|
+
"""Enum class for all possible sweeping types."""
|
|
24
|
+
|
|
25
|
+
STANDARD = 1
|
|
26
|
+
DEEP = 2
|
|
27
|
+
|
|
28
|
+
|
|
21
29
|
@dataclass(frozen=True)
|
|
22
30
|
class WaterInfoEvent(Event):
|
|
23
31
|
"""Water info event representation."""
|
|
24
32
|
|
|
25
33
|
amount: WaterAmount
|
|
26
34
|
# None means no data available
|
|
35
|
+
sweep_type: SweepType | None = None
|
|
27
36
|
mop_attached: bool | None = field(kw_only=True, default=None)
|