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.
- {conson_xp-1.51.1 → conson_xp-1.52.0}/PKG-INFO +1 -1
- {conson_xp-1.51.1 → conson_xp-1.52.0}/pyproject.toml +1 -1
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/__init__.py +1 -1
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/homekit/homekit_config.py +4 -0
- conson_xp-1.52.0/src/xp/services/term/homekit_accessory_driver.py +168 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/term/homekit_service.py +70 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/homekit.py +6 -6
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/utils/dependencies.py +10 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_conbus.py +1 -1
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_homekit_service.py +27 -4
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_tui/test_homekit_app.py +7 -5
- {conson_xp-1.51.1 → conson_xp-1.52.0}/LICENSE +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/README.md +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/__main__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_actiontable_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_autoreport_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_blink_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_config_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_custom_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_datapoint_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_discover_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_event_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_export_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_lightlevel_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_linknumber_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_modulenumber_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_msactiontable_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_output_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_raw_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_receive_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_scan_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/file_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/homekit/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/homekit/homekit.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/homekit/homekit_start_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/module_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/reverse_proxy_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/server/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/server/server_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_blink_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_checksum_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_discover_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_linknumber_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_parse_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_version_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/term/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/term/term.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/term/term_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/main.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/utils/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/utils/click_tree.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/utils/datapoint_type_choice.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/utils/decorators.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/utils/error_handlers.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/utils/formatters.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/utils/module_type_choice.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/utils/serial_number_type.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/utils/system_function_choice.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/actiontable/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/actiontable/actiontable.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/actiontable/actiontable_type.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/actiontable/msactiontable_xp20.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/actiontable/msactiontable_xp24.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/actiontable/msactiontable_xp33.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_autoreport.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_blink.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_client_config.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_connection_status.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_custom.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_datapoint.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_discover.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_event_list.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_event_raw.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_export.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_lightlevel.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_linknumber.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_logger_config.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_output.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_raw.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_receive.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/conbus/conbus_writeconfig.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/config/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/config/conson_module_config.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/homekit/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/homekit/homekit_accessory.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/log_entry.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/protocol/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/protocol/conbus_protocol.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/response.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/action_type.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/datapoint_type.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/event_telegram.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/event_type.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/input_action_type.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/input_type.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/module_type.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/module_type_code.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/output_telegram.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/reply_telegram.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/system_function.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/system_telegram.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/telegram.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/telegram_type.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/telegram/timeparam_type.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/term/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/term/accessory_state.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/term/connection_state.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/term/module_state.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/term/protocol_keys_config.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/term/status_message.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/term/telegram_display.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/models/write_config_type.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/actiontable/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/actiontable/actiontable_serializer.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/actiontable/download_state_machine.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/actiontable/msactiontable_serializer.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/actiontable/msactiontable_xp20_serializer.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/actiontable/msactiontable_xp24_serializer.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/actiontable/msactiontable_xp33_serializer.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/actiontable/serializer_protocol.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/actiontable/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/actiontable/actiontable_download_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/actiontable/actiontable_list_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/actiontable/actiontable_show_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/actiontable/actiontable_upload_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_blink_all_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_blink_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_custom_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_datapoint_queryall_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_datapoint_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_discover_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_event_list_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_event_raw_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_export_actiontable_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_export_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_output_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_raw_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_receive_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/conbus_scan_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/conbus/write_config_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_cache_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_conbus_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_config_validator.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_conson_validator.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_dimminglight.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_dimminglight_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_hap_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_lightbulb.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_lightbulb_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_module_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_outlet.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_outlet_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/homekit/homekit_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/log_file_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/module_type_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/protocol/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/protocol/conbus_event_protocol.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/protocol/protocol_factory.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/protocol/telegram_protocol.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/reverse_proxy_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/server/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/server/base_server_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/server/client_buffer_manager.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/server/cp20_server_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/server/device_service_factory.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/server/server_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/server/xp130_server_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/server/xp20_server_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/server/xp230_server_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/server/xp24_server_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/server/xp33_server_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/telegram/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_blink_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_checksum_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_datapoint_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_discover_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_link_number_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_output_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/telegram/telegram_version_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/term/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/term/protocol_monitor_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/services/term/state_monitor_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/homekit.tcss +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/protocol.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/protocol.tcss +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/state.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/state.tcss +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/widgets/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/widgets/help_menu.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/widgets/modules_list.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/widgets/protocol_log.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/widgets/room_list.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/term/widgets/status_footer.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/utils/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/utils/checksum.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/utils/event_helper.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/utils/logging.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/utils/serialization.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/utils/state_machine.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/utils/time_utils.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/.coverage +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/conftest.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/.coverage +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/telegram_test_data.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_actiontable_integration.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_api/.coverage +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_api/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_blink_integration.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_checksum_integration.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_conbus_blink_integration.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_conbus_datapoint_integration.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_conbus_raw_integration.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_conbus_receive_integration.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_discovery_integration.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_event_telegram_integration.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_homekit_config_integration.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_link_number_integration.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_module_integration.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_output_integration.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_reverse_proxy_integration.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_system_reply_telegram_integration.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_term_logging_integration.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_version_integration.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_xp20_action_table_integration.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/integration/test_xp24_action_table_integration.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_api/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/test_click_tree.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/test_conbus_actiontable_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/test_conbus_blink_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/test_conbus_msactiontable_upload_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/test_datapoint_type_choice.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/test_decorators.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/test_error_handlers.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/test_formatters.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/test_serial_number_type.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/test_system_function_choice.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_cli/test_term_commands.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_encoding/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_encoding/test_latin1_edge_cases.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_conbus_client_send.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_conbus_discover.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_conbus_linknumber.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_event_telegram.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_log_entry.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_logger_config.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_module_type.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_reply_telegram.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_system_telegram.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_system_telegram_enhancements.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_term/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_term/test_accessory_state.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_version_telegram.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_write_config_type.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_xp20_action_table.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_xp24_action_table.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_xp24_action_table_short_format.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_models/test_xp24_action_telegram.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_actiontable_download_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_actiontable_serializer.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_actiontable_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_actiontable_upload_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_base_server_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_blink_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_checksum_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_client_buffer_manager.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_blink_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_event_list_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_event_protocol.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_event_raw_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_output_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_raw_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_receive_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_reverse_proxy_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_conbus_scan_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_device_service_factory.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_discovery_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_homekit_cache_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_homekit_config_validator.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_homekit_conson_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_homekit_services.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_log_file_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_module_type_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_protocol.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_protocol_monitor_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_server_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_state_monitor_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_telegram_input_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_telegram_output_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_telegram_protocol.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_telegram_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_version_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_xp20_action_table_serializer.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_xp24_action_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_xp24_action_table_serializer.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_xp24_action_table_service.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_xp33_action_table_serializer.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_xp33_short_format.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_services/test_xp_server_services.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_tui/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_tui/test_protocol_log.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_tui/test_room_list.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_utils/__init__.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_utils/test_checksum.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_utils/test_event_helper.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_utils/test_logging.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_utils/test_serialization.py +0 -0
- {conson_xp-1.51.1 → conson_xp-1.52.0}/tests/unit/test_utils/test_time_utils.py +0 -0
|
@@ -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.
|
|
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.
|
|
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
|
-
"""
|
|
116
|
-
self.homekit_service.
|
|
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 "
|
|
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,
|
|
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(
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
mock_service.
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_actiontable_commands.py
RENAMED
|
File without changes
|
{conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_autoreport_commands.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_datapoint_commands.py
RENAMED
|
File without changes
|
{conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_discover_commands.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_lightlevel_commands.py
RENAMED
|
File without changes
|
{conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_linknumber_commands.py
RENAMED
|
File without changes
|
{conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_modulenumber_commands.py
RENAMED
|
File without changes
|
{conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/conbus/conbus_msactiontable_commands.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_blink_commands.py
RENAMED
|
File without changes
|
{conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_checksum_commands.py
RENAMED
|
File without changes
|
{conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_discover_commands.py
RENAMED
|
File without changes
|
{conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_linknumber_commands.py
RENAMED
|
File without changes
|
{conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_parse_commands.py
RENAMED
|
File without changes
|
{conson_xp-1.51.1 → conson_xp-1.52.0}/src/xp/cli/commands/telegram/telegram_version_commands.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|