conson-xp 1.40.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.40.0 → conson_xp-1.43.0}/PKG-INFO +3 -1
  2. {conson_xp-1.40.0 → conson_xp-1.43.0}/README.md +1 -0
  3. {conson_xp-1.40.0 → conson_xp-1.43.0}/pyproject.toml +2 -1
  4. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/__init__.py +1 -1
  5. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_actiontable_commands.py +5 -1
  6. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_msactiontable_commands.py +62 -0
  7. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/actiontable/msactiontable_xp24.py +4 -4
  8. conson_xp-1.43.0/src/xp/services/conbus/actiontable/actiontable_download_service.py +467 -0
  9. conson_xp-1.43.0/src/xp/services/conbus/msactiontable/msactiontable_upload_service.py +324 -0
  10. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/protocol/conbus_event_protocol.py +10 -0
  11. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/server/server_service.py +1 -1
  12. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/utils/dependencies.py +16 -0
  13. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/test_actiontable_integration.py +23 -5
  14. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_cli/test_conbus_actiontable_commands.py +16 -1
  15. conson_xp-1.43.0/tests/unit/test_cli/test_conbus_msactiontable_upload_commands.py +376 -0
  16. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_models/test_xp24_action_table_short_format.py +12 -14
  17. conson_xp-1.43.0/tests/unit/test_services/test_actiontable_download_service.py +410 -0
  18. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_actiontable_service.py +37 -23
  19. conson_xp-1.43.0/tests/unit/test_services/test_msactiontable_upload_service.py +503 -0
  20. conson_xp-1.40.0/src/xp/services/conbus/actiontable/actiontable_download_service.py +0 -203
  21. {conson_xp-1.40.0 → conson_xp-1.43.0}/LICENSE +0 -0
  22. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/__init__.py +0 -0
  23. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/__main__.py +0 -0
  24. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/__init__.py +0 -0
  25. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/__init__.py +0 -0
  26. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus.py +0 -0
  27. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_autoreport_commands.py +0 -0
  28. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_blink_commands.py +0 -0
  29. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_config_commands.py +0 -0
  30. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_custom_commands.py +0 -0
  31. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_datapoint_commands.py +0 -0
  32. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_discover_commands.py +0 -0
  33. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_event_commands.py +0 -0
  34. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_export_commands.py +0 -0
  35. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_lightlevel_commands.py +0 -0
  36. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_linknumber_commands.py +0 -0
  37. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_modulenumber_commands.py +0 -0
  38. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_output_commands.py +0 -0
  39. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_raw_commands.py +0 -0
  40. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_receive_commands.py +0 -0
  41. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/conbus/conbus_scan_commands.py +0 -0
  42. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/file_commands.py +0 -0
  43. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/homekit/__init__.py +0 -0
  44. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/homekit/homekit.py +0 -0
  45. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/homekit/homekit_start_commands.py +0 -0
  46. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/module_commands.py +0 -0
  47. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/reverse_proxy_commands.py +0 -0
  48. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/server/__init__.py +0 -0
  49. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/server/server_commands.py +0 -0
  50. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/telegram/__init__.py +0 -0
  51. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/telegram/telegram.py +0 -0
  52. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/telegram/telegram_blink_commands.py +0 -0
  53. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/telegram/telegram_checksum_commands.py +0 -0
  54. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/telegram/telegram_discover_commands.py +0 -0
  55. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/telegram/telegram_linknumber_commands.py +0 -0
  56. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/telegram/telegram_parse_commands.py +0 -0
  57. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/telegram/telegram_version_commands.py +0 -0
  58. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/term/__init__.py +0 -0
  59. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/term/term.py +0 -0
  60. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/commands/term/term_commands.py +0 -0
  61. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/main.py +0 -0
  62. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/utils/__init__.py +0 -0
  63. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/utils/click_tree.py +0 -0
  64. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/utils/datapoint_type_choice.py +0 -0
  65. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/utils/decorators.py +0 -0
  66. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/utils/error_handlers.py +0 -0
  67. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/utils/formatters.py +0 -0
  68. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/utils/module_type_choice.py +0 -0
  69. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/utils/serial_number_type.py +0 -0
  70. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/utils/system_function_choice.py +0 -0
  71. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/cli/utils/xp_module_type.py +0 -0
  72. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/__init__.py +0 -0
  73. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/actiontable/__init__.py +0 -0
  74. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/actiontable/actiontable.py +0 -0
  75. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/actiontable/msactiontable_xp20.py +0 -0
  76. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/actiontable/msactiontable_xp33.py +0 -0
  77. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/conbus/__init__.py +0 -0
  78. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus.py +0 -0
  79. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_autoreport.py +0 -0
  80. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_blink.py +0 -0
  81. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_client_config.py +0 -0
  82. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_connection_status.py +0 -0
  83. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_custom.py +0 -0
  84. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_datapoint.py +0 -0
  85. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_discover.py +0 -0
  86. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_event_list.py +0 -0
  87. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_event_raw.py +0 -0
  88. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_export.py +0 -0
  89. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_lightlevel.py +0 -0
  90. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_linknumber.py +0 -0
  91. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_logger_config.py +0 -0
  92. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_output.py +0 -0
  93. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_raw.py +0 -0
  94. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_receive.py +0 -0
  95. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/conbus/conbus_writeconfig.py +0 -0
  96. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/config/__init__.py +0 -0
  97. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/config/conson_module_config.py +0 -0
  98. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/homekit/__init__.py +0 -0
  99. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/homekit/homekit_accessory.py +0 -0
  100. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/homekit/homekit_config.py +0 -0
  101. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/log_entry.py +0 -0
  102. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/protocol/__init__.py +0 -0
  103. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/protocol/conbus_protocol.py +0 -0
  104. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/response.py +0 -0
  105. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/telegram/__init__.py +0 -0
  106. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/telegram/action_type.py +0 -0
  107. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/telegram/datapoint_type.py +0 -0
  108. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/telegram/event_telegram.py +0 -0
  109. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/telegram/event_type.py +0 -0
  110. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/telegram/input_action_type.py +0 -0
  111. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/telegram/input_type.py +0 -0
  112. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/telegram/module_type.py +0 -0
  113. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/telegram/module_type_code.py +0 -0
  114. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/telegram/output_telegram.py +0 -0
  115. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/telegram/reply_telegram.py +0 -0
  116. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/telegram/system_function.py +0 -0
  117. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/telegram/system_telegram.py +0 -0
  118. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/telegram/telegram.py +0 -0
  119. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/telegram/telegram_type.py +0 -0
  120. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/telegram/timeparam_type.py +0 -0
  121. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/term/__init__.py +0 -0
  122. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/term/connection_state.py +0 -0
  123. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/term/module_state.py +0 -0
  124. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/term/protocol_keys_config.py +0 -0
  125. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/term/status_message.py +0 -0
  126. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/term/telegram_display.py +0 -0
  127. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/models/write_config_type.py +0 -0
  128. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/__init__.py +0 -0
  129. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/actiontable/__init__.py +0 -0
  130. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/actiontable/actiontable_serializer.py +0 -0
  131. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/actiontable/msactiontable_serializer.py +0 -0
  132. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/actiontable/msactiontable_xp20_serializer.py +0 -0
  133. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/actiontable/msactiontable_xp24_serializer.py +0 -0
  134. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/actiontable/msactiontable_xp33_serializer.py +0 -0
  135. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/__init__.py +0 -0
  136. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/actiontable/__init__.py +0 -0
  137. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/actiontable/actiontable_list_service.py +0 -0
  138. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/actiontable/actiontable_show_service.py +0 -0
  139. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/actiontable/actiontable_upload_service.py +0 -0
  140. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_blink_all_service.py +0 -0
  141. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_blink_service.py +0 -0
  142. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_custom_service.py +0 -0
  143. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_datapoint_queryall_service.py +0 -0
  144. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_datapoint_service.py +0 -0
  145. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_discover_service.py +0 -0
  146. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_event_list_service.py +0 -0
  147. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_event_raw_service.py +0 -0
  148. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_export_service.py +0 -0
  149. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_output_service.py +0 -0
  150. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_raw_service.py +0 -0
  151. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_receive_service.py +0 -0
  152. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/conbus_scan_service.py +0 -0
  153. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/msactiontable/__init__.py +0 -0
  154. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/msactiontable/msactiontable_download_service.py +0 -0
  155. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/msactiontable/msactiontable_list_service.py +0 -0
  156. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/msactiontable/msactiontable_show_service.py +0 -0
  157. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/conbus/write_config_service.py +0 -0
  158. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/homekit/__init__.py +0 -0
  159. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_cache_service.py +0 -0
  160. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_conbus_service.py +0 -0
  161. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_config_validator.py +0 -0
  162. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_conson_validator.py +0 -0
  163. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_dimminglight.py +0 -0
  164. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_dimminglight_service.py +0 -0
  165. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_hap_service.py +0 -0
  166. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_lightbulb.py +0 -0
  167. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_lightbulb_service.py +0 -0
  168. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_module_service.py +0 -0
  169. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_outlet.py +0 -0
  170. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_outlet_service.py +0 -0
  171. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/homekit/homekit_service.py +0 -0
  172. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/log_file_service.py +0 -0
  173. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/module_type_service.py +0 -0
  174. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/protocol/__init__.py +0 -0
  175. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/protocol/conbus_protocol.py +0 -0
  176. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/protocol/protocol_factory.py +0 -0
  177. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/protocol/telegram_protocol.py +0 -0
  178. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/reverse_proxy_service.py +0 -0
  179. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/server/__init__.py +0 -0
  180. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/server/base_server_service.py +0 -0
  181. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/server/client_buffer_manager.py +0 -0
  182. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/server/cp20_server_service.py +0 -0
  183. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/server/device_service_factory.py +0 -0
  184. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/server/xp130_server_service.py +0 -0
  185. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/server/xp20_server_service.py +0 -0
  186. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/server/xp230_server_service.py +0 -0
  187. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/server/xp24_server_service.py +0 -0
  188. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/server/xp33_server_service.py +0 -0
  189. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/telegram/__init__.py +0 -0
  190. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/telegram/telegram_blink_service.py +0 -0
  191. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/telegram/telegram_checksum_service.py +0 -0
  192. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/telegram/telegram_datapoint_service.py +0 -0
  193. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/telegram/telegram_discover_service.py +0 -0
  194. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/telegram/telegram_link_number_service.py +0 -0
  195. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/telegram/telegram_output_service.py +0 -0
  196. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/telegram/telegram_service.py +0 -0
  197. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/telegram/telegram_version_service.py +0 -0
  198. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/term/__init__.py +0 -0
  199. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/term/protocol_monitor_service.py +0 -0
  200. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/services/term/state_monitor_service.py +0 -0
  201. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/term/__init__.py +0 -0
  202. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/term/protocol.py +0 -0
  203. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/term/protocol.tcss +0 -0
  204. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/term/state.py +0 -0
  205. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/term/state.tcss +0 -0
  206. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/term/widgets/__init__.py +0 -0
  207. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/term/widgets/help_menu.py +0 -0
  208. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/term/widgets/modules_list.py +0 -0
  209. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/term/widgets/protocol_log.py +0 -0
  210. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/term/widgets/status_footer.py +0 -0
  211. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/utils/__init__.py +0 -0
  212. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/utils/checksum.py +0 -0
  213. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/utils/event_helper.py +0 -0
  214. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/utils/logging.py +0 -0
  215. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/utils/serialization.py +0 -0
  216. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/utils/state_machine.py +0 -0
  217. {conson_xp-1.40.0 → conson_xp-1.43.0}/src/xp/utils/time_utils.py +0 -0
  218. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/.coverage +0 -0
  219. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/__init__.py +0 -0
  220. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/conftest.py +0 -0
  221. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/.coverage +0 -0
  222. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/__init__.py +0 -0
  223. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/telegram_test_data.py +0 -0
  224. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/test_api/.coverage +0 -0
  225. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/test_api/__init__.py +0 -0
  226. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/test_blink_integration.py +0 -0
  227. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/test_checksum_integration.py +0 -0
  228. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/test_conbus_blink_integration.py +0 -0
  229. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/test_conbus_datapoint_integration.py +0 -0
  230. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/test_conbus_raw_integration.py +0 -0
  231. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/test_conbus_receive_integration.py +0 -0
  232. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/test_discovery_integration.py +0 -0
  233. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/test_event_telegram_integration.py +0 -0
  234. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/test_homekit_config_integration.py +0 -0
  235. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/test_link_number_integration.py +0 -0
  236. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/test_module_integration.py +0 -0
  237. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/test_output_integration.py +0 -0
  238. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/test_reverse_proxy_integration.py +0 -0
  239. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/test_system_reply_telegram_integration.py +0 -0
  240. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/test_term_logging_integration.py +0 -0
  241. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/test_version_integration.py +0 -0
  242. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/test_xp20_action_table_integration.py +0 -0
  243. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/integration/test_xp24_action_table_integration.py +0 -0
  244. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/__init__.py +0 -0
  245. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_api/__init__.py +0 -0
  246. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_cli/__init__.py +0 -0
  247. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_cli/test_click_tree.py +0 -0
  248. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_cli/test_conbus_blink_commands.py +0 -0
  249. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_cli/test_datapoint_type_choice.py +0 -0
  250. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_cli/test_decorators.py +0 -0
  251. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_cli/test_error_handlers.py +0 -0
  252. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_cli/test_formatters.py +0 -0
  253. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_cli/test_serial_number_type.py +0 -0
  254. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_cli/test_system_function_choice.py +0 -0
  255. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_cli/test_term_commands.py +0 -0
  256. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_encoding/__init__.py +0 -0
  257. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_encoding/test_latin1_edge_cases.py +0 -0
  258. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_models/__init__.py +0 -0
  259. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_models/test_conbus.py +0 -0
  260. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_models/test_conbus_client_send.py +0 -0
  261. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_models/test_conbus_discover.py +0 -0
  262. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_models/test_conbus_linknumber.py +0 -0
  263. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_models/test_event_telegram.py +0 -0
  264. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_models/test_log_entry.py +0 -0
  265. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_models/test_logger_config.py +0 -0
  266. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_models/test_module_type.py +0 -0
  267. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_models/test_reply_telegram.py +0 -0
  268. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_models/test_system_telegram.py +0 -0
  269. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_models/test_system_telegram_enhancements.py +0 -0
  270. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_models/test_version_telegram.py +0 -0
  271. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_models/test_write_config_type.py +0 -0
  272. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_models/test_xp20_action_table.py +0 -0
  273. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_models/test_xp24_action_table.py +0 -0
  274. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_models/test_xp24_action_telegram.py +0 -0
  275. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/__init__.py +0 -0
  276. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_actiontable_serializer.py +0 -0
  277. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_actiontable_upload_service.py +0 -0
  278. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_base_server_service.py +0 -0
  279. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_blink_service.py +0 -0
  280. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_checksum_service.py +0 -0
  281. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_client_buffer_manager.py +0 -0
  282. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_conbus_blink_service.py +0 -0
  283. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_conbus_event_list_service.py +0 -0
  284. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_conbus_event_protocol.py +0 -0
  285. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_conbus_event_raw_service.py +0 -0
  286. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_conbus_output_service.py +0 -0
  287. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_conbus_raw_service.py +0 -0
  288. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_conbus_receive_service.py +0 -0
  289. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_conbus_reverse_proxy_service.py +0 -0
  290. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_conbus_scan_service.py +0 -0
  291. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_device_service_factory.py +0 -0
  292. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_discovery_service.py +0 -0
  293. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_homekit_cache_service.py +0 -0
  294. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_homekit_config_validator.py +0 -0
  295. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_homekit_conson_service.py +0 -0
  296. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_homekit_services.py +0 -0
  297. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_log_file_service.py +0 -0
  298. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_module_type_service.py +0 -0
  299. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_protocol.py +0 -0
  300. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_protocol_monitor_service.py +0 -0
  301. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_server_service.py +0 -0
  302. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_state_monitor_service.py +0 -0
  303. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_telegram_input_service.py +0 -0
  304. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_telegram_output_service.py +0 -0
  305. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_telegram_protocol.py +0 -0
  306. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_telegram_service.py +0 -0
  307. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_version_service.py +0 -0
  308. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_xp20_action_table_serializer.py +0 -0
  309. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_xp24_action_service.py +0 -0
  310. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_xp24_action_table_serializer.py +0 -0
  311. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_xp24_action_table_service.py +0 -0
  312. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_xp33_action_table_serializer.py +0 -0
  313. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_xp33_short_format.py +0 -0
  314. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_services/test_xp_server_services.py +0 -0
  315. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_tui/__init__.py +0 -0
  316. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_tui/test_protocol_log.py +0 -0
  317. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_utils/__init__.py +0 -0
  318. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_utils/test_checksum.py +0 -0
  319. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_utils/test_event_helper.py +0 -0
  320. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_utils/test_logging.py +0 -0
  321. {conson_xp-1.40.0 → conson_xp-1.43.0}/tests/unit/test_utils/test_serialization.py +0 -0
  322. {conson_xp-1.40.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.40.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
@@ -376,6 +377,7 @@ xp conbus msactiontable
376
377
  xp conbus msactiontable download
377
378
  xp conbus msactiontable list
378
379
  xp conbus msactiontable show
380
+ xp conbus msactiontable upload
379
381
 
380
382
 
381
383
  xp conbus output
@@ -323,6 +323,7 @@ xp conbus msactiontable
323
323
  xp conbus msactiontable download
324
324
  xp conbus msactiontable list
325
325
  xp conbus msactiontable show
326
+ xp conbus msactiontable upload
326
327
 
327
328
 
328
329
  xp conbus output
@@ -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.40.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.40.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()
@@ -25,6 +25,9 @@ from xp.services.conbus.msactiontable.msactiontable_list_service import (
25
25
  from xp.services.conbus.msactiontable.msactiontable_show_service import (
26
26
  MsActionTableShowService,
27
27
  )
28
+ from xp.services.conbus.msactiontable.msactiontable_upload_service import (
29
+ MsActionTableUploadService,
30
+ )
28
31
 
29
32
 
30
33
  @conbus_msactiontable.command("download", short_help="Download MSActionTable")
@@ -206,6 +209,65 @@ def conbus_show_msactiontable(ctx: Context, serial_number: str) -> None:
206
209
  )
207
210
 
208
211
 
212
+ @conbus_msactiontable.command("upload", short_help="Upload MSActionTable")
213
+ @click.argument("serial_number", type=SERIAL)
214
+ @click.argument("xpmoduletype", type=XP_MODULE_TYPE)
215
+ @click.pass_context
216
+ @connection_command()
217
+ def conbus_upload_msactiontable(
218
+ ctx: Context, serial_number: str, xpmoduletype: str
219
+ ) -> None:
220
+ """Upload MS action table from conson.yml to XP module.
221
+
222
+ Args:
223
+ ctx: Click context object.
224
+ serial_number: 10-digit module serial number.
225
+ xpmoduletype: XP module type.
226
+ """
227
+ service: MsActionTableUploadService = (
228
+ ctx.obj.get("container").get_container().resolve(MsActionTableUploadService)
229
+ )
230
+
231
+ def on_progress(progress: str) -> None:
232
+ """Handle progress updates during MS action table upload.
233
+
234
+ Args:
235
+ progress: Progress message string.
236
+ """
237
+ click.echo(progress, nl=False)
238
+
239
+ def on_finish(success: bool) -> None:
240
+ """Handle successful completion of MS action table upload.
241
+
242
+ Args:
243
+ success: Whether upload was successful.
244
+ """
245
+ service.stop_reactor()
246
+ if success:
247
+ click.echo("\nMsactiontable uploaded successfully")
248
+
249
+ def on_error(error: str) -> None:
250
+ """Handle errors during MS action table upload.
251
+
252
+ Args:
253
+ error: Error message string.
254
+ """
255
+ service.stop_reactor()
256
+ click.echo(f"\nError: {error}")
257
+
258
+ click.echo(f"Uploading msactiontable to {serial_number}...")
259
+
260
+ with service:
261
+ service.on_progress.connect(on_progress)
262
+ service.on_error.connect(on_error)
263
+ service.on_finish.connect(on_finish)
264
+ service.start(
265
+ serial_number=serial_number,
266
+ xpmoduletype=xpmoduletype,
267
+ )
268
+ service.start_reactor()
269
+
270
+
209
271
  def _format_yaml(data: dict, indent: int = 0) -> str:
210
272
  """Format a dictionary as YAML-like output.
211
273
 
@@ -178,7 +178,7 @@ class Xp24MsActionTable(BaseModel):
178
178
  param_value = action.param.value
179
179
  action_parts.append(f"{short_code}:{param_value}")
180
180
 
181
- result = f"XP24 {' '.join(action_parts)}"
181
+ result = " ".join(action_parts)
182
182
 
183
183
  # Add settings
184
184
  settings = (
@@ -212,14 +212,14 @@ class Xp24MsActionTable(BaseModel):
212
212
 
213
213
  # Parse action part
214
214
  tokens = action_part.split()
215
- if len(tokens) != 5 or tokens[0] != "XP24":
215
+ if len(tokens) != 4:
216
216
  raise ValueError(
217
- f"Invalid short format: expected 'XP24 <a1> <a2> <a3> <a4>', got '{action_part}'"
217
+ f"Invalid short format: expected '<a1> <a2> <a3> <a4>', got '{action_part}'"
218
218
  )
219
219
 
220
220
  # Parse input actions
221
221
  input_actions = []
222
- for i, token in enumerate(tokens[1:5], 1):
222
+ for i, token in enumerate(tokens[0:4], 1):
223
223
  if ":" not in token:
224
224
  raise ValueError(f"Invalid action format at position {i}: '{token}'")
225
225
 
@@ -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()