conson-xp 1.41.0__tar.gz → 1.43.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 (322) hide show
  1. {conson_xp-1.41.0 → conson_xp-1.43.0}/PKG-INFO +2 -1
  2. {conson_xp-1.41.0 → conson_xp-1.43.0}/pyproject.toml +2 -1
  3. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/__init__.py +1 -1
  4. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_actiontable_commands.py +5 -1
  5. conson_xp-1.43.0/src/xp/services/conbus/actiontable/actiontable_download_service.py +467 -0
  6. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/protocol/conbus_event_protocol.py +10 -0
  7. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/server/server_service.py +1 -1
  8. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/test_actiontable_integration.py +23 -5
  9. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_cli/test_conbus_actiontable_commands.py +16 -1
  10. conson_xp-1.43.0/tests/unit/test_services/test_actiontable_download_service.py +410 -0
  11. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_actiontable_service.py +37 -23
  12. conson_xp-1.41.0/src/xp/services/conbus/actiontable/actiontable_download_service.py +0 -203
  13. {conson_xp-1.41.0 → conson_xp-1.43.0}/LICENSE +0 -0
  14. {conson_xp-1.41.0 → conson_xp-1.43.0}/README.md +0 -0
  15. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/__init__.py +0 -0
  16. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/__main__.py +0 -0
  17. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/__init__.py +0 -0
  18. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/__init__.py +0 -0
  19. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus.py +0 -0
  20. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_autoreport_commands.py +0 -0
  21. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_blink_commands.py +0 -0
  22. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_config_commands.py +0 -0
  23. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_custom_commands.py +0 -0
  24. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_datapoint_commands.py +0 -0
  25. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_discover_commands.py +0 -0
  26. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_event_commands.py +0 -0
  27. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_export_commands.py +0 -0
  28. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_lightlevel_commands.py +0 -0
  29. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_linknumber_commands.py +0 -0
  30. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_modulenumber_commands.py +0 -0
  31. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_msactiontable_commands.py +0 -0
  32. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_output_commands.py +0 -0
  33. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_raw_commands.py +0 -0
  34. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_receive_commands.py +0 -0
  35. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_scan_commands.py +0 -0
  36. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/file_commands.py +0 -0
  37. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/homekit/__init__.py +0 -0
  38. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/homekit/homekit.py +0 -0
  39. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/homekit/homekit_start_commands.py +0 -0
  40. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/module_commands.py +0 -0
  41. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/reverse_proxy_commands.py +0 -0
  42. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/server/__init__.py +0 -0
  43. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/server/server_commands.py +0 -0
  44. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/telegram/__init__.py +0 -0
  45. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/telegram/telegram.py +0 -0
  46. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/telegram/telegram_blink_commands.py +0 -0
  47. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/telegram/telegram_checksum_commands.py +0 -0
  48. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/telegram/telegram_discover_commands.py +0 -0
  49. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/telegram/telegram_linknumber_commands.py +0 -0
  50. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/telegram/telegram_parse_commands.py +0 -0
  51. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/telegram/telegram_version_commands.py +0 -0
  52. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/term/__init__.py +0 -0
  53. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/term/term.py +0 -0
  54. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/commands/term/term_commands.py +0 -0
  55. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/main.py +0 -0
  56. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/utils/__init__.py +0 -0
  57. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/utils/click_tree.py +0 -0
  58. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/utils/datapoint_type_choice.py +0 -0
  59. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/utils/decorators.py +0 -0
  60. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/utils/error_handlers.py +0 -0
  61. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/utils/formatters.py +0 -0
  62. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/utils/module_type_choice.py +0 -0
  63. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/utils/serial_number_type.py +0 -0
  64. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/utils/system_function_choice.py +0 -0
  65. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/cli/utils/xp_module_type.py +0 -0
  66. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/__init__.py +0 -0
  67. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/actiontable/__init__.py +0 -0
  68. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/actiontable/actiontable.py +0 -0
  69. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/actiontable/msactiontable_xp20.py +0 -0
  70. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/actiontable/msactiontable_xp24.py +0 -0
  71. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/actiontable/msactiontable_xp33.py +0 -0
  72. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/conbus/__init__.py +0 -0
  73. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus.py +0 -0
  74. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_autoreport.py +0 -0
  75. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_blink.py +0 -0
  76. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_client_config.py +0 -0
  77. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_connection_status.py +0 -0
  78. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_custom.py +0 -0
  79. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_datapoint.py +0 -0
  80. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_discover.py +0 -0
  81. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_event_list.py +0 -0
  82. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_event_raw.py +0 -0
  83. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_export.py +0 -0
  84. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_lightlevel.py +0 -0
  85. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_linknumber.py +0 -0
  86. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_logger_config.py +0 -0
  87. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_output.py +0 -0
  88. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_raw.py +0 -0
  89. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_receive.py +0 -0
  90. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_writeconfig.py +0 -0
  91. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/config/__init__.py +0 -0
  92. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/config/conson_module_config.py +0 -0
  93. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/homekit/__init__.py +0 -0
  94. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/homekit/homekit_accessory.py +0 -0
  95. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/homekit/homekit_config.py +0 -0
  96. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/log_entry.py +0 -0
  97. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/protocol/__init__.py +0 -0
  98. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/protocol/conbus_protocol.py +0 -0
  99. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/response.py +0 -0
  100. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/telegram/__init__.py +0 -0
  101. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/telegram/action_type.py +0 -0
  102. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/telegram/datapoint_type.py +0 -0
  103. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/telegram/event_telegram.py +0 -0
  104. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/telegram/event_type.py +0 -0
  105. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/telegram/input_action_type.py +0 -0
  106. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/telegram/input_type.py +0 -0
  107. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/telegram/module_type.py +0 -0
  108. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/telegram/module_type_code.py +0 -0
  109. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/telegram/output_telegram.py +0 -0
  110. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/telegram/reply_telegram.py +0 -0
  111. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/telegram/system_function.py +0 -0
  112. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/telegram/system_telegram.py +0 -0
  113. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/telegram/telegram.py +0 -0
  114. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/telegram/telegram_type.py +0 -0
  115. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/telegram/timeparam_type.py +0 -0
  116. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/term/__init__.py +0 -0
  117. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/term/connection_state.py +0 -0
  118. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/term/module_state.py +0 -0
  119. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/term/protocol_keys_config.py +0 -0
  120. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/term/status_message.py +0 -0
  121. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/term/telegram_display.py +0 -0
  122. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/models/write_config_type.py +0 -0
  123. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/__init__.py +0 -0
  124. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/actiontable/__init__.py +0 -0
  125. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/actiontable/actiontable_serializer.py +0 -0
  126. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/actiontable/msactiontable_serializer.py +0 -0
  127. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/actiontable/msactiontable_xp20_serializer.py +0 -0
  128. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/actiontable/msactiontable_xp24_serializer.py +0 -0
  129. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/actiontable/msactiontable_xp33_serializer.py +0 -0
  130. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/__init__.py +0 -0
  131. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/actiontable/__init__.py +0 -0
  132. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/actiontable/actiontable_list_service.py +0 -0
  133. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/actiontable/actiontable_show_service.py +0 -0
  134. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/actiontable/actiontable_upload_service.py +0 -0
  135. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_blink_all_service.py +0 -0
  136. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_blink_service.py +0 -0
  137. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_custom_service.py +0 -0
  138. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_datapoint_queryall_service.py +0 -0
  139. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_datapoint_service.py +0 -0
  140. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_discover_service.py +0 -0
  141. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_event_list_service.py +0 -0
  142. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_event_raw_service.py +0 -0
  143. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_export_service.py +0 -0
  144. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_output_service.py +0 -0
  145. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_raw_service.py +0 -0
  146. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_receive_service.py +0 -0
  147. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_scan_service.py +0 -0
  148. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/msactiontable/__init__.py +0 -0
  149. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/msactiontable/msactiontable_download_service.py +0 -0
  150. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/msactiontable/msactiontable_list_service.py +0 -0
  151. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/msactiontable/msactiontable_show_service.py +0 -0
  152. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/msactiontable/msactiontable_upload_service.py +0 -0
  153. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/conbus/write_config_service.py +0 -0
  154. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/homekit/__init__.py +0 -0
  155. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_cache_service.py +0 -0
  156. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_conbus_service.py +0 -0
  157. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_config_validator.py +0 -0
  158. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_conson_validator.py +0 -0
  159. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_dimminglight.py +0 -0
  160. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_dimminglight_service.py +0 -0
  161. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_hap_service.py +0 -0
  162. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_lightbulb.py +0 -0
  163. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_lightbulb_service.py +0 -0
  164. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_module_service.py +0 -0
  165. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_outlet.py +0 -0
  166. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_outlet_service.py +0 -0
  167. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_service.py +0 -0
  168. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/log_file_service.py +0 -0
  169. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/module_type_service.py +0 -0
  170. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/protocol/__init__.py +0 -0
  171. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/protocol/conbus_protocol.py +0 -0
  172. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/protocol/protocol_factory.py +0 -0
  173. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/protocol/telegram_protocol.py +0 -0
  174. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/reverse_proxy_service.py +0 -0
  175. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/server/__init__.py +0 -0
  176. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/server/base_server_service.py +0 -0
  177. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/server/client_buffer_manager.py +0 -0
  178. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/server/cp20_server_service.py +0 -0
  179. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/server/device_service_factory.py +0 -0
  180. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/server/xp130_server_service.py +0 -0
  181. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/server/xp20_server_service.py +0 -0
  182. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/server/xp230_server_service.py +0 -0
  183. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/server/xp24_server_service.py +0 -0
  184. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/server/xp33_server_service.py +0 -0
  185. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/telegram/__init__.py +0 -0
  186. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/telegram/telegram_blink_service.py +0 -0
  187. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/telegram/telegram_checksum_service.py +0 -0
  188. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/telegram/telegram_datapoint_service.py +0 -0
  189. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/telegram/telegram_discover_service.py +0 -0
  190. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/telegram/telegram_link_number_service.py +0 -0
  191. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/telegram/telegram_output_service.py +0 -0
  192. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/telegram/telegram_service.py +0 -0
  193. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/telegram/telegram_version_service.py +0 -0
  194. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/term/__init__.py +0 -0
  195. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/term/protocol_monitor_service.py +0 -0
  196. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/services/term/state_monitor_service.py +0 -0
  197. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/term/__init__.py +0 -0
  198. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/term/protocol.py +0 -0
  199. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/term/protocol.tcss +0 -0
  200. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/term/state.py +0 -0
  201. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/term/state.tcss +0 -0
  202. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/term/widgets/__init__.py +0 -0
  203. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/term/widgets/help_menu.py +0 -0
  204. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/term/widgets/modules_list.py +0 -0
  205. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/term/widgets/protocol_log.py +0 -0
  206. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/term/widgets/status_footer.py +0 -0
  207. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/utils/__init__.py +0 -0
  208. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/utils/checksum.py +0 -0
  209. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/utils/dependencies.py +0 -0
  210. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/utils/event_helper.py +0 -0
  211. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/utils/logging.py +0 -0
  212. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/utils/serialization.py +0 -0
  213. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/utils/state_machine.py +0 -0
  214. {conson_xp-1.41.0 → conson_xp-1.43.0}/src/xp/utils/time_utils.py +0 -0
  215. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/.coverage +0 -0
  216. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/__init__.py +0 -0
  217. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/conftest.py +0 -0
  218. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/.coverage +0 -0
  219. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/__init__.py +0 -0
  220. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/telegram_test_data.py +0 -0
  221. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/test_api/.coverage +0 -0
  222. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/test_api/__init__.py +0 -0
  223. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/test_blink_integration.py +0 -0
  224. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/test_checksum_integration.py +0 -0
  225. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/test_conbus_blink_integration.py +0 -0
  226. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/test_conbus_datapoint_integration.py +0 -0
  227. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/test_conbus_raw_integration.py +0 -0
  228. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/test_conbus_receive_integration.py +0 -0
  229. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/test_discovery_integration.py +0 -0
  230. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/test_event_telegram_integration.py +0 -0
  231. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/test_homekit_config_integration.py +0 -0
  232. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/test_link_number_integration.py +0 -0
  233. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/test_module_integration.py +0 -0
  234. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/test_output_integration.py +0 -0
  235. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/test_reverse_proxy_integration.py +0 -0
  236. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/test_system_reply_telegram_integration.py +0 -0
  237. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/test_term_logging_integration.py +0 -0
  238. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/test_version_integration.py +0 -0
  239. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/test_xp20_action_table_integration.py +0 -0
  240. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/integration/test_xp24_action_table_integration.py +0 -0
  241. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/__init__.py +0 -0
  242. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_api/__init__.py +0 -0
  243. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_cli/__init__.py +0 -0
  244. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_cli/test_click_tree.py +0 -0
  245. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_cli/test_conbus_blink_commands.py +0 -0
  246. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_cli/test_conbus_msactiontable_upload_commands.py +0 -0
  247. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_cli/test_datapoint_type_choice.py +0 -0
  248. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_cli/test_decorators.py +0 -0
  249. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_cli/test_error_handlers.py +0 -0
  250. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_cli/test_formatters.py +0 -0
  251. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_cli/test_serial_number_type.py +0 -0
  252. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_cli/test_system_function_choice.py +0 -0
  253. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_cli/test_term_commands.py +0 -0
  254. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_encoding/__init__.py +0 -0
  255. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_encoding/test_latin1_edge_cases.py +0 -0
  256. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_models/__init__.py +0 -0
  257. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_models/test_conbus.py +0 -0
  258. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_models/test_conbus_client_send.py +0 -0
  259. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_models/test_conbus_discover.py +0 -0
  260. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_models/test_conbus_linknumber.py +0 -0
  261. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_models/test_event_telegram.py +0 -0
  262. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_models/test_log_entry.py +0 -0
  263. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_models/test_logger_config.py +0 -0
  264. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_models/test_module_type.py +0 -0
  265. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_models/test_reply_telegram.py +0 -0
  266. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_models/test_system_telegram.py +0 -0
  267. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_models/test_system_telegram_enhancements.py +0 -0
  268. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_models/test_version_telegram.py +0 -0
  269. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_models/test_write_config_type.py +0 -0
  270. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_models/test_xp20_action_table.py +0 -0
  271. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_models/test_xp24_action_table.py +0 -0
  272. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_models/test_xp24_action_table_short_format.py +0 -0
  273. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_models/test_xp24_action_telegram.py +0 -0
  274. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/__init__.py +0 -0
  275. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_actiontable_serializer.py +0 -0
  276. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_actiontable_upload_service.py +0 -0
  277. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_base_server_service.py +0 -0
  278. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_blink_service.py +0 -0
  279. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_checksum_service.py +0 -0
  280. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_client_buffer_manager.py +0 -0
  281. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_conbus_blink_service.py +0 -0
  282. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_conbus_event_list_service.py +0 -0
  283. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_conbus_event_protocol.py +0 -0
  284. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_conbus_event_raw_service.py +0 -0
  285. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_conbus_output_service.py +0 -0
  286. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_conbus_raw_service.py +0 -0
  287. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_conbus_receive_service.py +0 -0
  288. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_conbus_reverse_proxy_service.py +0 -0
  289. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_conbus_scan_service.py +0 -0
  290. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_device_service_factory.py +0 -0
  291. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_discovery_service.py +0 -0
  292. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_homekit_cache_service.py +0 -0
  293. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_homekit_config_validator.py +0 -0
  294. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_homekit_conson_service.py +0 -0
  295. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_homekit_services.py +0 -0
  296. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_log_file_service.py +0 -0
  297. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_module_type_service.py +0 -0
  298. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_msactiontable_upload_service.py +0 -0
  299. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_protocol.py +0 -0
  300. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_protocol_monitor_service.py +0 -0
  301. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_server_service.py +0 -0
  302. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_state_monitor_service.py +0 -0
  303. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_telegram_input_service.py +0 -0
  304. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_telegram_output_service.py +0 -0
  305. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_telegram_protocol.py +0 -0
  306. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_telegram_service.py +0 -0
  307. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_version_service.py +0 -0
  308. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_xp20_action_table_serializer.py +0 -0
  309. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_xp24_action_service.py +0 -0
  310. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_xp24_action_table_serializer.py +0 -0
  311. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_xp24_action_table_service.py +0 -0
  312. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_xp33_action_table_serializer.py +0 -0
  313. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_xp33_short_format.py +0 -0
  314. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_services/test_xp_server_services.py +0 -0
  315. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_tui/__init__.py +0 -0
  316. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_tui/test_protocol_log.py +0 -0
  317. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_utils/__init__.py +0 -0
  318. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_utils/test_checksum.py +0 -0
  319. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_utils/test_event_helper.py +0 -0
  320. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_utils/test_logging.py +0 -0
  321. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_utils/test_serialization.py +0 -0
  322. {conson_xp-1.41.0 → conson_xp-1.43.0}/tests/unit/test_utils/test_time_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: conson-xp
