velbus-aio 2025.5.0__tar.gz → 2025.8.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.

Potentially problematic release.


This version of velbus-aio might be problematic. Click here for more details.

Files changed (199) hide show
  1. {velbus_aio-2025.5.0/velbus_aio.egg-info → velbus_aio-2025.8.0}/PKG-INFO +1 -1
  2. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/pyproject.toml +2 -2
  3. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0/velbus_aio.egg-info}/PKG-INFO +1 -1
  4. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/controller.py +11 -8
  5. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/handler.py +113 -85
  6. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/message.py +7 -1
  7. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/module_type.py +1 -0
  8. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module.py +33 -11
  9. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/protocol.py +29 -6
  10. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/LICENSE +0 -0
  11. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/MANIFEST.in +0 -0
  12. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/README.md +0 -0
  13. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/requirements.txt +0 -0
  14. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/setup.cfg +0 -0
  15. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/setup.py +0 -0
  16. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbus_aio.egg-info/SOURCES.txt +0 -0
  17. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbus_aio.egg-info/dependency_links.txt +0 -0
  18. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbus_aio.egg-info/not-zip-safe +0 -0
  19. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbus_aio.egg-info/requires.txt +0 -0
  20. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbus_aio.egg-info/top_level.txt +0 -0
  21. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/__init__.py +0 -0
  22. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/channels.py +0 -0
  23. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/command_registry.py +0 -0
  24. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/const.py +0 -0
  25. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/discovery.py +0 -0
  26. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/exceptions.py +0 -0
  27. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/helpers.py +0 -0
  28. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/__init__.py +0 -0
  29. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/blind_status.py +0 -0
  30. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/bus_active.py +0 -0
  31. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/bus_error_counter_status.py +0 -0
  32. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/bus_error_counter_status_request.py +0 -0
  33. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/bus_off.py +0 -0
  34. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/channel_name_part1.py +0 -0
  35. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/channel_name_part2.py +0 -0
  36. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/channel_name_part3.py +0 -0
  37. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/channel_name_request.py +0 -0
  38. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/clear_led.py +0 -0
  39. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/counter_status.py +0 -0
  40. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/counter_status_request.py +0 -0
  41. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/counter_value.py +0 -0
  42. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/cover_down.py +0 -0
  43. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/cover_off.py +0 -0
  44. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/cover_position.py +0 -0
  45. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/cover_up.py +0 -0
  46. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/dali_device_settings.py +0 -0
  47. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/dali_device_settings_request.py +0 -0
  48. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/dali_dim_value_status.py +0 -0
  49. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/dimmer_channel_status.py +0 -0
  50. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/dimmer_status.py +0 -0
  51. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/edge_set_color.py +0 -0
  52. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/edge_set_custom_color.py +0 -0
  53. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/fast_blinking_led.py +0 -0
  54. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/forced_off.py +0 -0
  55. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/forced_on.py +0 -0
  56. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/interface_status_request.py +0 -0
  57. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/ir_receiver_status.py +0 -0
  58. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/kwh_status.py +0 -0
  59. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/light_value_request.py +0 -0
  60. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/memo_text.py +0 -0
  61. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/memory_data.py +0 -0
  62. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/memory_data_block.py +0 -0
  63. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/memory_dump_request.py +0 -0
  64. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/module_status.py +0 -0
  65. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/module_status_request.py +0 -0
  66. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/module_subtype.py +0 -0
  67. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/module_type_request.py +0 -0
  68. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/psu_load.py +0 -0
  69. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/psu_values.py +0 -0
  70. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/push_button_status.py +0 -0
  71. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/raw.py +0 -0
  72. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/read_data_block_from_memory.py +0 -0
  73. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/read_data_from_memory.py +0 -0
  74. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/realtime_clock_status_request.py +0 -0
  75. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/receive_buffer_full.py +0 -0
  76. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/receive_ready.py +0 -0
  77. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/relay_status.py +0 -0
  78. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/restore_dimmer.py +0 -0
  79. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/select_program.py +0 -0
  80. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/sensor_settings_request.py +0 -0
  81. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/sensor_temp_request.py +0 -0
  82. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/sensor_temperature.py +0 -0
  83. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/set_date.py +0 -0
  84. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/set_daylight_saving.py +0 -0
  85. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/set_dimmer.py +0 -0
  86. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/set_led.py +0 -0
  87. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/set_realtime_clock.py +0 -0
  88. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/set_temperature.py +0 -0
  89. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/slider_status.py +0 -0
  90. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/slow_blinking_led.py +0 -0
  91. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/start_relay_blinking_timer.py +0 -0
  92. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/start_relay_timer.py +0 -0
  93. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/switch_relay_off.py +0 -0
  94. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/switch_relay_on.py +0 -0
  95. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/switch_to_comfort.py +0 -0
  96. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/switch_to_day.py +0 -0
  97. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/switch_to_night.py +0 -0
  98. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/switch_to_safe.py +0 -0
  99. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/temp_sensor_settings_part1.py +0 -0
  100. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/temp_sensor_settings_part2.py +0 -0
  101. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/temp_sensor_settings_part3.py +0 -0
  102. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/temp_sensor_settings_part4.py +0 -0
  103. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/temp_sensor_settings_request.py +0 -0
  104. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/temp_sensor_status.py +0 -0
  105. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/temp_set_cooling.py +0 -0
  106. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/temp_set_heating.py +0 -0
  107. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/update_led_status.py +0 -0
  108. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/very_fast_blinking_led.py +0 -0
  109. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/write_data_to_memory.py +0 -0
  110. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/write_memory_block.py +0 -0
  111. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/messages/write_module_address_and_serial_number.py +0 -0
  112. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/01.json +0 -0
  113. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/02.json +0 -0
  114. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/03.json +0 -0
  115. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/04.json +0 -0
  116. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/05.json +0 -0
  117. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/06.json +0 -0
  118. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/07.json +0 -0
  119. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/08.json +0 -0
  120. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/09.json +0 -0
  121. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/0A.json +0 -0
  122. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/0B.json +0 -0
  123. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/0C.json +0 -0
  124. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/0E.json +0 -0
  125. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/0F.json +0 -0
  126. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/10.json +0 -0
  127. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/11.json +0 -0
  128. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/12.json +0 -0
  129. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/13.json +0 -0
  130. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/14.json +0 -0
  131. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/15.json +0 -0
  132. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/16.json +0 -0
  133. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/17.json +0 -0
  134. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/18.json +0 -0
  135. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/1A.json +0 -0
  136. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/1B.json +0 -0
  137. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/1D.json +0 -0
  138. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/1E.json +0 -0
  139. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/1F.json +0 -0
  140. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/20.json +0 -0
  141. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/21.json +0 -0
  142. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/22.json +0 -0
  143. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/23.json +0 -0
  144. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/25.json +0 -0
  145. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/28.json +0 -0
  146. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/29.json +0 -0
  147. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/2A.json +0 -0
  148. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/2B.json +0 -0
  149. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/2C.json +0 -0
  150. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/2D.json +0 -0
  151. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/2E.json +0 -0
  152. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/2F.json +0 -0
  153. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/30.json +0 -0
  154. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/31.json +0 -0
  155. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/32.json +0 -0
  156. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/33.json +0 -0
  157. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/34.json +0 -0
  158. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/35.json +0 -0
  159. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/36.json +0 -0
  160. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/37.json +0 -0
  161. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/38.json +0 -0
  162. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/39.json +0 -0
  163. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/3A.json +0 -0
  164. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/3B.json +0 -0
  165. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/3C.json +0 -0
  166. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/3D.json +0 -0
  167. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/3E.json +0 -0
  168. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/3F.json +0 -0
  169. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/40.json +0 -0
  170. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/41.json +0 -0
  171. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/42.json +0 -0
  172. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/43.json +0 -0
  173. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/44.json +0 -0
  174. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/45.json +0 -0
  175. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/48.json +0 -0
  176. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/49.json +0 -0
  177. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/4A.json +0 -0
  178. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/4B.json +0 -0
  179. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/4C.json +0 -0
  180. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/4D.json +0 -0
  181. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/4E.json +0 -0
  182. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/4F.json +0 -0
  183. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/50.json +0 -0
  184. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/51.json +0 -0
  185. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/52.json +0 -0
  186. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/54.json +0 -0
  187. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/55.json +0 -0
  188. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/56.json +0 -0
  189. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/57.json +0 -0
  190. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/5A.json +0 -0
  191. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/5B.json +0 -0
  192. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/5C.json +0 -0
  193. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/5F.json +0 -0
  194. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/60.json +0 -0
  195. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/module_spec/broadcast.json +0 -0
  196. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/py.typed +0 -0
  197. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/raw_message.py +0 -0
  198. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/util.py +0 -0
  199. {velbus_aio-2025.5.0 → velbus_aio-2025.8.0}/velbusaio/vlp_reader.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velbus-aio
