conson-xp 1.27.0__tar.gz → 1.29.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 (312) hide show
  1. {conson_xp-1.27.0 → conson_xp-1.29.0}/PKG-INFO +2 -1
  2. {conson_xp-1.27.0 → conson_xp-1.29.0}/README.md +1 -0
  3. {conson_xp-1.27.0 → conson_xp-1.29.0}/pyproject.toml +1 -1
  4. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/__init__.py +1 -1
  5. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/term/term_commands.py +21 -0
  6. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/term/__init__.py +2 -0
  7. conson_xp-1.29.0/src/xp/models/term/module_state.py +30 -0
  8. conson_xp-1.29.0/src/xp/services/server/client_buffer_manager.py +69 -0
  9. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/server/server_service.py +21 -11
  10. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/server/xp24_server_service.py +1 -4
  11. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/server/xp33_server_service.py +1 -4
  12. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/telegram/telegram_output_service.py +33 -0
  13. conson_xp-1.29.0/src/xp/services/term/__init__.py +6 -0
  14. conson_xp-1.29.0/src/xp/services/term/state_monitor_service.py +413 -0
  15. conson_xp-1.29.0/src/xp/term/state.py +97 -0
  16. conson_xp-1.29.0/src/xp/term/state.tcss +86 -0
  17. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/term/widgets/help_menu.py +3 -3
  18. conson_xp-1.29.0/src/xp/term/widgets/modules_list.py +224 -0
  19. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/term/widgets/status_footer.py +5 -4
  20. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/utils/dependencies.py +20 -0
  21. conson_xp-1.29.0/tests/unit/test_services/test_client_buffer_manager.py +313 -0
  22. conson_xp-1.29.0/tests/unit/test_services/test_state_monitor_service.py +364 -0
  23. conson_xp-1.29.0/tests/unit/test_services/test_telegram_output_service.py +83 -0
  24. conson_xp-1.27.0/src/xp/services/term/__init__.py +0 -5
  25. {conson_xp-1.27.0 → conson_xp-1.29.0}/LICENSE +0 -0
  26. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/__init__.py +0 -0
  27. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/__main__.py +0 -0
  28. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/__init__.py +0 -0
  29. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/conbus/__init__.py +0 -0
  30. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/conbus/conbus.py +0 -0
  31. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/conbus/conbus_actiontable_commands.py +0 -0
  32. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/conbus/conbus_autoreport_commands.py +0 -0
  33. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/conbus/conbus_blink_commands.py +0 -0
  34. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/conbus/conbus_config_commands.py +0 -0
  35. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/conbus/conbus_custom_commands.py +0 -0
  36. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/conbus/conbus_datapoint_commands.py +0 -0
  37. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/conbus/conbus_discover_commands.py +0 -0
  38. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/conbus/conbus_event_commands.py +0 -0
  39. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/conbus/conbus_lightlevel_commands.py +0 -0
  40. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/conbus/conbus_linknumber_commands.py +0 -0
  41. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/conbus/conbus_modulenumber_commands.py +0 -0
  42. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/conbus/conbus_msactiontable_commands.py +0 -0
  43. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/conbus/conbus_output_commands.py +0 -0
  44. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/conbus/conbus_raw_commands.py +0 -0
  45. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/conbus/conbus_receive_commands.py +0 -0
  46. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/conbus/conbus_scan_commands.py +0 -0
  47. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/file_commands.py +0 -0
  48. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/homekit/__init__.py +0 -0
  49. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/homekit/homekit.py +0 -0
  50. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/homekit/homekit_start_commands.py +0 -0
  51. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/module_commands.py +0 -0
  52. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/reverse_proxy_commands.py +0 -0
  53. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/server/__init__.py +0 -0
  54. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/server/server_commands.py +0 -0
  55. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/telegram/__init__.py +0 -0
  56. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/telegram/telegram.py +0 -0
  57. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/telegram/telegram_blink_commands.py +0 -0
  58. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/telegram/telegram_checksum_commands.py +0 -0
  59. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/telegram/telegram_discover_commands.py +0 -0
  60. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/telegram/telegram_linknumber_commands.py +0 -0
  61. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/telegram/telegram_parse_commands.py +0 -0
  62. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/telegram/telegram_version_commands.py +0 -0
  63. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/term/__init__.py +0 -0
  64. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/commands/term/term.py +0 -0
  65. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/main.py +0 -0
  66. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/utils/__init__.py +0 -0
  67. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/utils/click_tree.py +0 -0
  68. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/utils/datapoint_type_choice.py +0 -0
  69. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/utils/decorators.py +0 -0
  70. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/utils/error_handlers.py +0 -0
  71. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/utils/formatters.py +0 -0
  72. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/utils/module_type_choice.py +0 -0
  73. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/utils/serial_number_type.py +0 -0
  74. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/utils/system_function_choice.py +0 -0
  75. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/cli/utils/xp_module_type.py +0 -0
  76. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/connection/__init__.py +0 -0
  77. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/connection/exceptions.py +0 -0
  78. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/__init__.py +0 -0
  79. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/actiontable/__init__.py +0 -0
  80. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/actiontable/actiontable.py +0 -0
  81. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/actiontable/msactiontable_xp20.py +0 -0
  82. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/actiontable/msactiontable_xp24.py +0 -0
  83. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/actiontable/msactiontable_xp33.py +0 -0
  84. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/conbus/__init__.py +0 -0
  85. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/conbus/conbus.py +0 -0
  86. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/conbus/conbus_autoreport.py +0 -0
  87. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/conbus/conbus_blink.py +0 -0
  88. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/conbus/conbus_client_config.py +0 -0
  89. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/conbus/conbus_connection_status.py +0 -0
  90. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/conbus/conbus_custom.py +0 -0
  91. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/conbus/conbus_datapoint.py +0 -0
  92. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/conbus/conbus_discover.py +0 -0
  93. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/conbus/conbus_event_list.py +0 -0
  94. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/conbus/conbus_event_raw.py +0 -0
  95. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/conbus/conbus_lightlevel.py +0 -0
  96. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/conbus/conbus_linknumber.py +0 -0
  97. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/conbus/conbus_logger_config.py +0 -0
  98. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/conbus/conbus_output.py +0 -0
  99. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/conbus/conbus_raw.py +0 -0
  100. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/conbus/conbus_receive.py +0 -0
  101. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/conbus/conbus_writeconfig.py +0 -0
  102. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/homekit/__init__.py +0 -0
  103. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/homekit/homekit_accessory.py +0 -0
  104. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/homekit/homekit_config.py +0 -0
  105. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/homekit/homekit_conson_config.py +0 -0
  106. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/log_entry.py +0 -0
  107. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/protocol/__init__.py +0 -0
  108. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/protocol/conbus_protocol.py +0 -0
  109. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/response.py +0 -0
  110. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/telegram/__init__.py +0 -0
  111. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/telegram/action_type.py +0 -0
  112. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/telegram/datapoint_type.py +0 -0
  113. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/telegram/event_telegram.py +0 -0
  114. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/telegram/event_type.py +0 -0
  115. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/telegram/input_action_type.py +0 -0
  116. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/telegram/input_type.py +0 -0
  117. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/telegram/module_type.py +0 -0
  118. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/telegram/module_type_code.py +0 -0
  119. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/telegram/output_telegram.py +0 -0
  120. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/telegram/reply_telegram.py +0 -0
  121. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/telegram/system_function.py +0 -0
  122. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/telegram/system_telegram.py +0 -0
  123. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/telegram/telegram.py +0 -0
  124. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/telegram/telegram_type.py +0 -0
  125. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/telegram/timeparam_type.py +0 -0
  126. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/term/connection_state.py +0 -0
  127. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/term/protocol_keys_config.py +0 -0
  128. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/term/status_message.py +0 -0
  129. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/term/telegram_display.py +0 -0
  130. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/models/write_config_type.py +0 -0
  131. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/__init__.py +0 -0
  132. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/actiontable/__init__.py +0 -0
  133. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/actiontable/actiontable_serializer.py +0 -0
  134. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/actiontable/msactiontable_serializer.py +0 -0
  135. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/actiontable/msactiontable_xp20_serializer.py +0 -0
  136. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/actiontable/msactiontable_xp24_serializer.py +0 -0
  137. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/actiontable/msactiontable_xp33_serializer.py +0 -0
  138. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/conbus/__init__.py +0 -0
  139. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/conbus/actiontable/__init__.py +0 -0
  140. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/conbus/actiontable/actiontable_download_service.py +0 -0
  141. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/conbus/actiontable/actiontable_list_service.py +0 -0
  142. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/conbus/actiontable/actiontable_show_service.py +0 -0
  143. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/conbus/actiontable/actiontable_upload_service.py +0 -0
  144. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/conbus/actiontable/msactiontable_service.py +0 -0
  145. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/conbus/conbus_blink_all_service.py +0 -0
  146. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/conbus/conbus_blink_service.py +0 -0
  147. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/conbus/conbus_custom_service.py +0 -0
  148. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/conbus/conbus_datapoint_queryall_service.py +0 -0
  149. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/conbus/conbus_datapoint_service.py +0 -0
  150. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/conbus/conbus_discover_service.py +0 -0
  151. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/conbus/conbus_event_list_service.py +0 -0
  152. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/conbus/conbus_event_raw_service.py +0 -0
  153. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/conbus/conbus_output_service.py +0 -0
  154. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/conbus/conbus_raw_service.py +0 -0
  155. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/conbus/conbus_receive_service.py +0 -0
  156. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/conbus/conbus_scan_service.py +0 -0
  157. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/conbus/write_config_service.py +0 -0
  158. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/homekit/__init__.py +0 -0
  159. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/homekit/homekit_cache_service.py +0 -0
  160. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/homekit/homekit_conbus_service.py +0 -0
  161. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/homekit/homekit_config_validator.py +0 -0
  162. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/homekit/homekit_conson_validator.py +0 -0
  163. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/homekit/homekit_dimminglight.py +0 -0
  164. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/homekit/homekit_dimminglight_service.py +0 -0
  165. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/homekit/homekit_hap_service.py +0 -0
  166. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/homekit/homekit_lightbulb.py +0 -0
  167. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/homekit/homekit_lightbulb_service.py +0 -0
  168. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/homekit/homekit_module_service.py +0 -0
  169. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/homekit/homekit_outlet.py +0 -0
  170. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/homekit/homekit_outlet_service.py +0 -0
  171. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/homekit/homekit_service.py +0 -0
  172. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/log_file_service.py +0 -0
  173. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/module_type_service.py +0 -0
  174. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/protocol/__init__.py +0 -0
  175. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/protocol/conbus_event_protocol.py +0 -0
  176. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/protocol/conbus_protocol.py +0 -0
  177. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/protocol/protocol_factory.py +0 -0
  178. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/protocol/telegram_protocol.py +0 -0
  179. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/reverse_proxy_service.py +0 -0
  180. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/server/__init__.py +0 -0
  181. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/server/base_server_service.py +0 -0
  182. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/server/cp20_server_service.py +0 -0
  183. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/server/device_service_factory.py +0 -0
  184. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/server/xp130_server_service.py +0 -0
  185. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/server/xp20_server_service.py +0 -0
  186. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/server/xp230_server_service.py +0 -0
  187. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/telegram/__init__.py +0 -0
  188. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/telegram/telegram_blink_service.py +0 -0
  189. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/telegram/telegram_checksum_service.py +0 -0
  190. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/telegram/telegram_datapoint_service.py +0 -0
  191. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/telegram/telegram_discover_service.py +0 -0
  192. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/telegram/telegram_link_number_service.py +0 -0
  193. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/telegram/telegram_service.py +0 -0
  194. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/telegram/telegram_version_service.py +0 -0
  195. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/services/term/protocol_monitor_service.py +0 -0
  196. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/term/__init__.py +0 -0
  197. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/term/protocol.py +0 -0
  198. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/term/protocol.tcss +0 -0
  199. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/term/widgets/__init__.py +0 -0
  200. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/term/widgets/protocol_log.py +0 -0
  201. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/utils/__init__.py +0 -0
  202. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/utils/checksum.py +0 -0
  203. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/utils/event_helper.py +0 -0
  204. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/utils/logging.py +0 -0
  205. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/utils/serialization.py +0 -0
  206. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/utils/state_machine.py +0 -0
  207. {conson_xp-1.27.0 → conson_xp-1.29.0}/src/xp/utils/time_utils.py +0 -0
  208. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/.coverage +0 -0
  209. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/__init__.py +0 -0
  210. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/conftest.py +0 -0
  211. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/.coverage +0 -0
  212. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/__init__.py +0 -0
  213. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/telegram_test_data.py +0 -0
  214. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/test_actiontable_integration.py +0 -0
  215. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/test_api/.coverage +0 -0
  216. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/test_api/__init__.py +0 -0
  217. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/test_blink_integration.py +0 -0
  218. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/test_checksum_integration.py +0 -0
  219. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/test_conbus_blink_integration.py +0 -0
  220. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/test_conbus_datapoint_integration.py +0 -0
  221. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/test_conbus_raw_integration.py +0 -0
  222. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/test_conbus_receive_integration.py +0 -0
  223. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/test_discovery_integration.py +0 -0
  224. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/test_event_telegram_integration.py +0 -0
  225. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/test_homekit_config_integration.py +0 -0
  226. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/test_link_number_integration.py +0 -0
  227. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/test_module_integration.py +0 -0
  228. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/test_output_integration.py +0 -0
  229. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/test_reverse_proxy_integration.py +0 -0
  230. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/test_system_reply_telegram_integration.py +0 -0
  231. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/test_term_logging_integration.py +0 -0
  232. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/test_version_integration.py +0 -0
  233. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/test_xp20_action_table_integration.py +0 -0
  234. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/integration/test_xp24_action_table_integration.py +0 -0
  235. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/__init__.py +0 -0
  236. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_api/__init__.py +0 -0
  237. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_cli/__init__.py +0 -0
  238. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_cli/test_click_tree.py +0 -0
  239. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_cli/test_conbus_actiontable_commands.py +0 -0
  240. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_cli/test_conbus_blink_commands.py +0 -0
  241. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_cli/test_datapoint_type_choice.py +0 -0
  242. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_cli/test_decorators.py +0 -0
  243. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_cli/test_error_handlers.py +0 -0
  244. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_cli/test_formatters.py +0 -0
  245. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_cli/test_serial_number_type.py +0 -0
  246. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_cli/test_system_function_choice.py +0 -0
  247. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_cli/test_term_commands.py +0 -0
  248. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_connection/__init__.py +0 -0
  249. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_connection/test_connection_init.py +0 -0
  250. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_connection/test_exceptions.py +0 -0
  251. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_encoding/__init__.py +0 -0
  252. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_encoding/test_latin1_edge_cases.py +0 -0
  253. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_models/__init__.py +0 -0
  254. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_models/test_conbus.py +0 -0
  255. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_models/test_conbus_client_send.py +0 -0
  256. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_models/test_conbus_discover.py +0 -0
  257. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_models/test_conbus_linknumber.py +0 -0
  258. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_models/test_event_telegram.py +0 -0
  259. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_models/test_log_entry.py +0 -0
  260. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_models/test_logger_config.py +0 -0
  261. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_models/test_module_type.py +0 -0
  262. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_models/test_reply_telegram.py +0 -0
  263. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_models/test_system_telegram.py +0 -0
  264. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_models/test_system_telegram_enhancements.py +0 -0
  265. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_models/test_version_telegram.py +0 -0
  266. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_models/test_write_config_type.py +0 -0
  267. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_models/test_xp20_action_table.py +0 -0
  268. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_models/test_xp24_action_table.py +0 -0
  269. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_models/test_xp24_action_telegram.py +0 -0
  270. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/__init__.py +0 -0
  271. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_actiontable_serializer.py +0 -0
  272. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_actiontable_service.py +0 -0
  273. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_actiontable_upload_service.py +0 -0
  274. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_base_server_service.py +0 -0
  275. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_blink_service.py +0 -0
  276. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_checksum_service.py +0 -0
  277. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_conbus_blink_service.py +0 -0
  278. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_conbus_event_list_service.py +0 -0
  279. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_conbus_event_protocol.py +0 -0
  280. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_conbus_event_raw_service.py +0 -0
  281. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_conbus_raw_service.py +0 -0
  282. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_conbus_receive_service.py +0 -0
  283. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_conbus_reverse_proxy_service.py +0 -0
  284. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_device_service_factory.py +0 -0
  285. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_discovery_service.py +0 -0
  286. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_homekit_cache_service.py +0 -0
  287. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_homekit_config_validator.py +0 -0
  288. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_homekit_conson_service.py +0 -0
  289. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_homekit_services.py +0 -0
  290. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_log_file_service.py +0 -0
  291. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_module_type_service.py +0 -0
  292. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_protocol.py +0 -0
  293. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_protocol_monitor_service.py +0 -0
  294. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_server_service.py +0 -0
  295. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_telegram_input_service.py +0 -0
  296. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_telegram_protocol.py +0 -0
  297. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_telegram_service.py +0 -0
  298. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_version_service.py +0 -0
  299. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_xp20_action_table_serializer.py +0 -0
  300. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_xp24_action_service.py +0 -0
  301. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_xp24_action_table_serializer.py +0 -0
  302. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_xp24_action_table_service.py +0 -0
  303. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_xp33_action_table_serializer.py +0 -0
  304. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_services/test_xp_server_services.py +0 -0
  305. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_tui/__init__.py +0 -0
  306. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_tui/test_protocol_log.py +0 -0
  307. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_utils/__init__.py +0 -0
  308. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_utils/test_checksum.py +0 -0
  309. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_utils/test_event_helper.py +0 -0
  310. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_utils/test_logging.py +0 -0
  311. {conson_xp-1.27.0 → conson_xp-1.29.0}/tests/unit/test_utils/test_serialization.py +0 -0
  312. {conson_xp-1.27.0 → conson_xp-1.29.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.27.0
3
+ Version: 1.29.0
4
4
  Summary: XP Protocol Communication Tools
5
5
  Author-Email: ldvchosal <ldvchosal@github.com>
6
6
  License: MIT License
@@ -417,6 +417,7 @@ xp telegram version
417
417
 
418
418
  xp term
419
419
  xp term protocol
420
+ xp term state
420
421
 
421
422
  <!-- END CLI HELP -->
422
423
  ```
@@ -364,6 +364,7 @@ xp telegram version
364
364
 
365
365
  xp term
366
366
  xp term protocol
367
+ xp term state
367
368
 
368
369
  <!-- END CLI HELP -->
369
370
  ```
@@ -22,7 +22,7 @@ dependencies = [
22
22
  ]
23
23
  requires-python = ">=3.11"
24
24
  readme = "README.md"
25
- version = "1.27.0"
25
+ version = "1.29.0"
26
26
 
27
27
  [project.license]
28
28
  file = "LICENSE"
@@ -3,7 +3,7 @@
3
3
  conson-xp package.
4
4
  """
5
5
 
6
- __version__ = "1.27.0"
6
+ __version__ = "1.29.0"
7
7
  __manufacturer__ = "salchichon"
8
8
  __model__ = "xp.cli"
9
9
  __serial__ = "2025.09.23.000"
@@ -25,3 +25,24 @@ def protocol_monitor(ctx: Context) -> None:
25
25
 
26
26
  # Resolve ProtocolMonitorApp from container and run
27
27
  ctx.obj.get("container").get_container().resolve(ProtocolMonitorApp).run()
28
+
29
+
30
+ @term.command("state")
31
+ @click.pass_context
32
+ def state_monitor(ctx: Context) -> None:
33
+ r"""Start TUI for module state monitoring.
34
+
35
+ Displays module states from Conson configuration with real-time
36
+ updates in an interactive terminal interface.
37
+
38
+ Args:
39
+ ctx: Click context object.
40
+
41
+ Examples:
42
+ \b
43
+ xp term state
44
+ """
45
+ from xp.term.state import StateMonitorApp
46
+
47
+ # Resolve StateMonitorApp from container and run
48
+ ctx.obj.get("container").get_container().resolve(StateMonitorApp).run()
@@ -1,11 +1,13 @@
1
1
  """Terminal UI models."""
2
2
 
3
+ from xp.models.term.module_state import ModuleState
3
4
  from xp.models.term.protocol_keys_config import (
4
5
  ProtocolKeyConfig,
5
6
  ProtocolKeysConfig,
6
7
  )
7
8
 
8
9
  __all__ = [
10
+ "ModuleState",
9
11
  "ProtocolKeyConfig",
10
12
  "ProtocolKeysConfig",
11
13
  ]
@@ -0,0 +1,30 @@
1
+ """Module state data model."""
2
+
3
+ from dataclasses import dataclass
4
+ from datetime import datetime
5
+ from typing import Optional
6
+
7
+
8
+ @dataclass
9
+ class ModuleState:
10
+ """State of a Conson module for TUI display.
11
+
12
+ Attributes:
13
+ name: Module name/identifier (e.g., A01, A02).
14
+ serial_number: Module serial number.
15
+ module_type: Module type designation (e.g., XP130, XP230, XP24).
16
+ link_number: Link number for the module.
17
+ outputs: Output states as space-separated binary values. Empty string for modules without outputs.
18
+ auto_report: Auto-report enabled status (Y/N).
19
+ error_status: Module status ("OK" or error code like "E10").
20
+ last_update: Last communication timestamp. None if never updated.
21
+ """
22
+
23
+ name: str
24
+ serial_number: str
25
+ module_type: str
26
+ link_number: int
27
+ outputs: str
28
+ auto_report: bool
29
+ error_status: str
30
+ last_update: Optional[datetime]
@@ -0,0 +1,69 @@
1
+ """Client buffer manager for broadcasting telegrams to connected clients.
2
+
3
+ This module provides thread-safe management of per-client telegram queues,
4
+ enabling broadcast of telegrams from device services to all connected clients.
5
+ """
6
+
7
+ import queue
8
+ import socket
9
+ import threading
10
+ from typing import Dict, Optional
11
+
12
+
13
+ class ClientBufferManager:
14
+ """
15
+ Thread-safe manager for client telegram queues.
16
+
17
+ Manages individual queues for each connected client, providing thread-safe
18
+ operations for client registration, unregistration, and telegram broadcasting.
19
+ """
20
+
21
+ def __init__(self) -> None:
22
+ """Initialize the client buffer manager."""
23
+ self._buffers: Dict[socket.socket, queue.Queue[str]] = {}
24
+ self._lock = threading.Lock()
25
+
26
+ def register_client(self, client_socket: socket.socket) -> queue.Queue[str]:
27
+ """Register a new client and create its telegram queue.
28
+
29
+ Args:
30
+ client_socket: The socket of the connecting client.
31
+
32
+ Returns:
33
+ The newly created queue for this client.
34
+ """
35
+ with self._lock:
36
+ client_queue: queue.Queue[str] = queue.Queue()
37
+ self._buffers[client_socket] = client_queue
38
+ return client_queue
39
+
40
+ def unregister_client(self, client_socket: socket.socket) -> None:
41
+ """Unregister a client and remove its telegram queue.
42
+
43
+ Args:
44
+ client_socket: The socket of the disconnecting client.
45
+ """
46
+ with self._lock:
47
+ self._buffers.pop(client_socket, None)
48
+
49
+ def broadcast(self, telegram: str) -> None:
50
+ """Broadcast a telegram to all connected clients.
51
+
52
+ Args:
53
+ telegram: The telegram string to broadcast.
54
+ """
55
+ with self._lock:
56
+ for client_queue in self._buffers.values():
57
+ client_queue.put(telegram)
58
+
59
+ def get_queue(self, client_socket: socket.socket) -> Optional[queue.Queue[str]]:
60
+ """Retrieve the queue for a specific client.
61
+
62
+ Args:
63
+ client_socket: The socket of the client.
64
+
65
+ Returns:
66
+ The client's queue if registered, None otherwise.
67
+ """
68
+ with self._lock:
69
+ return self._buffers.get(client_socket)
@@ -5,6 +5,7 @@ Discover Request telegrams with configurable device information.
5
5
  """
6
6
 
7
7
  import logging
8
+ import queue
8
9
  import socket
9
10
  import threading
10
11
  from pathlib import Path
@@ -15,6 +16,7 @@ from xp.models.homekit.homekit_conson_config import (
15
16
  ConsonModuleListConfig,
16
17
  )
17
18
  from xp.services.server.base_server_service import BaseServerService
19
+ from xp.services.server.client_buffer_manager import ClientBufferManager
18
20
  from xp.services.server.device_service_factory import DeviceServiceFactory
19
21
  from xp.services.telegram.telegram_discover_service import TelegramDiscoverService
20
22
  from xp.services.telegram.telegram_service import TelegramService
@@ -68,7 +70,7 @@ class ServerService:
68
70
  None # Background thread for storm
69
71
  )
