deebot-client 4.2.0__tar.gz → 4.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-4.2.0/deebot_client.egg-info → deebot-client-4.3.0}/PKG-INFO +2 -2
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/mqtt_client.py +3 -4
- deebot-client-4.3.0/deebot_client/util/continents.py +264 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0/deebot_client.egg-info}/PKG-INFO +2 -2
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client.egg-info/SOURCES.txt +2 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client.egg-info/requires.txt +1 -1
- {deebot-client-4.2.0 → deebot-client-4.3.0}/requirements.txt +1 -1
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/conftest.py +1 -1
- deebot-client-4.3.0/tests/mqtt_util.py +41 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/test_mqtt_client.py +13 -103
- deebot-client-4.3.0/tests/test_mqtt_client_reconnect.py +79 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/util/test_continents.py +7 -6
- deebot-client-4.2.0/deebot_client/util/continents.py +0 -264
- {deebot-client-4.2.0 → deebot-client-4.3.0}/.codecov.yml +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/.coveragerc +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/.devcontainer.json +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/.gitattributes +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/.github/FUNDING.yml +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/.github/dependabot.yml +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/.github/release-drafter.yml +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/.github/workflows/ci.yml +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/.github/workflows/codeql-analysis.yml +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/.github/workflows/python-publish.yml +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/.github/workflows/release-drafter.yml +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/.gitignore +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/.pre-commit-config.yaml +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/.prettierrc.js +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/.yamllint +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/LICENSE.txt +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/README.md +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/__init__.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/api_client.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/authentication.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/capabilities.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/command.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/__init__.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/__init__.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/advanced_mode.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/battery.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/carpet.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/charge.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/charge_state.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/clean.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/clean_count.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/clean_logs.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/clean_preference.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/common.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/const.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/continuous_cleaning.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/custom.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/efficiency.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/error.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/fan_speed.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/life_span.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/map.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/multimap_state.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/network.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/play_sound.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/pos.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/relocation.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/stats.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/true_detect.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/voice_assistant_state.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/volume.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/water_info.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/work_mode.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/xml/__init__.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/xml/common.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/xml/error.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/const.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/device.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/event_bus.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/events/__init__.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/events/base.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/events/efficiency_mode.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/events/fan_speed.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/events/map.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/events/network.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/events/water_info.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/events/work_mode.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/exceptions.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/__init__.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/2o4lnm.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/626v6g.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/85nbtp.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/9ku8nu.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/__init__.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/clojes.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/fallback.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/lx3j7m.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/p1jij8.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/p95mgv.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/rss8xk.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/umwv6z.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/vi829v.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/x5d34r.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/yna5xi.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/zjavof.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/logging_filter.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/map.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/message.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/messages/__init__.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/messages/json/__init__.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/messages/json/battery.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/messages/json/stats.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/models.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/py.typed +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/util/__init__.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client.egg-info/dependency_links.txt +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client.egg-info/top_level.txt +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/mypy.ini +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/pyproject.toml +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/pytest.ini +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/requirements-dev.txt +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/requirements-test.txt +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/scripts/check_getLogger.sh +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/scripts/run-in-env.sh +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/setup.cfg +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/__init__.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/__init__.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/__init__.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_advanced_mode.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_battery.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_carpet.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_charge.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_charge_state.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_clean.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_clean_count.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_clean_log.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_clean_preference.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_common.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_continuous_cleaning.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_custom.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_efficiency.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_error.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_fan_speed.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_life_span.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_map.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_mulitmap_state.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_network.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_true_detect.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_voice_assistant_state.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_volume.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_water_info.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_work_mode.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/xml/__init__.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/xml/test_error.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/events/__init__.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/events/test_water_info.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/fixtures/__init__.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/fixtures/base.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/fixtures/mqtt_server.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/hardware/__init__.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/hardware/test_init.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/helpers/__init__.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/helpers/tasks.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/messages/__init__.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/messages/json/__init__.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/messages/json/test_battery.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/messages/json/test_stats.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/messages/test_get_messages.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/messages/test_messages.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/test_authentication.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/test_command.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/test_device.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/test_event_bus.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/test_map.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/util/__init__.py +0 -0
- {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/util/test_init.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: deebot-client
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.3.0
|
|
4
4
|
Summary: Deebot client library in python 3
|
|
5
5
|
Author-email: Robert Resch <robert@resch.dev>
|
|
6
6
|
License: GPL-3.0
|
|
@@ -18,7 +18,7 @@ Requires-Python: >=3.11.0
|
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
License-File: LICENSE.txt
|
|
20
20
|
Requires-Dist: aiohttp~=3.9
|
|
21
|
-
Requires-Dist: aiomqtt<
|
|
21
|
+
Requires-Dist: aiomqtt<3.0,>=2.0.0
|
|
22
22
|
Requires-Dist: cachetools<6.0,>=5.0.0
|
|
23
23
|
Requires-Dist: defusedxml
|
|
24
24
|
Requires-Dist: numpy<2.0,>=1.23.2
|
|
@@ -145,7 +145,7 @@ class MqttClient:
|
|
|
145
145
|
username=credentials.user_id,
|
|
146
146
|
password=credentials.token,
|
|
147
147
|
logger=_CLIENT_LOGGER,
|
|
148
|
-
|
|
148
|
+
identifier=client_id,
|
|
149
149
|
tls_context=self._config.ssl_context,
|
|
150
150
|
)
|
|
151
151
|
|
|
@@ -166,9 +166,8 @@ class MqttClient:
|
|
|
166
166
|
await client.subscribe(topic)
|
|
167
167
|
|
|
168
168
|
async def listen() -> None:
|
|
169
|
-
async
|
|
170
|
-
|
|
171
|
-
self._handle_message(message)
|
|
169
|
+
async for message in client.messages:
|
|
170
|
+
self._handle_message(message)
|
|
172
171
|
|
|
173
172
|
tasks = [
|
|
174
173
|
asyncio.create_task(listen()),
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"""Continents module."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def get_continent(country: str | None) -> str:
|
|
5
|
+
"""Return the continent for the given country or ww."""
|
|
6
|
+
if not country:
|
|
7
|
+
return "ww"
|
|
8
|
+
return COUNTRIES_TO_CONTINENTS.get(country.upper(), "ww")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Copied from https://github.com/mrbungle64/ecovacs-deebot.js/blob/master/countries.json on 11.01.2024
|
|
12
|
+
COUNTRIES_TO_CONTINENTS = {
|
|
13
|
+
"AD": "eu",
|
|
14
|
+
"AE": "as",
|
|
15
|
+
"AF": "as",
|
|
16
|
+
"AG": "na",
|
|
17
|
+
"AI": "na",
|
|
18
|
+
"AL": "eu",
|
|
19
|
+
"AM": "as",
|
|
20
|
+
"AO": "ww",
|
|
21
|
+
"AQ": "ww",
|
|
22
|
+
"AR": "ww",
|
|
23
|
+
"AS": "ww",
|
|
24
|
+
"AT": "eu",
|
|
25
|
+
"AU": "ww",
|
|
26
|
+
"AW": "na",
|
|
27
|
+
"AX": "eu",
|
|
28
|
+
"AZ": "as",
|
|
29
|
+
"BA": "eu",
|
|
30
|
+
"BB": "na",
|
|
31
|
+
"BD": "as",
|
|
32
|
+
"BE": "eu",
|
|
33
|
+
"BF": "ww",
|
|
34
|
+
"BG": "eu",
|
|
35
|
+
"BH": "as",
|
|
36
|
+
"BI": "ww",
|
|
37
|
+
"BJ": "ww",
|
|
38
|
+
"BL": "na",
|
|
39
|
+
"BM": "na",
|
|
40
|
+
"BN": "as",
|
|
41
|
+
"BO": "ww",
|
|
42
|
+
"BQ": "na",
|
|
43
|
+
"BR": "ww",
|
|
44
|
+
"BS": "na",
|
|
45
|
+
"BT": "as",
|
|
46
|
+
"BV": "ww",
|
|
47
|
+
"BW": "ww",
|
|
48
|
+
"BY": "eu",
|
|
49
|
+
"BZ": "na",
|
|
50
|
+
"CA": "na",
|
|
51
|
+
"CC": "as",
|
|
52
|
+
"CD": "ww",
|
|
53
|
+
"CF": "ww",
|
|
54
|
+
"CG": "ww",
|
|
55
|
+
"CH": "eu",
|
|
56
|
+
"CI": "ww",
|
|
57
|
+
"CK": "ww",
|
|
58
|
+
"CL": "ww",
|
|
59
|
+
"CM": "ww",
|
|
60
|
+
"CN": "ww",
|
|
61
|
+
"CO": "ww",
|
|
62
|
+
"CR": "na",
|
|
63
|
+
"CU": "na",
|
|
64
|
+
"CV": "ww",
|
|
65
|
+
"CW": "na",
|
|
66
|
+
"CX": "as",
|
|
67
|
+
"CY": "eu",
|
|
68
|
+
"CZ": "eu",
|
|
69
|
+
"DE": "eu",
|
|
70
|
+
"DJ": "ww",
|
|
71
|
+
"DK": "eu",
|
|
72
|
+
"DM": "na",
|
|
73
|
+
"DO": "na",
|
|
74
|
+
"DZ": "ww",
|
|
75
|
+
"EC": "ww",
|
|
76
|
+
"EE": "eu",
|
|
77
|
+
"EG": "ww",
|
|
78
|
+
"EH": "ww",
|
|
79
|
+
"ER": "ww",
|
|
80
|
+
"ES": "eu",
|
|
81
|
+
"ET": "ww",
|
|
82
|
+
"FI": "eu",
|
|
83
|
+
"FJ": "ww",
|
|
84
|
+
"FK": "ww",
|
|
85
|
+
"FM": "ww",
|
|
86
|
+
"FO": "eu",
|
|
87
|
+
"FR": "eu",
|
|
88
|
+
"GA": "ww",
|
|
89
|
+
"GB": "eu",
|
|
90
|
+
"GD": "na",
|
|
91
|
+
"GE": "as",
|
|
92
|
+
"GF": "ww",
|
|
93
|
+
"GG": "eu",
|
|
94
|
+
"GH": "ww",
|
|
95
|
+
"GI": "eu",
|
|
96
|
+
"GL": "na",
|
|
97
|
+
"GM": "ww",
|
|
98
|
+
"GN": "ww",
|
|
99
|
+
"GP": "na",
|
|
100
|
+
"GQ": "ww",
|
|
101
|
+
"GR": "eu",
|
|
102
|
+
"GS": "ww",
|
|
103
|
+
"GT": "na",
|
|
104
|
+
"GU": "ww",
|
|
105
|
+
"GW": "ww",
|
|
106
|
+
"GY": "ww",
|
|
107
|
+
"HK": "as",
|
|
108
|
+
"HM": "ww",
|
|
109
|
+
"HN": "na",
|
|
110
|
+
"HR": "eu",
|
|
111
|
+
"HT": "na",
|
|
112
|
+
"HU": "eu",
|
|
113
|
+
"ID": "as",
|
|
114
|
+
"IE": "eu",
|
|
115
|
+
"IL": "as",
|
|
116
|
+
"IM": "eu",
|
|
117
|
+
"IN": "as",
|
|
118
|
+
"IO": "as",
|
|
119
|
+
"IQ": "as",
|
|
120
|
+
"IR": "as",
|
|
121
|
+
"IS": "eu",
|
|
122
|
+
"IT": "eu",
|
|
123
|
+
"JE": "eu",
|
|
124
|
+
"JM": "na",
|
|
125
|
+
"JO": "as",
|
|
126
|
+
"JP": "as",
|
|
127
|
+
"KE": "ww",
|
|
128
|
+
"KG": "as",
|
|
129
|
+
"KH": "as",
|
|
130
|
+
"KI": "ww",
|
|
131
|
+
"KM": "ww",
|
|
132
|
+
"KN": "na",
|
|
133
|
+
"KP": "as",
|
|
134
|
+
"KR": "as",
|
|
135
|
+
"KW": "as",
|
|
136
|
+
"KY": "na",
|
|
137
|
+
"KZ": "as",
|
|
138
|
+
"LA": "as",
|
|
139
|
+
"LB": "as",
|
|
140
|
+
"LC": "na",
|
|
141
|
+
"LI": "eu",
|
|
142
|
+
"LK": "as",
|
|
143
|
+
"LR": "ww",
|
|
144
|
+
"LS": "ww",
|
|
145
|
+
"LT": "eu",
|
|
146
|
+
"LU": "eu",
|
|
147
|
+
"LV": "eu",
|
|
148
|
+
"LY": "ww",
|
|
149
|
+
"MA": "ww",
|
|
150
|
+
"MC": "eu",
|
|
151
|
+
"MD": "eu",
|
|
152
|
+
"ME": "eu",
|
|
153
|
+
"MF": "na",
|
|
154
|
+
"MG": "ww",
|
|
155
|
+
"MH": "ww",
|
|
156
|
+
"MK": "eu",
|
|
157
|
+
"ML": "ww",
|
|
158
|
+
"MM": "as",
|
|
159
|
+
"MN": "as",
|
|
160
|
+
"MO": "as",
|
|
161
|
+
"MP": "ww",
|
|
162
|
+
"MQ": "na",
|
|
163
|
+
"MR": "ww",
|
|
164
|
+
"MS": "na",
|
|
165
|
+
"MT": "eu",
|
|
166
|
+
"MU": "ww",
|
|
167
|
+
"MV": "as",
|
|
168
|
+
"MW": "ww",
|
|
169
|
+
"MX": "na",
|
|
170
|
+
"MY": "as",
|
|
171
|
+
"MZ": "ww",
|
|
172
|
+
"na": "ww",
|
|
173
|
+
"NC": "ww",
|
|
174
|
+
"NE": "ww",
|
|
175
|
+
"NF": "ww",
|
|
176
|
+
"NG": "ww",
|
|
177
|
+
"NI": "na",
|
|
178
|
+
"NL": "eu",
|
|
179
|
+
"NO": "eu",
|
|
180
|
+
"NP": "as",
|
|
181
|
+
"NR": "ww",
|
|
182
|
+
"NU": "ww",
|
|
183
|
+
"NZ": "ww",
|
|
184
|
+
"OM": "as",
|
|
185
|
+
"PA": "na",
|
|
186
|
+
"PE": "ww",
|
|
187
|
+
"PF": "ww",
|
|
188
|
+
"PG": "ww",
|
|
189
|
+
"PH": "as",
|
|
190
|
+
"PK": "as",
|
|
191
|
+
"PL": "eu",
|
|
192
|
+
"PM": "na",
|
|
193
|
+
"PN": "ww",
|
|
194
|
+
"PR": "na",
|
|
195
|
+
"PS": "as",
|
|
196
|
+
"PT": "eu",
|
|
197
|
+
"PW": "ww",
|
|
198
|
+
"PY": "ww",
|
|
199
|
+
"QA": "as",
|
|
200
|
+
"RE": "ww",
|
|
201
|
+
"RO": "eu",
|
|
202
|
+
"RS": "eu",
|
|
203
|
+
"RU": "eu",
|
|
204
|
+
"RW": "ww",
|
|
205
|
+
"SA": "as",
|
|
206
|
+
"SB": "ww",
|
|
207
|
+
"SC": "ww",
|
|
208
|
+
"SD": "ww",
|
|
209
|
+
"SE": "eu",
|
|
210
|
+
"SG": "as",
|
|
211
|
+
"SH": "ww",
|
|
212
|
+
"SI": "eu",
|
|
213
|
+
"SJ": "eu",
|
|
214
|
+
"SK": "eu",
|
|
215
|
+
"SL": "ww",
|
|
216
|
+
"SM": "eu",
|
|
217
|
+
"SN": "ww",
|
|
218
|
+
"SO": "ww",
|
|
219
|
+
"SR": "ww",
|
|
220
|
+
"SS": "ww",
|
|
221
|
+
"ST": "ww",
|
|
222
|
+
"SV": "na",
|
|
223
|
+
"SX": "na",
|
|
224
|
+
"SY": "as",
|
|
225
|
+
"SZ": "ww",
|
|
226
|
+
"TC": "na",
|
|
227
|
+
"TD": "ww",
|
|
228
|
+
"TF": "ww",
|
|
229
|
+
"TG": "ww",
|
|
230
|
+
"TH": "as",
|
|
231
|
+
"TJ": "as",
|
|
232
|
+
"TK": "ww",
|
|
233
|
+
"TL": "ww",
|
|
234
|
+
"TM": "as",
|
|
235
|
+
"TN": "ww",
|
|
236
|
+
"TO": "ww",
|
|
237
|
+
"TR": "as",
|
|
238
|
+
"TT": "na",
|
|
239
|
+
"TV": "ww",
|
|
240
|
+
"TW": "as",
|
|
241
|
+
"TZ": "ww",
|
|
242
|
+
"UA": "eu",
|
|
243
|
+
"UG": "ww",
|
|
244
|
+
"UK": "eu",
|
|
245
|
+
"UM": "ww",
|
|
246
|
+
"US": "na",
|
|
247
|
+
"UY": "ww",
|
|
248
|
+
"UZ": "as",
|
|
249
|
+
"VA": "eu",
|
|
250
|
+
"VC": "na",
|
|
251
|
+
"VE": "ww",
|
|
252
|
+
"VG": "na",
|
|
253
|
+
"VI": "na",
|
|
254
|
+
"VN": "as",
|
|
255
|
+
"VU": "ww",
|
|
256
|
+
"WF": "ww",
|
|
257
|
+
"WS": "ww",
|
|
258
|
+
"XK": "eu",
|
|
259
|
+
"YE": "as",
|
|
260
|
+
"YT": "ww",
|
|
261
|
+
"ZA": "ww",
|
|
262
|
+
"ZM": "ww",
|
|
263
|
+
"ZW": "ww",
|
|
264
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: deebot-client
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.3.0
|
|
4
4
|
Summary: Deebot client library in python 3
|
|
5
5
|
Author-email: Robert Resch <robert@resch.dev>
|
|
6
6
|
License: GPL-3.0
|
|
@@ -18,7 +18,7 @@ Requires-Python: >=3.11.0
|
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
License-File: LICENSE.txt
|
|
20
20
|
Requires-Dist: aiohttp~=3.9
|
|
21
|
-
Requires-Dist: aiomqtt<
|
|
21
|
+
Requires-Dist: aiomqtt<3.0,>=2.0.0
|
|
22
22
|
Requires-Dist: cachetools<6.0,>=5.0.0
|
|
23
23
|
Requires-Dist: defusedxml
|
|
24
24
|
Requires-Dist: numpy<2.0,>=1.23.2
|
|
@@ -112,12 +112,14 @@ scripts/check_getLogger.sh
|
|
|
112
112
|
scripts/run-in-env.sh
|
|
113
113
|
tests/__init__.py
|
|
114
114
|
tests/conftest.py
|
|
115
|
+
tests/mqtt_util.py
|
|
115
116
|
tests/test_authentication.py
|
|
116
117
|
tests/test_command.py
|
|
117
118
|
tests/test_device.py
|
|
118
119
|
tests/test_event_bus.py
|
|
119
120
|
tests/test_map.py
|
|
120
121
|
tests/test_mqtt_client.py
|
|
122
|
+
tests/test_mqtt_client_reconnect.py
|
|
121
123
|
tests/commands/__init__.py
|
|
122
124
|
tests/commands/json/__init__.py
|
|
123
125
|
tests/commands/json/test_advanced_mode.py
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Utilities for testing MQTT."""
|
|
2
|
+
import asyncio
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
import datetime
|
|
5
|
+
import json
|
|
6
|
+
from unittest.mock import MagicMock, Mock
|
|
7
|
+
|
|
8
|
+
from aiomqtt import Client
|
|
9
|
+
|
|
10
|
+
from deebot_client.event_bus import EventBus
|
|
11
|
+
from deebot_client.models import DeviceInfo
|
|
12
|
+
from deebot_client.mqtt_client import MqttClient, SubscriberInfo
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
async def verify_subscribe(
|
|
16
|
+
test_client: Client, device_info: DeviceInfo, mock: Mock, *, expected_called: bool
|
|
17
|
+
) -> None:
|
|
18
|
+
command = "test"
|
|
19
|
+
data = json.dumps({"test": str(datetime.datetime.now())}).encode("utf-8")
|
|
20
|
+
topic = f"iot/atr/{command}/{device_info.did}/{device_info.get_class}/{device_info.resource}/j"
|
|
21
|
+
await test_client.publish(topic, data)
|
|
22
|
+
|
|
23
|
+
await asyncio.sleep(0.1)
|
|
24
|
+
if expected_called:
|
|
25
|
+
mock.assert_called_with(command, data)
|
|
26
|
+
else:
|
|
27
|
+
mock.assert_not_called()
|
|
28
|
+
|
|
29
|
+
mock.reset_mock()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
async def subscribe(
|
|
33
|
+
mqtt_client: MqttClient, device_info: DeviceInfo
|
|
34
|
+
) -> tuple[Mock, Mock, Callable[[], None]]:
|
|
35
|
+
events = Mock(spec=EventBus)
|
|
36
|
+
callback = MagicMock()
|
|
37
|
+
unsubscribe = await mqtt_client.subscribe(
|
|
38
|
+
SubscriberInfo(device_info, events, callback)
|
|
39
|
+
)
|
|
40
|
+
await asyncio.sleep(0.1)
|
|
41
|
+
return (events, callback, unsubscribe)
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
from collections.abc import Callable
|
|
3
2
|
import datetime
|
|
4
3
|
import json
|
|
5
4
|
import logging
|
|
@@ -16,43 +15,11 @@ from deebot_client.authentication import Authenticator
|
|
|
16
15
|
from deebot_client.commands.json.battery import GetBattery
|
|
17
16
|
from deebot_client.commands.json.volume import SetVolume
|
|
18
17
|
from deebot_client.const import DataType
|
|
19
|
-
from deebot_client.event_bus import EventBus
|
|
20
18
|
from deebot_client.exceptions import AuthenticationError
|
|
21
19
|
from deebot_client.models import Configuration, DeviceInfo
|
|
22
|
-
from deebot_client.mqtt_client import MqttClient, MqttConfiguration
|
|
20
|
+
from deebot_client.mqtt_client import MqttClient, MqttConfiguration
|
|
23
21
|
|
|
24
|
-
from .
|
|
25
|
-
|
|
26
|
-
_WAITING_AFTER_RESTART = 30
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
async def _verify_subscribe(
|
|
30
|
-
test_client: Client, device_info: DeviceInfo, mock: Mock, *, expected_called: bool
|
|
31
|
-
) -> None:
|
|
32
|
-
command = "test"
|
|
33
|
-
data = json.dumps({"test": str(datetime.datetime.now())}).encode("utf-8")
|
|
34
|
-
topic = f"iot/atr/{command}/{device_info.did}/{device_info.get_class}/{device_info.resource}/j"
|
|
35
|
-
await test_client.publish(topic, data)
|
|
36
|
-
|
|
37
|
-
await asyncio.sleep(0.1)
|
|
38
|
-
if expected_called:
|
|
39
|
-
mock.assert_called_with(command, data)
|
|
40
|
-
else:
|
|
41
|
-
mock.assert_not_called()
|
|
42
|
-
|
|
43
|
-
mock.reset_mock()
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
async def _subscribe(
|
|
47
|
-
mqtt_client: MqttClient, device_info: DeviceInfo
|
|
48
|
-
) -> tuple[Mock, Mock, Callable[[], None]]:
|
|
49
|
-
events = Mock(spec=EventBus)
|
|
50
|
-
callback = MagicMock()
|
|
51
|
-
unsubscribe = await mqtt_client.subscribe(
|
|
52
|
-
SubscriberInfo(device_info, events, callback)
|
|
53
|
-
)
|
|
54
|
-
await asyncio.sleep(0.1)
|
|
55
|
-
return (events, callback, unsubscribe)
|
|
22
|
+
from .mqtt_util import subscribe, verify_subscribe
|
|
56
23
|
|
|
57
24
|
|
|
58
25
|
async def test_last_message_received_at(
|
|
@@ -75,63 +42,6 @@ async def test_last_message_received_at(
|
|
|
75
42
|
assert mqtt_client.last_message_received_at == expected
|
|
76
43
|
|
|
77
44
|
|
|
78
|
-
@pytest.mark.skip(reason="Wait for sbtinstruments/aiomqtt#232 be merged")
|
|
79
|
-
@pytest.mark.timeout(_WAITING_AFTER_RESTART + 10)
|
|
80
|
-
async def test_client_reconnect_on_broker_error(
|
|
81
|
-
mqtt_client: MqttClient,
|
|
82
|
-
mqtt_server: MqttServer,
|
|
83
|
-
device_info: DeviceInfo,
|
|
84
|
-
mqtt_config: MqttConfiguration,
|
|
85
|
-
caplog: pytest.LogCaptureFixture,
|
|
86
|
-
) -> None:
|
|
87
|
-
(_, callback, _) = await _subscribe(mqtt_client, device_info)
|
|
88
|
-
async with Client(
|
|
89
|
-
hostname=mqtt_config.hostname,
|
|
90
|
-
port=mqtt_config.port,
|
|
91
|
-
client_id="Test-helper",
|
|
92
|
-
tls_context=mqtt_config.ssl_context,
|
|
93
|
-
) as client:
|
|
94
|
-
# test client cannot be used as we restart the broker in this test
|
|
95
|
-
await _verify_subscribe(client, device_info, callback, expected_called=True)
|
|
96
|
-
|
|
97
|
-
caplog.clear()
|
|
98
|
-
mqtt_server.stop()
|
|
99
|
-
await asyncio.sleep(0.1)
|
|
100
|
-
|
|
101
|
-
assert (
|
|
102
|
-
"deebot_client.mqtt_client",
|
|
103
|
-
logging.WARNING,
|
|
104
|
-
"Connection lost; Reconnecting in 5 seconds ...",
|
|
105
|
-
) in caplog.record_tuples
|
|
106
|
-
caplog.clear()
|
|
107
|
-
|
|
108
|
-
mqtt_server.run()
|
|
109
|
-
|
|
110
|
-
expected_log_tuple = (
|
|
111
|
-
"deebot_client.mqtt_client",
|
|
112
|
-
logging.DEBUG,
|
|
113
|
-
"All mqtt tasks created",
|
|
114
|
-
)
|
|
115
|
-
for i in range(_WAITING_AFTER_RESTART):
|
|
116
|
-
print(f"Wait for success reconnect... {i}/{_WAITING_AFTER_RESTART}")
|
|
117
|
-
if expected_log_tuple in caplog.record_tuples:
|
|
118
|
-
async with Client(
|
|
119
|
-
hostname=mqtt_config.hostname,
|
|
120
|
-
port=mqtt_config.port,
|
|
121
|
-
client_id="Test-helper",
|
|
122
|
-
tls_context=mqtt_config.ssl_context,
|
|
123
|
-
) as client:
|
|
124
|
-
# test client cannot be used as we restart the broker in this test
|
|
125
|
-
await _verify_subscribe(
|
|
126
|
-
client, device_info, callback, expected_called=True
|
|
127
|
-
)
|
|
128
|
-
return
|
|
129
|
-
|
|
130
|
-
await asyncio.sleep(1)
|
|
131
|
-
|
|
132
|
-
pytest.fail("Reconnect failed")
|
|
133
|
-
|
|
134
|
-
|
|
135
45
|
_test_MqttConfiguration_data = [
|
|
136
46
|
("cn", None, "mq.ecouser.net"),
|
|
137
47
|
("cn", "localhost", "localhost"),
|
|
@@ -182,16 +92,16 @@ def test_MqttConfiguration_hostname_none(config: Configuration) -> None:
|
|
|
182
92
|
async def test_client_bot_subscription(
|
|
183
93
|
mqtt_client: MqttClient, device_info: DeviceInfo, test_mqtt_client: Client
|
|
184
94
|
) -> None:
|
|
185
|
-
(_, callback, unsubscribe) = await
|
|
95
|
+
(_, callback, unsubscribe) = await subscribe(mqtt_client, device_info)
|
|
186
96
|
|
|
187
|
-
await
|
|
97
|
+
await verify_subscribe(
|
|
188
98
|
test_mqtt_client, device_info, callback, expected_called=True
|
|
189
99
|
)
|
|
190
100
|
|
|
191
101
|
unsubscribe()
|
|
192
102
|
await asyncio.sleep(0.1)
|
|
193
103
|
|
|
194
|
-
await
|
|
104
|
+
await verify_subscribe(
|
|
195
105
|
test_mqtt_client, device_info, callback, expected_called=False
|
|
196
106
|
)
|
|
197
107
|
|
|
@@ -199,21 +109,21 @@ async def test_client_bot_subscription(
|
|
|
199
109
|
async def test_client_reconnect_manual(
|
|
200
110
|
mqtt_client: MqttClient, device_info: DeviceInfo, test_mqtt_client: Client
|
|
201
111
|
) -> None:
|
|
202
|
-
(_, callback, _) = await
|
|
112
|
+
(_, callback, _) = await subscribe(mqtt_client, device_info)
|
|
203
113
|
|
|
204
|
-
await
|
|
114
|
+
await verify_subscribe(
|
|
205
115
|
test_mqtt_client, device_info, callback, expected_called=True
|
|
206
116
|
)
|
|
207
117
|
|
|
208
118
|
await mqtt_client.disconnect()
|
|
209
|
-
await
|
|
119
|
+
await verify_subscribe(
|
|
210
120
|
test_mqtt_client, device_info, callback, expected_called=False
|
|
211
121
|
)
|
|
212
122
|
|
|
213
123
|
await mqtt_client.connect()
|
|
214
124
|
await asyncio.sleep(0.1)
|
|
215
125
|
|
|
216
|
-
await
|
|
126
|
+
await verify_subscribe(
|
|
217
127
|
test_mqtt_client, device_info, callback, expected_called=True
|
|
218
128
|
)
|
|
219
129
|
|
|
@@ -244,7 +154,7 @@ async def test_p2p_success(
|
|
|
244
154
|
test_mqtt_client: Client,
|
|
245
155
|
) -> None:
|
|
246
156
|
"""Test p2p workflow on SetVolume."""
|
|
247
|
-
(events, _, _) = await
|
|
157
|
+
(events, _, _) = await subscribe(mqtt_client, device_info)
|
|
248
158
|
assert len(mqtt_client._received_p2p_commands) == 0
|
|
249
159
|
|
|
250
160
|
command_object = Mock(spec=SetVolume)
|
|
@@ -293,7 +203,7 @@ async def test_p2p_not_supported(
|
|
|
293
203
|
caplog: pytest.LogCaptureFixture,
|
|
294
204
|
) -> None:
|
|
295
205
|
"""Test that unsupported command will be logged."""
|
|
296
|
-
await
|
|
206
|
+
await subscribe(mqtt_client, device_info)
|
|
297
207
|
command_name: str = GetBattery.name
|
|
298
208
|
|
|
299
209
|
await _publish_p2p(
|
|
@@ -344,7 +254,7 @@ async def test_p2p_to_late(
|
|
|
344
254
|
"""Test p2p when response comes in to late."""
|
|
345
255
|
# reduce ttl to 1 seconds
|
|
346
256
|
mqtt_client._received_p2p_commands = TTLCache(maxsize=60 * 60, ttl=1)
|
|
347
|
-
await
|
|
257
|
+
await subscribe(mqtt_client, device_info)
|
|
348
258
|
assert len(mqtt_client._received_p2p_commands) == 0
|
|
349
259
|
|
|
350
260
|
command_object = Mock(spec=SetVolume)
|
|
@@ -393,7 +303,7 @@ async def test_p2p_parse_error(
|
|
|
393
303
|
caplog: pytest.LogCaptureFixture,
|
|
394
304
|
) -> None:
|
|
395
305
|
"""Test p2p parse error."""
|
|
396
|
-
await
|
|
306
|
+
await subscribe(mqtt_client, device_info)
|
|
397
307
|
|
|
398
308
|
command_object = Mock(spec=SetVolume)
|
|
399
309
|
command_name = SetVolume.name
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from collections.abc import Generator
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
from aiomqtt import Client
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from deebot_client.models import DeviceInfo
|
|
9
|
+
from deebot_client.mqtt_client import MqttClient, MqttConfiguration
|
|
10
|
+
|
|
11
|
+
from .fixtures.mqtt_server import MqttServer
|
|
12
|
+
from .mqtt_util import subscribe, verify_subscribe
|
|
13
|
+
|
|
14
|
+
_WAITING_AFTER_RESTART = 30
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.fixture
|
|
18
|
+
def mqtt_server() -> Generator[MqttServer, None, None]:
|
|
19
|
+
server = MqttServer()
|
|
20
|
+
server.config.options["ports"] = {"1883/tcp": 54321}
|
|
21
|
+
server.run()
|
|
22
|
+
yield server
|
|
23
|
+
server.stop()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@pytest.mark.timeout(_WAITING_AFTER_RESTART + 10)
|
|
27
|
+
async def test_client_reconnect_on_broker_error(
|
|
28
|
+
mqtt_client: MqttClient,
|
|
29
|
+
mqtt_server: MqttServer,
|
|
30
|
+
device_info: DeviceInfo,
|
|
31
|
+
mqtt_config: MqttConfiguration,
|
|
32
|
+
caplog: pytest.LogCaptureFixture,
|
|
33
|
+
) -> None:
|
|
34
|
+
(_, callback, _) = await subscribe(mqtt_client, device_info)
|
|
35
|
+
async with Client(
|
|
36
|
+
hostname=mqtt_config.hostname,
|
|
37
|
+
port=mqtt_config.port,
|
|
38
|
+
identifier="Test-helper",
|
|
39
|
+
tls_context=mqtt_config.ssl_context,
|
|
40
|
+
) as client:
|
|
41
|
+
# test client cannot be used as we restart the broker in this test
|
|
42
|
+
await verify_subscribe(client, device_info, callback, expected_called=True)
|
|
43
|
+
|
|
44
|
+
caplog.clear()
|
|
45
|
+
mqtt_server.stop()
|
|
46
|
+
await asyncio.sleep(0.1)
|
|
47
|
+
|
|
48
|
+
assert (
|
|
49
|
+
"deebot_client.mqtt_client",
|
|
50
|
+
logging.WARNING,
|
|
51
|
+
"Connection lost; Reconnecting in 5 seconds ...",
|
|
52
|
+
) in caplog.record_tuples
|
|
53
|
+
caplog.clear()
|
|
54
|
+
|
|
55
|
+
mqtt_server.run()
|
|
56
|
+
|
|
57
|
+
expected_log_tuple = (
|
|
58
|
+
"deebot_client.mqtt_client",
|
|
59
|
+
logging.DEBUG,
|
|
60
|
+
"All mqtt tasks created",
|
|
61
|
+
)
|
|
62
|
+
for i in range(_WAITING_AFTER_RESTART):
|
|
63
|
+
print(f"Wait for success reconnect... {i}/{_WAITING_AFTER_RESTART}")
|
|
64
|
+
if expected_log_tuple in caplog.record_tuples:
|
|
65
|
+
async with Client(
|
|
66
|
+
hostname=mqtt_config.hostname,
|
|
67
|
+
port=mqtt_config.port,
|
|
68
|
+
identifier="Test-helper",
|
|
69
|
+
tls_context=mqtt_config.ssl_context,
|
|
70
|
+
) as client:
|
|
71
|
+
# test client cannot be used as we restart the broker in this test
|
|
72
|
+
await verify_subscribe(
|
|
73
|
+
client, device_info, callback, expected_called=True
|
|
74
|
+
)
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
await asyncio.sleep(1)
|
|
78
|
+
|
|
79
|
+
pytest.fail("Reconnect failed")
|