3
- Version: 2025.5.0
3
+ Version: 2025.8.0
4
4
  Summary: Open-source home automation platform running on Python 3.
5
5
  Author-email: Maikel Punie <maikel.punie@gmail.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ requires = ["setuptools", "wheel"]
4
4
  [project]
5
5
  name = "velbus-aio"
6
6
  license = {text = "MIT"}
7
- version = "2025.5.0"
7
+ version = "2025.8.0"
8
8
  description = "Open-source home automation platform running on Python 3."
9
9
  readme = "README.md"
10
10
  authors = [
@@ -52,7 +52,7 @@ exclude_dirs = ["tests"]
52
52
  skips = ["B301", "B403", "B323", "B104", "B110"]
53
53
 
54
54
  [tool.bumpver]
55
- current_version = "2025.5.0"
55
+ current_version = "2025.8.0"
56
56
  version_pattern = "YYYY.MM.INC0"
57
57
  commit_message = "bump version {old_version} -> {new_version}"
58
58
  commit = true
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velbus-aio
3
- Version: 2025.5.0
3
+ Version: 2025.8.0
4
4
  Summary: Open-source home automation platform running on Python 3.
5
5
  Author-email: Maikel Punie <maikel.punie@gmail.com>
6
6
  License: MIT
@@ -17,7 +17,6 @@ from velbusaio.channels import (
17
17
  Blind,
18
18
  Button,
19
19
  ButtonCounter,
20
- Channel,
21
20
  Dimmer,
22
21
  EdgeLit,
23
22
  LightSensor,
@@ -61,7 +60,7 @@ class Velbus:
61
60
  self._closing = False
62
61
  self._auto_reconnect = True
63
62
 
64
- self._dsn = dsn
63
+ self._destination = dsn
65
64
  self._handler = PacketHandler(self, one_address)
66
65
  self._modules: dict[int, Module] = {}
67
66
  self._submodules: list[int] = []
@@ -146,12 +145,12 @@ class Velbus:
146
145
  """Connect to the bus and load all the data."""
147
146
  await self._handler.read_protocol_data()
148
147
  # connect to the bus
149
- if ":" in self._dsn:
148
+ if ":" in self._destination:
150
149
  # tcp/ip combination
151
- if not re.search(r"^[A-Za-z0-9+.\-]+://", self._dsn):
150
+ if not re.search(r"^[A-Za-z0-9+.\-]+://", self._destination):
152
151
  # if no scheme, then add the tcp://
153
- self._dsn = f"tcp://{self._dsn}"
154
- parts = urlparse(self._dsn)
152
+ self._destination = f"tcp://{self._destination}"
153
+ parts = urlparse(self._destination)
155
154
  if parts.scheme == "tls":
156
155
  ctx = ssl._create_unverified_context()
157
156
  else:
@@ -176,7 +175,7 @@ class Velbus:
176
175
  await serial_asyncio_fast.create_serial_connection(
177
176
  asyncio.get_event_loop(),
178
177
  lambda: self._protocol,
179
- url=self._dsn,
178
+ url=self._destination,
180
179
  baudrate=38400,
181
180
  bytesize=serial.EIGHTBITS,
182
181
  parity=serial.PARITY_NONE,
@@ -190,7 +189,7 @@ class Velbus:
190
189
 
191
190
  async def start(self) -> None:
192
191
  # if auth is required send the auth key
193
- parts = urlparse(self._dsn)
192
+ parts = urlparse(self._destination)
194
193
  if parts.username:
195
194
  await self._protocol.write_auth_key(parts.username)
196
195
  # scan the bus
@@ -277,3 +276,7 @@ class Velbus:
277
276
  await self.send(SetRealtimeClock(wday=lclt[6], hour=lclt[3], min=lclt[4]))
278
277
  await self.send(SetDate(day=lclt[2], mon=lclt[1], year=lclt[0]))
279
278
  await self.send(SetDaylightSaving(ds=not lclt[8]))
279
+
280
+ async def wait_on_all_messages_sent_async(self) -> None:
281
+ """Wait for all messages to be sent."""
282
+ await self._protocol.wait_on_all_messages_sent_async()
@@ -11,9 +11,9 @@ import json
11
11
  import logging
12
12
  import os
13
13
  import pathlib
14
- import pprint
15
14
  import sys
16
- from typing import TYPE_CHECKING, Awaitable, Callable
15
+ import time
16
+ from typing import TYPE_CHECKING
17
17
 
18
18
  from aiofile import async_open
19
19
 
@@ -23,10 +23,9 @@ from velbusaio.const import (
23
23
  SCAN_MODULEINFO_TIMEOUT_INTERVAL,
24
24
  SCAN_MODULETYPE_TIMEOUT,
25
25
  )
26
- from velbusaio.helpers import h2, keys_exists
27
- from velbusaio.message import Message
28
26
  from velbusaio.messages.module_subtype import ModuleSubTypeMessage
29
27
  from velbusaio.messages.module_type import ModuleType2Message, ModuleTypeMessage
28
+ from velbusaio.module import Module
30
29
  from velbusaio.raw_message import RawMessage
31
30
 
32
31
  if TYPE_CHECKING:
@@ -35,7 +34,7 @@ if TYPE_CHECKING:
35
34
 
36
35
  class PacketHandler:
37
36
  """
38
- The packetHandler class
37
+ The PacketHandler class
39
38
  """
40
39
 
41
40
  def __init__(
@@ -49,9 +48,11 @@ class PacketHandler:
49
48
  self._one_address = one_address
50
49
  self._typeResponseReceived = asyncio.Event()
51
50
  self._scanLock = asyncio.Lock()
51
+ self._fullScanLock = asyncio.Lock()
52
52
  self._modulescan_address = 0
53
53
  self._scan_complete = False
54
54
  self._scan_delay_msec = 0
55
+ self.__scan_found_addresses: dict[int, ModuleTypeMessage | None] | None = None
55
56
 
56
57
  async def read_protocol_data(self):
57
58
  if sys.version_info >= (3, 13):
@@ -85,84 +86,123 @@ class PacketHandler:
85
86
  return False
86
87
 
87
88
  async def scan(self, reload_cache: bool = False) -> None:
88
- if reload_cache:
89
- self._modulescan_address = 0
90
- self._scan_complete = False
91
- # non-blocking check to see if the cache_dir is empty
92
- loop = asyncio.get_running_loop()
93
- if not reload_cache and await loop.run_in_executor(None, self.empty_cache):
94
- self._log.info("No cache yet, so forcing a bus scan")
95
- reload_cache = True
89
+ start_address = 1
90
+ max_address = 254 + 1
91
+ if self._one_address is not None:
92
+ start_address = self._one_address
93
+ max_address = self._one_address + 1
94
+ self._log.info(
95
+ f"Scanning only one address {self._one_address} ({self._one_address:#02x})"
96
+ )
97
+
96
98
  self._log.info("Start module scan")
97
- while self._modulescan_address < 254:
98
- address = 0
99
- module = None
100
- async with self._scanLock:
101
- self._modulescan_address = self._modulescan_address + 1
102
- address = self._modulescan_address
103
- if self._velbus.addr_is_submodule(address):
99
+ async with self._fullScanLock:
100
+ start_time = time.perf_counter()
101
+ self._scan_complete = False
102
+
103
+ self._log.debug("Waiting for Velbus bus to be ready to scan...")
104
+ await self._velbus.wait_on_all_messages_sent_async() # don't start a scan while messages are still in the queue
105
+ self._log.debug("Velbus bus is ready to scan!")
106
+
107
+ self._log.info("Sending scan type requests to all addresses...")
108
+ start_scan_time = time.perf_counter()
109
+ self.__scan_found_addresses = {}
110
+ for address in range(start_address, max_address):
111
+ cfile = pathlib.Path(f"{self._velbus.get_cache_dir()}/{address}.json")
112
+ if reload_cache and os.path.isfile(cfile):
104
113
  self._log.info(
105
- f"Skipping submodule address {address}, already handled"
114
+ f"Reloading cache for address {address} ({address:#02x})"
115
+ )
116
+ os.remove(cfile)
117
+
118
+ self.__scan_found_addresses[address] = None
119
+ async with self._scanLock:
120
+ await self._velbus.sendTypeRequestMessage(address)
121
+
122
+ await self._velbus.wait_on_all_messages_sent_async()
123
+ scan_time = time.perf_counter() - start_scan_time
124
+ self._log.info(
125
+ f"Sent scan type requests to all addresses in {scan_time:.2f}. Going to wait for responses..."
126
+ )
127
+
128
+ await asyncio.sleep(SCAN_MODULETYPE_TIMEOUT / 1000) # wait for responses
129
+
130
+ self._log.info(
131
+ "Waiting for responses done. Going to check for responses..."
132
+ )
133
+ for address in range(start_address, max_address):
134
+ start_module_scan = time.perf_counter()
135
+ module_type_message: ModuleTypeMessage | None = (
136
+ self.__scan_found_addresses[address]
137
+ )
138
+ module: Module | None = None
139
+ if module_type_message is None:
140
+ self._log.debug(
141
+ f"No module found at address {address} ({address:#02x}). Skipping it."
106
142
  )
107
143
  continue
108
- self._log.info(f"Starting handling scan {address}")
109
- module = self._velbus.get_module(address)
110
144
 
111
- if self._one_address is not None and address != int(self._one_address):
112
145
  self._log.info(
113
- f"Skipping address {address} as we requested to only scan one address {self._one_address}"
146
+ f"Found module at address {address} ({address:#02x}): {module_type_message.module_type_name()}"
114
147
  )
115
- continue
116
-
117
- cfile = pathlib.Path(f"{self._velbus.get_cache_dir()}/{address}.json")
118
- # cleanup the old module cache if needed
119
- scanModule = reload_cache
120
- if scanModule and os.path.isfile(cfile):
121
- os.remove(cfile)
122
- elif os.path.isfile(cfile):
123
- scanModule = os.path.isfile(cfile)
124
- if scanModule:
148
+ # cache_file = pathlib.Path(f"{self._velbus.get_cache_dir()}/{address}.json")
149
+ # TODO: check if cached file module type is the same?
150
+ await self._handle_module_type(module_type_message)
151
+ async with self._scanLock:
152
+ module = self._velbus.get_module(address)
153
+
154
+ if module is None:
155
+ self._log.info(
156
+ f"Module at address {address} ({address:#02x}) could not be loaded. Skipping it."
157
+ )
158
+ continue
159
+
125
160
  try:
126
- self._log.info(f"Starting scan {address}")
127
- self._typeResponseReceived.clear()
128
- await self._velbus.sendTypeRequestMessage(address)
161
+ self._log.debug(
162
+ f"Module {module.get_address()} ({module.get_address():#02x}) detected: start loading"
163
+ )
129
164
  await asyncio.wait_for(
130
- self._typeResponseReceived.wait(),
131
- SCAN_MODULETYPE_TIMEOUT / 1000.0,
165
+ module.load(from_cache=True),
166
+ SCAN_MODULEINFO_TIMEOUT_INITIAL / 1000.0,
132
167
  )
133
- async with self._scanLock:
134
- module = self._velbus.get_module(address)
135
- except asyncio.TimeoutError:
168
+ self._scan_delay_msec = module.get_initial_timeout()
169
+ while self._scan_delay_msec > 50 and not await module.is_loaded():
170
+ # self._log.debug(
171
+ # f"\t... waiting {self._scan_delay_msec} is_loaded={await module.is_loaded()}"
172
+ # )
173
+ self._scan_delay_msec = self._scan_delay_msec - 50
174
+ await asyncio.sleep(0.05)
175
+ module_scan_time = time.perf_counter() - start_module_scan
136
176
  self._log.info(
137
- f"Scan module {address} failed: not present or unavailable"
177
+ f"Scan module {address} ({address:#02x}, {module.get_type_name()}) completed in {module_scan_time:.2f}, module loaded={await module.is_loaded()}"
138
178
  )
139
- if module is not None:
140
- try:
141
- self._log.debug(
142
- f"Module {module.get_address()} detected: start loading"
143
- )
144
- await asyncio.wait_for(
145
- module.load(from_cache=True),
146
- SCAN_MODULEINFO_TIMEOUT_INITIAL / 1000.0,
147
- )
148
- self._scan_delay_msec = module.get_initial_timeout()
149
- while (
150
- self._scan_delay_msec > 50 and not await module.is_loaded()
151
- ):
152
- # self._log.debug(
153
- # f"\t... waiting {self._scan_delay_msec} is_loaded={await module.is_loaded()}"
154
- # )
155
- self._scan_delay_msec = self._scan_delay_msec - 50
156
- await asyncio.sleep(0.05)
157
- self._log.info(
158
- f"Scan module {address} completed, module loaded={await module.is_loaded()}"
159
- )
160
- except asyncio.TimeoutError:
161
- self._log.error(
162
- f"Module {address} did not respond to info requests after successful type request"
163
- )
164
- self._scan_complete = True
165
- self._log.info("Module scan completed")
179
+ except asyncio.TimeoutError:
180
+ self._log.error(
181
+ f"Module {address} ({address:#02x}) did not respond to info requests after successful type request"
182
+ )
183
+
184
+ self._scan_complete = True
185
+ total_time = time.perf_counter() - start_time
186
+ self._log.info(f"Module scan completed in {total_time:.2f} seconds")
187
+
188
+ async def __handle_module_type_response_async(self, rawmsg: RawMessage) -> None:
189
+ """
190
+ Handle a received module type response packet
191
+ """
192
+ address = rawmsg.address
193
+
194
+ if self.__scan_found_addresses is None:
195
+ self._log.warning(
196
+ f"Received module type response for address {address} ({address:#02x}) but no scan in progress"
197
+ )
198
+ return
199
+
200
+ tmsg: ModuleTypeMessage = ModuleTypeMessage()
201
+ tmsg.populate(rawmsg.priority, address, rawmsg.rtr, rawmsg.data_only)
202
+ self._log.debug(
203
+ f"A '{tmsg.module_type_name()}' ({tmsg.module_type:#02x}) lives on address {address} ({address:#02x})"
204
+ )
205
+ self.__scan_found_addresses[address] = tmsg
166
206
 
167
207
  async def handle(self, rawmsg: RawMessage) -> None:
168
208
  """
@@ -180,20 +220,8 @@ class PacketHandler:
180
220
  data = rawmsg.data_only
181
221
 
182
222
  # handle module type response message
183
- if command_value == 0xFF and not self._scan_complete:
184
- tmsg: ModuleTypeMessage = ModuleTypeMessage()
185
- tmsg.populate(priority, address, rtr, data)
186
- async with self._scanLock:
187
- await self._handle_module_type(tmsg)
188
- if address == self._modulescan_address:
189
- self._typeResponseReceived.set()
190
- else:
191
- self._log.debug(
192
- f"Unexpected module type message module address {address}, Velbuslink scan?"
193
- )
194
- self._modulescan_address = address - 1
195
-
196
- self._typeResponseReceived.set()
223
+ if command_value == 0xFF:
224
+ await self.__handle_module_type_response_async(rawmsg)
197
225
 
198
226
  # handle module subtype response message
199
227
  elif command_value in (0xB0, 0xA7, 0xA6) and not self._scan_complete:
@@ -4,6 +4,7 @@ The velbus abstract message class
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
+ import enum
7
8
  import json
8
9
 
9
10
  from velbusaio.const import PRIORITY_FIRMWARE, PRIORITY_HIGH, PRIORITY_LOW
@@ -65,8 +66,13 @@ class Message:
65
66
  continue
66
67
  if callable(getattr(self, key)) or key.startswith("__"):
67
68
  del me[key]
68
- if isinstance(me[key], (bytes, bytearray)):
69
+ if isinstance(me[key], (bytes, bytearray, enum.Enum)):
69
70
  me[key] = str(me[key])
71
+ else:
72
+ try:
73
+ json.dumps(me[key]) # Test if the value is JSON serializable
74
+ except (TypeError, ValueError):
75
+ me[key] = str(me[key]) # Convert non-serializable objects to string
70
76
  return me
71
77
 
72
78
  def to_json(self) -> str:
@@ -114,6 +114,7 @@ class ModuleTypeMessage(Message):
114
114
  """
115
115
  :return: str
116
116
  """
117
+
117
118
  return (
118
119
  MODULE_DIRECTORY[self.module_type]
119
120
  if self.module_type in MODULE_DIRECTORY
@@ -165,6 +165,7 @@ class Module:
165
165
  self._is_loading = False
166
166
  self._channels = {}
167
167
  self.loaded = False
168
+ self._loaded_cache = {}
168
169
 
169
170
  def get_initial_timeout(self) -> int:
170
171
  return SCAN_MODULEINFO_TIMEOUT_INITIAL
@@ -576,12 +577,8 @@ class Module:
576
577
  # start the loading
577
578
  self._is_loading = True
578
579
  # see if we have a cache
579
- try:
580
- cfile = pathlib.Path(f"{self._cache_dir}/{self._address}.json")
581
- async with async_open(cfile, "r") as fl:
582
- cache = json.loads(await fl.read())
583
- except OSError:
584
- cache = {}
580
+ cache = await self._get_cache()
581
+ self._loaded_cache = cache
585
582
  # load default channels
586
583
  await self._load_default_channels()
587
584
 
@@ -596,8 +593,8 @@ class Module:
596
593
  if "channels" in cache:
597
594
  for num, chan in cache["channels"].items():
598
595
  self._channels[int(num)]._name = chan["name"]
599
- if "sub_device" in chan:
600
- self._channels[int(num)]._sub_device = chan["sub_device"]
596
+ if "subdevice" in chan:
597
+ self._channels[int(num)]._sub_device = chan["subdevice"]
601
598
  else:
602
599
  self._channels[int(num)]._sub_device = False
603
600
  if "Unit" in chan:
@@ -611,6 +608,15 @@ class Module:
611
608
  self._is_loading = False
612
609
  await self._request_module_status()
613
610
 
611
+ async def _get_cache(self):
612
+ try:
613
+ cfile = pathlib.Path(f"{self._cache_dir}/{self._address}.json")
614
+ async with async_open(cfile, "r") as fl:
615
+ cache = json.loads(await fl.read())
616
+ except OSError:
617
+ cache = {}
618
+ return cache
619
+
614
620
  def _load(self) -> None:
615
621
  """
616
622
  Method for per module type loading
@@ -864,7 +870,25 @@ class VmbDali(Module):
864
870
  if message.channel in self._channels:
865
871
  del self._channels[message.channel]
866
872
  elif message.data.device_type == DaliDeviceType.LedModule:
867
- if self._channels.get(message.channel).__class__ != Dimmer:
873
+ cache = self._loaded_cache
874
+ if (
875
+ "channels" in cache
876
+ and str(message.channel) in cache["channels"]
877
+ and cache["channels"][str(message.channel)]["type"] == "Dimmer"
878
+ ):
879
+ # If we have a cached dimmer channel, use that name
880
+ name = cache["channels"][str(message.channel)]["name"]
881
+ self._channels[message.channel] = Dimmer(
882
+ self,
883
+ message.channel,
884
+ name,
885
+ False, # set False to enable an already loaded Dimmer
886
+ True,
887
+ self._writer,
888
+ self._address,
889
+ slider_scale=254,
890
+ )
891
+ elif self._channels.get(message.channel).__class__ != Dimmer:
868
892
  # New or changed type, replace channel:
869
893
  self._channels[message.channel] = Dimmer(
870
894
  self,
@@ -921,8 +945,6 @@ class VmbDali(Module):
921
945
  else:
922
946
  return await super().on_message(message)
923
947
 
924
- await self._cache()
925
-
926
948
  async def _request_channel_name(self) -> None:
927
949
  # Channel names are requested after channel scan
928
950
  # don't do them here (at initialization time)
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  import asyncio
4
4
  import binascii
5
5
  import logging
6
+ import time
6
7
  import typing as t
7
8
  from asyncio import transports
8
9
 
@@ -180,20 +181,22 @@ class VelbusProtocol(asyncio.BufferedProtocol):
180
181
  await self._write_transport_lock.acquire()
181
182
  while self._restart_writer:
182
183
  # wait for an item from the queue
183
- msg_info = await self._send_queue.get()
184
+ msg_info: RawMessage | None = await self._send_queue.get()
184
185
  if msg_info is None:
185
186
  self._restart_writer = False
186
187
  return
187
188
  message_sent = False
188
189
  try:
190
+ start_time = time.perf_counter()
189
191
  while not message_sent:
190
192
  message_sent = await self._write_message(msg_info)
191
- if msg_info.command == 0xEF:
192
- # 'channel name request' command provokes in worst case 99 answer packets from VMBGPOD
193
- queue_sleep_time = SLEEP_TIME * 33
194
- else:
195
- queue_sleep_time = SLEEP_TIME
193
+ send_time = time.perf_counter() - start_time
194
+
195
+ self._send_queue.task_done() # indicate that the item of the queue has been processed
196
+
197
+ queue_sleep_time = self._calculate_queue_sleep_time(msg_info, send_time)
196
198
  await asyncio.sleep(queue_sleep_time)
199
+
197
200
  except (asyncio.CancelledError, GeneratorExit) as exc:
198
201
  if not self._closing:
199
202
  self._log.error(f"Stopping Velbus writer due to {exc!r}")
@@ -205,6 +208,22 @@ class VelbusProtocol(asyncio.BufferedProtocol):
205
208
  self._write_transport_lock.release()
206
209
  self._log.debug("Ending Velbus write message from send queue")
207
210
 
211
+ @staticmethod
212
+ def _calculate_queue_sleep_time(msg_info, send_time):
213
+ sleep_time = SLEEP_TIME
214
+
215
+ if msg_info.rtr:
216
+ sleep_time = SLEEP_TIME # this is a scan command. We could be quicker?
217
+
218
+ if msg_info.command == 0xEF:
219
+ # 'channel name request' command provokes in worst case 99 answer packets from VMBGPOD
220
+ sleep_time = SLEEP_TIME * 33 # TODO make this adaptable on module_type
221
+
222
+ if send_time > sleep_time:
223
+ return 0 # no need to wait, we are already late
224
+ else:
225
+ return sleep_time - send_time
226
+
208
227
  @backoff.on_predicate(
209
228
  backoff.expo,
210
229
  lambda is_sent: not is_sent,
@@ -218,3 +237,7 @@ class VelbusProtocol(asyncio.BufferedProtocol):
218
237
  return True
219
238
  else:
220
239
  return False
240
+
241
+ async def wait_on_all_messages_sent_async(self) -> None:
242
+ self._log.debug("Waiting on all messages sent")
243
+ await self._send_queue.join()
File without changes
File without changes
File without changes
File without changes
File without changes