70
72
  self.collector_stop_event = threading.Event() # Event to stop thread
71
- self.collector_buffer: list[str] = [] # All collected buffers
73
+ self.client_buffers = ClientBufferManager() # Per-client queue manager
72
74
 
73
75
  # Set up logging
74
76
  self.logger = logging.getLogger(__name__)
@@ -191,6 +193,9 @@ class ServerService:
191
193
  self, client_socket: socket.socket, client_address: tuple[str, int]
192
194
  ) -> None:
193
195
  """Handle individual client connection."""
196
+ # Register client and get its dedicated queue
197
+ client_queue = self.client_buffers.register_client(client_socket)
198
+
194
199
  try:
195
200
 
196
201
  idle_timeout = 300
@@ -202,11 +207,14 @@ class ServerService:
202
207
 
203
208
  while True:
204
209
 
205
- # send waiting buffer
206
- for i in range(len(self.collector_buffer)):
207
- buffer = self.collector_buffer.pop()
208
- client_socket.send(buffer.encode("latin-1"))
209
- self.logger.debug(f"Sent buffer to {client_address}")
210
+ # send waiting buffer from client's dedicated queue
211
+ while not client_queue.empty():
212
+ try:
213
+ buffer = client_queue.get_nowait()
214
+ client_socket.send(buffer.encode("latin-1"))
215
+ self.logger.debug(f"Sent buffer to {client_address}")
216
+ except queue.Empty:
217
+ break
210
218
 