3
- Version: 1.41.0
3
+ Version: 1.43.0
4
4
  Summary: XP Protocol Communication Tools
5
5
  Author-Email: ldvchosal <ldvchosal@github.com>
6
6
  License: MIT License
@@ -49,6 +49,7 @@ Requires-Dist: twisted>=25.5.0
49
49
  Requires-Dist: bubus>=1.5.6
50
50
  Requires-Dist: psygnal>=0.15.0
51
51
  Requires-Dist: textual>=1.0.0
52
+ Requires-Dist: python-statemachine>=2.5.0
52
53
  Description-Content-Type: text/markdown
53
54
 
54
55
  # 🔌 XP Protocol Communication Tool
@@ -19,10 +19,11 @@ dependencies = [
19
19
  "bubus>=1.5.6",
20
20
  "psygnal>=0.15.0",
21
21
  "textual>=1.0.0",
22
+ "python-statemachine>=2.5.0",
22
23
  ]
23
24
  requires-python = ">=3.11"
24
25
  readme = "README.md"
25
- version = "1.41.0"
26
+ version = "1.43.0"
26
27
 
27
28
  [project.license]
28
29
  file = "LICENSE"
@@ -3,7 +3,7 @@
3
3
  conson-xp package.
4
4
  """
5
5
 
6
- __version__ = "1.41.0"
6
+ __version__ = "1.43.0"
7
7
  __manufacturer__ = "salchichon"
8
8
  __model__ = "xp.cli"
9
9
  __serial__ = "2025.09.23.000"
@@ -61,7 +61,7 @@ def conbus_download_actiontable(ctx: Context, serial_number: str) -> None:
61
61
  """
