conson-xp 1.51.0__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.
- {conson_xp-1.51.0 → conson_xp-1.52.0}/PKG-INFO +2 -1
- {conson_xp-1.51.0 → conson_xp-1.52.0}/README.md +1 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/pyproject.toml +1 -1
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/__init__.py +1 -1
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/term/term_commands.py +23 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/homekit/homekit_config.py +6 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/term/__init__.py +2 -0
- conson_xp-1.52.0/src/xp/models/term/accessory_state.py +50 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_config_validator.py +1 -1
- conson_xp-1.52.0/src/xp/services/term/homekit_accessory_driver.py +168 -0
- conson_xp-1.52.0/src/xp/services/term/homekit_service.py +582 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/term/state_monitor_service.py +1 -1
- conson_xp-1.52.0/src/xp/term/homekit.py +116 -0
- conson_xp-1.52.0/src/xp/term/homekit.tcss +86 -0
- conson_xp-1.52.0/src/xp/term/widgets/room_list.py +232 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/term/widgets/status_footer.py +6 -3
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/utils/dependencies.py +31 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_models/test_conbus.py +1 -1
- conson_xp-1.52.0/tests/unit/test_models/test_term/__init__.py +1 -0
- conson_xp-1.52.0/tests/unit/test_models/test_term/test_accessory_state.py +170 -0
- conson_xp-1.52.0/tests/unit/test_services/test_homekit_service.py +711 -0
- conson_xp-1.52.0/tests/unit/test_tui/test_homekit_app.py +147 -0
- conson_xp-1.52.0/tests/unit/test_tui/test_room_list.py +300 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/LICENSE +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/__main__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_actiontable_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_autoreport_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_blink_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_config_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_custom_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_datapoint_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_discover_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_event_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_export_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_lightlevel_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_linknumber_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_modulenumber_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_msactiontable_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_output_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_raw_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_receive_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_scan_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/file_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/homekit/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/homekit/homekit.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/homekit/homekit_start_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/module_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/reverse_proxy_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/server/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/server/server_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_blink_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_checksum_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_discover_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_linknumber_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_parse_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_version_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/term/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/commands/term/term.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/main.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/utils/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/utils/click_tree.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/utils/datapoint_type_choice.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/utils/decorators.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/utils/error_handlers.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/utils/formatters.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/utils/module_type_choice.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/utils/serial_number_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/cli/utils/system_function_choice.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/actiontable/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/actiontable/actiontable.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/actiontable/actiontable_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/actiontable/msactiontable_xp20.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/actiontable/msactiontable_xp24.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/actiontable/msactiontable_xp33.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/conbus/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/conbus/conbus.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_autoreport.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_blink.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_client_config.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_connection_status.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_custom.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_datapoint.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_discover.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_event_list.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_event_raw.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_export.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_lightlevel.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_linknumber.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_logger_config.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_output.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_raw.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_receive.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_writeconfig.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/config/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/config/conson_module_config.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/homekit/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/homekit/homekit_accessory.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/log_entry.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/protocol/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/protocol/conbus_protocol.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/response.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/telegram/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/telegram/action_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/telegram/datapoint_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/telegram/event_telegram.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/telegram/event_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/telegram/input_action_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/telegram/input_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/telegram/module_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/telegram/module_type_code.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/telegram/output_telegram.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/telegram/reply_telegram.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/telegram/system_function.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/telegram/system_telegram.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/telegram/telegram.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/telegram/telegram_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/telegram/timeparam_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/term/connection_state.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/term/module_state.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/term/protocol_keys_config.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/term/status_message.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/term/telegram_display.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/models/write_config_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/actiontable/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/actiontable/actiontable_serializer.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/actiontable/download_state_machine.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/actiontable/msactiontable_serializer.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/actiontable/msactiontable_xp20_serializer.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/actiontable/msactiontable_xp24_serializer.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/actiontable/msactiontable_xp33_serializer.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/actiontable/serializer_protocol.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/conbus/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/conbus/actiontable/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/conbus/actiontable/actiontable_download_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/conbus/actiontable/actiontable_list_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/conbus/actiontable/actiontable_show_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/conbus/actiontable/actiontable_upload_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_blink_all_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_blink_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_custom_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_datapoint_queryall_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_datapoint_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_discover_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_event_list_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_event_raw_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_export_actiontable_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_export_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_output_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_raw_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_receive_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_scan_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/conbus/write_config_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/homekit/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_cache_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_conbus_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_conson_validator.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_dimminglight.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_dimminglight_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_hap_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_lightbulb.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_lightbulb_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_module_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_outlet.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_outlet_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/log_file_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/module_type_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/protocol/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/protocol/conbus_event_protocol.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/protocol/protocol_factory.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/protocol/telegram_protocol.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/reverse_proxy_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/server/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/server/base_server_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/server/client_buffer_manager.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/server/cp20_server_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/server/device_service_factory.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/server/server_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/server/xp130_server_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/server/xp20_server_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/server/xp230_server_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/server/xp24_server_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/server/xp33_server_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/telegram/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_blink_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_checksum_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_datapoint_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_discover_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_link_number_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_output_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_version_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/term/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/services/term/protocol_monitor_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/term/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/term/protocol.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/term/protocol.tcss +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/term/state.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/term/state.tcss +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/term/widgets/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/term/widgets/help_menu.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/term/widgets/modules_list.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/term/widgets/protocol_log.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/utils/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/utils/checksum.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/utils/event_helper.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/utils/logging.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/utils/serialization.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/utils/state_machine.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/src/xp/utils/time_utils.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/.coverage +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/conftest.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/.coverage +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/telegram_test_data.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/test_actiontable_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/test_api/.coverage +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/test_api/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/test_blink_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/test_checksum_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/test_conbus_blink_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/test_conbus_datapoint_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/test_conbus_raw_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/test_conbus_receive_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/test_discovery_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/test_event_telegram_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/test_homekit_config_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/test_link_number_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/test_module_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/test_output_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/test_reverse_proxy_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/test_system_reply_telegram_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/test_term_logging_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/test_version_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/test_xp20_action_table_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/integration/test_xp24_action_table_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_api/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_cli/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_cli/test_click_tree.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_cli/test_conbus_actiontable_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_cli/test_conbus_blink_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_cli/test_conbus_msactiontable_upload_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_cli/test_datapoint_type_choice.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_cli/test_decorators.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_cli/test_error_handlers.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_cli/test_formatters.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_cli/test_serial_number_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_cli/test_system_function_choice.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_cli/test_term_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_encoding/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_encoding/test_latin1_edge_cases.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_models/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_models/test_conbus_client_send.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_models/test_conbus_discover.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_models/test_conbus_linknumber.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_models/test_event_telegram.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_models/test_log_entry.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_models/test_logger_config.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_models/test_module_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_models/test_reply_telegram.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_models/test_system_telegram.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_models/test_system_telegram_enhancements.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_models/test_version_telegram.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_models/test_write_config_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_models/test_xp20_action_table.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_models/test_xp24_action_table.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_models/test_xp24_action_table_short_format.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_models/test_xp24_action_telegram.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_actiontable_download_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_actiontable_serializer.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_actiontable_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_actiontable_upload_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_base_server_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_blink_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_checksum_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_client_buffer_manager.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_blink_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_event_list_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_event_protocol.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_event_raw_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_output_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_raw_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_receive_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_reverse_proxy_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_scan_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_device_service_factory.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_discovery_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_homekit_cache_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_homekit_config_validator.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_homekit_conson_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_homekit_services.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_log_file_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_module_type_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_protocol.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_protocol_monitor_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_server_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_state_monitor_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_telegram_input_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_telegram_output_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_telegram_protocol.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_telegram_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_version_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_xp20_action_table_serializer.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_xp24_action_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_xp24_action_table_serializer.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_xp24_action_table_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_xp33_action_table_serializer.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_xp33_short_format.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_services/test_xp_server_services.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_tui/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_tui/test_protocol_log.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_utils/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_utils/test_checksum.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_utils/test_event_helper.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_utils/test_logging.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.52.0}/tests/unit/test_utils/test_serialization.py +0 -0
- {conson_xp-1.51.0 → 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.
|
|
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
|
|
@@ -450,6 +450,7 @@ xp telegram version
|
|
|
450
450
|
|
|
451
451
|
|
|
452
452
|
xp term
|
|
453
|
+
xp term homekit
|
|
453
454
|
xp term protocol
|
|
454
455
|
xp term state
|
|
455
456
|
|
|
@@ -48,3 +48,26 @@ def state_monitor(ctx: Context) -> None:
|
|
|
48
48
|
|
|
49
49
|
# Resolve StateMonitorApp from container and run
|
|
50
50
|
ctx.obj.get("container").get_container().resolve(StateMonitorApp).run()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@term.command("homekit")
|
|
54
|
+
@click.pass_context
|
|
55
|
+
def homekit_monitor(ctx: Context) -> None:
|
|
56
|
+
r"""
|
|
57
|
+
Start TUI for HomeKit accessory monitoring.
|
|
58
|
+
|
|
59
|
+
Displays HomeKit rooms and accessories with real-time state updates
|
|
60
|
+
in an interactive terminal interface. Press action keys (a-z) to
|
|
61
|
+
toggle accessories.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
ctx: Click context object.
|
|
65
|
+
|
|
66
|
+
Examples:
|
|
67
|
+
\b
|
|
68
|
+
xp term homekit
|
|
69
|
+
"""
|
|
70
|
+
from xp.term.homekit import HomekitApp
|
|
71
|
+
|
|
72
|
+
# Resolve HomekitApp from container and run
|
|
73
|
+
ctx.obj.get("container").get_container().resolve(HomekitApp).run()
|
|
@@ -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):
|
|
@@ -61,6 +65,7 @@ class HomekitAccessoryConfig(BaseModel):
|
|
|
61
65
|
service: Service type for the accessory.
|
|
62
66
|
on_action: on code for the accessory.
|
|
63
67
|
off_action: off code for the accessory.
|
|
68
|
+
toggle_action: Optional toggle action code for the accessory.
|
|
64
69
|
hap_accessory: Optional HAP accessory identifier.
|
|
65
70
|
"""
|
|
66
71
|
|
|
@@ -72,6 +77,7 @@ class HomekitAccessoryConfig(BaseModel):
|
|
|
72
77
|
service: str
|
|
73
78
|
on_action: str
|
|
74
79
|
off_action: str
|
|
80
|
+
toggle_action: Optional[str] = None
|
|
75
81
|
hap_accessory: Optional[int] = None
|
|
76
82
|
|
|
77
83
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Terminal UI models."""
|
|
2
2
|
|
|
3
|
+
from xp.models.term.accessory_state import AccessoryState
|
|
3
4
|
from xp.models.term.module_state import ModuleState
|
|
4
5
|
from xp.models.term.protocol_keys_config import (
|
|
5
6
|
ProtocolKeyConfig,
|
|
@@ -7,6 +8,7 @@ from xp.models.term.protocol_keys_config import (
|
|
|
7
8
|
)
|
|
8
9
|
|
|
9
10
|
__all__ = [
|
|
11
|
+
"AccessoryState",
|
|
10
12
|
"ModuleState",
|
|
11
13
|
"ProtocolKeyConfig",
|
|
12
14
|
"ProtocolKeysConfig",
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Accessory state data model for Homekit TUI."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class AccessoryState:
|
|
10
|
+
"""
|
|
11
|
+
State of a HomeKit accessory for TUI display.
|
|
12
|
+
|
|
13
|
+
Attributes:
|
|
14
|
+
room_name: Room containing the accessory (e.g., "Salon").
|
|
15
|
+
accessory_name: Accessory display name (e.g., "Variateur salon").
|
|
16
|
+
action: Action key (a-z) for toggle control.
|
|
17
|
+
output_state: Output state ("ON", "OFF", "?").
|
|
18
|
+
dimming_state: Dimming percentage for dimmable modules, "-" if OFF, empty otherwise.
|
|
19
|
+
module_name: Module identifier (e.g., "A12").
|
|
20
|
+
serial_number: Module serial number.
|
|
21
|
+
module_type: Module type (e.g., "XP24", "XP33LED").
|
|
22
|
+
error_status: Status code ("OK" or error like "E10").
|
|
23
|
+
output: Module output number (1-based for display).
|
|
24
|
+
sort: Sort accessories according to homekit.yml configuration.
|
|
25
|
+
last_update: Last communication timestamp. None if never updated.
|
|
26
|
+
toggle_action: Raw toggle action telegram (e.g., "E02L12I02").
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
room_name: str
|
|
30
|
+
accessory_name: str
|
|
31
|
+
action: str
|
|
32
|
+
output_state: str
|
|
33
|
+
dimming_state: str
|
|
34
|
+
module_name: str
|
|
35
|
+
serial_number: str
|
|
36
|
+
module_type: str
|
|
37
|
+
error_status: str
|
|
38
|
+
output: int
|
|
39
|
+
sort: int
|
|
40
|
+
last_update: Optional[datetime] = None
|
|
41
|
+
toggle_action: Optional[str] = None
|
|
42
|
+
|
|
43
|
+
def is_dimmable(self) -> bool:
|
|
44
|
+
"""
|
|
45
|
+
Check if accessory is dimmable.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
True if module type is XP33LR or XP33LED, False otherwise.
|
|
49
|
+
"""
|
|
50
|
+
return self.module_type in ("XP33LR", "XP33LED")
|
|
@@ -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}")
|