conson-xp 1.19.0__tar.gz → 1.21.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 (294) hide show
  1. {conson_xp-1.19.0 → conson_xp-1.21.0}/PKG-INFO +6 -1
  2. {conson_xp-1.19.0 → conson_xp-1.21.0}/README.md +4 -0
  3. {conson_xp-1.19.0 → conson_xp-1.21.0}/pyproject.toml +3 -1
  4. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/__init__.py +1 -1
  5. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/__init__.py +4 -0
  6. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/conbus/conbus_receive_commands.py +2 -1
  7. conson_xp-1.21.0/src/xp/cli/commands/term/__init__.py +5 -0
  8. conson_xp-1.21.0/src/xp/cli/commands/term/term.py +12 -0
  9. conson_xp-1.21.0/src/xp/cli/commands/term/term_commands.py +31 -0
  10. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/main.py +7 -35
  11. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/conbus/conbus_client_config.py +1 -0
  12. conson_xp-1.21.0/src/xp/models/conbus/conbus_logger_config.py +107 -0
  13. conson_xp-1.21.0/src/xp/models/term/__init__.py +11 -0
  14. conson_xp-1.21.0/src/xp/models/term/protocol_keys_config.py +45 -0
  15. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/conbus/conbus_receive_service.py +58 -30
  16. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/protocol/conbus_event_protocol.py +36 -3
  17. conson_xp-1.21.0/src/xp/term/__init__.py +1 -0
  18. conson_xp-1.21.0/src/xp/term/app.py +158 -0
  19. conson_xp-1.21.0/src/xp/term/protocol.tcss +135 -0
  20. conson_xp-1.21.0/src/xp/term/protocol.yml +139 -0
  21. conson_xp-1.21.0/src/xp/term/widgets/__init__.py +1 -0
  22. conson_xp-1.21.0/src/xp/term/widgets/protocol_log.py +393 -0
  23. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/utils/dependencies.py +25 -6
  24. conson_xp-1.21.0/src/xp/utils/logging.py +102 -0
  25. conson_xp-1.21.0/src/xp/utils/state_machine.py +81 -0
  26. conson_xp-1.21.0/tests/integration/test_term_logging_integration.py +10 -0
  27. conson_xp-1.21.0/tests/unit/test_cli/test_term_commands.py +10 -0
  28. conson_xp-1.21.0/tests/unit/test_models/test_logger_config.py +188 -0
  29. conson_xp-1.21.0/tests/unit/test_services/test_conbus_receive_service.py +283 -0
  30. conson_xp-1.21.0/tests/unit/test_tui/__init__.py +1 -0
  31. conson_xp-1.21.0/tests/unit/test_tui/test_protocol_log.py +234 -0
  32. conson_xp-1.21.0/tests/unit/test_utils/test_logging.py +317 -0
  33. {conson_xp-1.19.0 → conson_xp-1.21.0}/LICENSE +0 -0
  34. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/__init__.py +0 -0
  35. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/__main__.py +0 -0
  36. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/conbus/__init__.py +0 -0
  37. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/conbus/conbus.py +0 -0
  38. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/conbus/conbus_actiontable_commands.py +0 -0
  39. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/conbus/conbus_autoreport_commands.py +0 -0
  40. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/conbus/conbus_blink_commands.py +0 -0
  41. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/conbus/conbus_config_commands.py +0 -0
  42. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/conbus/conbus_custom_commands.py +0 -0
  43. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/conbus/conbus_datapoint_commands.py +0 -0
  44. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/conbus/conbus_discover_commands.py +0 -0
  45. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/conbus/conbus_event_commands.py +0 -0
  46. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/conbus/conbus_lightlevel_commands.py +0 -0
  47. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/conbus/conbus_linknumber_commands.py +0 -0
  48. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/conbus/conbus_modulenumber_commands.py +0 -0
  49. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/conbus/conbus_msactiontable_commands.py +0 -0
  50. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/conbus/conbus_output_commands.py +0 -0
  51. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/conbus/conbus_raw_commands.py +0 -0
  52. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/conbus/conbus_scan_commands.py +0 -0
  53. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/file_commands.py +0 -0
  54. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/homekit/__init__.py +0 -0
  55. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/homekit/homekit.py +0 -0
  56. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/homekit/homekit_start_commands.py +0 -0
  57. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/module_commands.py +0 -0
  58. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/reverse_proxy_commands.py +0 -0
  59. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/server/__init__.py +0 -0
  60. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/server/server_commands.py +0 -0
  61. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/telegram/__init__.py +0 -0
  62. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/telegram/telegram.py +0 -0
  63. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/telegram/telegram_blink_commands.py +0 -0
  64. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/telegram/telegram_checksum_commands.py +0 -0
  65. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/telegram/telegram_discover_commands.py +0 -0
  66. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/telegram/telegram_linknumber_commands.py +0 -0
  67. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/telegram/telegram_parse_commands.py +0 -0
  68. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/commands/telegram/telegram_version_commands.py +0 -0
  69. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/utils/__init__.py +0 -0
  70. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/utils/click_tree.py +0 -0
  71. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/utils/datapoint_type_choice.py +0 -0
  72. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/utils/decorators.py +0 -0
  73. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/utils/error_handlers.py +0 -0
  74. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/utils/formatters.py +0 -0
  75. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/utils/module_type_choice.py +0 -0
  76. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/utils/serial_number_type.py +0 -0
  77. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/utils/system_function_choice.py +0 -0
  78. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/cli/utils/xp_module_type.py +0 -0
  79. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/connection/__init__.py +0 -0
  80. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/connection/exceptions.py +0 -0
  81. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/__init__.py +0 -0
  82. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/actiontable/__init__.py +0 -0
  83. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/actiontable/actiontable.py +0 -0
  84. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/actiontable/msactiontable_xp20.py +0 -0
  85. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/actiontable/msactiontable_xp24.py +0 -0
  86. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/actiontable/msactiontable_xp33.py +0 -0
  87. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/conbus/__init__.py +0 -0
  88. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/conbus/conbus.py +0 -0
  89. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/conbus/conbus_autoreport.py +0 -0
  90. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/conbus/conbus_blink.py +0 -0
  91. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/conbus/conbus_connection_status.py +0 -0
  92. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/conbus/conbus_custom.py +0 -0
  93. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/conbus/conbus_datapoint.py +0 -0
  94. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/conbus/conbus_discover.py +0 -0
  95. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/conbus/conbus_event_list.py +0 -0
  96. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/conbus/conbus_event_raw.py +0 -0
  97. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/conbus/conbus_lightlevel.py +0 -0
  98. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/conbus/conbus_linknumber.py +0 -0
  99. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/conbus/conbus_output.py +0 -0
  100. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/conbus/conbus_raw.py +0 -0
  101. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/conbus/conbus_receive.py +0 -0
  102. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/conbus/conbus_writeconfig.py +0 -0
  103. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/homekit/__init__.py +0 -0
  104. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/homekit/homekit_accessory.py +0 -0
  105. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/homekit/homekit_config.py +0 -0
  106. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/homekit/homekit_conson_config.py +0 -0
  107. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/log_entry.py +0 -0
  108. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/protocol/__init__.py +0 -0
  109. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/protocol/conbus_protocol.py +0 -0
  110. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/response.py +0 -0
  111. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/telegram/__init__.py +0 -0
  112. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/telegram/action_type.py +0 -0
  113. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/telegram/datapoint_type.py +0 -0
  114. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/telegram/event_telegram.py +0 -0
  115. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/telegram/event_type.py +0 -0
  116. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/telegram/input_action_type.py +0 -0
  117. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/telegram/input_type.py +0 -0
  118. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/telegram/module_type.py +0 -0
  119. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/telegram/module_type_code.py +0 -0
  120. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/telegram/output_telegram.py +0 -0
  121. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/telegram/reply_telegram.py +0 -0
  122. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/telegram/system_function.py +0 -0
  123. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/telegram/system_telegram.py +0 -0
  124. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/telegram/telegram.py +0 -0
  125. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/telegram/telegram_type.py +0 -0
  126. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/telegram/timeparam_type.py +0 -0
  127. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/models/write_config_type.py +0 -0
  128. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/__init__.py +0 -0
  129. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/actiontable/__init__.py +0 -0
  130. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/actiontable/actiontable_serializer.py +0 -0
  131. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/actiontable/msactiontable_serializer.py +0 -0
  132. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/actiontable/msactiontable_xp20_serializer.py +0 -0
  133. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/actiontable/msactiontable_xp24_serializer.py +0 -0
  134. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/actiontable/msactiontable_xp33_serializer.py +0 -0
  135. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/conbus/__init__.py +0 -0
  136. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/conbus/actiontable/__init__.py +0 -0
  137. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/conbus/actiontable/actiontable_download_service.py +0 -0
  138. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/conbus/actiontable/actiontable_list_service.py +0 -0
  139. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/conbus/actiontable/actiontable_show_service.py +0 -0
  140. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/conbus/actiontable/actiontable_upload_service.py +0 -0
  141. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/conbus/actiontable/msactiontable_service.py +0 -0
  142. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/conbus/conbus_blink_all_service.py +0 -0
  143. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/conbus/conbus_blink_service.py +0 -0
  144. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/conbus/conbus_custom_service.py +0 -0
  145. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/conbus/conbus_datapoint_queryall_service.py +0 -0
  146. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/conbus/conbus_datapoint_service.py +0 -0
  147. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/conbus/conbus_discover_service.py +0 -0
  148. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/conbus/conbus_event_list_service.py +0 -0
  149. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/conbus/conbus_event_raw_service.py +0 -0
  150. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/conbus/conbus_output_service.py +0 -0
  151. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/conbus/conbus_raw_service.py +0 -0
  152. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/conbus/conbus_scan_service.py +0 -0
  153. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/conbus/write_config_service.py +0 -0
  154. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/homekit/__init__.py +0 -0
  155. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/homekit/homekit_cache_service.py +0 -0
  156. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/homekit/homekit_conbus_service.py +0 -0
  157. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/homekit/homekit_config_validator.py +0 -0
  158. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/homekit/homekit_conson_validator.py +0 -0
  159. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/homekit/homekit_dimminglight.py +0 -0
  160. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/homekit/homekit_dimminglight_service.py +0 -0
  161. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/homekit/homekit_hap_service.py +0 -0
  162. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/homekit/homekit_lightbulb.py +0 -0
  163. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/homekit/homekit_lightbulb_service.py +0 -0
  164. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/homekit/homekit_module_service.py +0 -0
  165. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/homekit/homekit_outlet.py +0 -0
  166. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/homekit/homekit_outlet_service.py +0 -0
  167. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/homekit/homekit_service.py +0 -0
  168. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/log_file_service.py +0 -0
  169. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/module_type_service.py +0 -0
  170. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/protocol/__init__.py +0 -0
  171. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/protocol/conbus_protocol.py +0 -0
  172. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/protocol/protocol_factory.py +0 -0
  173. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/protocol/telegram_protocol.py +0 -0
  174. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/reverse_proxy_service.py +0 -0
  175. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/server/__init__.py +0 -0
  176. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/server/base_server_service.py +0 -0
  177. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/server/cp20_server_service.py +0 -0
  178. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/server/device_service_factory.py +0 -0
  179. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/server/server_service.py +0 -0
  180. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/server/xp130_server_service.py +0 -0
  181. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/server/xp20_server_service.py +0 -0
  182. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/server/xp230_server_service.py +0 -0
  183. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/server/xp24_server_service.py +0 -0
  184. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/server/xp33_server_service.py +0 -0
  185. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/telegram/__init__.py +0 -0
  186. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/telegram/telegram_blink_service.py +0 -0
  187. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/telegram/telegram_checksum_service.py +0 -0
  188. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/telegram/telegram_datapoint_service.py +0 -0
  189. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/telegram/telegram_discover_service.py +0 -0
  190. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/telegram/telegram_link_number_service.py +0 -0
  191. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/telegram/telegram_output_service.py +0 -0
  192. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/telegram/telegram_service.py +0 -0
  193. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/services/telegram/telegram_version_service.py +0 -0
  194. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/utils/__init__.py +0 -0
  195. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/utils/checksum.py +0 -0
  196. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/utils/event_helper.py +0 -0
  197. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/utils/serialization.py +0 -0
  198. {conson_xp-1.19.0 → conson_xp-1.21.0}/src/xp/utils/time_utils.py +0 -0
  199. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/.coverage +0 -0
  200. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/__init__.py +0 -0
  201. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/conftest.py +0 -0
  202. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/.coverage +0 -0
  203. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/__init__.py +0 -0
  204. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/telegram_test_data.py +0 -0
  205. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/test_actiontable_integration.py +0 -0
  206. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/test_api/.coverage +0 -0
  207. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/test_api/__init__.py +0 -0
  208. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/test_blink_integration.py +0 -0
  209. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/test_checksum_integration.py +0 -0
  210. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/test_conbus_blink_integration.py +0 -0
  211. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/test_conbus_datapoint_integration.py +0 -0
  212. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/test_conbus_raw_integration.py +0 -0
  213. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/test_conbus_receive_integration.py +0 -0
  214. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/test_discovery_integration.py +0 -0
  215. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/test_event_telegram_integration.py +0 -0
  216. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/test_homekit_config_integration.py +0 -0
  217. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/test_link_number_integration.py +0 -0
  218. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/test_module_integration.py +0 -0
  219. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/test_output_integration.py +0 -0
  220. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/test_reverse_proxy_integration.py +0 -0
  221. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/test_system_reply_telegram_integration.py +0 -0
  222. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/test_version_integration.py +0 -0
  223. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/test_xp20_action_table_integration.py +0 -0
  224. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/integration/test_xp24_action_table_integration.py +0 -0
  225. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/__init__.py +0 -0
  226. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_api/__init__.py +0 -0
  227. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_cli/__init__.py +0 -0
  228. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_cli/test_click_tree.py +0 -0
  229. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_cli/test_conbus_actiontable_commands.py +0 -0
  230. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_cli/test_conbus_blink_commands.py +0 -0
  231. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_cli/test_datapoint_type_choice.py +0 -0
  232. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_cli/test_decorators.py +0 -0
  233. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_cli/test_error_handlers.py +0 -0
  234. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_cli/test_formatters.py +0 -0
  235. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_cli/test_serial_number_type.py +0 -0
  236. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_cli/test_system_function_choice.py +0 -0
  237. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_connection/__init__.py +0 -0
  238. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_connection/test_connection_init.py +0 -0
  239. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_connection/test_exceptions.py +0 -0
  240. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_encoding/__init__.py +0 -0
  241. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_encoding/test_latin1_edge_cases.py +0 -0
  242. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_models/__init__.py +0 -0
  243. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_models/test_conbus.py +0 -0
  244. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_models/test_conbus_client_send.py +0 -0
  245. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_models/test_conbus_discover.py +0 -0
  246. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_models/test_conbus_linknumber.py +0 -0
  247. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_models/test_event_telegram.py +0 -0
  248. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_models/test_log_entry.py +0 -0
  249. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_models/test_module_type.py +0 -0
  250. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_models/test_reply_telegram.py +0 -0
  251. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_models/test_system_telegram.py +0 -0
  252. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_models/test_system_telegram_enhancements.py +0 -0
  253. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_models/test_version_telegram.py +0 -0
  254. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_models/test_write_config_type.py +0 -0
  255. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_models/test_xp20_action_table.py +0 -0
  256. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_models/test_xp24_action_table.py +0 -0
  257. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_models/test_xp24_action_telegram.py +0 -0
  258. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/__init__.py +0 -0
  259. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_actiontable_serializer.py +0 -0
  260. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_actiontable_service.py +0 -0
  261. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_actiontable_upload_service.py +0 -0
  262. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_base_server_service.py +0 -0
  263. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_blink_service.py +0 -0
  264. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_checksum_service.py +0 -0
  265. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_conbus_blink_service.py +0 -0
  266. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_conbus_event_list_service.py +0 -0
  267. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_conbus_event_raw_service.py +0 -0
  268. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_conbus_raw_service.py +0 -0
  269. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_conbus_reverse_proxy_service.py +0 -0
  270. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_device_service_factory.py +0 -0
  271. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_discovery_service.py +0 -0
  272. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_homekit_cache_service.py +0 -0
  273. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_homekit_config_validator.py +0 -0
  274. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_homekit_conson_service.py +0 -0
  275. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_homekit_services.py +0 -0
  276. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_log_file_service.py +0 -0
  277. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_module_type_service.py +0 -0
  278. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_protocol.py +0 -0
  279. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_server_service.py +0 -0
  280. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_telegram_input_service.py +0 -0
  281. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_telegram_protocol.py +0 -0
  282. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_telegram_service.py +0 -0
  283. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_version_service.py +0 -0
  284. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_xp20_action_table_serializer.py +0 -0
  285. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_xp24_action_service.py +0 -0
  286. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_xp24_action_table_serializer.py +0 -0
  287. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_xp24_action_table_service.py +0 -0
  288. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_xp33_action_table_serializer.py +0 -0
  289. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_services/test_xp_server_services.py +0 -0
  290. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_utils/__init__.py +0 -0
  291. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_utils/test_checksum.py +0 -0
  292. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_utils/test_event_helper.py +0 -0
  293. {conson_xp-1.19.0 → conson_xp-1.21.0}/tests/unit/test_utils/test_serialization.py +0 -0
  294. {conson_xp-1.19.0 → conson_xp-1.21.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.19.0
3
+ Version: 1.21.0
4
4
  Summary: XP Protocol Communication Tools
5
5
  Author-Email: ldvchosal <ldvchosal@github.com>
6
6
  License: MIT License
@@ -48,6 +48,7 @@ Requires-Dist: punq>=0.7.0
48
48
  Requires-Dist: twisted>=25.5.0
49
49
  Requires-Dist: bubus>=1.5.6
50
50
  Requires-Dist: psygnal>=0.15.0
51
+ Requires-Dist: textual>=1.0.0
51
52
  Description-Content-Type: text/markdown
52
53
 
53
54
  # 🔌 XP Protocol Communication Tool
@@ -398,6 +399,10 @@ xp telegram parse
398
399
  xp telegram validate
399
400
  xp telegram version
400
401
 
402
+
403
+ xp term
404
+ xp term protocol
405
+
401
406
  <!-- END CLI HELP -->
402
407
  ```
403
408
  </details>
@@ -346,6 +346,10 @@ xp telegram parse
346
346
  xp telegram validate
347
347
  xp telegram version
348
348
 
349
+
350
+ xp term
351
+ xp term protocol
352
+
349
353
  <!-- END CLI HELP -->
350
354
  ```
351
355
  </details>
@@ -18,10 +18,11 @@ dependencies = [
18
18
  "twisted>=25.5.0",
19
19
  "bubus>=1.5.6",
20
20
  "psygnal>=0.15.0",
21
+ "textual>=1.0.0",
21
22
  ]
22
23
  requires-python = ">=3.11"
23
24
  readme = "README.md"
24
- version = "1.19.0"
25
+ version = "1.21.0"
25
26
 
26
27
  [project.license]
27
28
  file = "LICENSE"
@@ -56,6 +57,7 @@ dev = [
56
57
  "isort>=6.0.1",
57
58
  "absolufy-imports>=0.3.1",
58
59
  "pytest-asyncio>=1.2.0",
60
+ "types-docutils>=0.22.2.20251006",
59
61
  ]
60
62
 
61
63
  [tool.pytest.ini_options]
@@ -3,7 +3,7 @@
3
3
  conson-xp package.
4
4
  """
5
5
 
6
- __version__ = "1.19.0"
6
+ __version__ = "1.21.0"
7
7
  __manufacturer__ = "salchichon"
8
8
  __model__ = "xp.cli"
9
9
  __serial__ = "2025.09.23.000"
@@ -86,6 +86,8 @@ from xp.cli.commands.telegram.telegram_parse_commands import (
86
86
  validate_telegram,
87
87
  )
88
88
  from xp.cli.commands.telegram.telegram_version_commands import generate_version_request
89
+ from xp.cli.commands.term.term import term
90
+ from xp.cli.commands.term.term_commands import protocol_monitor
89
91
 
90
92
  __all__ = [
91
93
  # Main command groups (conbus excluded to avoid module shadowing)
@@ -109,7 +111,9 @@ __all__ = [
109
111
  "checksum",
110
112
  "homekit",
111
113
  "homekit_start",
114
+ "term",
112
115
  # Individual command functions
116
+ "protocol_monitor",
113
117
  "conbus_download_msactiontable",
114
118
  "conbus_download_actiontable",
115
119
  "send_blink_on_telegram",
@@ -56,4 +56,5 @@ def receive_telegrams(ctx: Context, timeout: float) -> None:
56
56
  ctx.obj.get("container").get_container().resolve(ConbusReceiveService)
57
57
  )
58
58
  with service:
59
- service.start(progress, on_finish, timeout)
59
+ service.init(progress, on_finish, timeout)
60
+ service.start_reactor()
@@ -0,0 +1,5 @@
1
+ """Term CLI commands package for TUI interfaces."""
2
+
3
+ from xp.cli.commands.term.term import term
4
+
5
+ __all__ = ["term"]
@@ -0,0 +1,12 @@
1
+ """Term CLI group definition for TUI commands."""
2
+
3
+ import click
4
+ from click_help_colors import HelpColorsGroup
5
+
6
+
7
+ @click.group(
8
+ cls=HelpColorsGroup, help_headers_color="yellow", help_options_color="green"
9
+ )
10
+ def term() -> None:
11
+ """Terminal UI commands for interactive monitoring and control."""
12
+ pass
@@ -0,0 +1,31 @@
1
+ """Term protocol CLI command for TUI monitoring."""
2
+
3
+ import click
4
+ from click import Context
5
+
6
+ from xp.cli.commands.term.term import term
7
+
8
+
9
+ @term.command("protocol")
10
+ @click.pass_context
11
+ def protocol_monitor(ctx: Context) -> None:
12
+ r"""Start TUI for real-time protocol monitoring.
13
+
14
+ Displays live RX/TX telegram stream from Conbus server
15
+ in an interactive terminal interface.
16
+
17
+ Args:
18
+ ctx: Click context object.
19
+
20
+ Examples:
21
+ \b
22
+ xp term protocol
23
+ """
24
+ from xp.term.app import ProtocolMonitorApp
25
+
26
+ # Resolve ServiceContainer from context
27
+ container = ctx.obj.get("container").get_container()
28
+
29
+ # Initialize and run Textual app
30
+ app = ProtocolMonitorApp(container=container)
31
+ app.run()
@@ -1,7 +1,5 @@
1
1
  """XP CLI tool entry point with modular command structure."""
2
2
 
3
- import logging
4
-
5
3
  import click
6
4
  from click_help_colors import HelpColorsGroup
7
5
 
@@ -16,8 +14,10 @@ from xp.cli.commands.server.server_commands import server
16
14
 
17
15
  # Import command groups from modular structure
18
16
  from xp.cli.commands.telegram.telegram_parse_commands import telegram
17
+ from xp.cli.commands.term.term import term
19
18
  from xp.cli.utils.click_tree import add_tree_command
20
19
  from xp.utils.dependencies import ServiceContainer
20
+ from xp.utils.logging import LoggerService
21
21
 
22
22
 
23
23
  @click.group(
@@ -31,44 +31,15 @@ def cli(ctx: click.Context) -> None:
31
31
  Args:
32
32
  ctx: Click context object for passing state between commands.
33
33
  """
34
- # Configure logging with thread information
35
- log_format = "%(asctime)s - [%(threadName)s-%(thread)d] - %(levelname)s - %(name)s - %(message)s"
36
- date_format = "%H:%M:%S"
37
-
38
- # Force format on root logger and all handlers
39
- formatter = logging.Formatter(log_format, datefmt=date_format)
40
- root_logger = logging.getLogger()
41
- root_logger.setLevel(logging.DEBUG)
42
-
43
- # Update all existing handlers or create new one
44
- if root_logger.handlers:
45
- for handler in root_logger.handlers:
46
- handler.setFormatter(formatter)
47
- else:
48
- handler = logging.StreamHandler()
49
- handler.setFormatter(formatter)
50
- root_logger.addHandler(handler)
51
-
52
- # Suppress pyhap.hap_protocol logs
53
-
54
- # bubus
55
- logging.getLogger("bubus").setLevel(logging.WARNING)
56
-
57
- # xp
58
- logging.getLogger("xp").setLevel(logging.DEBUG)
59
- logging.getLogger("xp.services.homekit").setLevel(logging.DEBUG)
60
-
61
- # pyhap
62
- logging.getLogger("pyhap").setLevel(logging.WARNING)
63
- logging.getLogger("pyhap.hap_handler").setLevel(logging.WARNING)
64
- logging.getLogger("pyhap.hap_protocol").setLevel(logging.WARNING)
65
- # logging.getLogger('pyhap.accessory_driver').setLevel(logging.WARNING)
34
+ container = ServiceContainer()
35
+ logger_config = container.get_container().resolve(LoggerService)
36
+ logger_config.setup()
66
37
 
67
38
  # Initialize the service container and store it in the context
68
39
  ctx.ensure_object(dict)
69
40
  # Only create a new container if one wasn't provided (e.g., for testing)
70
41
  if "container" not in ctx.obj:
71
- ctx.obj["container"] = ServiceContainer()
42
+ ctx.obj["container"] = container
72
43
 
73
44
 
74
45
  # Register all command groups
@@ -79,6 +50,7 @@ cli.add_command(module)
79
50
  cli.add_command(file)
80
51
  cli.add_command(server)
81
52
  cli.add_command(reverse_proxy)
53
+ cli.add_command(term)
82
54
 
83
55
  # Add the tree command
84
56
  add_tree_command(cli)
@@ -49,6 +49,7 @@ class ConbusClientConfig(BaseModel):
49
49
  except FileNotFoundError:
50
50
  logger.error(f"File {file_path} does not exist, loading default")
51
51
  return cls()
52
+
52
53
  except yaml.YAMLError:
53
54
  logger.error(f"File {file_path} is not valid")
54
55
  # Return default config if YAML parsing fails
@@ -0,0 +1,107 @@
1
+ """Logger configuration models for XP application."""
2
+
3
+ import logging
4
+ from pathlib import Path
5
+ from typing import Dict, Union
6
+
7
+ import yaml
8
+ from pydantic import BaseModel, Field, field_validator
9
+
10
+
11
+ class LoggingConfig(BaseModel):
12
+ """Logging configuration.
13
+
14
+ Attributes:
15
+ path: log folder.
16
+ default_level: DEBUG, WARNING, INFO, ERROR, CRITICAL.
17
+ levels: Per-module log level overrides.
18
+ max_bytes: Maximum size in bytes before rotating (default: 1MB).
19
+ backup_count: Number of backup files to keep (default: 365).
20
+ log_format: Log message format string.
21
+ date_format: Date format string for timestamps.
22
+ """
23
+
24
+ path: str = "log"
25
+ default_level: str = "DEBUG"
26
+ levels: Dict[str, int] = {
27
+ "xp": logging.DEBUG,
28
+ "xp.services.homekit": logging.WARNING,
29
+ "xp.services.server": logging.WARNING,
30
+ }
31
+ max_bytes: int = 1024 * 1024 # 1MB
32
+ backup_count: int = 365
33
+ log_format: str = (
34
+ "%(asctime)s - [%(threadName)s-%(thread)d] - %(levelname)s - %(name)s - %(message)s"
35
+ )
36
+ date_format: str = "%H:%M:%S"
37
+
38
+ @field_validator("levels", mode="before")
39
+ @classmethod
40
+ def convert_level_names(cls, v: Dict[str, Union[str, int]]) -> Dict[str, int]:
41
+ """Convert string level names to numeric values.
42
+
43
+ Args:
44
+ v: Dictionary with string or int log levels.
45
+
46
+ Returns:
47
+ Dictionary with numeric log levels.
48
+
49
+ Raises:
50
+ ValueError: If an invalid log level name is provided.
51
+ """
52
+ level_map = {
53
+ "DEBUG": logging.DEBUG,
54
+ "INFO": logging.INFO,
55
+ "WARNING": logging.WARNING,
56
+ "ERROR": logging.ERROR,
57
+ "CRITICAL": logging.CRITICAL,
58
+ }
59
+
60
+ result = {}
61
+ for module, level in v.items():
62
+ if isinstance(level, str):
63
+ level_upper = level.upper()
64
+ if level_upper not in level_map:
65
+ raise ValueError(
66
+ f"Invalid log level '{level}' for module '{module}'. "
67
+ f"Must be one of: {', '.join(level_map.keys())}"
68
+ )
69
+ result[module] = level_map[level_upper]
70
+ else:
71
+ result[module] = level
72
+ return result
73
+
74
+
75
+ class ConbusLoggerConfig(BaseModel):
76
+ """Logging configuration.
77
+
78
+ Attributes:
79
+ log: LoggingConfig instance for logging settings.
80
+ """
81
+
82
+ log: LoggingConfig = Field(default_factory=LoggingConfig)
83
+
84
+ @classmethod
85
+ def from_yaml(cls, file_path: str) -> "ConbusLoggerConfig":
86
+ """Load configuration from YAML file.
87
+
88
+ Args:
89
+ file_path: Path to the YAML configuration file.
90
+
91
+ Returns:
92
+ ConbusClientConfig instance loaded from file or default config.
93
+ """
94
+ logger = logging.getLogger(__name__)
95
+ try:
96
+ with Path(file_path).open("r") as file:
97
+ data = yaml.safe_load(file)
98
+ return cls(**data)
99
+
100
+ except FileNotFoundError:
101
+ logger.error(f"File {file_path} does not exist, loading default")
102
+ return cls()
103
+
104
+ except yaml.YAMLError:
105
+ logger.error(f"File {file_path} is not valid")
106
+ # Return default config if YAML parsing fails
107
+ return cls()
@@ -0,0 +1,11 @@
1
+ """Terminal UI models."""
2
+
3
+ from xp.models.term.protocol_keys_config import (
4
+ ProtocolKeyConfig,
5
+ ProtocolKeysConfig,
6
+ )
7
+
8
+ __all__ = [
9
+ "ProtocolKeyConfig",
10
+ "ProtocolKeysConfig",
11
+ ]
@@ -0,0 +1,45 @@
1
+ """Protocol keys configuration model."""
2
+
3
+ from pathlib import Path
4
+ from typing import Dict
5
+
6
+ import yaml
7
+ from pydantic import BaseModel, Field
8
+
9
+
10
+ class ProtocolKeyConfig(BaseModel):
11
+ """Configuration for a single protocol key.
12
+
13
+ Attributes:
14
+ name: Human-readable command name.
15
+ telegrams: List of raw telegram strings to send (without angle brackets).
16
+ """
17
+
18
+ name: str = Field(..., description="Human-readable command name")
19
+ telegrams: list[str] = Field(..., description="List of raw telegram strings")
20
+
21
+
22
+ class ProtocolKeysConfig(BaseModel):
23
+ """Protocol keys configuration.
24
+
25
+ Attributes:
26
+ protocol: Dictionary mapping key to protocol configuration.
27
+ """
28
+
29
+ protocol: Dict[str, ProtocolKeyConfig] = Field(
30
+ default_factory=dict, description="Protocol key mappings"
31
+ )
32
+
33
+ @classmethod
34
+ def from_yaml(cls, config_path: Path) -> "ProtocolKeysConfig":
35
+ """Load protocol keys from YAML file.
36
+
37
+ Args:
38
+ config_path: Path to YAML configuration file.
39
+
40
+ Returns:
41
+ ProtocolKeysConfig instance.
42
+ """
43
+ with config_path.open("r") as f:
44
+ data = yaml.safe_load(f)
45
+ return cls(**data)
@@ -1,51 +1,55 @@
1
1
  """Conbus Receive Service for receiving telegrams from Conbus servers.
2
2
 
3
- This service uses ConbusProtocol to provide receive-only functionality,
3
+ This service uses ConbusEventProtocol to provide receive-only functionality,
4
4
  allowing clients to receive waiting event telegrams using empty telegram sends.
5
5
  """
6
6
 
7
+ import asyncio
7
8
  import logging
8
- from typing import Callable, Optional
9
+ from typing import Any, Callable, Optional
9
10
 
10
- from twisted.internet.posixbase import PosixReactorBase
11
-
12
- from xp.models import ConbusClientConfig
13
11
  from xp.models.conbus.conbus_receive import ConbusReceiveResponse
14
12
  from xp.models.protocol.conbus_protocol import TelegramReceivedEvent
15
- from xp.services.protocol import ConbusProtocol
13
+ from xp.services.protocol.conbus_event_protocol import ConbusEventProtocol
16
14
 
17
15
 
18
- class ConbusReceiveService(ConbusProtocol):
16
+ class ConbusReceiveService:
19
17
  """
20
18
  Service for receiving telegrams from Conbus servers.
21
19
 
22
- Uses ConbusProtocol to provide receive-only functionality
20
+ Uses ConbusEventProtocol to provide receive-only functionality
23
21
  for collecting waiting event telegrams from the server.
22
+
23
+ Attributes:
24
+ conbus_protocol: Protocol instance for Conbus communication.
24
25
  """
25
26
 
26
- def __init__(
27
- self,
28
- cli_config: ConbusClientConfig,
29
- reactor: PosixReactorBase,
30
- ) -> None:
27
+ conbus_protocol: ConbusEventProtocol
28
+
29
+ def __init__(self, conbus_protocol: ConbusEventProtocol) -> None:
31
30
  """Initialize the Conbus receive service.
32
31
 
33
32
  Args:
34
- cli_config: Conbus client configuration.
35
- reactor: Twisted reactor instance.
33
+ conbus_protocol: ConbusEventProtocol instance.
36
34
  """
37
- super().__init__(cli_config, reactor)
38
35
  self.progress_callback: Optional[Callable[[str], None]] = None
39
36
  self.finish_callback: Optional[Callable[[ConbusReceiveResponse], None]] = None
40
37
  self.receive_response: ConbusReceiveResponse = ConbusReceiveResponse(
41
38
  success=True
42
39
  )
43
40
 
41
+ self.conbus_protocol: ConbusEventProtocol = conbus_protocol
42
+ self.conbus_protocol.on_connection_made.connect(self.connection_made)
43
+ self.conbus_protocol.on_telegram_sent.connect(self.telegram_sent)
44
+ self.conbus_protocol.on_telegram_received.connect(self.telegram_received)
45
+ self.conbus_protocol.on_timeout.connect(self.timeout)
46
+ self.conbus_protocol.on_failed.connect(self.failed)
47
+
44
48
  # Set up logging
45
49
  self.logger = logging.getLogger(__name__)
46
50
 
47
- def connection_established(self) -> None:
48
- """Handle connection established event."""
51
+ def connection_made(self) -> None:
52
+ """Handle connection made event."""
49
53
  self.logger.debug("Connection established, waiting for telegrams.")
50
54
 
51
55
  def telegram_sent(self, telegram_sent: str) -> None:
@@ -70,17 +74,13 @@ class ConbusReceiveService(ConbusProtocol):
70
74
  self.receive_response.received_telegrams = []
71
75
  self.receive_response.received_telegrams.append(telegram_received.frame)
72
76
 
73
- def timeout(self) -> bool:
74
- """Handle timeout event to stop receiving.
75
-
76
- Returns:
77
- False to stop the reactor.
78
- """
79
- self.logger.info("Receive stopped after: %ss", self.timeout_seconds)
77
+ def timeout(self) -> None:
78
+ """Handle timeout event to stop receiving."""
79
+ timeout = self.conbus_protocol.timeout_seconds
80
+ self.logger.info("Receive stopped after: %ss", timeout)
80
81
  self.receive_response.success = True
81
82
  if self.finish_callback:
82
83
  self.finish_callback(self.receive_response)
83
- return False
84
84
 
85
85
  def failed(self, message: str) -> None:
86
86
  """Handle failed connection event.
@@ -94,22 +94,50 @@ class ConbusReceiveService(ConbusProtocol):
94
94
  if self.finish_callback:
95
95
  self.finish_callback(self.receive_response)
96
96
 
97
- def start(
97
+ def init(
98
98
  self,
99
99
  progress_callback: Callable[[str], None],
100
100
  finish_callback: Callable[[ConbusReceiveResponse], None],
101
101
  timeout_seconds: Optional[float] = None,
102
+ event_loop: Optional[asyncio.AbstractEventLoop] = None,
102
103
  ) -> None:
103
- """Run reactor in dedicated thread with its own event loop.
104
+ """Setup callbacks and timeout for receiving telegrams.
104
105
 
105
106
  Args:
106
107
  progress_callback: Callback for each received telegram.
107
108
  finish_callback: Callback when receiving completes.
108
109
  timeout_seconds: Optional timeout in seconds.
110
+ event_loop: Optional event loop to use for async operations.
109
111
  """
110
112
  self.logger.info("Starting receive")
111
113
  if timeout_seconds:
112
- self.timeout_seconds = timeout_seconds
114
+ self.conbus_protocol.timeout_seconds = timeout_seconds
113
115
  self.progress_callback = progress_callback
114
116
  self.finish_callback = finish_callback
115
- self.start_reactor()
117
+
118
+ if event_loop:
119
+ self.conbus_protocol.set_event_loop(event_loop)
120
+
121
+ def start_reactor(self) -> None:
122
+ """Start the reactor."""
123
+ self.conbus_protocol.start_reactor()
124
+
125
+ def __enter__(self) -> "ConbusReceiveService":
126
+ """Enter context manager.
127
+
128
+ Returns:
129
+ Self for context manager protocol.
130
+ """
131
+ # Reset state for singleton reuse
132
+ self.receive_response = ConbusReceiveResponse(success=True)
133
+ return self
134
+
135
+ def __exit__(
136
+ self, _exc_type: Optional[type], _exc_val: Optional[Exception], _exc_tb: Any
137
+ ) -> None:
138
+ """Exit context manager and disconnect signals."""
139
+ self.conbus_protocol.on_connection_made.disconnect(self.connection_made)
140
+ self.conbus_protocol.on_telegram_sent.disconnect(self.telegram_sent)
141
+ self.conbus_protocol.on_telegram_received.disconnect(self.telegram_received)
142
+ self.conbus_protocol.on_timeout.disconnect(self.timeout)
143
+ self.conbus_protocol.on_failed.disconnect(self.failed)
@@ -3,6 +3,7 @@
3
3
  This module implements the Twisted protocol for Conbus communication.
4
4
  """
5
5
 
6
+ import asyncio
6
7
  import logging
7
8
  from queue import SimpleQueue
8
9
  from random import randint
@@ -208,6 +209,14 @@ class ConbusEventProtocol(protocol.Protocol, protocol.ClientFactory):
208
209
  f"F{system_function.value}"
209
210
  f"D{data_value}"
210
211
  )
212
+ self.send_raw_telegram(payload)
213
+
214
+ def send_raw_telegram(self, payload: str) -> None:
215
+ """Send telegram with specified parameters.
216
+
217
+ Args:
218
+ payload: Telegram to send.
219
+ """
211
220
  self.telegram_queue.put_nowait(payload.encode())
212
221
  self.call_later(0.0, self.start_queue_manager)
213
222
 
@@ -302,14 +311,21 @@ class ConbusEventProtocol(protocol.Protocol, protocol.ClientFactory):
302
311
  self.logger.info("Stopping reactor")
303
312
  self._reactor.stop()
304
313
 
305
- def start_reactor(self) -> None:
306
- """Start the reactor if it's running."""
307
- # Connect to TCP server
314
+ def connect(self) -> None:
315
+ """Connect to TCP server."""
308
316
  self.logger.info(
309
317
  f"Connecting to TCP server {self.cli_config.ip}:{self.cli_config.port}"
310
318
  )
311
319
  self._reactor.connectTCP(self.cli_config.ip, self.cli_config.port, self)
312
320
 
321
+ def disconnect(self) -> None:
322
+ """Disconnect from TCP server."""
323
+ self.logger.info("Disconnecting TCP server")
324
+ self._reactor.disconnectAll()
325
+
326
+ def start_reactor(self) -> None:
327
+ """Start the reactor if it's running."""
328
+ self.connect()
313
329
  # Run the reactor (which now uses asyncio underneath)
314
330
  self.logger.info("Starting reactor event loop.")
315
331
  self._reactor.run()
@@ -340,6 +356,23 @@ class ConbusEventProtocol(protocol.Protocol, protocol.ClientFactory):
340
356
  later = randint(10, 80) / 100
341
357
  self.call_later(later, self.process_telegram_queue)
342
358
 
359
+ def set_event_loop(self, event_loop: asyncio.AbstractEventLoop) -> None:
360
+ """Change the event loop.
361
+
362
+ Args:
363
+ event_loop: the event loop instance.
364
+ """
365
+ reactor = self._reactor
366
+ if hasattr(reactor, "_asyncioEventloop"):
367
+ reactor._asyncioEventloop = event_loop
368
+
369
+ # Set reactor to running state
370
+ if not reactor.running:
371
+ reactor.running = True
372
+ if hasattr(reactor, "startRunning"):
373
+ reactor.startRunning()
374
+ self.logger.info("Set reactor to running state")
375
+
343
376
  def __enter__(self) -> "ConbusEventProtocol":
344
377
  """Enter context manager.
345
378
 
@@ -0,0 +1 @@
1
+ """TUI (Terminal User Interface) module for XP."""