211
219
  # Receive data from client
212
220
  data = None
@@ -230,11 +238,8 @@ class ServerService:
230
238
 
231
239
  # Process request (discover or data request)
232
240
  responses = self._process_request(message)
233
-
234
- # Send responses
235
241
  for response in responses:
236
- client_socket.send(response.encode("latin-1"))
237
- self.logger.debug(f"Sent to {client_address}: {response[:-1]}")
242
+ self.client_buffers.broadcast(response)
238
243
 
239
244
  except socket.timeout:
240
245
  self.logger.debug(f"Client {client_address} timed out")
@@ -242,6 +247,8 @@ class ServerService:
242
247
  self.logger.error(f"Error handling client {client_address}: {e}")
243
248
  finally:
244
249
  try:
250
+ # Unregister client before closing socket
251
+ self.client_buffers.unregister_client(client_socket)
245
252
  client_socket.close()
246
253
  self.logger.info(f"Client {client_address} disconnected")
247
254
  except Exception as e:
@@ -330,6 +337,8 @@ class ServerService:
330
337
  self.logger.warning(f"Failed to parse telegram: {telegram}")
331
338
  return responses
332
339
 
340
+ self.client_buffers.broadcast(parsed_telegram.raw_telegram)
341
+
333
342
  # Handle discover requests
