conson-xp 1.46.0__py3-none-any.whl → 1.47.0__py3-none-any.whl
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.46.0.dist-info → conson_xp-1.47.0.dist-info}/METADATA +1 -1
- conson_xp-1.47.0.dist-info/RECORD +210 -0
- xp/__init__.py +3 -2
- xp/cli/commands/conbus/conbus.py +1 -1
- xp/cli/commands/conbus/conbus_actiontable_commands.py +33 -15
- xp/cli/commands/conbus/conbus_autoreport_commands.py +8 -4
- xp/cli/commands/conbus/conbus_blink_commands.py +20 -10
- xp/cli/commands/conbus/conbus_config_commands.py +2 -1
- xp/cli/commands/conbus/conbus_custom_commands.py +4 -2
- xp/cli/commands/conbus/conbus_datapoint_commands.py +10 -5
- xp/cli/commands/conbus/conbus_discover_commands.py +8 -4
- xp/cli/commands/conbus/conbus_event_commands.py +8 -4
- xp/cli/commands/conbus/conbus_export_commands.py +8 -4
- xp/cli/commands/conbus/conbus_lightlevel_commands.py +16 -8
- xp/cli/commands/conbus/conbus_linknumber_commands.py +8 -4
- xp/cli/commands/conbus/conbus_modulenumber_commands.py +8 -4
- xp/cli/commands/conbus/conbus_msactiontable_commands.py +78 -40
- xp/cli/commands/conbus/conbus_output_commands.py +16 -8
- xp/cli/commands/conbus/conbus_raw_commands.py +6 -3
- xp/cli/commands/conbus/conbus_receive_commands.py +6 -3
- xp/cli/commands/conbus/conbus_scan_commands.py +6 -3
- xp/cli/commands/file_commands.py +6 -3
- xp/cli/commands/homekit/homekit.py +4 -2
- xp/cli/commands/homekit/homekit_start_commands.py +2 -1
- xp/cli/commands/module_commands.py +8 -4
- xp/cli/commands/reverse_proxy_commands.py +8 -4
- xp/cli/commands/server/server_commands.py +6 -3
- xp/cli/commands/telegram/telegram_blink_commands.py +4 -2
- xp/cli/commands/telegram/telegram_checksum_commands.py +4 -2
- xp/cli/commands/telegram/telegram_discover_commands.py +2 -1
- xp/cli/commands/telegram/telegram_linknumber_commands.py +4 -2
- xp/cli/commands/telegram/telegram_parse_commands.py +4 -2
- xp/cli/commands/telegram/telegram_version_commands.py +2 -1
- xp/cli/commands/term/term_commands.py +4 -2
- xp/cli/main.py +2 -1
- xp/cli/utils/click_tree.py +6 -3
- xp/cli/utils/datapoint_type_choice.py +4 -2
- xp/cli/utils/decorators.py +42 -21
- xp/cli/utils/error_handlers.py +16 -8
- xp/cli/utils/formatters.py +22 -11
- xp/cli/utils/module_type_choice.py +4 -2
- xp/cli/utils/serial_number_type.py +4 -2
- xp/cli/utils/system_function_choice.py +4 -2
- xp/cli/utils/xp_module_type.py +4 -2
- xp/models/actiontable/actiontable.py +8 -8
- xp/models/actiontable/actiontable_type.py +20 -0
- xp/models/actiontable/msactiontable_xp20.py +8 -4
- xp/models/actiontable/msactiontable_xp24.py +12 -6
- xp/models/actiontable/msactiontable_xp33.py +20 -10
- xp/models/conbus/conbus.py +8 -4
- xp/models/conbus/conbus_autoreport.py +4 -2
- xp/models/conbus/conbus_blink.py +4 -2
- xp/models/conbus/conbus_client_config.py +6 -3
- xp/models/conbus/conbus_connection_status.py +4 -2
- xp/models/conbus/conbus_custom.py +4 -2
- xp/models/conbus/conbus_datapoint.py +4 -2
- xp/models/conbus/conbus_discover.py +6 -3
- xp/models/conbus/conbus_event_list.py +4 -2
- xp/models/conbus/conbus_event_raw.py +4 -2
- xp/models/conbus/conbus_export.py +2 -1
- xp/models/conbus/conbus_lightlevel.py +4 -2
- xp/models/conbus/conbus_linknumber.py +4 -2
- xp/models/conbus/conbus_logger_config.py +8 -4
- xp/models/conbus/conbus_output.py +4 -2
- xp/models/conbus/conbus_raw.py +4 -2
- xp/models/conbus/conbus_receive.py +4 -2
- xp/models/conbus/conbus_writeconfig.py +4 -2
- xp/models/config/conson_module_config.py +8 -4
- xp/models/homekit/homekit_accessory.py +4 -2
- xp/models/homekit/homekit_config.py +12 -6
- xp/models/log_entry.py +16 -8
- xp/models/protocol/conbus_protocol.py +36 -18
- xp/models/response.py +12 -8
- xp/models/telegram/action_type.py +4 -2
- xp/models/telegram/datapoint_type.py +4 -2
- xp/models/telegram/event_telegram.py +14 -7
- xp/models/telegram/event_type.py +2 -1
- xp/models/telegram/input_action_type.py +2 -1
- xp/models/telegram/input_type.py +2 -1
- xp/models/telegram/module_type.py +24 -12
- xp/models/telegram/module_type_code.py +2 -1
- xp/models/telegram/output_telegram.py +16 -10
- xp/models/telegram/reply_telegram.py +24 -13
- xp/models/telegram/system_function.py +6 -3
- xp/models/telegram/system_telegram.py +10 -6
- xp/models/telegram/telegram.py +2 -1
- xp/models/telegram/telegram_type.py +2 -1
- xp/models/telegram/timeparam_type.py +2 -1
- xp/models/term/connection_state.py +4 -2
- xp/models/term/module_state.py +2 -1
- xp/models/term/protocol_keys_config.py +6 -3
- xp/models/term/status_message.py +2 -1
- xp/models/term/telegram_display.py +2 -1
- xp/models/write_config_type.py +4 -2
- xp/services/actiontable/actiontable_serializer.py +34 -41
- xp/services/{conbus/actiontable/actiontable_download_state_machine.py → actiontable/download_state_machine.py} +13 -8
- xp/services/actiontable/msactiontable_xp20_serializer.py +77 -49
- xp/services/actiontable/msactiontable_xp24_serializer.py +78 -53
- xp/services/actiontable/msactiontable_xp33_serializer.py +39 -9
- xp/services/actiontable/serializer_protocol.py +76 -0
- xp/services/conbus/actiontable/actiontable_download_service.py +63 -29
- xp/services/conbus/actiontable/actiontable_list_service.py +17 -4
- xp/services/conbus/actiontable/actiontable_show_service.py +10 -6
- xp/services/conbus/actiontable/actiontable_upload_service.py +17 -9
- xp/services/conbus/conbus_blink_all_service.py +16 -8
- xp/services/conbus/conbus_blink_service.py +14 -7
- xp/services/conbus/conbus_custom_service.py +16 -8
- xp/services/conbus/conbus_datapoint_queryall_service.py +18 -9
- xp/services/conbus/conbus_datapoint_service.py +18 -9
- xp/services/conbus/conbus_discover_service.py +24 -13
- xp/services/conbus/conbus_event_list_service.py +11 -7
- xp/services/conbus/conbus_event_raw_service.py +18 -10
- xp/services/conbus/conbus_export_service.py +28 -14
- xp/services/conbus/conbus_output_service.py +18 -10
- xp/services/conbus/conbus_raw_service.py +16 -8
- xp/services/conbus/conbus_receive_service.py +18 -10
- xp/services/conbus/conbus_scan_service.py +18 -10
- xp/services/conbus/msactiontable/msactiontable_upload_service.py +17 -9
- xp/services/conbus/write_config_service.py +18 -9
- xp/services/homekit/homekit_cache_service.py +12 -6
- xp/services/homekit/homekit_conbus_service.py +12 -6
- xp/services/homekit/homekit_config_validator.py +34 -17
- xp/services/homekit/homekit_conson_validator.py +18 -9
- xp/services/homekit/homekit_dimminglight.py +14 -7
- xp/services/homekit/homekit_dimminglight_service.py +14 -7
- xp/services/homekit/homekit_hap_service.py +18 -9
- xp/services/homekit/homekit_lightbulb.py +10 -5
- xp/services/homekit/homekit_lightbulb_service.py +10 -5
- xp/services/homekit/homekit_module_service.py +8 -4
- xp/services/homekit/homekit_outlet.py +14 -7
- xp/services/homekit/homekit_outlet_service.py +12 -6
- xp/services/homekit/homekit_service.py +24 -12
- xp/services/log_file_service.py +16 -8
- xp/services/module_type_service.py +10 -5
- xp/services/protocol/conbus_event_protocol.py +46 -24
- xp/services/protocol/conbus_protocol.py +36 -19
- xp/services/protocol/protocol_factory.py +12 -6
- xp/services/protocol/telegram_protocol.py +12 -6
- xp/services/reverse_proxy_service.py +26 -14
- xp/services/server/base_server_service.py +42 -23
- xp/services/server/client_buffer_manager.py +12 -7
- xp/services/server/cp20_server_service.py +10 -7
- xp/services/server/device_service_factory.py +12 -8
- xp/services/server/server_service.py +18 -11
- xp/services/server/xp130_server_service.py +11 -8
- xp/services/server/xp20_server_service.py +16 -10
- xp/services/server/xp230_server_service.py +10 -7
- xp/services/server/xp24_server_service.py +22 -13
- xp/services/server/xp33_server_service.py +44 -25
- xp/services/telegram/telegram_blink_service.py +14 -8
- xp/services/telegram/telegram_checksum_service.py +12 -7
- xp/services/telegram/telegram_datapoint_service.py +14 -9
- xp/services/telegram/telegram_discover_service.py +28 -15
- xp/services/telegram/telegram_link_number_service.py +18 -10
- xp/services/telegram/telegram_output_service.py +24 -12
- xp/services/telegram/telegram_service.py +22 -11
- xp/services/telegram/telegram_version_service.py +14 -8
- xp/services/term/protocol_monitor_service.py +30 -16
- xp/services/term/state_monitor_service.py +39 -21
- xp/term/protocol.py +12 -6
- xp/term/state.py +12 -7
- xp/term/widgets/help_menu.py +6 -3
- xp/term/widgets/modules_list.py +20 -10
- xp/term/widgets/protocol_log.py +12 -6
- xp/term/widgets/status_footer.py +10 -5
- xp/utils/checksum.py +6 -3
- xp/utils/dependencies.py +25 -30
- xp/utils/event_helper.py +6 -4
- xp/utils/logging.py +6 -3
- xp/utils/serialization.py +30 -16
- xp/utils/state_machine.py +16 -9
- xp/utils/time_utils.py +6 -3
- conson_xp-1.46.0.dist-info/RECORD +0 -211
- xp/services/conbus/msactiontable/msactiontable_download_service.py +0 -275
- xp/services/conbus/msactiontable/msactiontable_list_service.py +0 -100
- xp/services/conbus/msactiontable/msactiontable_show_service.py +0 -89
- {conson_xp-1.46.0.dist-info → conson_xp-1.47.0.dist-info}/WHEEL +0 -0
- {conson_xp-1.46.0.dist-info → conson_xp-1.47.0.dist-info}/entry_points.txt +0 -0
- {conson_xp-1.46.0.dist-info → conson_xp-1.47.0.dist-info}/licenses/LICENSE +0 -0
xp/term/protocol.py
CHANGED
|
@@ -13,7 +13,8 @@ from xp.term.widgets.status_footer import StatusFooterWidget
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class ProtocolMonitorApp(App[None]):
|
|
16
|
-
"""
|
|
16
|
+
"""
|
|
17
|
+
Textual app for real-time protocol monitoring.
|
|
17
18
|
|
|
18
19
|
Displays live RX/TX telegram stream from Conbus server in an interactive
|
|
19
20
|
terminal interface with keyboard shortcuts for control.
|
|
@@ -38,7 +39,8 @@ class ProtocolMonitorApp(App[None]):
|
|
|
38
39
|
]
|
|
39
40
|
|
|
40
41
|
def __init__(self, protocol_service: ProtocolMonitorService) -> None:
|
|
41
|
-
"""
|
|
42
|
+
"""
|
|
43
|
+
Initialize the Protocol Monitor app.
|
|
42
44
|
|
|
43
45
|
Args:
|
|
44
46
|
protocol_service: ProtocolMonitorService for protocol operations.
|
|
@@ -50,7 +52,8 @@ class ProtocolMonitorApp(App[None]):
|
|
|
50
52
|
self.footer_widget: Optional[StatusFooterWidget] = None
|
|
51
53
|
|
|
52
54
|
def compose(self) -> ComposeResult:
|
|
53
|
-
"""
|
|
55
|
+
"""
|
|
56
|
+
Compose the app layout with widgets.
|
|
54
57
|
|
|
55
58
|
Yields:
|
|
56
59
|
ProtocolLogWidget and Footer widgets.
|
|
@@ -71,7 +74,8 @@ class ProtocolMonitorApp(App[None]):
|
|
|
71
74
|
yield self.footer_widget
|
|
72
75
|
|
|
73
76
|
async def on_mount(self) -> None:
|
|
74
|
-
"""
|
|
77
|
+
"""
|
|
78
|
+
Initialize app after UI is mounted.
|
|
75
79
|
|
|
76
80
|
Delays connection by 0.5s to let UI render first.
|
|
77
81
|
"""
|
|
@@ -82,7 +86,8 @@ class ProtocolMonitorApp(App[None]):
|
|
|
82
86
|
self.protocol_service.connect()
|
|
83
87
|
|
|
84
88
|
def action_toggle_connection(self) -> None:
|
|
85
|
-
"""
|
|
89
|
+
"""
|
|
90
|
+
Toggle connection on 'c' key press.
|
|
86
91
|
|
|
87
92
|
Connects if disconnected/failed, disconnects if connected/connecting.
|
|
88
93
|
"""
|
|
@@ -94,7 +99,8 @@ class ProtocolMonitorApp(App[None]):
|
|
|
94
99
|
self.protocol_widget.clear_log()
|
|
95
100
|
|
|
96
101
|
def on_key(self, event: Any) -> None:
|
|
97
|
-
"""
|
|
102
|
+
"""
|
|
103
|
+
Handle key press events for protocol keys.
|
|
98
104
|
|
|
99
105
|
Args:
|
|
100
106
|
event: Key press event from Textual.
|
xp/term/state.py
CHANGED
|
@@ -11,7 +11,8 @@ from xp.term.widgets.status_footer import StatusFooterWidget
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class StateMonitorApp(App[None]):
|
|
14
|
-
"""
|
|
14
|
+
"""
|
|
15
|
+
Textual app for module state monitoring.
|
|
15
16
|
|
|
16
17
|
Displays module states from Conson configuration in an interactive
|
|
17
18
|
terminal interface with real-time updates.
|
|
@@ -35,7 +36,8 @@ class StateMonitorApp(App[None]):
|
|
|
35
36
|
]
|
|
36
37
|
|
|
37
38
|
def __init__(self, state_service: StateMonitorService) -> None:
|
|
38
|
-
"""
|
|
39
|
+
"""
|
|
40
|
+
Initialize the State Monitor app.
|
|
39
41
|
|
|
40
42
|
Args:
|
|
41
43
|
state_service: StateMonitorService for module state operations.
|
|
@@ -46,7 +48,8 @@ class StateMonitorApp(App[None]):
|
|
|
46
48
|
self.footer_widget: Optional[StatusFooterWidget] = None
|
|
47
49
|
|
|
48
50
|
def compose(self) -> ComposeResult:
|
|
49
|
-
"""
|
|
51
|
+
"""
|
|
52
|
+
Compose the app layout with widgets.
|
|
50
53
|
|
|
51
54
|
Yields:
|
|
52
55
|
ModulesListWidget and StatusFooterWidget.
|
|
@@ -62,10 +65,11 @@ class StateMonitorApp(App[None]):
|
|
|
62
65
|
yield self.footer_widget
|
|
63
66
|
|
|
64
67
|
async def on_mount(self) -> None:
|
|
65
|
-
"""
|
|
68
|
+
"""
|
|
69
|
+
Initialize app after UI is mounted.
|
|
66
70
|
|
|
67
|
-
Delays connection by 0.5s to let UI render first.
|
|
68
|
-
|
|
71
|
+
Delays connection by 0.5s to let UI render first. Sets up automatic screen
|
|
72
|
+
refresh every second to update elapsed times.
|
|
69
73
|
"""
|
|
70
74
|
import asyncio
|
|
71
75
|
|
|
@@ -82,7 +86,8 @@ class StateMonitorApp(App[None]):
|
|
|
82
86
|
self.modules_widget.refresh_last_update_times()
|
|
83
87
|
|
|
84
88
|
def action_toggle_connection(self) -> None:
|
|
85
|
-
"""
|
|
89
|
+
"""
|
|
90
|
+
Toggle connection on 'c' key press.
|
|
86
91
|
|
|
87
92
|
Connects if disconnected/failed, disconnects if connected/connecting.
|
|
88
93
|
"""
|
xp/term/widgets/help_menu.py
CHANGED
|
@@ -11,7 +11,8 @@ if TYPE_CHECKING:
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class HelpMenuWidget(Vertical):
|
|
14
|
-
"""
|
|
14
|
+
"""
|
|
15
|
+
Help menu widget displaying keyboard shortcuts and protocol keys.
|
|
15
16
|
|
|
16
17
|
Displays a table of available keyboard shortcuts mapped to their
|
|
17
18
|
corresponding protocol commands.
|
|
@@ -27,7 +28,8 @@ class HelpMenuWidget(Vertical):
|
|
|
27
28
|
*args: Any,
|
|
28
29
|
**kwargs: Any,
|
|
29
30
|
) -> None:
|
|
30
|
-
"""
|
|
31
|
+
"""
|
|
32
|
+
Initialize the Help Menu widget.
|
|
31
33
|
|
|
32
34
|
Args:
|
|
33
35
|
service: ProtocolMonitorService instance.
|
|
@@ -42,7 +44,8 @@ class HelpMenuWidget(Vertical):
|
|
|
42
44
|
self.border_title = "Help menu"
|
|
43
45
|
|
|
44
46
|
def compose(self) -> ComposeResult:
|
|
45
|
-
"""
|
|
47
|
+
"""
|
|
48
|
+
Compose the help menu layout.
|
|
46
49
|
|
|
47
50
|
Yields:
|
|
48
51
|
DataTable widget with key mappings.
|
xp/term/widgets/modules_list.py
CHANGED
|
@@ -12,7 +12,8 @@ from xp.services.term.state_monitor_service import StateMonitorService
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class ModulesListWidget(Static):
|
|
15
|
-
"""
|
|
15
|
+
"""
|
|
16
|
+
Widget displaying module states in a data table.
|
|
16
17
|
|
|
17
18
|
Shows module information with real-time updates from StateMonitorService.
|
|
18
19
|
Table displays: name, serial_number, module_type, link_number, outputs, report, status, last_update.
|
|
@@ -28,7 +29,8 @@ class ModulesListWidget(Static):
|
|
|
28
29
|
*args: Any,
|
|
29
30
|
**kwargs: Any,
|
|
30
31
|
) -> None:
|
|
31
|
-
"""
|
|
32
|
+
"""
|
|
33
|
+
Initialize the Modules List widget.
|
|
32
34
|
|
|
33
35
|
Args:
|
|
34
36
|
service: Optional StateMonitorService for signal subscriptions.
|
|
@@ -41,7 +43,8 @@ class ModulesListWidget(Static):
|
|
|
41
43
|
self._row_keys: dict[str, Any] = {} # Map serial_number to row key
|
|
42
44
|
|
|
43
45
|
def compose(self) -> ComposeResult:
|
|
44
|
-
"""
|
|
46
|
+
"""
|
|
47
|
+
Compose the widget layout.
|
|
45
48
|
|
|
46
49
|
Yields:
|
|
47
50
|
DataTable widget.
|
|
@@ -76,7 +79,8 @@ class ModulesListWidget(Static):
|
|
|
76
79
|
self.service.on_module_state_changed.disconnect(self.update_module_state)
|
|
77
80
|
|
|
78
81
|
def update_module_list(self, module_states: List[ModuleState]) -> None:
|
|
79
|
-
"""
|
|
82
|
+
"""
|
|
83
|
+
Update entire module list from service.
|
|
80
84
|
|
|
81
85
|
Clears existing table and repopulates with all modules.
|
|
82
86
|
|
|
@@ -95,7 +99,8 @@ class ModulesListWidget(Static):
|
|
|
95
99
|
self._add_module_row(module_state)
|
|
96
100
|
|
|
97
101
|
def update_module_state(self, module_state: ModuleState) -> None:
|
|
98
|
-
"""
|
|
102
|
+
"""
|
|
103
|
+
Update individual module state in table.
|
|
99
104
|
|
|
100
105
|
Updates existing row if module exists, otherwise adds new row.
|
|
101
106
|
|
|
@@ -133,7 +138,8 @@ class ModulesListWidget(Static):
|
|
|
133
138
|
self._add_module_row(module_state)
|
|
134
139
|
|
|
135
140
|
def _add_module_row(self, module_state: ModuleState) -> None:
|
|
136
|
-
"""
|
|
141
|
+
"""
|
|
142
|
+
Add a module row to the table.
|
|
137
143
|
|
|
138
144
|
Args:
|
|
139
145
|
module_state: Module state to add.
|
|
@@ -154,7 +160,8 @@ class ModulesListWidget(Static):
|
|
|
154
160
|
self._row_keys[module_state.serial_number] = row_key
|
|
155
161
|
|
|
156
162
|
def _format_outputs(self, outputs: str) -> str:
|
|
157
|
-
"""
|
|
163
|
+
"""
|
|
164
|
+
Format outputs for display.
|
|
158
165
|
|
|
159
166
|
Args:
|
|
160
167
|
outputs: Raw output string.
|
|
@@ -165,7 +172,8 @@ class ModulesListWidget(Static):
|
|
|
165
172
|
return outputs
|
|
166
173
|
|
|
167
174
|
def _format_report(self, auto_report: bool) -> str:
|
|
168
|
-
"""
|
|
175
|
+
"""
|
|
176
|
+
Format auto-report status for display.
|
|
169
177
|
|
|
170
178
|
Args:
|
|
171
179
|
auto_report: Auto-report boolean value.
|
|
@@ -176,7 +184,8 @@ class ModulesListWidget(Static):
|
|
|
176
184
|
return "Y" if auto_report else "N"
|
|
177
185
|
|
|
178
186
|
def _format_last_update(self, last_update: Optional[datetime]) -> str:
|
|
179
|
-
"""
|
|
187
|
+
"""
|
|
188
|
+
Format last update timestamp for display.
|
|
180
189
|
|
|
181
190
|
Shows elapsed time in HH:MM:SS format or "--:--:--" if never updated.
|
|
182
191
|
|
|
@@ -200,7 +209,8 @@ class ModulesListWidget(Static):
|
|
|
200
209
|
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
|
|
201
210
|
|
|
202
211
|
def refresh_last_update_times(self) -> None:
|
|
203
|
-
"""
|
|
212
|
+
"""
|
|
213
|
+
Refresh only the last_update column for all modules.
|
|
204
214
|
|
|
205
215
|
Updates the elapsed time display without querying the service.
|
|
206
216
|
"""
|
xp/term/widgets/protocol_log.py
CHANGED
|
@@ -11,7 +11,8 @@ from xp.services.term.protocol_monitor_service import ProtocolMonitorService
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class ProtocolLogWidget(Widget):
|
|
14
|
-
"""
|
|
14
|
+
"""
|
|
15
|
+
Widget for displaying protocol telegram stream.
|
|
15
16
|
|
|
16
17
|
Displays live RX/TX telegram stream with color-coded direction markers
|
|
17
18
|
via ProtocolMonitorService.
|
|
@@ -23,7 +24,8 @@ class ProtocolLogWidget(Widget):
|
|
|
23
24
|
"""
|
|
24
25
|
|
|
25
26
|
def __init__(self, service: ProtocolMonitorService) -> None:
|
|
26
|
-
"""
|
|
27
|
+
"""
|
|
28
|
+
Initialize the Protocol Log widget.
|
|
27
29
|
|
|
28
30
|
Args:
|
|
29
31
|
service: ProtocolMonitorService instance for protocol operations.
|
|
@@ -35,7 +37,8 @@ class ProtocolLogWidget(Widget):
|
|
|
35
37
|
self.log_widget: Optional[RichLog] = None
|
|
36
38
|
|
|
37
39
|
def compose(self) -> Any:
|
|
38
|
-
"""
|
|
40
|
+
"""
|
|
41
|
+
Compose the widget layout.
|
|
39
42
|
|
|
40
43
|
Yields:
|
|
41
44
|
RichLog widget for message display.
|
|
@@ -44,7 +47,8 @@ class ProtocolLogWidget(Widget):
|
|
|
44
47
|
yield self.log_widget
|
|
45
48
|
|
|
46
49
|
def on_mount(self) -> None:
|
|
47
|
-
"""
|
|
50
|
+
"""
|
|
51
|
+
Initialize widget when mounted.
|
|
48
52
|
|
|
49
53
|
Connects to service signals for telegram display.
|
|
50
54
|
"""
|
|
@@ -52,7 +56,8 @@ class ProtocolLogWidget(Widget):
|
|
|
52
56
|
self.service.on_telegram_display.connect(self._on_telegram_display)
|
|
53
57
|
|
|
54
58
|
def _on_telegram_display(self, event: TelegramDisplayEvent) -> None:
|
|
55
|
-
"""
|
|
59
|
+
"""
|
|
60
|
+
Handle telegram display event from service.
|
|
56
61
|
|
|
57
62
|
Args:
|
|
58
63
|
event: Telegram display event with direction and telegram data.
|
|
@@ -69,7 +74,8 @@ class ProtocolLogWidget(Widget):
|
|
|
69
74
|
self.log_widget.clear()
|
|
70
75
|
|
|
71
76
|
def on_unmount(self) -> None:
|
|
72
|
-
"""
|
|
77
|
+
"""
|
|
78
|
+
Clean up when widget unmounts.
|
|
73
79
|
|
|
74
80
|
Disconnects signals from service.
|
|
75
81
|
"""
|
xp/term/widgets/status_footer.py
CHANGED
|
@@ -12,7 +12,8 @@ from xp.services.term.state_monitor_service import StateMonitorService
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class StatusFooterWidget(Horizontal):
|
|
15
|
-
"""
|
|
15
|
+
"""
|
|
16
|
+
Footer widget with connection status indicator.
|
|
16
17
|
|
|
17
18
|
Combines the Textual Footer with a status indicator dot that shows
|
|
18
19
|
the current connection state. Subscribes directly to service signals.
|
|
@@ -29,7 +30,8 @@ class StatusFooterWidget(Horizontal):
|
|
|
29
30
|
*args: Any,
|
|
30
31
|
**kwargs: Any,
|
|
31
32
|
) -> None:
|
|
32
|
-
"""
|
|
33
|
+
"""
|
|
34
|
+
Initialize the Status Footer widget.
|
|
33
35
|
|
|
34
36
|
Args:
|
|
35
37
|
service: Optional ProtocolMonitorService or StateMonitorService for signal subscriptions.
|
|
@@ -42,7 +44,8 @@ class StatusFooterWidget(Horizontal):
|
|
|
42
44
|
self.status_widget: Static = Static("○", id="status-line")
|
|
43
45
|
|
|
44
46
|
def compose(self) -> ComposeResult:
|
|
45
|
-
"""
|
|
47
|
+
"""
|
|
48
|
+
Compose the footer layout.
|
|
46
49
|
|
|
47
50
|
Yields:
|
|
48
51
|
Footer and status indicator widgets.
|
|
@@ -64,7 +67,8 @@ class StatusFooterWidget(Horizontal):
|
|
|
64
67
|
self.service.on_status_message.disconnect(self.update_message)
|
|
65
68
|
|
|
66
69
|
def update_status(self, state: ConnectionState) -> None:
|
|
67
|
-
"""
|
|
70
|
+
"""
|
|
71
|
+
Update status indicator with connection state.
|
|
68
72
|
|
|
69
73
|
Args:
|
|
70
74
|
state: Current connection state (ConnectionState enum).
|
|
@@ -80,7 +84,8 @@ class StatusFooterWidget(Horizontal):
|
|
|
80
84
|
self.status_widget.update(dot)
|
|
81
85
|
|
|
82
86
|
def update_message(self, message: str) -> None:
|
|
83
|
-
"""
|
|
87
|
+
"""
|
|
88
|
+
Update status text with message.
|
|
84
89
|
|
|
85
90
|
Args:
|
|
86
91
|
message: Status message to display.
|
xp/utils/checksum.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Checksum utility functions for protocol interoperability.
|
|
2
3
|
|
|
3
4
|
This module provides standard checksum calculation functions for protocol
|
|
4
5
|
communication compatibility, including XOR checksum and IEEE 802.3 CRC32.
|
|
@@ -12,7 +13,8 @@ from xp.utils.serialization import nibble
|
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
def calculate_checksum(buffer: str) -> str:
|
|
15
|
-
"""
|
|
16
|
+
"""
|
|
17
|
+
Calculate simple XOR checksum of a string buffer.
|
|
16
18
|
|
|
17
19
|
Args:
|
|
18
20
|
buffer: Input string to calculate checksum for
|
|
@@ -28,7 +30,8 @@ def calculate_checksum(buffer: str) -> str:
|
|
|
28
30
|
|
|
29
31
|
|
|
30
32
|
def calculate_checksum32(buffer: bytes) -> str:
|
|
31
|
-
"""
|
|
33
|
+
"""
|
|
34
|
+
Calculate CRC32 checksum for protocol interoperability.
|
|
32
35
|
|
|
33
36
|
Implements standard CRC32 algorithm using IEEE 802.3 polynomial 0xEDB88320
|
|
34
37
|
for interoperability with XP protocol communications. This is a standard
|
xp/utils/dependencies.py
CHANGED
|
@@ -51,15 +51,6 @@ from xp.services.conbus.conbus_output_service import ConbusOutputService
|
|
|
51
51
|
from xp.services.conbus.conbus_raw_service import ConbusRawService
|
|
52
52
|
from xp.services.conbus.conbus_receive_service import ConbusReceiveService
|
|
53
53
|
from xp.services.conbus.conbus_scan_service import ConbusScanService
|
|
54
|
-
from xp.services.conbus.msactiontable.msactiontable_download_service import (
|
|
55
|
-
MsActionTableDownloadService,
|
|
56
|
-
)
|
|
57
|
-
from xp.services.conbus.msactiontable.msactiontable_list_service import (
|
|
58
|
-
MsActionTableListService,
|
|
59
|
-
)
|
|
60
|
-
from xp.services.conbus.msactiontable.msactiontable_show_service import (
|
|
61
|
-
MsActionTableShowService,
|
|
62
|
-
)
|
|
63
54
|
from xp.services.conbus.msactiontable.msactiontable_upload_service import (
|
|
64
55
|
MsActionTableUploadService,
|
|
65
56
|
)
|
|
@@ -100,8 +91,8 @@ class ServiceContainer:
|
|
|
100
91
|
"""
|
|
101
92
|
Service container that manages dependency injection for all XP services.
|
|
102
93
|
|
|
103
|
-
Uses the service dependency graph from Dependencies.dot to properly
|
|
104
|
-
|
|
94
|
+
Uses the service dependency graph from Dependencies.dot to properly wire up all
|
|
95
|
+
services with their dependencies.
|
|
105
96
|
"""
|
|
106
97
|
|
|
107
98
|
def __init__(
|
|
@@ -327,6 +318,15 @@ class ServiceContainer:
|
|
|
327
318
|
factory=lambda: ActionTableDownloadService(
|
|
328
319
|
conbus_protocol=self.container.resolve(ConbusEventProtocol),
|
|
329
320
|
actiontable_serializer=self.container.resolve(ActionTableSerializer),
|
|
321
|
+
msactiontable_serializer_xp20=self.container.resolve(
|
|
322
|
+
Xp20MsActionTableSerializer
|
|
323
|
+
),
|
|
324
|
+
msactiontable_serializer_xp24=self.container.resolve(
|
|
325
|
+
Xp24MsActionTableSerializer
|
|
326
|
+
),
|
|
327
|
+
msactiontable_serializer_xp33=self.container.resolve(
|
|
328
|
+
Xp33MsActionTableSerializer
|
|
329
|
+
),
|
|
330
330
|
),
|
|
331
331
|
scope=punq.Scope.singleton,
|
|
332
332
|
)
|
|
@@ -373,13 +373,19 @@ class ServiceContainer:
|
|
|
373
373
|
)
|
|
374
374
|
|
|
375
375
|
self.container.register(
|
|
376
|
-
|
|
377
|
-
factory=lambda:
|
|
376
|
+
ActionTableDownloadService,
|
|
377
|
+
factory=lambda: ActionTableDownloadService(
|
|
378
378
|
conbus_protocol=self.container.resolve(ConbusEventProtocol),
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
379
|
+
actiontable_serializer=self.container.resolve(ActionTableSerializer),
|
|
380
|
+
msactiontable_serializer_xp20=self.container.resolve(
|
|
381
|
+
Xp20MsActionTableSerializer
|
|
382
|
+
),
|
|
383
|
+
msactiontable_serializer_xp24=self.container.resolve(
|
|
384
|
+
Xp24MsActionTableSerializer
|
|
385
|
+
),
|
|
386
|
+
msactiontable_serializer_xp33=self.container.resolve(
|
|
387
|
+
Xp33MsActionTableSerializer
|
|
388
|
+
),
|
|
383
389
|
),
|
|
384
390
|
scope=punq.Scope.singleton,
|
|
385
391
|
)
|
|
@@ -494,18 +500,6 @@ class ServiceContainer:
|
|
|
494
500
|
scope=punq.Scope.singleton,
|
|
495
501
|
)
|
|
496
502
|
|
|
497
|
-
self.container.register(
|
|
498
|
-
MsActionTableListService,
|
|
499
|
-
factory=MsActionTableListService,
|
|
500
|
-
scope=punq.Scope.singleton,
|
|
501
|
-
)
|
|
502
|
-
|
|
503
|
-
self.container.register(
|
|
504
|
-
MsActionTableShowService,
|
|
505
|
-
factory=MsActionTableShowService,
|
|
506
|
-
scope=punq.Scope.singleton,
|
|
507
|
-
)
|
|
508
|
-
|
|
509
503
|
# Server services layer
|
|
510
504
|
self.container.register(
|
|
511
505
|
ServerService,
|
|
@@ -629,7 +623,8 @@ class ServiceContainer:
|
|
|
629
623
|
)
|
|
630
624
|
|
|
631
625
|
def _load_protocol_keys(self) -> "ProtocolKeysConfig":
|
|
632
|
-
"""
|
|
626
|
+
"""
|
|
627
|
+
Load protocol keys from YAML config file.
|
|
633
628
|
|
|
634
629
|
Returns:
|
|
635
630
|
ProtocolKeysConfig instance loaded from configuration path.
|
xp/utils/event_helper.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Event handling utilities for PyDispatcher integration.
|
|
2
3
|
|
|
3
|
-
This module provides clean, reusable utilities for handling PyDispatcher
|
|
4
|
-
|
|
4
|
+
This module provides clean, reusable utilities for handling PyDispatcher responses
|
|
5
|
+
across all HomeKit accessory classes.
|
|
5
6
|
"""
|
|
6
7
|
|
|
7
8
|
from typing import Any, Callable, List, Tuple
|
|
@@ -10,7 +11,8 @@ from typing import Any, Callable, List, Tuple
|
|
|
10
11
|
def get_first_response(
|
|
11
12
|
responses: List[Tuple[Callable, Any]], default: Any = None
|
|
12
13
|
) -> Any:
|
|
13
|
-
"""
|
|
14
|
+
"""
|
|
15
|
+
Extract the first non-None response from PyDispatcher responses.
|
|
14
16
|
|
|
15
17
|
Args:
|
|
16
18
|
responses: List of (receiver_function, return_value) tuples from dispatcher.send()
|
xp/utils/logging.py
CHANGED
|
@@ -11,7 +11,8 @@ class LoggerService:
|
|
|
11
11
|
"""Service for managing logging configuration and setup."""
|
|
12
12
|
|
|
13
13
|
def __init__(self, logger_config: ConbusLoggerConfig):
|
|
14
|
-
"""
|
|
14
|
+
"""
|
|
15
|
+
Initialize LoggerService with configuration.
|
|
15
16
|
|
|
16
17
|
Args:
|
|
17
18
|
logger_config: Logger configuration object.
|
|
@@ -44,7 +45,8 @@ class LoggerService:
|
|
|
44
45
|
logging.getLogger(module).setLevel(self.logging_config.levels[module])
|
|
45
46
|
|
|
46
47
|
def setup_console_logging(self, log_format: str, date_format: str) -> None:
|
|
47
|
-
"""
|
|
48
|
+
"""
|
|
49
|
+
Setup console logging with specified format.
|
|
48
50
|
|
|
49
51
|
Args:
|
|
50
52
|
log_format: Log message format string.
|
|
@@ -68,7 +70,8 @@ class LoggerService:
|
|
|
68
70
|
root_logger.addHandler(handler)
|
|
69
71
|
|
|
70
72
|
def setup_file_logging(self, log_format: str, date_format: str) -> None:
|
|
71
|
-
"""
|
|
73
|
+
"""
|
|
74
|
+
Setup file logging with rotation for term application.
|
|
72
75
|
|
|
73
76
|
Args:
|
|
74
77
|
log_format: Log message format string.
|
xp/utils/serialization.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Binary serialization utility functions.
|
|
2
3
|
|
|
3
|
-
This module provides common binary manipulation functions used across
|
|
4
|
-
|
|
4
|
+
This module provides common binary manipulation functions used across the XP protocol
|
|
5
|
+
serializers for consistent data encoding/decoding.
|
|
5
6
|
"""
|
|
6
7
|
|
|
7
8
|
from typing import List
|
|
@@ -14,7 +15,8 @@ UPPER5 = 248 # 0xF8
|
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
def de_bcd(byte_val: int) -> int:
|
|
17
|
-
"""
|
|
18
|
+
"""
|
|
19
|
+
Convert BCD byte to decimal.
|
|
18
20
|
|
|
19
21
|
Args:
|
|
20
22
|
byte_val: BCD encoded byte
|
|
@@ -26,7 +28,8 @@ def de_bcd(byte_val: int) -> int:
|
|
|
26
28
|
|
|
27
29
|
|
|
28
30
|
def to_bcd(decimal_val: int) -> int:
|
|
29
|
-
"""
|
|
31
|
+
"""
|
|
32
|
+
Convert decimal to BCD byte.
|
|
30
33
|
|
|
31
34
|
Args:
|
|
32
35
|
decimal_val: Decimal value to convert
|
|
@@ -40,7 +43,8 @@ def to_bcd(decimal_val: int) -> int:
|
|
|
40
43
|
|
|
41
44
|
|
|
42
45
|
def lower3(byte_val: int) -> int:
|
|
43
|
-
"""
|
|
46
|
+
"""
|
|
47
|
+
Extract lower 3 bits from byte.
|
|
44
48
|
|
|
45
49
|
Args:
|
|
46
50
|
byte_val: Input byte
|
|
@@ -52,7 +56,8 @@ def lower3(byte_val: int) -> int:
|
|
|
52
56
|
|
|
53
57
|
|
|
54
58
|
def upper5(byte_val: int) -> int:
|
|
55
|
-
"""
|
|
59
|
+
"""
|
|
60
|
+
Extract upper 5 bits from byte.
|
|
56
61
|
|
|
57
62
|
Args:
|
|
58
63
|
byte_val: Input byte
|
|
@@ -64,7 +69,8 @@ def upper5(byte_val: int) -> int:
|
|
|
64
69
|
|
|
65
70
|
|
|
66
71
|
def byte_to_bits(byte_value: int) -> List[bool]:
|
|
67
|
-
"""
|
|
72
|
+
"""
|
|
73
|
+
Convert a byte value to 8-bit boolean array.
|
|
68
74
|
|
|
69
75
|
Args:
|
|
70
76
|
byte_value: Byte value to convert
|
|
@@ -76,7 +82,8 @@ def byte_to_bits(byte_value: int) -> List[bool]:
|
|
|
76
82
|
|
|
77
83
|
|
|
78
84
|
def bits_to_byte(bits: List[bool]) -> int:
|
|
79
|
-
"""
|
|
85
|
+
"""
|
|
86
|
+
Convert boolean array to byte value.
|
|
80
87
|
|
|
81
88
|
Args:
|
|
82
89
|
bits: List of boolean values representing bits
|
|
@@ -92,7 +99,8 @@ def bits_to_byte(bits: List[bool]) -> int:
|
|
|
92
99
|
|
|
93
100
|
|
|
94
101
|
def highest_bit_set(value: int) -> int:
|
|
95
|
-
"""
|
|
102
|
+
"""
|
|
103
|
+
Remove the high bit (0x80) from a byte value.
|
|
96
104
|
|
|
97
105
|
Args:
|
|
98
106
|
value: Byte value to process
|
|
@@ -104,7 +112,8 @@ def highest_bit_set(value: int) -> int:
|
|
|
104
112
|
|
|
105
113
|
|
|
106
114
|
def remove_highest_bit(value: int) -> int:
|
|
107
|
-
"""
|
|
115
|
+
"""
|
|
116
|
+
Remove the high bit (0x80) from a byte value.
|
|
108
117
|
|
|
109
118
|
Args:
|
|
110
119
|
value: Byte value to process
|
|
@@ -116,7 +125,8 @@ def remove_highest_bit(value: int) -> int:
|
|
|
116
125
|
|
|
117
126
|
|
|
118
127
|
def byte_to_unsigned(byte_val: int) -> int:
|
|
119
|
-
"""
|
|
128
|
+
"""
|
|
129
|
+
Convert signed byte to unsigned integer.
|
|
120
130
|
|
|
121
131
|
Args:
|
|
122
132
|
byte_val: Byte value (can be negative)
|
|
@@ -130,7 +140,8 @@ def byte_to_unsigned(byte_val: int) -> int:
|
|
|
130
140
|
|
|
131
141
|
|
|
132
142
|
def nibble(byte_val: int) -> str:
|
|
133
|
-
"""
|
|
143
|
+
"""
|
|
144
|
+
Convert byte value to two-character nibble representation.
|
|
134
145
|
|
|
135
146
|
Args:
|
|
136
147
|
byte_val: Byte value (0-255)
|
|
@@ -144,7 +155,8 @@ def nibble(byte_val: int) -> str:
|
|
|
144
155
|
|
|
145
156
|
|
|
146
157
|
def de_nibble(nibble_str: str) -> int:
|
|
147
|
-
"""
|
|
158
|
+
"""
|
|
159
|
+
Convert two-character nibble string to byte value.
|
|
148
160
|
|
|
149
161
|
Based on pseudocode: A=0, B=1, C=2, ..., P=15
|
|
150
162
|
|
|
@@ -171,7 +183,8 @@ def de_nibble(nibble_str: str) -> int:
|
|
|
171
183
|
|
|
172
184
|
|
|
173
185
|
def de_nibbles(str_val: str) -> bytearray:
|
|
174
|
-
"""
|
|
186
|
+
"""
|
|
187
|
+
Convert hex string with A-P encoding to list of integers.
|
|
175
188
|
|
|
176
189
|
Based on pseudocode: A=0, B=1, C=2, ..., P=15
|
|
177
190
|
|
|
@@ -194,7 +207,8 @@ def de_nibbles(str_val: str) -> bytearray:
|
|
|
194
207
|
|
|
195
208
|
|
|
196
209
|
def nibbles(data: bytes) -> str:
|
|
197
|
-
"""
|
|
210
|
+
"""
|
|
211
|
+
Convert bytes data to nibble string representation.
|
|
198
212
|
|
|
199
213
|
Args:
|
|
200
214
|
data: Bytes data to convert
|