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