334
343
  if self.discover_service.is_discover_request(parsed_telegram):
335
344
  for device_service in self.device_services.values():
@@ -421,7 +430,8 @@ class ServerService:
421
430
  collected = 0
422
431
  for device_service in self.device_services.values():
423
432
  telegram_buffer = device_service.collect_telegram_buffer()
424
- self.collector_buffer.extend(telegram_buffer)
433
+ for telegram in telegram_buffer:
434
+ self.client_buffers.broadcast(telegram)
425
435
  collected += len(telegram_buffer)
426
436
 
427
437
  # Wait a bit before checking again
@@ -161,10 +161,7 @@ class XP24ServerService(BaseServerService):
161
161
 
162
162
  data_value = handler()
163
163
  data_part = (
164
- f"R{self.serial_number}"
165
- f"F02D{datapoint_type.value}"
166
- f"{self.module_type_code.value:02}"
167
- f"{data_value}"
164
+ f"R{self.serial_number}" f"F02D{datapoint_type.value}" f"{data_value}"
168
165
  )
169
166
  telegram = self._build_response_telegram(data_part)
170
167
 
@@ -226,10 +226,7 @@ class XP33ServerService(BaseServerService):
226
226
 
227
227
  data_value = handler()
228
228
  data_part = (
229
- f"R{self.serial_number}"
230
- f"F02D{datapoint_type.value}"
231
- f"{self.module_type_code.value:02}"
232
- f"{data_value}"
229
+ f"R{self.serial_number}" f"F02D{datapoint_type.value}" f"{data_value}"
233
230
  )
