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.
Files changed (171) hide show
  1. {deebot-client-4.2.0/deebot_client.egg-info → deebot-client-4.3.0}/PKG-INFO +2 -2
  2. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/mqtt_client.py +3 -4
  3. deebot-client-4.3.0/deebot_client/util/continents.py +264 -0
  4. {deebot-client-4.2.0 → deebot-client-4.3.0/deebot_client.egg-info}/PKG-INFO +2 -2
  5. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client.egg-info/SOURCES.txt +2 -0
  6. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client.egg-info/requires.txt +1 -1
  7. {deebot-client-4.2.0 → deebot-client-4.3.0}/requirements.txt +1 -1
  8. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/conftest.py +1 -1
  9. deebot-client-4.3.0/tests/mqtt_util.py +41 -0
  10. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/test_mqtt_client.py +13 -103
  11. deebot-client-4.3.0/tests/test_mqtt_client_reconnect.py +79 -0
  12. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/util/test_continents.py +7 -6
  13. deebot-client-4.2.0/deebot_client/util/continents.py +0 -264
  14. {deebot-client-4.2.0 → deebot-client-4.3.0}/.codecov.yml +0 -0
  15. {deebot-client-4.2.0 → deebot-client-4.3.0}/.coveragerc +0 -0
  16. {deebot-client-4.2.0 → deebot-client-4.3.0}/.devcontainer.json +0 -0
  17. {deebot-client-4.2.0 → deebot-client-4.3.0}/.gitattributes +0 -0
  18. {deebot-client-4.2.0 → deebot-client-4.3.0}/.github/FUNDING.yml +0 -0
  19. {deebot-client-4.2.0 → deebot-client-4.3.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  20. {deebot-client-4.2.0 → deebot-client-4.3.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  21. {deebot-client-4.2.0 → deebot-client-4.3.0}/.github/dependabot.yml +0 -0
  22. {deebot-client-4.2.0 → deebot-client-4.3.0}/.github/release-drafter.yml +0 -0
  23. {deebot-client-4.2.0 → deebot-client-4.3.0}/.github/workflows/ci.yml +0 -0
  24. {deebot-client-4.2.0 → deebot-client-4.3.0}/.github/workflows/codeql-analysis.yml +0 -0
  25. {deebot-client-4.2.0 → deebot-client-4.3.0}/.github/workflows/python-publish.yml +0 -0
  26. {deebot-client-4.2.0 → deebot-client-4.3.0}/.github/workflows/release-drafter.yml +0 -0
  27. {deebot-client-4.2.0 → deebot-client-4.3.0}/.gitignore +0 -0
  28. {deebot-client-4.2.0 → deebot-client-4.3.0}/.pre-commit-config.yaml +0 -0
  29. {deebot-client-4.2.0 → deebot-client-4.3.0}/.prettierrc.js +0 -0
  30. {deebot-client-4.2.0 → deebot-client-4.3.0}/.yamllint +0 -0
  31. {deebot-client-4.2.0 → deebot-client-4.3.0}/LICENSE.txt +0 -0
  32. {deebot-client-4.2.0 → deebot-client-4.3.0}/README.md +0 -0
  33. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/__init__.py +0 -0
  34. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/api_client.py +0 -0
  35. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/authentication.py +0 -0
  36. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/capabilities.py +0 -0
  37. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/command.py +0 -0
  38. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/__init__.py +0 -0
  39. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/__init__.py +0 -0
  40. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/advanced_mode.py +0 -0
  41. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/battery.py +0 -0
  42. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/carpet.py +0 -0
  43. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/charge.py +0 -0
  44. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/charge_state.py +0 -0
  45. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/clean.py +0 -0
  46. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/clean_count.py +0 -0
  47. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/clean_logs.py +0 -0
  48. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/clean_preference.py +0 -0
  49. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/common.py +0 -0
  50. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/const.py +0 -0
  51. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/continuous_cleaning.py +0 -0
  52. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/custom.py +0 -0
  53. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/efficiency.py +0 -0
  54. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/error.py +0 -0
  55. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/fan_speed.py +0 -0
  56. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/life_span.py +0 -0
  57. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/map.py +0 -0
  58. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/multimap_state.py +0 -0
  59. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/network.py +0 -0
  60. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/play_sound.py +0 -0
  61. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/pos.py +0 -0
  62. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/relocation.py +0 -0
  63. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/stats.py +0 -0
  64. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/true_detect.py +0 -0
  65. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/voice_assistant_state.py +0 -0
  66. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/volume.py +0 -0
  67. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/water_info.py +0 -0
  68. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/json/work_mode.py +0 -0
  69. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/xml/__init__.py +0 -0
  70. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/xml/common.py +0 -0
  71. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/commands/xml/error.py +0 -0
  72. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/const.py +0 -0
  73. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/device.py +0 -0
  74. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/event_bus.py +0 -0
  75. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/events/__init__.py +0 -0
  76. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/events/base.py +0 -0
  77. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/events/efficiency_mode.py +0 -0
  78. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/events/fan_speed.py +0 -0
  79. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/events/map.py +0 -0
  80. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/events/network.py +0 -0
  81. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/events/water_info.py +0 -0
  82. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/events/work_mode.py +0 -0
  83. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/exceptions.py +0 -0
  84. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/__init__.py +0 -0
  85. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/2o4lnm.py +0 -0
  86. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/626v6g.py +0 -0
  87. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/85nbtp.py +0 -0
  88. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/9ku8nu.py +0 -0
  89. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/__init__.py +0 -0
  90. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/clojes.py +0 -0
  91. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/fallback.py +0 -0
  92. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/lx3j7m.py +0 -0
  93. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/p1jij8.py +0 -0
  94. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/p95mgv.py +0 -0
  95. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/rss8xk.py +0 -0
  96. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/umwv6z.py +0 -0
  97. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/vi829v.py +0 -0
  98. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/x5d34r.py +0 -0
  99. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/yna5xi.py +0 -0
  100. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/hardware/deebot/zjavof.py +0 -0
  101. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/logging_filter.py +0 -0
  102. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/map.py +0 -0
  103. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/message.py +0 -0
  104. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/messages/__init__.py +0 -0
  105. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/messages/json/__init__.py +0 -0
  106. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/messages/json/battery.py +0 -0
  107. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/messages/json/stats.py +0 -0
  108. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/models.py +0 -0
  109. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/py.typed +0 -0
  110. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client/util/__init__.py +0 -0
  111. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client.egg-info/dependency_links.txt +0 -0
  112. {deebot-client-4.2.0 → deebot-client-4.3.0}/deebot_client.egg-info/top_level.txt +0 -0
  113. {deebot-client-4.2.0 → deebot-client-4.3.0}/mypy.ini +0 -0
  114. {deebot-client-4.2.0 → deebot-client-4.3.0}/pyproject.toml +0 -0
  115. {deebot-client-4.2.0 → deebot-client-4.3.0}/pytest.ini +0 -0
  116. {deebot-client-4.2.0 → deebot-client-4.3.0}/requirements-dev.txt +0 -0
  117. {deebot-client-4.2.0 → deebot-client-4.3.0}/requirements-test.txt +0 -0
  118. {deebot-client-4.2.0 → deebot-client-4.3.0}/scripts/check_getLogger.sh +0 -0
  119. {deebot-client-4.2.0 → deebot-client-4.3.0}/scripts/run-in-env.sh +0 -0
  120. {deebot-client-4.2.0 → deebot-client-4.3.0}/setup.cfg +0 -0
  121. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/__init__.py +0 -0
  122. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/__init__.py +0 -0
  123. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/__init__.py +0 -0
  124. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_advanced_mode.py +0 -0
  125. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_battery.py +0 -0
  126. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_carpet.py +0 -0
  127. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_charge.py +0 -0
  128. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_charge_state.py +0 -0
  129. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_clean.py +0 -0
  130. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_clean_count.py +0 -0
  131. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_clean_log.py +0 -0
  132. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_clean_preference.py +0 -0
  133. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_common.py +0 -0
  134. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_continuous_cleaning.py +0 -0
  135. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_custom.py +0 -0
  136. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_efficiency.py +0 -0
  137. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_error.py +0 -0
  138. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_fan_speed.py +0 -0
  139. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_life_span.py +0 -0
  140. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_map.py +0 -0
  141. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_mulitmap_state.py +0 -0
  142. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_network.py +0 -0
  143. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_true_detect.py +0 -0
  144. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_voice_assistant_state.py +0 -0
  145. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_volume.py +0 -0
  146. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_water_info.py +0 -0
  147. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/json/test_work_mode.py +0 -0
  148. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/xml/__init__.py +0 -0
  149. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/commands/xml/test_error.py +0 -0
  150. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/events/__init__.py +0 -0
  151. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/events/test_water_info.py +0 -0
  152. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/fixtures/__init__.py +0 -0
  153. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/fixtures/base.py +0 -0
  154. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/fixtures/mqtt_server.py +0 -0
  155. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/hardware/__init__.py +0 -0
  156. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/hardware/test_init.py +0 -0
  157. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/helpers/__init__.py +0 -0
  158. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/helpers/tasks.py +0 -0
  159. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/messages/__init__.py +0 -0
  160. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/messages/json/__init__.py +0 -0
  161. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/messages/json/test_battery.py +0 -0
  162. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/messages/json/test_stats.py +0 -0
  163. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/messages/test_get_messages.py +0 -0
  164. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/messages/test_messages.py +0 -0
  165. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/test_authentication.py +0 -0
  166. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/test_command.py +0 -0
  167. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/test_device.py +0 -0
  168. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/test_event_bus.py +0 -0
  169. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/test_map.py +0 -0
  170. {deebot-client-4.2.0 → deebot-client-4.3.0}/tests/util/__init__.py +0 -0
  171. {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.2.0
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<2.0,>=1.0.0
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
- client_id=client_id,
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 with client.messages() as messages:
170
- async for message in messages:
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.2.0
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<2.0,>=1.0.0
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
@@ -1,5 +1,5 @@
1
1
  aiohttp~=3.9
2
- aiomqtt<2.0,>=1.0.0
2
+ aiomqtt<3.0,>=2.0.0
3
3
  cachetools<6.0,>=5.0.0
4
4
  defusedxml
5
5
  numpy<2.0,>=1.23.2
@@ -1,5 +1,5 @@
1
1
  aiohttp~=3.9
2
- aiomqtt>=1.0.0,<2.0
2
+ aiomqtt>=2.0.0,<3.0
3
3
  cachetools>=5.0.0,<6.0
4
4
  defusedxml
5
5
  numpy>=1.23.2,<2.0
@@ -99,7 +99,7 @@ async def test_mqtt_client(
99
99
  async with Client(
100
100
  hostname=mqtt_config.hostname,
101
101
  port=mqtt_config.port,
102
- client_id="Test-helper",
102
+ identifier="Test-helper",
103
103
  tls_context=mqtt_config.ssl_context,
104
104
  ) as client:
105
105
  yield client
@@ -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, SubscriberInfo
20
+ from deebot_client.mqtt_client import MqttClient, MqttConfiguration
23
21
 
24
- from .fixtures.mqtt_server import MqttServer
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 _subscribe(mqtt_client, device_info)
95
+ (_, callback, unsubscribe) = await subscribe(mqtt_client, device_info)
186
96
 
187
- await _verify_subscribe(
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 _verify_subscribe(
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 _subscribe(mqtt_client, device_info)
112
+ (_, callback, _) = await subscribe(mqtt_client, device_info)
203
113
 
204
- await _verify_subscribe(
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 _verify_subscribe(
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 _verify_subscribe(
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 _subscribe(mqtt_client, device_info)
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 _subscribe(mqtt_client, device_info)
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 _subscribe(mqtt_client, device_info)
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 _subscribe(mqtt_client, device_info)
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")