62
62
  click.echo(progress, nl=False)
63
63
 
64
- def on_finish(
64
+ def on_actiontable_received(
65
65
  _actiontable: ActionTable,
66
66
  actiontable_dict: Dict[str, Any],
67
67
  actiontable_short: list[str],
@@ -79,6 +79,9 @@ def conbus_download_actiontable(ctx: Context, serial_number: str) -> None:
79
79
  "actiontable": actiontable_dict,
80
80
  }
81
81
  click.echo(json.dumps(output, indent=2, default=str))
82
+
83
+ def on_finish() -> None:
84
+ """Handle successful completion of action table download."""
82
85
  service.stop_reactor()
83
86
 
84
87
  def on_error(error: str) -> None:
@@ -93,6 +96,7 @@ def conbus_download_actiontable(ctx: Context, serial_number: str) -> None:
93
96
  with service:
94
97
  service.on_progress.connect(on_progress)
95
98
  service.on_finish.connect(on_finish)
99
+ service.on_actiontable_received.connect(on_actiontable_received)
96
100
  service.on_error.connect(on_error)
97
101
  service.start(serial_number=serial_number)
98
102
  service.start_reactor()
@@ -0,0 +1,467 @@
1
+ """Service for downloading ActionTable via Conbus protocol."""
2
+
3
+ import logging
4
+ from dataclasses import asdict
5
+ from typing import Any, Dict, Optional
6
+
7
+ from psygnal import SignalInstance
8
+ from statemachine import State, StateMachine
9
+
10
+ from xp.models.actiontable.actiontable import ActionTable
11
+ from xp.models.protocol.conbus_protocol import TelegramReceivedEvent
12
+ from xp.models.telegram.datapoint_type import DataPointType
13
+ from xp.models.telegram.reply_telegram import ReplyTelegram
14
+ from xp.models.telegram.system_function import SystemFunction
15
+ from xp.models.telegram.telegram_type import TelegramType
16
+ from xp.services.actiontable.actiontable_serializer import ActionTableSerializer
17
+ from xp.services.protocol.conbus_event_protocol import ConbusEventProtocol
18
+ from xp.services.telegram.telegram_service import TelegramService
19
+
20
+
21
+ class ActionTableDownloadService(StateMachine):
22
+ """TCP client service for downloading action tables from Conbus modules.
23
+
24
+ Inherits from StateMachine - the service IS the state machine.
25
+
26
+ Manages TCP socket connections, handles telegram generation and transmission,
27
+ and processes server responses for action table downloads.
28
+
29
+ Attributes:
30
+ on_progress: Signal emitted with telegram frame when progress is made.
31
+ on_error: Signal emitted with error message string when an error occurs.
32
+ on_finish: Signal emitted with (ActionTable, Dict[str, Any], list[str]) when complete.
33
+ idle: Initial state, waiting for connection.
34
+ receiving: Listening for telegrams, filtering relevant responses.
35
+ resetting: Timeout occurred, preparing error status query.
36
+ waiting_ok: Sent error status query, awaiting ACK/NAK.
37
+ requesting: Ready to send download request.
38
+ waiting_data: Awaiting actiontable chunk or EOF.
39
+ receiving_chunk: Processing received actiontable data.
40
+ processing_eof: Received EOF, deserializing actiontable.
41
+ completed: Download finished successfully.
42
+ do_connect: Transition from idle to receiving.
43
+ do_timeout: Transition from receiving to resetting.
44
+ send_error_status: Transition from resetting to waiting_ok.
45
+ error_status_received: Transition from waiting_ok to receiving (retry).
46
+ no_error_status_received: Transition from waiting_ok to requesting or completed.
47
+ send_download: Transition from requesting to waiting_data.
48
+ receive_chunk: Transition from waiting_data to receiving_chunk.
49
+ send_ack: Transition from receiving_chunk to waiting_data.
50
+ receive_eof: Transition from waiting_data to processing_eof.
51
+ do_finish: Transition from processing_eof to receiving.
52
+ receiving2: Second receiving state after EOF processing.
53
+ resetting2: Second resetting state for finalization phase.
54
+ waiting_ok2: Second waiting_ok state for finalization phase.
55
+ filter_telegram: Self-transition for filtering telegrams in receiving state.
56
+ filter_telegram2: Self-transition for filtering telegrams in receiving2 state.
57
+ do_timeout2: Timeout transition for finalization phase.
58
+ send_error_status2: Error status query transition for finalization phase.
59
+ error_status_received2: Error received transition for finalization phase.
60
+ no_error_status_received2: No error received transition to completed state.
61
+ """
62
+
63
+ # States (9 states as per spec)
64
+ idle = State(initial=True)
65
+ receiving = State()
66
+ resetting = State()
67
+ waiting_ok = State()
68
+
69
+ requesting = State()
70
+ waiting_data = State()
71
+ receiving_chunk = State()
72
+ processing_eof = State()
73
+
74
+ receiving2 = State()
75
+ resetting2 = State()
76
+ waiting_ok2 = State()
77
+
78
+ completed = State(final=True)
79
+
80
+ # Phase 1: Connection & Initialization
81
+ do_connect = idle.to(receiving)
82
+ filter_telegram = receiving.to(receiving) # Self-transition for filtering
83
+ do_timeout = receiving.to(resetting) | waiting_ok.to(receiving)
84
+ send_error_status = resetting.to(waiting_ok)
85
+ error_status_received = waiting_ok.to(receiving)
86
+ no_error_status_received = waiting_ok.to(requesting)
87
+
88
+ # Phase 2: Download
89
+ send_download = requesting.to(waiting_data)
90
+ receive_chunk = waiting_data.to(receiving_chunk)
91
+ send_ack = receiving_chunk.to(waiting_data)
92
+ receive_eof = waiting_data.to(processing_eof)
93
+
94
+ # Phase 3: Finalization
95
+ do_finish = processing_eof.to(receiving2)
96
+ filter_telegram2 = receiving2.to(receiving2) # Self-transition for filtering
97
+ do_timeout2 = receiving2.to(resetting2) | waiting_ok2.to(receiving2)
98
+ send_error_status2 = resetting2.to(waiting_ok2)
99
+ error_status_received2 = waiting_ok2.to(receiving2)
100
+ no_error_status_received2 = waiting_ok2.to(completed)
101
+
102
+ def __init__(
103
+ self,
104
+ conbus_protocol: ConbusEventProtocol,
105
+ actiontable_serializer: ActionTableSerializer,
106
+ telegram_service: TelegramService,
107
+ ) -> None:
108
+ """Initialize the action table download service.
109
+
110
+ Args:
111
+ conbus_protocol: ConbusEventProtocol instance.
112
+ actiontable_serializer: Action table serializer.
113
+ telegram_service: Telegram service for parsing.
114
+ """
115
+ self.conbus_protocol = conbus_protocol
116
+ self.serializer = actiontable_serializer
117
+ self.telegram_service = telegram_service
118
+ self.serial_number: str = ""
119
+ self.actiontable_data: list[str] = []
120
+ self.logger = logging.getLogger(__name__)
121
+
122
+ # Signals (instance attributes to avoid conflict with statemachine)
123
+ self.on_progress: SignalInstance = SignalInstance((str,))
124
+ self.on_error: SignalInstance = SignalInstance((str,))
125
+ self.on_finish: SignalInstance = SignalInstance()
126
+ self.on_actiontable_received: SignalInstance = SignalInstance(
127
+ (ActionTable, Dict[str, Any], list[str])
128
+ )
129
+
130
+ # Connect protocol signals
131
+ self.conbus_protocol.on_connection_made.connect(self._on_connection_made)
132
+ self.conbus_protocol.on_telegram_sent.connect(self._on_telegram_sent)
133
+ self.conbus_protocol.on_telegram_received.connect(self._on_telegram_received)
134
+ self.conbus_protocol.on_timeout.connect(self._on_timeout)
135
+ self.conbus_protocol.on_failed.connect(self._on_failed)
136
+
137
+ # Initialize state machine
138
+ super().__init__(allow_event_without_transition=True)
139
+
140
+ # State machine lifecycle hooks
141
+
142
+ def on_enter_receiving(self) -> None:
143
+ """Enter receiving state - listening for telegrams."""
144
+ self.logger.debug("Entering RECEIVING state - waiting for telegrams")
145
+ self.conbus_protocol.wait()
146
+
147
+ def on_enter_receiving2(self) -> None:
148
+ """Enter receiving state - listening for telegrams."""
149
+ self.logger.debug("Entering RECEIVING2 state - waiting for telegrams")
150
+ self.conbus_protocol.wait()
151
+
152
+ def on_enter_resetting(self) -> None:
153
+ """Enter resetting state - query error status."""
154
+ self.logger.debug("Entering RESETTING state - querying error status")
155
+
156
+ # query_datapoint_module_error_code
157
+ self.conbus_protocol.send_telegram(
158
+ telegram_type=TelegramType.SYSTEM,
159
+ serial_number=self.serial_number,
160
+ system_function=SystemFunction.READ_DATAPOINT,
161
+ data_value=DataPointType.MODULE_ERROR_CODE.value,
162
+ )
163
+ self.send_error_status()
164
+
165
+ def on_enter_resetting2(self) -> None:
166
+ """Enter resetting state - query error status."""
167
+ self.logger.debug("Entering RESETTING2 state - querying error status")
168
+
169
+ # query_datapoint_module_error_code
170
+ self.conbus_protocol.send_telegram(
171
+ telegram_type=TelegramType.SYSTEM,
172
+ serial_number=self.serial_number,
173
+ system_function=SystemFunction.READ_DATAPOINT,
174
+ data_value=DataPointType.MODULE_ERROR_CODE.value,
175
+ )
176
+ self.send_error_status2()
177
+
178
+ def on_enter_waiting_ok(self) -> None:
179
+ """Enter waiting_ok state - awaiting ERROR/NO_ERROR."""
180
+ self.logger.debug("Entering WAITING_OK state - awaiting ERROR/NO_ERROR")
181
+ self.conbus_protocol.wait()
182
+
183
+ def on_enter_waiting_ok2(self) -> None:
184
+ """Enter waiting_ok state - awaiting ERROR/NO_ERROR."""
185
+ self.logger.debug("Entering WAITING_OK state - awaiting ERROR/NO_ERROR")
186
+ self.conbus_protocol.wait()
187
+
188
+ def on_enter_requesting(self) -> None:
189
+ """Enter requesting state - send download request."""
190
+ self.logger.debug("Entering REQUESTING state - sending download request")
191
+ self.conbus_protocol.send_telegram(
192
+ telegram_type=TelegramType.SYSTEM,
193
+ serial_number=self.serial_number,
194
+ system_function=SystemFunction.DOWNLOAD_ACTIONTABLE,
195
+ data_value="00",
196
+ )
197
+ self.send_download()
198
+
199
+ def on_enter_waiting_data(self) -> None:
200
+ """Enter waiting_data state - wait for actiontable chunks."""
201
+ self.logger.debug("Entering WAITING_DATA state - awaiting chunks")
202
+ self.conbus_protocol.wait()
203
+
204
+ def on_enter_receiving_chunk(self) -> None:
205
+ """Enter receiving_chunk state - send ACK."""
206
+ self.logger.debug("Entering RECEIVING_CHUNK state - sending ACK")
207
+ self.conbus_protocol.send_telegram(
208
+ telegram_type=TelegramType.SYSTEM,
209
+ serial_number=self.serial_number,
210
+ system_function=SystemFunction.ACK,
211
+ data_value="00",
212
+ )
213
+ self.send_ack()
214
+
215
+ def on_enter_processing_eof(self) -> None:
216
+ """Enter processing_eof state - deserialize and emit result."""
217
+ self.logger.debug("Entering PROCESSING_EOF state - deserializing")
218
+ all_data = "".join(self.actiontable_data)
219
+ actiontable = self.serializer.from_encoded_string(all_data)
220
+ actiontable_dict = asdict(actiontable)
221
+ actiontable_short = self.serializer.format_decoded_output(actiontable)
222
+ self.on_actiontable_received.emit(
223
+ actiontable, actiontable_dict, actiontable_short
224
+ )
225
+ self.do_finish()
226
+
227
+ def on_enter_completed(self) -> None:
228
+ """Enter completed state - download finished."""
229
+ self.logger.debug("Entering COMPLETED state - download finished")
230
+ self.on_finish.emit()
231
+
232
+ # Protocol event handlers
233
+
234
+ def _on_connection_made(self) -> None:
235
+ """Handle connection established event."""
236
+ self.logger.debug("Connection made")
237
+ if self.idle.is_active:
238
+ self.do_connect()
239
+
240
+ def _on_telegram_sent(self, telegram_sent: str) -> None:
241
+ """Handle telegram sent event.
242
+
243
+ Args:
244
+ telegram_sent: The telegram that was sent.
245
+ """
246
+ self.logger.debug(f"Telegram sent: {telegram_sent}")
247
+
248
+ def _on_read_datapoint_received(self, reply_telegram: ReplyTelegram) -> None:
249
+ """Handle READ_DATAPOINT response for error status check.
250
+
251
+ Args:
252
+ reply_telegram: The parsed reply telegram.
253
+ """
254
+ self.logger.debug(f"Received READ_DATAPOINT in {self.current_state}")
255
+
256
+ if reply_telegram.datapoint_type != DataPointType.MODULE_ERROR_CODE:
257
+ self.logger.debug(
258
+ f"Filtered: not a MODULE_ERROR_CODE (got {reply_telegram.datapoint_type})"
259
+ )
260
+ return
261
+
262
+ if reply_telegram.data_value == "00":
263
+ if self.waiting_ok.is_active:
264
+ self.no_error_status_received()
265
+
266
+ if reply_telegram.data_value != "00":
267
+ if self.waiting_ok.is_active:
268
+ self.error_status_received()
269
+
270
+ if reply_telegram.data_value == "00":
271
+ if self.waiting_ok2.is_active:
272
+ self.no_error_status_received2()
273
+
274
+ if reply_telegram.data_value != "00":
275
+ if self.waiting_ok2.is_active:
276
+ self.error_status_received2()
277
+
278
+ def _on_ack_received(self, _reply_telegram: ReplyTelegram) -> None:
279
+ """Handle ACK telegram received.
280
+
281
+ Args:
282
+ _reply_telegram: The parsed reply telegram (unused).
283
+ """
284
+ self.logger.debug(f"Received ACK in {self.current_state}")
285
+ if self.waiting_ok.is_active:
286
+ self.ack_received()
287
+
288
+ if self.waiting_ok2.is_active:
289
+ self.ack_received2()
290
+
291
+ def _on_nack_received(self, _reply_telegram: ReplyTelegram) -> None:
292
+ """Handle NAK telegram received.
293
+
294
+ Args:
295
+ _reply_telegram: The parsed reply telegram (unused).
296
+ """
297
+ self.logger.debug(f"Received NAK in {self.current_state}")
298
+ if self.waiting_ok.is_active:
299
+ self.nak_received()
300
+
301
+ def _on_actiontable_chunk_received(self, reply_telegram: ReplyTelegram) -> None:
302
+ """Handle actiontable chunk telegram received.
303
+
304
+ Args:
305
+ reply_telegram: The parsed reply telegram containing chunk data.
306
+ """
307
+ self.logger.debug(f"Received actiontable chunk in {self.current_state}")
308
+ if self.waiting_data.is_active:
309
+ data_part = reply_telegram.data_value[2:]
310
+ self.actiontable_data.append(data_part)
311
+ self.on_progress.emit(".")
312
+ self.receive_chunk()
313
+
314
+ def _on_eof_received(self, _reply_telegram: ReplyTelegram) -> None:
315
+ """Handle EOF telegram received.
316
+
317
+ Args:
318
+ _reply_telegram: The parsed reply telegram (unused).
319
+ """
320
+ self.logger.debug(f"Received EOF in {self.current_state}")
321
+ if self.waiting_data.is_active:
322
+ self.receive_eof()
323
+
324
+ def _on_telegram_received(self, telegram_received: TelegramReceivedEvent) -> None:
325
+ """Handle telegram received event.
326
+
327
+ Args:
328
+ telegram_received: The telegram received event.
329
+ """
330
+ self.logger.debug(f"Received{telegram_received} in {self.current_state}")
331
+ if self.receiving.is_active:
332
+ self.filter_telegram()
333
+ return
334
+
335
+ # Filter invalid telegrams
336
+ if not telegram_received.checksum_valid:
337
+ self.logger.debug("Filtered: invalid checksum")
338
+ return
339
+ if telegram_received.telegram_type != TelegramType.REPLY.value:
340
+ self.logger.debug(
341
+ f"Filtered: not a reply (got {telegram_received.telegram_type})"
342
+ )
343
+ return
344
+ if telegram_received.serial_number != self.serial_number:
345
+ self.logger.debug(
346
+ f"Filtered: wrong serial {telegram_received.serial_number} != {self.serial_number}"
347
+ )
348
+ return
349
+
350
+ reply_telegram = self.telegram_service.parse_reply_telegram(
351
+ telegram_received.frame
352
+ )
353
+
354
+ if reply_telegram.system_function == SystemFunction.READ_DATAPOINT:
355
+ self._on_read_datapoint_received(reply_telegram)
356
+ return
357
+
358
+ if reply_telegram.system_function == SystemFunction.ACK:
359
+ self._on_ack_received(reply_telegram)
360
+ return
361
+
362
+ if reply_telegram.system_function == SystemFunction.NAK:
363
+ self._on_nack_received(reply_telegram)
364
+ return
365
+
366
+ if reply_telegram.system_function == SystemFunction.ACTIONTABLE:
367
+ self._on_actiontable_chunk_received(reply_telegram)
368
+ return
369
+
370
+ if reply_telegram.system_function == SystemFunction.EOF:
371
+ self._on_eof_received(reply_telegram)
372
+ return
373
+
374
+ def _on_timeout(self) -> None:
375
+ """Handle timeout event."""
376
+ self.logger.debug("Timeout occurred")
377
+ if self.receiving.is_active:
378
+ self.do_timeout() # receiving -> resetting
379
+ elif self.waiting_ok.is_active:
380
+ self.do_timeout() # waiting_ok -> receiving
381
+ elif self.receiving2.is_active:
382
+ self.do_timeout2() # receiving2 -> resetting2
383
+ elif self.waiting_ok2.is_active:
384
+ self.do_timeout2() # waiting_ok2 -> receiving2
385
+ else:
386
+ self.logger.debug("Timeout in non-recoverable state")
387
+ self.on_error.emit("Timeout")
388
+
389
+ def _on_failed(self, message: str) -> None:
390
+ """Handle failed connection event.
391
+
392
+ Args:
393
+ message: Failure message.
394
+ """
395
+ self.logger.debug(f"Failed: {message}")
396
+ self.on_error.emit(message)
397
+
398
+ # Public API
399
+
400
+ def start(
401
+ self,
402
+ serial_number: str,
403
+ timeout_seconds: Optional[float] = 2.0,
404
+ ) -> None:
405
+ """Run reactor in dedicated thread with its own event loop.
406
+
407
+ Args:
408
+ serial_number: Module serial number.
409
+ timeout_seconds: Optional timeout in seconds.
410
+ """
411
+ self.logger.info("Starting actiontable download")
412
+ self.serial_number = serial_number
413
+ if timeout_seconds:
414
+ self.conbus_protocol.timeout_seconds = timeout_seconds
415
+
416
+ def set_timeout(self, timeout_seconds: float) -> None:
417
+ """Set operation timeout.
418
+
419
+ Args:
420
+ timeout_seconds: Timeout in seconds.
421
+ """
422
+ self.conbus_protocol.timeout_seconds = timeout_seconds
423
+
424
+ def start_reactor(self) -> None:
425
+ """Start the reactor."""
426
+ self.conbus_protocol.start_reactor()
427
+
428
+ def stop_reactor(self) -> None:
429
+ """Stop the reactor."""
430
+ self.conbus_protocol.stop_reactor()
431
+
432
+ def __enter__(self) -> "ActionTableDownloadService":
433
+ """Enter context manager - reset state for singleton reuse.
434
+
435
+ Returns:
436
+ Self for context manager protocol.
437
+ """
438
+ # Reset state for singleton reuse
439
+ self.actiontable_data = []
440
+ self._download_complete = False
441
+ # Reset state machine to idle
442
+ self._reset_state()
443
+ return self
444
+
445
+ def _reset_state(self) -> None:
446
+ """Reset state machine to initial state."""
447
+ # python-statemachine uses model.state to track current state
448
+ # Set it directly to the initial state id
449
+ self.model.state = self.idle.id
450
+ self._download_complete = False
451
+
452
+ def __exit__(
453
+ self, _exc_type: Optional[type], _exc_val: Optional[Exception], _exc_tb: Any
454
+ ) -> None:
455
+ """Exit context manager and disconnect signals."""
456
+ # Disconnect protocol signals
457
+ self.conbus_protocol.on_connection_made.disconnect(self._on_connection_made)
458
+ self.conbus_protocol.on_telegram_sent.disconnect(self._on_telegram_sent)
459
+ self.conbus_protocol.on_telegram_received.disconnect(self._on_telegram_received)
460
+ self.conbus_protocol.on_timeout.disconnect(self._on_timeout)
461
+ self.conbus_protocol.on_failed.disconnect(self._on_failed)
462
+ # Disconnect service signals
463
+ self.on_progress.disconnect()
464
+ self.on_error.disconnect()
465
+ self.on_finish.disconnect()
466
+ # Stop reactor
467
+ self.stop_reactor()
@@ -103,6 +103,16 @@ class ConbusEventProtocol(protocol.Protocol, protocol.ClientFactory):
103
103
  # Start inactivity timeout
104
104
  self._reset_timeout()
105
105
 
106
+ def wait(self, wait_timeout: Optional[float] = None) -> None:
107
+ """Wait for incoming telegrams with optional timeout override.
108
+
109
+ Args:
110
+ wait_timeout: Optional timeout in seconds to override default.
111
+ """
112
+ if wait_timeout:
113
+ self.timeout_seconds = wait_timeout
114
+ self._reset_timeout()
115
+
106
116
  def dataReceived(self, data: bytes) -> None:
107
117
  """Handle received data from TCP connection.
108
118
 
@@ -337,7 +337,7 @@ class ServerService:
337
337
  self.logger.warning(f"Failed to parse telegram: {telegram}")
338
338
  return responses
339
339
 
340
- self.client_buffers.broadcast(parsed_telegram.raw_telegram)
340
+ # self.client_buffers.broadcast(parsed_telegram.raw_telegram)
341
341
 
342
342
  # Handle discover requests
343
343
  if self.discover_service.is_discover_request(parsed_telegram):
@@ -103,7 +103,11 @@ class TestActionTableIntegration:
103
103
  mock_service.__exit__ = Mock(return_value=None)
104
104
 
105
105
  # Store the callbacks that are connected
106
- callbacks = {"on_finish": None, "on_progress": None}
106
+ callbacks = {
107
+ "on_finish": None,
108
+ "on_progress": None,
109
+ "on_actiontable_received": None,
110
+ }
107
111
 
108
112
  def mock_on_finish_connect(callback):
109
113
  """Mock on_finish event connection.
@@ -121,10 +125,21 @@ class TestActionTableIntegration:
121
125
  """
122
126
  callbacks["on_progress"] = callback
123
127
 
128
+ def mock_on_actiontable_received_connect(callback):
129
+ """Mock on_actiontable_received event connection.
130
+
131
+ Args:
132
+ callback: Callback function to store.
133
+ """
134
+ callbacks["on_actiontable_received"] = callback
135
+
124
136
  mock_service.on_finish.connect.side_effect = mock_on_finish_connect
125
137
  mock_service.on_progress.connect.side_effect = mock_on_progress_connect
138
+ mock_service.on_actiontable_received.connect.side_effect = (
139
+ mock_on_actiontable_received_connect
140
+ )
126
141
 
127
- # Mock the start method to call finish_callback immediately
142
+ # Mock the start method to call callbacks immediately
128
143
  def mock_start(serial_number):
129
144
  """Test helper function.
130
145
 
@@ -136,11 +151,14 @@ class TestActionTableIntegration:
136
151
  actiontable_short = ActionTableSerializer.format_decoded_output(
137
152
  sample_actiontable
138
153
  )
139
- # Call the on_finish callback that was connected
140
- if callbacks["on_finish"]:
141
- callbacks["on_finish"](
154
+ # Call the on_actiontable_received callback with data
155
+ if callbacks["on_actiontable_received"]:
156
+ callbacks["on_actiontable_received"](
142
157
  sample_actiontable, actiontable_dict, actiontable_short
143
158
  )
159
+ # Call the on_finish callback without arguments
160
+ if callbacks["on_finish"]:
161
+ callbacks["on_finish"]()
144
162
 
145
163
  def mock_start_reactor() -> None:
146
164
  """Mock reactor start method."""
@@ -60,11 +60,13 @@ class TestConbusActionTableCommands:
60
60
  # Create mock signals
61
61
  mock_service.on_progress = Mock()
62
62
  mock_service.on_finish = Mock()
63
+ mock_service.on_actiontable_received = Mock()
63
64
  mock_service.on_error = Mock()
64
65
 
65
66
  # Track connected callbacks
66
67
  progress_callbacks = []
67
68
  finish_callbacks = []
69
+ actiontable_received_callbacks = []
68
70
  error_callbacks = []
69
71
 
70
72
  def connect_progress(callback):
@@ -83,6 +85,14 @@ class TestConbusActionTableCommands:
83
85
  """
84
86
  finish_callbacks.append(callback)
85
87
 
88
+ def connect_actiontable_received(callback):
89
+ """Mock connect for actiontable_received signal.
90
+
91
+ Args:
92
+ callback: Callback function to connect.
93
+ """
94
+ actiontable_received_callbacks.append(callback)
95
+
86
96
  def connect_error(callback):
87
97
  """Mock connect for error signal.
88
98
 
@@ -93,6 +103,7 @@ class TestConbusActionTableCommands:
93
103
 
94
104
  mock_service.on_progress.connect = connect_progress
95
105
  mock_service.on_finish.connect = connect_finish
106
+ mock_service.on_actiontable_received.connect = connect_actiontable_received
96
107
  mock_service.on_error.connect = connect_error
97
108
 
98
109
  def mock_start_reactor():
@@ -107,8 +118,12 @@ class TestConbusActionTableCommands:
107
118
  actiontable_short = ActionTableSerializer.format_decoded_output(
108
119
  actiontable
109
120
  )
110
- for callback in finish_callbacks:
121
+ # Emit on_actiontable_received with data
122
+ for callback in actiontable_received_callbacks:
111
123
  callback(actiontable, actiontable_dict, actiontable_short)
124
+ # Emit on_finish without arguments
125
+ for callback in finish_callbacks:
126
+ callback()
112
127
 
113
128
  mock_service.start = Mock()
114
129
  mock_service.start_reactor.side_effect = mock_start_reactor