234
231
  telegram = self._build_response_telegram(data_part)
235
232
 
@@ -320,3 +320,36 @@ class TelegramOutputService:
320
320
  f"Timestamp: {telegram.timestamp}\n"
321
321
  f"Checksum: {telegram.checksum}{checksum_status}"
322
322
  )
323
+
324
+ @staticmethod
325
+ def format_output_state(data_value: str) -> str:
326
+ """Format module output state data value for display.
327
+
328
+ Algorithm:
329
+ 1. Remove 'x' characters
330
+ 2. Format to 4 chars with space padding on the right
331
+ 3. Invert order
332
+ 4. Add spaces between characters
333
+
334
+ Args:
335
+ data_value: Raw data value from module output state datapoint (e.g., "xxxx0101", "xx1110").
336
+
337
+ Returns:
338
+ Formatted output string with spaces (e.g., "1 0 1 0", "0 1 1 1").
339
+
340
+ Examples:
341
+ >>> TelegramOutputService.format_output_state("xxxx0101")
342
+ "1 0 1 0"
343
+ >>> TelegramOutputService.format_output_state("xx1110")
344
+ "0 1 1 1"
345
+ >>> TelegramOutputService.format_output_state("xxxx01")
346
+ " 1 0"
347
+ """
348
+ # Remove 'x' characters
349
+ cleaned = data_value.replace("x", "").replace("X", "")
350
+ # Format to 4 chars with space padding on the right
351
+ padded = cleaned.ljust(4)[:4]
352
+ # Invert order
353
+ inverted = padded[::-1]
354
+ # Add spaces between characters
355
+ return " ".join(inverted)
@@ -0,0 +1,6 @@
1
+ """Terminal interface services."""
2
+
3
+ from xp.services.term.protocol_monitor_service import ProtocolMonitorService
4
+ from xp.services.term.state_monitor_service import StateMonitorService
5
+
6
+ __all__ = ["ProtocolMonitorService", "StateMonitorService"]