conson-xp 1.51.1__tar.gz → 1.52.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 (328) hide show
  1. {conson_xp-1.51.1 → conson_xp-1.52.0}/PKG-INFO +1 -1
  2. {conson_xp-1.51.1 → conson_xp-1.52.0}/pyproject.toml +1 -1
  3. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/__init__.py +1 -1
  4. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/homekit/homekit_config.py +4 -0
  5. conson_xp-1.52.0/src/xp/services/term/homekit_accessory_driver.py +168 -0
  6. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/term/homekit_service.py +70 -0
  7. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/homekit.py +6 -6
  8. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/utils/dependencies.py +10 -0
  9. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_conbus.py +1 -1
  10. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_homekit_service.py +27 -4
  11. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_tui/test_homekit_app.py +7 -5
  12. {conson_xp-1.51.1 → conson_xp-1.52.0}/LICENSE +0 -0
  13. {conson_xp-1.51.1 → conson_xp-1.52.0}/README.md +0 -0
  14. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/__init__.py +0 -0
  15. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/__main__.py +0 -0
  16. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/__init__.py +0 -0
  17. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/__init__.py +0 -0
  18. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus.py +0 -0
  19. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_actiontable_commands.py +0 -0
  20. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_autoreport_commands.py +0 -0
  21. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_blink_commands.py +0 -0
  22. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_config_commands.py +0 -0
  23. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_custom_commands.py +0 -0
  24. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_datapoint_commands.py +0 -0
  25. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_discover_commands.py +0 -0
  26. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_event_commands.py +0 -0
  27. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_export_commands.py +0 -0
  28. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_lightlevel_commands.py +0 -0
  29. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_linknumber_commands.py +0 -0
  30. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_modulenumber_commands.py +0 -0
  31. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_msactiontable_commands.py +0 -0
  32. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_output_commands.py +0 -0
  33. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_raw_commands.py +0 -0
  34. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_receive_commands.py +0 -0
  35. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_scan_commands.py +0 -0
  36. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/file_commands.py +0 -0
  37. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/homekit/__init__.py +0 -0
  38. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/homekit/homekit.py +0 -0
  39. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/homekit/homekit_start_commands.py +0 -0
  40. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/module_commands.py +0 -0
  41. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/reverse_proxy_commands.py +0 -0
  42. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/server/__init__.py +0 -0
  43. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/server/server_commands.py +0 -0
  44. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/__init__.py +0 -0
  45. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram.py +0 -0
  46. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_blink_commands.py +0 -0
  47. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_checksum_commands.py +0 -0
  48. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_discover_commands.py +0 -0
  49. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_linknumber_commands.py +0 -0
  50. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_parse_commands.py +0 -0
  51. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_version_commands.py +0 -0
  52. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/term/__init__.py +0 -0
  53. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/term/term.py +0 -0
  54. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/term/term_commands.py +0 -0
  55. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/main.py +0 -0
  56. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/utils/__init__.py +0 -0
  57. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/utils/click_tree.py +0 -0
  58. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/utils/datapoint_type_choice.py +0 -0
  59. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/utils/decorators.py +0 -0
  60. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/utils/error_handlers.py +0 -0
  61. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/utils/formatters.py +0 -0
  62. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/utils/module_type_choice.py +0 -0
  63. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/utils/serial_number_type.py +0 -0
  64. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/utils/system_function_choice.py +0 -0
  65. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/__init__.py +0 -0
  66. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/actiontable/__init__.py +0 -0
  67. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/actiontable/actiontable.py +0 -0
  68. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/actiontable/actiontable_type.py +0 -0
  69. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/actiontable/msactiontable_xp20.py +0 -0
  70. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/actiontable/msactiontable_xp24.py +0 -0
  71. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/actiontable/msactiontable_xp33.py +0 -0
  72. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/__init__.py +0 -0
  73. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus.py +0 -0
  74. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_autoreport.py +0 -0
  75. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_blink.py +0 -0
  76. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_client_config.py +0 -0
  77. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_connection_status.py +0 -0
  78. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_custom.py +0 -0
  79. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_datapoint.py +0 -0
  80. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_discover.py +0 -0
  81. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_event_list.py +0 -0
  82. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_event_raw.py +0 -0
  83. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_export.py +0 -0
  84. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_lightlevel.py +0 -0
  85. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_linknumber.py +0 -0
  86. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_logger_config.py +0 -0
  87. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_output.py +0 -0
  88. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_raw.py +0 -0
  89. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_receive.py +0 -0
  90. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_writeconfig.py +0 -0
  91. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/config/__init__.py +0 -0
  92. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/config/conson_module_config.py +0 -0
  93. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/homekit/__init__.py +0 -0
  94. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/homekit/homekit_accessory.py +0 -0
  95. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/log_entry.py +0 -0
  96. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/protocol/__init__.py +0 -0
  97. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/protocol/conbus_protocol.py +0 -0
  98. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/response.py +0 -0
  99. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/__init__.py +0 -0
  100. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/action_type.py +0 -0
  101. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/datapoint_type.py +0 -0
  102. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/event_telegram.py +0 -0
  103. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/event_type.py +0 -0
  104. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/input_action_type.py +0 -0
  105. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/input_type.py +0 -0
  106. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/module_type.py +0 -0
  107. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/module_type_code.py +0 -0
  108. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/output_telegram.py +0 -0
  109. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/reply_telegram.py +0 -0
  110. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/system_function.py +0 -0
  111. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/system_telegram.py +0 -0
  112. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/telegram.py +0 -0
  113. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/telegram_type.py +0 -0
  114. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/timeparam_type.py +0 -0
  115. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/term/__init__.py +0 -0
  116. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/term/accessory_state.py +0 -0
  117. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/term/connection_state.py +0 -0
  118. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/term/module_state.py +0 -0
  119. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/term/protocol_keys_config.py +0 -0
  120. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/term/status_message.py +0 -0
  121. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/term/telegram_display.py +0 -0
  122. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/write_config_type.py +0 -0
  123. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/__init__.py +0 -0
  124. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/actiontable/__init__.py +0 -0
  125. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/actiontable/actiontable_serializer.py +0 -0
  126. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/actiontable/download_state_machine.py +0 -0
  127. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/actiontable/msactiontable_serializer.py +0 -0
  128. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/actiontable/msactiontable_xp20_serializer.py +0 -0
  129. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/actiontable/msactiontable_xp24_serializer.py +0 -0
  130. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/actiontable/msactiontable_xp33_serializer.py +0 -0
  131. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/actiontable/serializer_protocol.py +0 -0
  132. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/__init__.py +0 -0
  133. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/actiontable/__init__.py +0 -0
  134. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/actiontable/actiontable_download_service.py +0 -0
  135. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/actiontable/actiontable_list_service.py +0 -0
  136. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/actiontable/actiontable_show_service.py +0 -0
  137. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/actiontable/actiontable_upload_service.py +0 -0
  138. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_blink_all_service.py +0 -0
  139. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_blink_service.py +0 -0
  140. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_custom_service.py +0 -0
  141. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_datapoint_queryall_service.py +0 -0
  142. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_datapoint_service.py +0 -0
  143. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_discover_service.py +0 -0
  144. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_event_list_service.py +0 -0
  145. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_event_raw_service.py +0 -0
  146. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_export_actiontable_service.py +0 -0
  147. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_export_service.py +0 -0
  148. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_output_service.py +0 -0
  149. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_raw_service.py +0 -0
  150. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_receive_service.py +0 -0
  151. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_scan_service.py +0 -0
  152. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/write_config_service.py +0 -0
  153. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/__init__.py +0 -0
  154. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_cache_service.py +0 -0
  155. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_conbus_service.py +0 -0
  156. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_config_validator.py +0 -0
  157. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_conson_validator.py +0 -0
  158. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_dimminglight.py +0 -0
  159. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_dimminglight_service.py +0 -0
  160. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_hap_service.py +0 -0
  161. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_lightbulb.py +0 -0
  162. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_lightbulb_service.py +0 -0
  163. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_module_service.py +0 -0
  164. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_outlet.py +0 -0
  165. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_outlet_service.py +0 -0
  166. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_service.py +0 -0
  167. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/log_file_service.py +0 -0
  168. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/module_type_service.py +0 -0
  169. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/protocol/__init__.py +0 -0
  170. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/protocol/conbus_event_protocol.py +0 -0
  171. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/protocol/protocol_factory.py +0 -0
  172. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/protocol/telegram_protocol.py +0 -0
  173. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/reverse_proxy_service.py +0 -0
  174. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/server/__init__.py +0 -0
  175. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/server/base_server_service.py +0 -0
  176. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/server/client_buffer_manager.py +0 -0
  177. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/server/cp20_server_service.py +0 -0
  178. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/server/device_service_factory.py +0 -0
  179. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/server/server_service.py +0 -0
  180. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/server/xp130_server_service.py +0 -0
  181. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/server/xp20_server_service.py +0 -0
  182. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/server/xp230_server_service.py +0 -0
  183. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/server/xp24_server_service.py +0 -0
  184. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/server/xp33_server_service.py +0 -0
  185. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/telegram/__init__.py +0 -0
  186. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_blink_service.py +0 -0
  187. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_checksum_service.py +0 -0
  188. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_datapoint_service.py +0 -0
  189. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_discover_service.py +0 -0
  190. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_link_number_service.py +0 -0
  191. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_output_service.py +0 -0
  192. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_service.py +0 -0
  193. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_version_service.py +0 -0
  194. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/term/__init__.py +0 -0
  195. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/term/protocol_monitor_service.py +0 -0
  196. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/term/state_monitor_service.py +0 -0
  197. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/__init__.py +0 -0
  198. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/homekit.tcss +0 -0
  199. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/protocol.py +0 -0
  200. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/protocol.tcss +0 -0
  201. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/state.py +0 -0
  202. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/state.tcss +0 -0
  203. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/widgets/__init__.py +0 -0
  204. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/widgets/help_menu.py +0 -0
  205. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/widgets/modules_list.py +0 -0
  206. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/widgets/protocol_log.py +0 -0
  207. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/widgets/room_list.py +0 -0
  208. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/widgets/status_footer.py +0 -0
  209. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/utils/__init__.py +0 -0
  210. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/utils/checksum.py +0 -0
  211. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/utils/event_helper.py +0 -0
  212. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/utils/logging.py +0 -0
  213. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/utils/serialization.py +0 -0
  214. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/utils/state_machine.py +0 -0
  215. {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/utils/time_utils.py +0 -0
  216. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/.coverage +0 -0
  217. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/__init__.py +0 -0
  218. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/conftest.py +0 -0
  219. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/.coverage +0 -0
  220. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/__init__.py +0 -0
  221. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/telegram_test_data.py +0 -0
  222. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_actiontable_integration.py +0 -0
  223. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_api/.coverage +0 -0
  224. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_api/__init__.py +0 -0
  225. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_blink_integration.py +0 -0
  226. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_checksum_integration.py +0 -0
  227. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_conbus_blink_integration.py +0 -0
  228. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_conbus_datapoint_integration.py +0 -0
  229. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_conbus_raw_integration.py +0 -0
  230. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_conbus_receive_integration.py +0 -0
  231. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_discovery_integration.py +0 -0
  232. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_event_telegram_integration.py +0 -0
  233. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_homekit_config_integration.py +0 -0
  234. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_link_number_integration.py +0 -0
  235. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_module_integration.py +0 -0
  236. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_output_integration.py +0 -0
  237. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_reverse_proxy_integration.py +0 -0
  238. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_system_reply_telegram_integration.py +0 -0
  239. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_term_logging_integration.py +0 -0
  240. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_version_integration.py +0 -0
  241. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_xp20_action_table_integration.py +0 -0
  242. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_xp24_action_table_integration.py +0 -0
  243. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/__init__.py +0 -0
  244. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_api/__init__.py +0 -0
  245. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/__init__.py +0 -0
  246. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/test_click_tree.py +0 -0
  247. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/test_conbus_actiontable_commands.py +0 -0
  248. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/test_conbus_blink_commands.py +0 -0
  249. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/test_conbus_msactiontable_upload_commands.py +0 -0
  250. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/test_datapoint_type_choice.py +0 -0
  251. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/test_decorators.py +0 -0
  252. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/test_error_handlers.py +0 -0
  253. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/test_formatters.py +0 -0
  254. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/test_serial_number_type.py +0 -0
  255. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/test_system_function_choice.py +0 -0
  256. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/test_term_commands.py +0 -0
  257. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_encoding/__init__.py +0 -0
  258. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_encoding/test_latin1_edge_cases.py +0 -0
  259. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/__init__.py +0 -0
  260. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_conbus_client_send.py +0 -0
  261. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_conbus_discover.py +0 -0
  262. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_conbus_linknumber.py +0 -0
  263. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_event_telegram.py +0 -0
  264. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_log_entry.py +0 -0
  265. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_logger_config.py +0 -0
  266. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_module_type.py +0 -0
  267. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_reply_telegram.py +0 -0
  268. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_system_telegram.py +0 -0
  269. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_system_telegram_enhancements.py +0 -0
  270. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_term/__init__.py +0 -0
  271. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_term/test_accessory_state.py +0 -0
  272. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_version_telegram.py +0 -0
  273. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_write_config_type.py +0 -0
  274. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_xp20_action_table.py +0 -0
  275. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_xp24_action_table.py +0 -0
  276. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_xp24_action_table_short_format.py +0 -0
  277. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_xp24_action_telegram.py +0 -0
  278. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/__init__.py +0 -0
  279. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_actiontable_download_service.py +0 -0
  280. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_actiontable_serializer.py +0 -0
  281. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_actiontable_service.py +0 -0
  282. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_actiontable_upload_service.py +0 -0
  283. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_base_server_service.py +0 -0
  284. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_blink_service.py +0 -0
  285. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_checksum_service.py +0 -0
  286. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_client_buffer_manager.py +0 -0
  287. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_blink_service.py +0 -0
  288. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_event_list_service.py +0 -0
  289. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_event_protocol.py +0 -0
  290. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_event_raw_service.py +0 -0
  291. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_output_service.py +0 -0
  292. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_raw_service.py +0 -0
  293. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_receive_service.py +0 -0
  294. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_reverse_proxy_service.py +0 -0
  295. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_scan_service.py +0 -0
  296. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_device_service_factory.py +0 -0
  297. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_discovery_service.py +0 -0
  298. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_homekit_cache_service.py +0 -0
  299. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_homekit_config_validator.py +0 -0
  300. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_homekit_conson_service.py +0 -0
  301. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_homekit_services.py +0 -0
  302. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_log_file_service.py +0 -0
  303. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_module_type_service.py +0 -0
  304. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_protocol.py +0 -0
  305. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_protocol_monitor_service.py +0 -0
  306. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_server_service.py +0 -0
  307. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_state_monitor_service.py +0 -0
  308. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_telegram_input_service.py +0 -0
  309. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_telegram_output_service.py +0 -0
  310. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_telegram_protocol.py +0 -0
  311. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_telegram_service.py +0 -0
  312. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_version_service.py +0 -0
  313. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_xp20_action_table_serializer.py +0 -0
  314. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_xp24_action_service.py +0 -0
  315. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_xp24_action_table_serializer.py +0 -0
  316. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_xp24_action_table_service.py +0 -0
  317. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_xp33_action_table_serializer.py +0 -0
  318. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_xp33_short_format.py +0 -0
  319. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_xp_server_services.py +0 -0
  320. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_tui/__init__.py +0 -0
  321. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_tui/test_protocol_log.py +0 -0
  322. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_tui/test_room_list.py +0 -0
  323. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_utils/__init__.py +0 -0
  324. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_utils/test_checksum.py +0 -0
  325. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_utils/test_event_helper.py +0 -0
  326. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_utils/test_logging.py +0 -0
  327. {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_utils/test_serialization.py +0 -0
  328. {conson_xp-1.51.1 → conson_xp-1.52.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.51.1
3
+ Version: 1.52.0
4
4
  Summary: XP Protocol Communication Tools
5
5
  Author-Email: ldvchosal <ldvchosal@github.com>
6
6
  License: MIT License
@@ -23,7 +23,7 @@ dependencies = [
23
23
  ]
24
24
  requires-python = ">=3.11"
25
25
  readme = "README.md"
26
- version = "1.51.1"
26
+ version = "1.52.0"
27
27
 
28
28
  [project.license]
29
29
  file = "LICENSE"
@@ -4,7 +4,7 @@ XP CLI tool for remote console bus operations.
4
4
  conson-xp package.
5
5
  """
6
6
 
7
- __version__ = "1.51.1"
7
+ __version__ = "1.52.0"
8
8
  __manufacturer__ = "salchichon"
9
9
  __model__ = "xp.cli"
10
10
  __serial__ = "2025.09.23.000"
@@ -16,10 +16,14 @@ class NetworkConfig(BaseModel):
16
16
  Attributes:
17
17
  ip: IP address for the network connection.
18
18
  port: Port number for the network connection.
19
+ pincode: HomeKit pairing code (format: XXX-XX-XXX).
20
+ accessory_state_file: Path to file for persisting accessory state.
19
21
  """
20
22
 
21
23
  ip: Union[IPvAnyAddress, IPv4Address, IPv6Address, str] = "127.0.0.1"
22
24
  port: int = 51826
25
+ pincode: str = "031-45-154"
26
+ accessory_state_file: str = "./accessory.state"
23
27
 
24
28
 
25
29
  class RoomConfig(BaseModel):
@@ -0,0 +1,168 @@
1
+ """HomeKit Accessory Driver for pyhap integration."""
2
+
3
+ import asyncio
4
+ import logging
5
+ from typing import Callable, Dict, Optional
6
+
7
+ from pyhap.accessory import Accessory, Bridge
8
+ from pyhap.accessory_driver import AccessoryDriver
9
+ from pyhap.const import CATEGORY_LIGHTBULB, CATEGORY_OUTLET
10
+
11
+ from xp.models.homekit.homekit_config import HomekitConfig
12
+
13
+
14
+ class XPAccessory(Accessory):
15
+ """Single accessory wrapping a Conbus output."""
16
+
17
+ def __init__(
18
+ self,
19
+ driver: "HomekitAccessoryDriver",
20
+ name: str,
21
+ service_type: str,
22
+ aid: int,
23
+ ) -> None:
24
+ """
25
+ Initialize the XP accessory.
26
+
27
+ Args:
28
+ driver: HomekitAccessoryDriver instance.
29
+ name: Accessory name (unique identifier and display name).
30
+ service_type: Service type ('light', 'outlet', 'dimminglight').
31
+ aid: Accessory ID for HomeKit.
32
+ """
33
+ super().__init__(driver._driver, name, aid=aid)
34
+ self._hk_driver = driver
35
+ self._accessory_id = name
36
+ self.logger = logging.getLogger(__name__)
37
+
38
+ if service_type == "dimminglight":
39
+ self.category = CATEGORY_LIGHTBULB
40
+ serv = self.add_preload_service("Lightbulb", chars=["On", "Brightness"])
41
+ # Note: Brightness setter_callback deferred to future update
42
+ elif service_type == "outlet":
43
+ self.category = CATEGORY_OUTLET
44
+ serv = self.add_preload_service("Outlet")
45
+ else:
46
+ self.category = CATEGORY_LIGHTBULB
47
+ serv = self.add_preload_service("Lightbulb")
48
+
49
+ self._char_on = serv.configure_char("On", setter_callback=self._set_on)
50
+
51
+ def _set_on(self, value: bool) -> None:
52
+ """
53
+ Handle HomeKit set on/off request.
54
+
55
+ Args:
56
+ value: True for on, False for off.
57
+ """
58
+ if self._hk_driver._on_set:
59
+ self._hk_driver._on_set(self._accessory_id, value)
60
+
61
+ def update_state(self, is_on: bool) -> None:
62
+ """
63
+ Update accessory state from Conbus event.
64
+
65
+ Args:
66
+ is_on: True if accessory is on, False otherwise.
67
+ """
68
+ self._char_on.set_value(is_on)
69
+
70
+
71
+ class HomekitAccessoryDriver:
72
+ """Wrapper around pyhap AccessoryDriver."""
73
+
74
+ def __init__(self, homekit_config: HomekitConfig) -> None:
75
+ """
76
+ Initialize the HomeKit accessory driver.
77
+
78
+ Args:
79
+ homekit_config: HomekitConfig with network and accessory settings.
80
+ """
81
+ self.logger = logging.getLogger(__name__)
82
+ self._homekit_config = homekit_config
83
+ self._driver: Optional[AccessoryDriver] = None
84
+ self._accessories: Dict[str, XPAccessory] = {}
85
+ self._on_set: Optional[Callable[[str, bool], None]] = None
86
+
87
+ def set_callback(self, on_set: Callable[[str, bool], None]) -> None:
88
+ """
89
+ Set callback for HomeKit set events.
90
+
91
+ Args:
92
+ on_set: Callback(accessory_name, is_on) called when HomeKit app toggles.
93
+ """
94
+ self._on_set = on_set
95
+
96
+ def _setup_bridge(self, config: HomekitConfig) -> None:
97
+ """
98
+ Set up HomeKit bridge with accessories.
99
+
100
+ Args:
101
+ config: HomekitConfig with accessory definitions.
102
+ """
103
+ assert self._driver is not None
104
+ bridge = Bridge(self._driver, config.bridge.name)
105
+ aid = 2 # Bridge is 1
106
+
107
+ for acc_config in config.accessories:
108
+ accessory = XPAccessory(
109
+ driver=self,
110
+ name=acc_config.name,
111
+ service_type=acc_config.service,
112
+ aid=aid,
113
+ )
114
+ bridge.add_accessory(accessory)
115
+ self._accessories[acc_config.name] = accessory
116
+ aid += 1
117
+
118
+ self._driver.add_accessory(bridge)
119
+
120
+ async def start(self) -> None:
121
+ """Start the AccessoryDriver (non-blocking)."""
122
+ try:
123
+ # Enable pyhap debug logging
124
+ pyhap_logger = logging.getLogger("pyhap")
125
+ pyhap_logger.setLevel(logging.DEBUG)
126
+
127
+ # Create driver with the running event loop
128
+ loop = asyncio.get_running_loop()
129
+ config = self._homekit_config
130
+ pincode = config.homekit.pincode.encode()
131
+ self.logger.info(
132
+ f"Starting HAP driver on {config.homekit.ip}:{config.homekit.port} with pincode {config.homekit.pincode}"
133
+ )
134
+ self._driver = AccessoryDriver(
135
+ loop=loop,
136
+ address=str(config.homekit.ip),
137
+ port=config.homekit.port,
138
+ pincode=pincode,
139
+ persist_file=config.homekit.accessory_state_file,
140
+ )
141
+ self._setup_bridge(config)
142
+ await self._driver.async_start()
143
+ self.logger.info("AccessoryDriver started successfully")
144
+ except Exception as e:
145
+ self.logger.error(f"Error starting AccessoryDriver: {e}", exc_info=True)
146
+
147
+ async def stop(self) -> None:
148
+ """Stop the AccessoryDriver."""
149
+ if not self._driver:
150
+ return
151
+ try:
152
+ await self._driver.async_stop()
153
+ self.logger.info("AccessoryDriver stopped successfully")
154
+ except Exception as e:
155
+ self.logger.error(f"Error stopping AccessoryDriver: {e}", exc_info=True)
156
+
157
+ def update_state(self, accessory_name: str, is_on: bool) -> None:
158
+ """
159
+ Update accessory state from Conbus event.
160
+
161
+ Args:
162
+ accessory_name: Accessory name to update.
163
+ is_on: True if accessory is on, False otherwise.
164
+ """
165
+ if acc := self._accessories.get(accessory_name):
166
+ acc.update_state(is_on)
167
+ else:
168
+ self.logger.warning(f"Unknown accessory name: {accessory_name}")
@@ -18,6 +18,7 @@ from xp.models.term.connection_state import ConnectionState
18
18
  from xp.services.protocol.conbus_event_protocol import ConbusEventProtocol
19
19
  from xp.services.telegram.telegram_output_service import TelegramOutputService
20
20
  from xp.services.telegram.telegram_service import TelegramService
21
+ from xp.services.term.homekit_accessory_driver import HomekitAccessoryDriver
21
22
 
22
23
 
23
24
  class HomekitService:
@@ -50,6 +51,7 @@ class HomekitService:
50
51
  homekit_config: HomekitConfig,
51
52
  conson_config: ConsonModuleListConfig,
52
53
  telegram_service: TelegramService,
54
+ accessory_driver: HomekitAccessoryDriver,
53
55
  ) -> None:
54
56
  """
55
57
  Initialize the HomeKit service.
@@ -59,12 +61,14 @@ class HomekitService:
59
61
  homekit_config: HomekitConfig for accessory configuration.
60
62
  conson_config: ConsonModuleListConfig for module configuration.
61
63
  telegram_service: TelegramService for parsing telegrams.
64
+ accessory_driver: HomekitAccessoryDriver for pyhap integration.
62
65
  """
63
66
  self.logger = logging.getLogger(__name__)
64
67
  self._conbus_protocol = conbus_protocol
65
68
  self._homekit_config = homekit_config
66
69
  self._conson_config = conson_config
67
70
  self._telegram_service = telegram_service
71
+ self._accessory_driver = accessory_driver
68
72
  self._connection_state = ConnectionState.DISCONNECTED
69
73
  self._state_machine = ConnectionState.create_state_machine()
70
74
 
@@ -74,6 +78,9 @@ class HomekitService:
74
78
  # Action key to accessory ID mapping
75
79
  self._action_map: Dict[str, str] = {}
76
80
 
81
+ # Set up HomeKit callback
82
+ self._accessory_driver.set_callback(self._on_homekit_set)
83
+
77
84
  # Connect to protocol signals
78
85
  self._connect_signals()
79
86
 
@@ -152,6 +159,27 @@ class HomekitService:
152
159
  return accessory
153
160
  return None
154
161
 
162
+ def _find_accessory_config_by_output(
163
+ self, serial_number: str, output: int
164
+ ) -> Optional[HomekitAccessoryConfig]:
165
+ """
166
+ Find accessory config by serial number and output.
167
+
168
+ Args:
169
+ serial_number: Module serial number.
170
+ output: Output number (1-based).
171
+
172
+ Returns:
173
+ HomekitAccessoryConfig if found, None otherwise.
174
+ """
175
+ for accessory in self._homekit_config.accessories:
176
+ if (
177
+ accessory.serial_number == serial_number
178
+ and accessory.output_number == output - 1
179
+ ):
180
+ return accessory
181
+ return None
182
+
155
183
  def _connect_signals(self) -> None:
156
184
  """Connect to protocol signals."""
157
185
  self._conbus_protocol.on_connection_made.connect(self._on_connection_made)
@@ -242,6 +270,34 @@ class HomekitService:
242
270
  self.on_connection_state_changed.emit(self._connection_state)
243
271
  self.on_status_message.emit("Disconnected")
244
272
 
273
+ async def start(self) -> None:
274
+ """Start the service and AccessoryDriver."""
275
+ self.connect()
276
+ await self._accessory_driver.start()
277
+
278
+ async def stop(self) -> None:
279
+ """Stop the AccessoryDriver and cleanup."""
280
+ await self._accessory_driver.stop()
281
+ self.cleanup()
282
+
283
+ def _on_homekit_set(self, accessory_name: str, is_on: bool) -> None:
284
+ """
285
+ Handle HomeKit app toggle request.
286
+
287
+ Args:
288
+ accessory_name: Accessory name from HomeKit.
289
+ is_on: True for on, False for off.
290
+ """
291
+ config = self._find_accessory_config(accessory_name)
292
+ if config:
293
+ action = config.on_action if is_on else config.off_action
294
+ self._conbus_protocol.send_raw_telegram(action)
295
+ self.on_status_message.emit(
296
+ f"HomeKit: {accessory_name} {'ON' if is_on else 'OFF'}"
297
+ )
298
+ else:
299
+ self.logger.warning(f"No config found for accessory: {accessory_name}")
300
+
245
301
  def toggle_connection(self) -> None:
246
302
  """
247
303
  Toggle connection state between connected and disconnected.
@@ -406,6 +462,13 @@ class HomekitService:
406
462
  # Update dimming state for dimmable modules
407
463
  if state.is_dimmable():
408
464
  state.dimming_state = "-" if not is_on else ""
465
+
466
+ # Sync to HomeKit
467
+ config = self._find_accessory_config_by_output(
468
+ serial_number, state.output
469
+ )
470
+ if config:
471
+ self._accessory_driver.update_state(config.name, is_on)
409
472
  else:
410
473
  state.output_state = "?"
411
474
 
@@ -463,6 +526,13 @@ class HomekitService:
463
526
  if state.is_dimmable():
464
527
  state.dimming_state = "-" if not is_on else ""
465
528
 
529
+ # Sync to HomeKit
530
+ config = self._find_accessory_config_by_output(
531
+ state.serial_number, state.output
532
+ )
533
+ if config:
534
+ self._accessory_driver.update_state(config.name, is_on)
535
+
466
536
  state.last_update = datetime.now()
467
537
  self.on_module_state_changed.emit(state)
468
538
 
@@ -68,14 +68,14 @@ class HomekitApp(App[None]):
68
68
  """
69
69
  Initialize app after UI is mounted.
70
70
 
71
- Delays connection by 0.5s to let UI render first. Sets up automatic screen
72
- refresh every second to update elapsed times.
71
+ Delays connection by 0.5s to let UI render first. Starts the AccessoryDriver and
72
+ sets up automatic screen refresh every second to update elapsed times.
73
73
  """
74
74
  import asyncio
75
75
 
76
76
  # Delay connection to let UI render
77
77
  await asyncio.sleep(0.5)
78
- self.homekit_service.connect()
78
+ await self.homekit_service.start()
79
79
 
80
80
  # Set up periodic refresh to update elapsed times
81
81
  self.set_interval(1.0, self._refresh_last_update_column)
@@ -111,6 +111,6 @@ class HomekitApp(App[None]):
111
111
  """Refresh all module data on 'r' key press."""
112
112
  self.homekit_service.refresh_all()
113
113
 
114
- def on_unmount(self) -> None:
115
- """Clean up service when app unmounts."""
116
- self.homekit_service.cleanup()
114
+ async def on_unmount(self) -> None:
115
+ """Stop AccessoryDriver and clean up service when app unmounts."""
116
+ await self.homekit_service.stop()
@@ -77,6 +77,7 @@ from xp.services.telegram.telegram_discover_service import TelegramDiscoverServi
77
77
  from xp.services.telegram.telegram_link_number_service import LinkNumberService
78
78
  from xp.services.telegram.telegram_output_service import TelegramOutputService
79
79
  from xp.services.telegram.telegram_service import TelegramService
80
+ from xp.services.term.homekit_accessory_driver import HomekitAccessoryDriver
80
81
  from xp.services.term.homekit_service import HomekitService
81
82
  from xp.services.term.protocol_monitor_service import ProtocolMonitorService
82
83
  from xp.services.term.state_monitor_service import StateMonitorService
@@ -267,6 +268,14 @@ class ServiceContainer:
267
268
  scope=punq.Scope.singleton,
268
269
  )
269
270
 
271
+ self.container.register(
272
+ HomekitAccessoryDriver,
273
+ factory=lambda: HomekitAccessoryDriver(
274
+ homekit_config=self.container.resolve(HomekitConfig),
275
+ ),
276
+ scope=punq.Scope.singleton,
277
+ )
278
+
270
279
  self.container.register(
271
280
  HomekitService,
272
281
  factory=lambda: HomekitService(
@@ -274,6 +283,7 @@ class ServiceContainer:
274
283
  homekit_config=self.container.resolve(HomekitConfig),
275
284
  conson_config=self.container.resolve(ConsonModuleListConfig),
276
285
  telegram_service=self.container.resolve(TelegramService),
286
+ accessory_driver=self.container.resolve(HomekitAccessoryDriver),
277
287
  ),
278
288
  scope=punq.Scope.singleton,
279
289
  )
@@ -62,4 +62,4 @@ class TestConbusResponse:
62
62
  assert result["sent_telegrams"] == [] # post_init resets these
63
63
  assert result["received_telegrams"] == [] # post_init resets these
64
64
  assert result["error"] == "test error"
65
- assert "2025" in result["timestamp"]
65
+ assert "T" in result["timestamp"] # ISO format check
@@ -149,9 +149,26 @@ class TestHomekitService:
149
149
  service.parse_reply_telegram = Mock()
150
150
  return service
151
151
 
152
+ @pytest.fixture
153
+ def mock_accessory_driver(self):
154
+ """Create mock HomekitAccessoryDriver."""
155
+ from xp.services.term.homekit_accessory_driver import HomekitAccessoryDriver
156
+
157
+ driver = Mock(spec=HomekitAccessoryDriver)
158
+ driver.set_callback = Mock()
159
+ driver.start = Mock()
160
+ driver.stop = Mock()
161
+ driver.update_state = Mock()
162
+ return driver
163
+
152
164
  @pytest.fixture
153
165
  def service(
154
- self, mock_protocol, homekit_config, conson_config, mock_telegram_service
166
+ self,
167
+ mock_protocol,
168
+ homekit_config,
169
+ conson_config,
170
+ mock_telegram_service,
171
+ mock_accessory_driver,
155
172
  ):
156
173
  """Create service instance."""
157
174
  return HomekitService(
@@ -159,6 +176,7 @@ class TestHomekitService:
159
176
  homekit_config=homekit_config,
160
177
  conson_config=conson_config,
161
178
  telegram_service=mock_telegram_service,
179
+ accessory_driver=mock_accessory_driver,
162
180
  )
163
181
 
164
182
  def test_initialization(self, service):
@@ -276,7 +294,7 @@ class TestHomekitService:
276
294
  mock_protocol.send_raw_telegram.assert_not_called()
277
295
 
278
296
  def test_toggle_accessory_no_toggle_action(
279
- self, mock_protocol, conson_config, mock_telegram_service
297
+ self, mock_protocol, conson_config, mock_telegram_service, mock_accessory_driver
280
298
  ):
281
299
  """Test toggle_accessory returns False when no toggle_action."""
282
300
  # Create config without toggle_action
@@ -305,6 +323,7 @@ class TestHomekitService:
305
323
  homekit_config=homekit_config,
306
324
  conson_config=conson_config,
307
325
  telegram_service=mock_telegram_service,
326
+ accessory_driver=mock_accessory_driver,
308
327
  ).toggle_accessory("a")
309
328
 
310
329
  assert result is False
@@ -633,7 +652,7 @@ class TestHomekitService:
633
652
  mock_protocol.on_connection_made.disconnect.assert_called_once()
634
653
 
635
654
  def test_accessory_not_in_config(
636
- self, mock_protocol, conson_config, mock_telegram_service
655
+ self, mock_protocol, conson_config, mock_telegram_service, mock_accessory_driver
637
656
  ):
638
657
  """Test service handles missing accessory config gracefully."""
639
658
  homekit_config = HomekitConfig(
@@ -649,12 +668,15 @@ class TestHomekitService:
649
668
  homekit_config=homekit_config,
650
669
  conson_config=conson_config,
651
670
  telegram_service=mock_telegram_service,
671
+ accessory_driver=mock_accessory_driver,
652
672
  )
653
673
 
654
674
  # Should handle gracefully with empty states
655
675
  assert len(service.accessory_states) == 0
656
676
 
657
- def test_module_not_in_config(self, mock_protocol, mock_telegram_service):
677
+ def test_module_not_in_config(
678
+ self, mock_protocol, mock_telegram_service, mock_accessory_driver
679
+ ):
658
680
  """Test service handles missing module config gracefully."""
659
681
  homekit_config = HomekitConfig(
660
682
  bridge=BridgeConfig(
@@ -682,6 +704,7 @@ class TestHomekitService:
682
704
  homekit_config=homekit_config,
683
705
  conson_config=conson_config,
684
706
  telegram_service=mock_telegram_service,
707
+ accessory_driver=mock_accessory_driver,
685
708
  )
686
709
 
687
710
  # Should handle gracefully with empty states
@@ -1,6 +1,6 @@
1
1
  """Unit tests for HomekitApp."""
2
2
 
3
- from unittest.mock import Mock
3
+ from unittest.mock import AsyncMock, Mock
4
4
 
5
5
  import pytest
6
6
 
@@ -115,10 +115,12 @@ class TestHomekitApp:
115
115
 
116
116
  mock_service.toggle_accessory.assert_not_called()
117
117
 
118
- def test_on_unmount_cleanup(self, app, mock_service):
119
- """Test on_unmount calls service cleanup."""
120
- app.on_unmount()
121
- mock_service.cleanup.assert_called_once()
118
+ @pytest.mark.asyncio
119
+ async def test_on_unmount_cleanup(self, app, mock_service):
120
+ """Test on_unmount calls service stop."""
121
+ mock_service.stop = AsyncMock()
122
+ await app.on_unmount()
123
+ mock_service.stop.assert_called_once()
122
124
 
123
125
  def test_refresh_last_update_column_with_widget(self, app, mock_service):
124
126
  """Test _refresh_last_update_column calls widget method."""
File without changes
File without changes