conson-xp 1.51.0__tar.gz → 1.51.1__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.51.1}/PKG-INFO +2 -1
- {conson_xp-1.51.0 → conson_xp-1.51.1}/README.md +1 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/pyproject.toml +1 -1
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/__init__.py +1 -1
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/term/term_commands.py +23 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/homekit/homekit_config.py +2 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/term/__init__.py +2 -0
- conson_xp-1.51.1/src/xp/models/term/accessory_state.py +50 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/homekit/homekit_config_validator.py +1 -1
- conson_xp-1.51.1/src/xp/services/term/homekit_service.py +512 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/term/state_monitor_service.py +1 -1
- conson_xp-1.51.1/src/xp/term/homekit.py +116 -0
- conson_xp-1.51.1/src/xp/term/homekit.tcss +86 -0
- conson_xp-1.51.1/src/xp/term/widgets/room_list.py +232 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/term/widgets/status_footer.py +6 -3
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/utils/dependencies.py +21 -0
- conson_xp-1.51.1/tests/unit/test_models/test_term/__init__.py +1 -0
- conson_xp-1.51.1/tests/unit/test_models/test_term/test_accessory_state.py +170 -0
- conson_xp-1.51.1/tests/unit/test_services/test_homekit_service.py +688 -0
- conson_xp-1.51.1/tests/unit/test_tui/test_homekit_app.py +145 -0
- conson_xp-1.51.1/tests/unit/test_tui/test_room_list.py +300 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/LICENSE +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/__main__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/conbus/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/conbus/conbus.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/conbus/conbus_actiontable_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/conbus/conbus_autoreport_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/conbus/conbus_blink_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/conbus/conbus_config_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/conbus/conbus_custom_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/conbus/conbus_datapoint_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/conbus/conbus_discover_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/conbus/conbus_event_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/conbus/conbus_export_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/conbus/conbus_lightlevel_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/conbus/conbus_linknumber_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/conbus/conbus_modulenumber_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/conbus/conbus_msactiontable_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/conbus/conbus_output_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/conbus/conbus_raw_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/conbus/conbus_receive_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/conbus/conbus_scan_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/file_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/homekit/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/homekit/homekit.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/homekit/homekit_start_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/module_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/reverse_proxy_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/server/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/server/server_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/telegram/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/telegram/telegram.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/telegram/telegram_blink_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/telegram/telegram_checksum_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/telegram/telegram_discover_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/telegram/telegram_linknumber_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/telegram/telegram_parse_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/telegram/telegram_version_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/term/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/commands/term/term.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/main.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/utils/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/utils/click_tree.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/utils/datapoint_type_choice.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/utils/decorators.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/utils/error_handlers.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/utils/formatters.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/utils/module_type_choice.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/utils/serial_number_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/cli/utils/system_function_choice.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/actiontable/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/actiontable/actiontable.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/actiontable/actiontable_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/actiontable/msactiontable_xp20.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/actiontable/msactiontable_xp24.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/actiontable/msactiontable_xp33.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/conbus/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/conbus/conbus.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/conbus/conbus_autoreport.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/conbus/conbus_blink.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/conbus/conbus_client_config.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/conbus/conbus_connection_status.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/conbus/conbus_custom.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/conbus/conbus_datapoint.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/conbus/conbus_discover.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/conbus/conbus_event_list.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/conbus/conbus_event_raw.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/conbus/conbus_export.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/conbus/conbus_lightlevel.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/conbus/conbus_linknumber.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/conbus/conbus_logger_config.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/conbus/conbus_output.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/conbus/conbus_raw.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/conbus/conbus_receive.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/conbus/conbus_writeconfig.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/config/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/config/conson_module_config.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/homekit/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/homekit/homekit_accessory.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/log_entry.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/protocol/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/protocol/conbus_protocol.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/response.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/telegram/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/telegram/action_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/telegram/datapoint_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/telegram/event_telegram.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/telegram/event_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/telegram/input_action_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/telegram/input_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/telegram/module_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/telegram/module_type_code.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/telegram/output_telegram.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/telegram/reply_telegram.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/telegram/system_function.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/telegram/system_telegram.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/telegram/telegram.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/telegram/telegram_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/telegram/timeparam_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/term/connection_state.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/term/module_state.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/term/protocol_keys_config.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/term/status_message.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/term/telegram_display.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/models/write_config_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/actiontable/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/actiontable/actiontable_serializer.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/actiontable/download_state_machine.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/actiontable/msactiontable_serializer.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/actiontable/msactiontable_xp20_serializer.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/actiontable/msactiontable_xp24_serializer.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/actiontable/msactiontable_xp33_serializer.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/actiontable/serializer_protocol.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/conbus/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/conbus/actiontable/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/conbus/actiontable/actiontable_download_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/conbus/actiontable/actiontable_list_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/conbus/actiontable/actiontable_show_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/conbus/actiontable/actiontable_upload_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/conbus/conbus_blink_all_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/conbus/conbus_blink_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/conbus/conbus_custom_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/conbus/conbus_datapoint_queryall_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/conbus/conbus_datapoint_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/conbus/conbus_discover_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/conbus/conbus_event_list_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/conbus/conbus_event_raw_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/conbus/conbus_export_actiontable_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/conbus/conbus_export_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/conbus/conbus_output_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/conbus/conbus_raw_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/conbus/conbus_receive_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/conbus/conbus_scan_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/conbus/write_config_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/homekit/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/homekit/homekit_cache_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/homekit/homekit_conbus_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/homekit/homekit_conson_validator.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/homekit/homekit_dimminglight.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/homekit/homekit_dimminglight_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/homekit/homekit_hap_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/homekit/homekit_lightbulb.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/homekit/homekit_lightbulb_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/homekit/homekit_module_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/homekit/homekit_outlet.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/homekit/homekit_outlet_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/homekit/homekit_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/log_file_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/module_type_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/protocol/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/protocol/conbus_event_protocol.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/protocol/protocol_factory.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/protocol/telegram_protocol.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/reverse_proxy_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/server/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/server/base_server_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/server/client_buffer_manager.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/server/cp20_server_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/server/device_service_factory.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/server/server_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/server/xp130_server_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/server/xp20_server_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/server/xp230_server_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/server/xp24_server_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/server/xp33_server_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/telegram/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/telegram/telegram_blink_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/telegram/telegram_checksum_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/telegram/telegram_datapoint_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/telegram/telegram_discover_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/telegram/telegram_link_number_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/telegram/telegram_output_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/telegram/telegram_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/telegram/telegram_version_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/term/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/services/term/protocol_monitor_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/term/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/term/protocol.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/term/protocol.tcss +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/term/state.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/term/state.tcss +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/term/widgets/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/term/widgets/help_menu.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/term/widgets/modules_list.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/term/widgets/protocol_log.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/utils/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/utils/checksum.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/utils/event_helper.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/utils/logging.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/utils/serialization.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/utils/state_machine.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/src/xp/utils/time_utils.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/.coverage +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/conftest.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/.coverage +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/telegram_test_data.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/test_actiontable_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/test_api/.coverage +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/test_api/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/test_blink_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/test_checksum_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/test_conbus_blink_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/test_conbus_datapoint_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/test_conbus_raw_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/test_conbus_receive_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/test_discovery_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/test_event_telegram_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/test_homekit_config_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/test_link_number_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/test_module_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/test_output_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/test_reverse_proxy_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/test_system_reply_telegram_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/test_term_logging_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/test_version_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/test_xp20_action_table_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/integration/test_xp24_action_table_integration.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_api/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_cli/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_cli/test_click_tree.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_cli/test_conbus_actiontable_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_cli/test_conbus_blink_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_cli/test_conbus_msactiontable_upload_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_cli/test_datapoint_type_choice.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_cli/test_decorators.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_cli/test_error_handlers.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_cli/test_formatters.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_cli/test_serial_number_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_cli/test_system_function_choice.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_cli/test_term_commands.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_encoding/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_encoding/test_latin1_edge_cases.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_models/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_models/test_conbus.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_models/test_conbus_client_send.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_models/test_conbus_discover.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_models/test_conbus_linknumber.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_models/test_event_telegram.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_models/test_log_entry.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_models/test_logger_config.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_models/test_module_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_models/test_reply_telegram.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_models/test_system_telegram.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_models/test_system_telegram_enhancements.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_models/test_version_telegram.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_models/test_write_config_type.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_models/test_xp20_action_table.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_models/test_xp24_action_table.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_models/test_xp24_action_table_short_format.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_models/test_xp24_action_telegram.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_actiontable_download_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_actiontable_serializer.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_actiontable_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_actiontable_upload_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_base_server_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_blink_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_checksum_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_client_buffer_manager.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_conbus_blink_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_conbus_event_list_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_conbus_event_protocol.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_conbus_event_raw_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_conbus_output_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_conbus_raw_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_conbus_receive_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_conbus_reverse_proxy_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_conbus_scan_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_device_service_factory.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_discovery_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_homekit_cache_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_homekit_config_validator.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_homekit_conson_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_homekit_services.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_log_file_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_module_type_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_protocol.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_protocol_monitor_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_server_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_state_monitor_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_telegram_input_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_telegram_output_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_telegram_protocol.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_telegram_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_version_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_xp20_action_table_serializer.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_xp24_action_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_xp24_action_table_serializer.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_xp24_action_table_service.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_xp33_action_table_serializer.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_xp33_short_format.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_services/test_xp_server_services.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_tui/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_tui/test_protocol_log.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_utils/__init__.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_utils/test_checksum.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_utils/test_event_helper.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_utils/test_logging.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_utils/test_serialization.py +0 -0
- {conson_xp-1.51.0 → conson_xp-1.51.1}/tests/unit/test_utils/test_time_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: conson-xp
|
|
3
|
-
Version: 1.51.
|
|
3
|
+
Version: 1.51.1
|
|
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()
|
|
@@ -61,6 +61,7 @@ class HomekitAccessoryConfig(BaseModel):
|
|
|
61
61
|
service: Service type for the accessory.
|
|
62
62
|
on_action: on code for the accessory.
|
|
63
63
|
off_action: off code for the accessory.
|
|
64
|
+
toggle_action: Optional toggle action code for the accessory.
|
|
64
65
|
hap_accessory: Optional HAP accessory identifier.
|
|
65
66
|
"""
|
|
66
67
|
|
|
@@ -72,6 +73,7 @@ class HomekitAccessoryConfig(BaseModel):
|
|
|
72
73
|
service: str
|
|
73
74
|
on_action: str
|
|
74
75
|
off_action: str
|
|
76
|
+
toggle_action: Optional[str] = None
|
|
75
77
|
hap_accessory: Optional[int] = None
|
|
76
78
|
|
|
77
79
|
|
|
@@ -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,512 @@
|
|
|
1
|
+
"""HomeKit Service for terminal interface."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from typing import Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
from psygnal import Signal
|
|
8
|
+
|
|
9
|
+
from xp.models.config.conson_module_config import ConsonModuleListConfig
|
|
10
|
+
from xp.models.homekit.homekit_config import HomekitAccessoryConfig, HomekitConfig
|
|
11
|
+
from xp.models.protocol.conbus_protocol import TelegramReceivedEvent
|
|
12
|
+
from xp.models.telegram.datapoint_type import DataPointType
|
|
13
|
+
from xp.models.telegram.module_type_code import ModuleTypeCode
|
|
14
|
+
from xp.models.telegram.system_function import SystemFunction
|
|
15
|
+
from xp.models.telegram.telegram_type import TelegramType
|
|
16
|
+
from xp.models.term.accessory_state import AccessoryState
|
|
17
|
+
from xp.models.term.connection_state import ConnectionState
|
|
18
|
+
from xp.services.protocol.conbus_event_protocol import ConbusEventProtocol
|
|
19
|
+
from xp.services.telegram.telegram_output_service import TelegramOutputService
|
|
20
|
+
from xp.services.telegram.telegram_service import TelegramService
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class HomekitService:
|
|
24
|
+
"""
|
|
25
|
+
Service for HomeKit accessory monitoring in terminal interface.
|
|
26
|
+
|
|
27
|
+
Wraps ConbusEventProtocol, HomekitConfig, and ConsonModuleListConfig to provide
|
|
28
|
+
high-level accessory state tracking for the TUI.
|
|
29
|
+
|
|
30
|
+
Attributes:
|
|
31
|
+
on_connection_state_changed: Signal emitted when connection state changes.
|
|
32
|
+
on_room_list_updated: Signal emitted when accessory list refreshed from config.
|
|
33
|
+
on_module_state_changed: Signal emitted when individual accessory state updates.
|
|
34
|
+
on_module_error: Signal emitted when module error occurs.
|
|
35
|
+
on_status_message: Signal emitted for status messages.
|
|
36
|
+
connection_state: Property returning current connection state.
|
|
37
|
+
server_info: Property returning server connection info (IP:port).
|
|
38
|
+
accessory_states: Property returning list of all accessory states.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
on_connection_state_changed: Signal = Signal(ConnectionState)
|
|
42
|
+
on_room_list_updated: Signal = Signal(list)
|
|
43
|
+
on_module_state_changed: Signal = Signal(AccessoryState)
|
|
44
|
+
on_module_error: Signal = Signal(str, str)
|
|
45
|
+
on_status_message: Signal = Signal(str)
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
conbus_protocol: ConbusEventProtocol,
|
|
50
|
+
homekit_config: HomekitConfig,
|
|
51
|
+
conson_config: ConsonModuleListConfig,
|
|
52
|
+
telegram_service: TelegramService,
|
|
53
|
+
) -> None:
|
|
54
|
+
"""
|
|
55
|
+
Initialize the HomeKit service.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
conbus_protocol: ConbusEventProtocol instance.
|
|
59
|
+
homekit_config: HomekitConfig for accessory configuration.
|
|
60
|
+
conson_config: ConsonModuleListConfig for module configuration.
|
|
61
|
+
telegram_service: TelegramService for parsing telegrams.
|
|
62
|
+
"""
|
|
63
|
+
self.logger = logging.getLogger(__name__)
|
|
64
|
+
self._conbus_protocol = conbus_protocol
|
|
65
|
+
self._homekit_config = homekit_config
|
|
66
|
+
self._conson_config = conson_config
|
|
67
|
+
self._telegram_service = telegram_service
|
|
68
|
+
self._connection_state = ConnectionState.DISCONNECTED
|
|
69
|
+
self._state_machine = ConnectionState.create_state_machine()
|
|
70
|
+
|
|
71
|
+
# Accessory states keyed by unique identifier (e.g., "A12_1")
|
|
72
|
+
self._accessory_states: Dict[str, AccessoryState] = {}
|
|
73
|
+
|
|
74
|
+
# Action key to accessory ID mapping
|
|
75
|
+
self._action_map: Dict[str, str] = {}
|
|
76
|
+
|
|
77
|
+
# Connect to protocol signals
|
|
78
|
+
self._connect_signals()
|
|
79
|
+
|
|
80
|
+
# Initialize accessory states from config
|
|
81
|
+
self._initialize_accessory_states()
|
|
82
|
+
|
|
83
|
+
def _initialize_accessory_states(self) -> None:
|
|
84
|
+
"""Initialize accessory states from HomekitConfig and ConsonModuleListConfig."""
|
|
85
|
+
action_keys = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
86
|
+
action_index = 0
|
|
87
|
+
sort_order = 0
|
|
88
|
+
|
|
89
|
+
for room in self._homekit_config.bridge.rooms:
|
|
90
|
+
for accessory_name in room.accessories:
|
|
91
|
+
accessory_config = self._find_accessory_config(accessory_name)
|
|
92
|
+
if not accessory_config:
|
|
93
|
+
self.logger.warning(
|
|
94
|
+
f"Accessory config not found for {accessory_name}"
|
|
95
|
+
)
|
|
96
|
+
continue
|
|
97
|
+
|
|
98
|
+
module_config = self._conson_config.find_module(
|
|
99
|
+
accessory_config.serial_number
|
|
100
|
+
)
|
|
101
|
+
if not module_config:
|
|
102
|
+
self.logger.warning(
|
|
103
|
+
f"Module config not found for {accessory_config.serial_number}"
|
|
104
|
+
)
|
|
105
|
+
continue
|
|
106
|
+
|
|
107
|
+
# Create unique identifier
|
|
108
|
+
accessory_id = (
|
|
109
|
+
f"{module_config.name}_{accessory_config.output_number + 1}"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Assign action key
|
|
113
|
+
action_key = (
|
|
114
|
+
action_keys[action_index] if action_index < len(action_keys) else ""
|
|
115
|
+
)
|
|
116
|
+
action_index += 1
|
|
117
|
+
sort_order += 1
|
|
118
|
+
|
|
119
|
+
state = AccessoryState(
|
|
120
|
+
room_name=room.name,
|
|
121
|
+
accessory_name=accessory_config.description
|
|
122
|
+
or accessory_config.name,
|
|
123
|
+
action=action_key,
|
|
124
|
+
output_state="?",
|
|
125
|
+
dimming_state="",
|
|
126
|
+
module_name=module_config.name,
|
|
127
|
+
serial_number=accessory_config.serial_number,
|
|
128
|
+
module_type=module_config.module_type,
|
|
129
|
+
error_status="OK",
|
|
130
|
+
output=accessory_config.output_number + 1, # 1-based
|
|
131
|
+
sort=sort_order,
|
|
132
|
+
last_update=None,
|
|
133
|
+
toggle_action=accessory_config.toggle_action,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
self._accessory_states[accessory_id] = state
|
|
137
|
+
if action_key:
|
|
138
|
+
self._action_map[action_key] = accessory_id
|
|
139
|
+
|
|
140
|
+
def _find_accessory_config(self, name: str) -> Optional[HomekitAccessoryConfig]:
|
|
141
|
+
"""
|
|
142
|
+
Find accessory config by name.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
name: Accessory name to find.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
HomekitAccessoryConfig if found, None otherwise.
|
|
149
|
+
"""
|
|
150
|
+
for accessory in self._homekit_config.accessories:
|
|
151
|
+
if accessory.name == name:
|
|
152
|
+
return accessory
|
|
153
|
+
return None
|
|
154
|
+
|
|
155
|
+
def _connect_signals(self) -> None:
|
|
156
|
+
"""Connect to protocol signals."""
|
|
157
|
+
self._conbus_protocol.on_connection_made.connect(self._on_connection_made)
|
|
158
|
+
self._conbus_protocol.on_connection_failed.connect(self._on_connection_failed)
|
|
159
|
+
self._conbus_protocol.on_telegram_received.connect(self._on_telegram_received)
|
|
160
|
+
self._conbus_protocol.on_timeout.connect(self._on_timeout)
|
|
161
|
+
self._conbus_protocol.on_failed.connect(self._on_failed)
|
|
162
|
+
|
|
163
|
+
def _disconnect_signals(self) -> None:
|
|
164
|
+
"""Disconnect from protocol signals."""
|
|
165
|
+
self._conbus_protocol.on_connection_made.disconnect(self._on_connection_made)
|
|
166
|
+
self._conbus_protocol.on_connection_failed.disconnect(
|
|
167
|
+
self._on_connection_failed
|
|
168
|
+
)
|
|
169
|
+
self._conbus_protocol.on_telegram_received.disconnect(
|
|
170
|
+
self._on_telegram_received
|
|
171
|
+
)
|
|
172
|
+
self._conbus_protocol.on_timeout.disconnect(self._on_timeout)
|
|
173
|
+
self._conbus_protocol.on_failed.disconnect(self._on_failed)
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
def connection_state(self) -> ConnectionState:
|
|
177
|
+
"""
|
|
178
|
+
Get current connection state.
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
Current connection state.
|
|
182
|
+
"""
|
|
183
|
+
return self._connection_state
|
|
184
|
+
|
|
185
|
+
@property
|
|
186
|
+
def server_info(self) -> str:
|
|
187
|
+
"""
|
|
188
|
+
Get server connection info (IP:port).
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
Server address in format "IP:port".
|
|
192
|
+
"""
|
|
193
|
+
return f"{self._conbus_protocol.cli_config.ip}:{self._conbus_protocol.cli_config.port}"
|
|
194
|
+
|
|
195
|
+
@property
|
|
196
|
+
def accessory_states(self) -> List[AccessoryState]:
|
|
197
|
+
"""
|
|
198
|
+
Get all accessory states.
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
List of all accessory states.
|
|
202
|
+
"""
|
|
203
|
+
accessories = list(self._accessory_states.values())
|
|
204
|
+
# Sort modules by link_number
|
|
205
|
+
accessories.sort(key=lambda a: a.sort)
|
|
206
|
+
return accessories
|
|
207
|
+
|
|
208
|
+
def connect(self) -> None:
|
|
209
|
+
"""Initiate connection to server."""
|
|
210
|
+
if not self._state_machine.can_transition("connect"):
|
|
211
|
+
self.logger.warning(
|
|
212
|
+
f"Cannot connect: current state is {self._connection_state.value}"
|
|
213
|
+
)
|
|
214
|
+
return
|
|
215
|
+
|
|
216
|
+
if self._state_machine.transition("connecting", ConnectionState.CONNECTING):
|
|
217
|
+
self._connection_state = ConnectionState.CONNECTING
|
|
218
|
+
self.on_connection_state_changed.emit(self._connection_state)
|
|
219
|
+
self.on_status_message.emit(f"Connecting to {self.server_info}...")
|
|
220
|
+
|
|
221
|
+
self._conbus_protocol.connect()
|
|
222
|
+
|
|
223
|
+
def disconnect(self) -> None:
|
|
224
|
+
"""Disconnect from server."""
|
|
225
|
+
if not self._state_machine.can_transition("disconnect"):
|
|
226
|
+
self.logger.warning(
|
|
227
|
+
f"Cannot disconnect: current state is {self._connection_state.value}"
|
|
228
|
+
)
|
|
229
|
+
return
|
|
230
|
+
|
|
231
|
+
if self._state_machine.transition(
|
|
232
|
+
"disconnecting", ConnectionState.DISCONNECTING
|
|
233
|
+
):
|
|
234
|
+
self._connection_state = ConnectionState.DISCONNECTING
|
|
235
|
+
self.on_connection_state_changed.emit(self._connection_state)
|
|
236
|
+
self.on_status_message.emit("Disconnecting...")
|
|
237
|
+
|
|
238
|
+
self._conbus_protocol.disconnect()
|
|
239
|
+
|
|
240
|
+
if self._state_machine.transition("disconnected", ConnectionState.DISCONNECTED):
|
|
241
|
+
self._connection_state = ConnectionState.DISCONNECTED
|
|
242
|
+
self.on_connection_state_changed.emit(self._connection_state)
|
|
243
|
+
self.on_status_message.emit("Disconnected")
|
|
244
|
+
|
|
245
|
+
def toggle_connection(self) -> None:
|
|
246
|
+
"""
|
|
247
|
+
Toggle connection state between connected and disconnected.
|
|
248
|
+
|
|
249
|
+
Disconnects if currently connected or connecting. Connects if currently
|
|
250
|
+
disconnected or failed.
|
|
251
|
+
"""
|
|
252
|
+
if self._connection_state in (
|
|
253
|
+
ConnectionState.CONNECTED,
|
|
254
|
+
ConnectionState.CONNECTING,
|
|
255
|
+
):
|
|
256
|
+
self.disconnect()
|
|
257
|
+
else:
|
|
258
|
+
self.connect()
|
|
259
|
+
|
|
260
|
+
def toggle_accessory(self, action_key: str) -> bool:
|
|
261
|
+
"""
|
|
262
|
+
Toggle accessory by action key.
|
|
263
|
+
|
|
264
|
+
Sends the toggle_action telegram for the accessory mapped to the given key.
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
action_key: Action key (a-z).
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
True if toggle was sent, False otherwise.
|
|
271
|
+
"""
|
|
272
|
+
accessory_id = self._action_map.get(action_key)
|
|
273
|
+
if not accessory_id:
|
|
274
|
+
return False
|
|
275
|
+
|
|
276
|
+
state = self._accessory_states.get(accessory_id)
|
|
277
|
+
if not state or not state.toggle_action:
|
|
278
|
+
self.logger.warning(f"No toggle_action for accessory {accessory_id}")
|
|
279
|
+
return False
|
|
280
|
+
|
|
281
|
+
self._conbus_protocol.send_raw_telegram(state.toggle_action)
|
|
282
|
+
self.on_status_message.emit(f"Toggling {state.accessory_name}")
|
|
283
|
+
return True
|
|
284
|
+
|
|
285
|
+
def refresh_all(self) -> None:
|
|
286
|
+
"""
|
|
287
|
+
Refresh all module states.
|
|
288
|
+
|
|
289
|
+
Queries module_output_state datapoint for eligible modules (XP24, XP33LR,
|
|
290
|
+
XP33LED). Updates outputs column and last_update timestamp for each queried
|
|
291
|
+
module.
|
|
292
|
+
"""
|
|
293
|
+
self.on_status_message.emit("Refreshing module states...")
|
|
294
|
+
|
|
295
|
+
# Eligible module types that support output state queries
|
|
296
|
+
eligible_types = {"XP24", "XP33LR", "XP33LED"}
|
|
297
|
+
|
|
298
|
+
# Track already queried serial numbers to avoid duplicates
|
|
299
|
+
queried_serials: set[str] = set()
|
|
300
|
+
|
|
301
|
+
for state in self._accessory_states.values():
|
|
302
|
+
if (
|
|
303
|
+
state.module_type in eligible_types
|
|
304
|
+
and state.serial_number not in queried_serials
|
|
305
|
+
):
|
|
306
|
+
self._query_module_output_state(state.serial_number)
|
|
307
|
+
queried_serials.add(state.serial_number)
|
|
308
|
+
self.logger.debug(
|
|
309
|
+
f"Querying output state for {state.module_name} ({state.module_type})"
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
def _query_module_output_state(self, serial_number: str) -> None:
|
|
313
|
+
"""
|
|
314
|
+
Query module output state datapoint.
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
serial_number: Module serial number to query.
|
|
318
|
+
"""
|
|
319
|
+
self._conbus_protocol.send_telegram(
|
|
320
|
+
telegram_type=TelegramType.SYSTEM,
|
|
321
|
+
serial_number=serial_number,
|
|
322
|
+
system_function=SystemFunction.READ_DATAPOINT,
|
|
323
|
+
data_value=str(DataPointType.MODULE_OUTPUT_STATE.value),
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
def _on_connection_made(self) -> None:
|
|
327
|
+
"""Handle connection made event."""
|
|
328
|
+
if self._state_machine.transition("connected", ConnectionState.CONNECTED):
|
|
329
|
+
self._connection_state = ConnectionState.CONNECTED
|
|
330
|
+
self.on_connection_state_changed.emit(self._connection_state)
|
|
331
|
+
self.on_status_message.emit(f"Connected to {self.server_info}")
|
|
332
|
+
|
|
333
|
+
# Emit initial accessory list
|
|
334
|
+
self.on_room_list_updated.emit(self.accessory_states)
|
|
335
|
+
|
|
336
|
+
def _on_connection_failed(self, failure: Exception) -> None:
|
|
337
|
+
"""
|
|
338
|
+
Handle connection failed event.
|
|
339
|
+
|
|
340
|
+
Args:
|
|
341
|
+
failure: Exception that caused the failure.
|
|
342
|
+
"""
|
|
343
|
+
if self._state_machine.transition("failed", ConnectionState.FAILED):
|
|
344
|
+
self._connection_state = ConnectionState.FAILED
|
|
345
|
+
self.on_connection_state_changed.emit(self._connection_state)
|
|
346
|
+
self.on_status_message.emit(f"Connection failed: {failure}")
|
|
347
|
+
|
|
348
|
+
def _on_telegram_received(self, event: TelegramReceivedEvent) -> None:
|
|
349
|
+
"""
|
|
350
|
+
Handle telegram received event.
|
|
351
|
+
|
|
352
|
+
Routes telegrams to appropriate handlers based on type.
|
|
353
|
+
|
|
354
|
+
Args:
|
|
355
|
+
event: Telegram received event.
|
|
356
|
+
"""
|
|
357
|
+
if event.telegram_type == TelegramType.REPLY:
|
|
358
|
+
self._handle_reply_telegram(event)
|
|
359
|
+
elif event.telegram_type == TelegramType.EVENT:
|
|
360
|
+
self._handle_event_telegram(event)
|
|
361
|
+
|
|
362
|
+
def _handle_reply_telegram(self, event: TelegramReceivedEvent) -> None:
|
|
363
|
+
"""
|
|
364
|
+
Handle reply telegram for datapoint queries.
|
|
365
|
+
|
|
366
|
+
Args:
|
|
367
|
+
event: Telegram received event.
|
|
368
|
+
"""
|
|
369
|
+
serial_number = event.serial_number
|
|
370
|
+
if not serial_number:
|
|
371
|
+
return
|
|
372
|
+
|
|
373
|
+
# Parse the reply telegram
|
|
374
|
+
reply_telegram = self._telegram_service.parse_reply_telegram(event.frame)
|
|
375
|
+
if not reply_telegram:
|
|
376
|
+
return
|
|
377
|
+
|
|
378
|
+
# Check if this is a module output state response
|
|
379
|
+
if (
|
|
380
|
+
reply_telegram.system_function == SystemFunction.READ_DATAPOINT
|
|
381
|
+
and reply_telegram.datapoint_type == DataPointType.MODULE_OUTPUT_STATE
|
|
382
|
+
):
|
|
383
|
+
self._update_outputs_from_reply(serial_number, reply_telegram.data_value)
|
|
384
|
+
|
|
385
|
+
def _update_outputs_from_reply(self, serial_number: str, data_value: str) -> None:
|
|
386
|
+
"""
|
|
387
|
+
Update accessory outputs from module output state reply.
|
|
388
|
+
|
|
389
|
+
Args:
|
|
390
|
+
serial_number: Module serial number.
|
|
391
|
+
data_value: Output state data value from reply.
|
|
392
|
+
"""
|
|
393
|
+
# Parse output state bits using TelegramOutputService
|
|
394
|
+
outputs = TelegramOutputService.format_output_state(data_value)
|
|
395
|
+
output_list = outputs.split() if outputs else []
|
|
396
|
+
|
|
397
|
+
# Update all accessories for this serial_number
|
|
398
|
+
for state in self._accessory_states.values():
|
|
399
|
+
if state.serial_number == serial_number:
|
|
400
|
+
output_index = state.output - 1 # Convert to 0-based
|
|
401
|
+
|
|
402
|
+
if output_index < len(output_list):
|
|
403
|
+
is_on = output_list[output_index] == "1"
|
|
404
|
+
state.output_state = "ON" if is_on else "OFF"
|
|
405
|
+
|
|
406
|
+
# Update dimming state for dimmable modules
|
|
407
|
+
if state.is_dimmable():
|
|
408
|
+
state.dimming_state = "-" if not is_on else ""
|
|
409
|
+
else:
|
|
410
|
+
state.output_state = "?"
|
|
411
|
+
|
|
412
|
+
state.last_update = datetime.now()
|
|
413
|
+
self.on_module_state_changed.emit(state)
|
|
414
|
+
|
|
415
|
+
def _handle_event_telegram(self, event: TelegramReceivedEvent) -> None:
|
|
416
|
+
"""
|
|
417
|
+
Handle event telegram for output state changes.
|
|
418
|
+
|
|
419
|
+
Args:
|
|
420
|
+
event: Telegram received event.
|
|
421
|
+
"""
|
|
422
|
+
event_telegram = self._telegram_service.parse_event_telegram(event.frame)
|
|
423
|
+
if not event_telegram:
|
|
424
|
+
return
|
|
425
|
+
|
|
426
|
+
# Determine output number based on module type
|
|
427
|
+
output_number = None
|
|
428
|
+
|
|
429
|
+
if event_telegram.module_type == ModuleTypeCode.XP24.value:
|
|
430
|
+
if 80 <= event_telegram.input_number <= 83:
|
|
431
|
+
output_number = event_telegram.input_number - 80
|
|
432
|
+
else:
|
|
433
|
+
return
|
|
434
|
+
|
|
435
|
+
elif event_telegram.module_type in (
|
|
436
|
+
ModuleTypeCode.XP33.value,
|
|
437
|
+
ModuleTypeCode.XP33LR.value,
|
|
438
|
+
ModuleTypeCode.XP33LED.value,
|
|
439
|
+
):
|
|
440
|
+
if 80 <= event_telegram.input_number <= 82:
|
|
441
|
+
output_number = event_telegram.input_number - 80
|
|
442
|
+
else:
|
|
443
|
+
return
|
|
444
|
+
else:
|
|
445
|
+
return
|
|
446
|
+
|
|
447
|
+
# Find accessories matching link number and output
|
|
448
|
+
output_1_based = output_number + 1
|
|
449
|
+
for state in self._accessory_states.values():
|
|
450
|
+
module_config = self._conson_config.find_module(state.serial_number)
|
|
451
|
+
if not module_config:
|
|
452
|
+
continue
|
|
453
|
+
|
|
454
|
+
if (
|
|
455
|
+
module_config.link_number == event_telegram.link_number
|
|
456
|
+
and state.output == output_1_based
|
|
457
|
+
):
|
|
458
|
+
# Update output state (M=ON, B=OFF)
|
|
459
|
+
is_on = event_telegram.is_button_press
|
|
460
|
+
state.output_state = "ON" if is_on else "OFF"
|
|
461
|
+
|
|
462
|
+
# Update dimming state for dimmable modules
|
|
463
|
+
if state.is_dimmable():
|
|
464
|
+
state.dimming_state = "-" if not is_on else ""
|
|
465
|
+
|
|
466
|
+
state.last_update = datetime.now()
|
|
467
|
+
self.on_module_state_changed.emit(state)
|
|
468
|
+
|
|
469
|
+
self.logger.debug(
|
|
470
|
+
f"Updated {state.accessory_name} to {'ON' if is_on else 'OFF'}"
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
def _on_timeout(self) -> None:
|
|
474
|
+
"""Handle timeout event."""
|
|
475
|
+
self.on_status_message.emit("Waiting for action")
|
|
476
|
+
|
|
477
|
+
def _on_failed(self, failure: Exception) -> None:
|
|
478
|
+
"""
|
|
479
|
+
Handle protocol failure event.
|
|
480
|
+
|
|
481
|
+
Args:
|
|
482
|
+
failure: Exception that caused the failure.
|
|
483
|
+
"""
|
|
484
|
+
if self._state_machine.transition("failed", ConnectionState.FAILED):
|
|
485
|
+
self._connection_state = ConnectionState.FAILED
|
|
486
|
+
self.on_connection_state_changed.emit(self._connection_state)
|
|
487
|
+
self.on_status_message.emit(f"Protocol error: {failure}")
|
|
488
|
+
|
|
489
|
+
def cleanup(self) -> None:
|
|
490
|
+
"""Clean up service resources."""
|
|
491
|
+
self._disconnect_signals()
|
|
492
|
+
self.logger.debug("HomekitService cleaned up")
|
|
493
|
+
|
|
494
|
+
def __enter__(self) -> "HomekitService":
|
|
495
|
+
"""
|
|
496
|
+
Context manager entry.
|
|
497
|
+
|
|
498
|
+
Returns:
|
|
499
|
+
Self for context manager.
|
|
500
|
+
"""
|
|
501
|
+
return self
|
|
502
|
+
|
|
503
|
+
def __exit__(self, _exc_type: object, _exc_val: object, _exc_tb: object) -> None:
|
|
504
|
+
"""
|
|
505
|
+
Context manager exit.
|
|
506
|
+
|
|
507
|
+
Args:
|
|
508
|
+
_exc_type: Exception type.
|
|
509
|
+
_exc_val: Exception value.
|
|
510
|
+
_exc_tb: Exception traceback.
|
|
511
|
+
"""
|
|
512
|
+
self.cleanup()
|
|
@@ -299,7 +299,7 @@ class StateMonitorService:
|
|
|
299
299
|
|
|
300
300
|
def _on_timeout(self) -> None:
|
|
301
301
|
"""Handle timeout event."""
|
|
302
|
-
self.on_status_message.emit("
|
|
302
|
+
self.on_status_message.emit("Waiting for action")
|
|
303
303
|
|
|
304
304
|
def _on_failed(self, failure: Exception) -> None:
|
|
305
305
|
"""
|