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.
Files changed (210) hide show
  1. {deebot_client-7.2.0 → deebot_client-7.3.0}/.pre-commit-config.yaml +2 -2
  2. {deebot_client-7.2.0 → deebot_client-7.3.0}/PKG-INFO +1 -1
  3. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/capabilities.py +2 -0
  4. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/command.py +48 -26
  5. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/__init__.py +3 -0
  6. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/clean.py +1 -1
  7. deebot_client-7.3.0/deebot_client/commands/json/cut_direction.py +43 -0
  8. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/error.py +12 -7
  9. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/pos.py +5 -1
  10. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/water_info.py +20 -4
  11. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/xml/pos.py +4 -1
  12. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/const.py +44 -1
  13. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/device.py +24 -12
  14. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/event_bus.py +2 -1
  15. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/events/__init__.py +13 -1
  16. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/events/map.py +1 -0
  17. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/events/water_info.py +9 -0
  18. deebot_client-7.3.0/deebot_client/hardware/deebot/2ap5uq.py +139 -0
  19. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/5xu9h3.py +6 -0
  20. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/77atlz.py +6 -0
  21. deebot_client-7.3.0/deebot_client/hardware/deebot/9s1s80.py +226 -0
  22. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/e6ofmn.py +0 -1
  23. deebot_client-7.3.0/deebot_client/hardware/deebot/guzput.py +139 -0
  24. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/itk04l.py +6 -0
  25. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/lf3bn4.py +0 -1
  26. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/map.py +47 -94
  27. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client.egg-info/PKG-INFO +1 -1
  28. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client.egg-info/SOURCES.txt +6 -0
  29. {deebot_client-7.2.0 → deebot_client-7.3.0}/requirements-test.txt +5 -4
  30. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/__init__.py +17 -7
  31. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_common.py +1 -1
  32. deebot_client-7.3.0/tests/commands/json/test_cut_direction.py +20 -0
  33. deebot_client-7.3.0/tests/commands/json/test_error.py +35 -0
  34. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_life_span.py +48 -0
  35. deebot_client-7.3.0/tests/commands/json/test_pos.py +22 -0
  36. deebot_client-7.3.0/tests/commands/json/test_water_info.py +89 -0
  37. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/xml/test_pos.py +1 -1
  38. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/fixtures/base.py +2 -2
  39. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/hardware/test_init.py +7 -0
  40. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/test_command.py +2 -1
  41. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/test_device.py +3 -2
  42. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/test_map.py +104 -53
  43. deebot_client-7.2.0/tests/commands/json/test_error.py +0 -29
  44. deebot_client-7.2.0/tests/commands/json/test_water_info.py +0 -47
  45. {deebot_client-7.2.0 → deebot_client-7.3.0}/.codecov.yml +0 -0
  46. {deebot_client-7.2.0 → deebot_client-7.3.0}/.coveragerc +0 -0
  47. {deebot_client-7.2.0 → deebot_client-7.3.0}/.devcontainer.json +0 -0
  48. {deebot_client-7.2.0 → deebot_client-7.3.0}/.gitattributes +0 -0
  49. {deebot_client-7.2.0 → deebot_client-7.3.0}/.github/FUNDING.yml +0 -0
  50. {deebot_client-7.2.0 → deebot_client-7.3.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  51. {deebot_client-7.2.0 → deebot_client-7.3.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  52. {deebot_client-7.2.0 → deebot_client-7.3.0}/.github/dependabot.yml +0 -0
  53. {deebot_client-7.2.0 → deebot_client-7.3.0}/.github/release-drafter.yml +0 -0
  54. {deebot_client-7.2.0 → deebot_client-7.3.0}/.github/workflows/ci.yml +0 -0
  55. {deebot_client-7.2.0 → deebot_client-7.3.0}/.github/workflows/codeql-analysis.yml +0 -0
  56. {deebot_client-7.2.0 → deebot_client-7.3.0}/.github/workflows/python-publish.yml +0 -0
  57. {deebot_client-7.2.0 → deebot_client-7.3.0}/.github/workflows/release-drafter.yml +0 -0
  58. {deebot_client-7.2.0 → deebot_client-7.3.0}/.gitignore +0 -0
  59. {deebot_client-7.2.0 → deebot_client-7.3.0}/.prettierrc.js +0 -0
  60. {deebot_client-7.2.0 → deebot_client-7.3.0}/.yamllint +0 -0
  61. {deebot_client-7.2.0 → deebot_client-7.3.0}/LICENSE.txt +0 -0
  62. {deebot_client-7.2.0 → deebot_client-7.3.0}/README.md +0 -0
  63. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/__init__.py +0 -0
  64. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/api_client.py +0 -0
  65. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/authentication.py +0 -0
  66. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/__init__.py +0 -0
  67. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/advanced_mode.py +0 -0
  68. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/battery.py +0 -0
  69. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/border_switch.py +0 -0
  70. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/carpet.py +0 -0
  71. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/charge.py +0 -0
  72. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/charge_state.py +0 -0
  73. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/child_lock.py +0 -0
  74. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/clean_count.py +0 -0
  75. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/clean_logs.py +0 -0
  76. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/clean_preference.py +0 -0
  77. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/clear_map.py +0 -0
  78. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/common.py +0 -0
  79. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/const.py +0 -0
  80. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/continuous_cleaning.py +0 -0
  81. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/cross_map_border_warning.py +0 -0
  82. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/custom.py +0 -0
  83. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/efficiency.py +0 -0
  84. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/fan_speed.py +0 -0
  85. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/life_span.py +0 -0
  86. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/map.py +0 -0
  87. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/moveup_warning.py +0 -0
  88. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/multimap_state.py +0 -0
  89. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/network.py +0 -0
  90. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/ota.py +0 -0
  91. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/play_sound.py +0 -0
  92. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/relocation.py +0 -0
  93. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/safe_protect.py +0 -0
  94. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/stats.py +0 -0
  95. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/sweep_mode.py +0 -0
  96. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/true_detect.py +0 -0
  97. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/voice_assistant_state.py +0 -0
  98. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/volume.py +0 -0
  99. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/json/work_mode.py +0 -0
  100. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/xml/__init__.py +0 -0
  101. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/xml/charge_state.py +0 -0
  102. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/xml/common.py +0 -0
  103. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/xml/error.py +0 -0
  104. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/commands/xml/fan_speed.py +0 -0
  105. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/events/base.py +0 -0
  106. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/events/efficiency_mode.py +0 -0
  107. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/events/fan_speed.py +0 -0
  108. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/events/network.py +0 -0
  109. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/events/work_mode.py +0 -0
  110. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/exceptions.py +0 -0
  111. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/__init__.py +0 -0
  112. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/2o4lnm.py +0 -0
  113. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/55aiho.py +0 -0
  114. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/626v6g.py +0 -0
  115. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/85nbtp.py +0 -0
  116. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/9ku8nu.py +0 -0
  117. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/__init__.py +0 -0
  118. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/clojes.py +0 -0
  119. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/fallback.py +0 -0
  120. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/lx3j7m.py +0 -0
  121. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/p1jij8.py +0 -0
  122. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/p95mgv.py +0 -0
  123. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/paeygf.py +0 -0
  124. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/rss8xk.py +0 -0
  125. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/umwv6z.py +0 -0
  126. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/vi829v.py +0 -0
  127. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/x5d34r.py +0 -0
  128. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/yna5xi.py +0 -0
  129. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/hardware/deebot/zjavof.py +0 -0
  130. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/logging_filter.py +0 -0
  131. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/message.py +0 -0
  132. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/messages/__init__.py +0 -0
  133. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/messages/json/__init__.py +0 -0
  134. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/messages/json/battery.py +0 -0
  135. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/messages/json/map.py +0 -0
  136. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/messages/json/stats.py +0 -0
  137. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/models.py +0 -0
  138. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/mqtt_client.py +0 -0
  139. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/py.typed +0 -0
  140. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/util/__init__.py +0 -0
  141. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/util/continents.py +0 -0
  142. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client/util/countries.py +0 -0
  143. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client.egg-info/dependency_links.txt +0 -0
  144. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client.egg-info/requires.txt +0 -0
  145. {deebot_client-7.2.0 → deebot_client-7.3.0}/deebot_client.egg-info/top_level.txt +0 -0
  146. {deebot_client-7.2.0 → deebot_client-7.3.0}/mypy.ini +0 -0
  147. {deebot_client-7.2.0 → deebot_client-7.3.0}/pyproject.toml +0 -0
  148. {deebot_client-7.2.0 → deebot_client-7.3.0}/pytest.ini +0 -0
  149. {deebot_client-7.2.0 → deebot_client-7.3.0}/requirements-dev.txt +0 -0
  150. {deebot_client-7.2.0 → deebot_client-7.3.0}/requirements.txt +0 -0
  151. {deebot_client-7.2.0 → deebot_client-7.3.0}/scripts/check_getLogger.sh +0 -0
  152. {deebot_client-7.2.0 → deebot_client-7.3.0}/scripts/run-in-env.sh +0 -0
  153. {deebot_client-7.2.0 → deebot_client-7.3.0}/setup.cfg +0 -0
  154. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/__init__.py +0 -0
  155. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/__init__.py +0 -0
  156. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_advanced_mode.py +0 -0
  157. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_battery.py +0 -0
  158. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_border_switch.py +0 -0
  159. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_carpet.py +0 -0
  160. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_charge.py +0 -0
  161. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_charge_state.py +0 -0
  162. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_child_lock.py +0 -0
  163. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_clean.py +0 -0
  164. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_clean_count.py +0 -0
  165. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_clean_log.py +0 -0
  166. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_clean_preference.py +0 -0
  167. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_continuous_cleaning.py +0 -0
  168. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_cross_map_border_warning.py +0 -0
  169. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_custom.py +0 -0
  170. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_efficiency.py +0 -0
  171. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_fan_speed.py +0 -0
  172. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_map.py +0 -0
  173. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_moveup_warning.py +0 -0
  174. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_mulitmap_state.py +0 -0
  175. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_network.py +0 -0
  176. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_ota.py +0 -0
  177. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_safe_protect.py +0 -0
  178. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_sweep_mode.py +0 -0
  179. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_true_detect.py +0 -0
  180. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_voice_assistant_state.py +0 -0
  181. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_volume.py +0 -0
  182. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/json/test_work_mode.py +0 -0
  183. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/xml/__init__.py +0 -0
  184. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/xml/test_charge_state.py +0 -0
  185. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/xml/test_error.py +0 -0
  186. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/commands/xml/test_fan_speed.py +0 -0
  187. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/common.py +0 -0
  188. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/conftest.py +0 -0
  189. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/events/__init__.py +0 -0
  190. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/fixtures/__init__.py +0 -0
  191. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/fixtures/mqtt_server.py +0 -0
  192. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/hardware/__init__.py +0 -0
  193. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/helpers/__init__.py +0 -0
  194. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/helpers/tasks.py +0 -0
  195. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/messages/__init__.py +0 -0
  196. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/messages/json/__init__.py +0 -0
  197. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/messages/json/test_battery.py +0 -0
  198. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/messages/json/test_map.py +0 -0
  199. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/messages/json/test_stats.py +0 -0
  200. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/messages/test_get_messages.py +0 -0
  201. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/messages/test_messages.py +0 -0
  202. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/mqtt_util.py +0 -0
  203. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/test_authentication.py +0 -0
  204. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/test_event_bus.py +0 -0
  205. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/test_mqtt_client.py +0 -0
  206. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/test_mqtt_client_reconnect.py +0 -0
  207. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/util/__init__.py +0 -0
  208. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/util/test_continents.py +0 -0
  209. {deebot_client-7.2.0 → deebot_client-7.3.0}/tests/util/test_countries.py +0 -0
  210. {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.3
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.2.6
28
+ rev: v2.3.0
29
29
  hooks:
30
30
  - id: codespell
31
31
  args:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: deebot-client
3
- Version: 7.2.0
3
+ Version: 7.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
@@ -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 ApiTimeoutError, DeebotError
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
- ) -> bool:
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(authenticator, device_info, event_bus)
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 self._targets_bot
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 CommandResult(
128
- HandlingState.ANALYSE_LOGGED,
129
- result.args,
130
- result.requested_commands,
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
- values[param.name or name] = _pop_or_raise(name, param.type_, data)
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
- try:
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
- if error is not None:
37
- description = ERROR_CODES.get(error)
38
- if error != 0:
39
- event_bus.notify(StateEvent(State.ERROR))
40
- event_bus.notify(ErrorEvent(error, description))
41
- return HandlingResult.success()
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), x=entry["x"], y=entry["y"]
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(WaterAmount(int(data["amount"])), mop_attached=mop_attached)
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__(self, amount: WaterAmount | str) -> None:
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
- super().__init__({"amount": amount.value})
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
- position = Position(type=PositionType.DEEBOT, x=int(p_x), y=int(p_y))
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
- 312: "Please replace the Dust Bag.",
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 Command
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) -> None:
111
- """Execute given command."""
112
- await self._execute_command(command)
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(self, command: Command) -> bool:
168
+ async def _execute_command(
169
+ self,
170
+ command: Command,
171
+ ) -> DeviceCommandResult:
160
172
  """Execute given command."""
161
173
  async with self._semaphore:
162
- if await command.execute(
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
- return False
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: Callable[[Command], Coroutine[Any, Any, None]],
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
@@ -27,6 +27,7 @@ class Position:
27
27
  type: PositionType
28
28
  x: int
29
29
  y: int
30
+ a: int
30
31
 
31
32
 
32
33
  @dataclass(frozen=True)
@@ -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)