conson-xp 1.46.0__py3-none-any.whl → 1.48.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.48.0.dist-info}/METADATA +1 -1
- conson_xp-1.48.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 -19
- 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 +73 -50
- xp/services/actiontable/msactiontable_xp24_serializer.py +73 -54
- xp/services/actiontable/msactiontable_xp33_serializer.py +44 -20
- xp/services/actiontable/serializer_protocol.py +76 -0
- xp/services/conbus/actiontable/actiontable_download_service.py +68 -31
- 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 +18 -10
- 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 +51 -26
- 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.48.0.dist-info}/WHEEL +0 -0
- {conson_xp-1.46.0.dist-info → conson_xp-1.48.0.dist-info}/entry_points.txt +0 -0
- {conson_xp-1.46.0.dist-info → conson_xp-1.48.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -20,7 +20,8 @@ from xp.services.telegram.telegram_service import TelegramService
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class StateMonitorService:
|
|
23
|
-
"""
|
|
23
|
+
"""
|
|
24
|
+
Service for module state monitoring in terminal interface.
|
|
24
25
|
|
|
25
26
|
Wraps ConbusEventProtocol and ConsonModuleListConfig to provide
|
|
26
27
|
high-level module state tracking for the TUI.
|
|
@@ -48,7 +49,8 @@ class StateMonitorService:
|
|
|
48
49
|
conson_config: ConsonModuleListConfig,
|
|
49
50
|
telegram_service: TelegramService,
|
|
50
51
|
) -> None:
|
|
51
|
-
"""
|
|
52
|
+
"""
|
|
53
|
+
Initialize the State Monitor service.
|
|
52
54
|
|
|
53
55
|
Args:
|
|
54
56
|
conbus_protocol: ConbusEventProtocol instance.
|
|
@@ -109,7 +111,8 @@ class StateMonitorService:
|
|
|
109
111
|
|
|
110
112
|
@property
|
|
111
113
|
def connection_state(self) -> ConnectionState:
|
|
112
|
-
"""
|
|
114
|
+
"""
|
|
115
|
+
Get current connection state.
|
|
113
116
|
|
|
114
117
|
Returns:
|
|
115
118
|
Current connection state.
|
|
@@ -118,7 +121,8 @@ class StateMonitorService:
|
|
|
118
121
|
|
|
119
122
|
@property
|
|
120
123
|
def server_info(self) -> str:
|
|
121
|
-
"""
|
|
124
|
+
"""
|
|
125
|
+
Get server connection info (IP:port).
|
|
122
126
|
|
|
123
127
|
Returns:
|
|
124
128
|
Server address in format "IP:port".
|
|
@@ -127,7 +131,8 @@ class StateMonitorService:
|
|
|
127
131
|
|
|
128
132
|
@property
|
|
129
133
|
def module_states(self) -> List[ModuleState]:
|
|
130
|
-
"""
|
|
134
|
+
"""
|
|
135
|
+
Get all module states.
|
|
131
136
|
|
|
132
137
|
Returns:
|
|
133
138
|
List of all module states.
|
|
@@ -172,10 +177,11 @@ class StateMonitorService:
|
|
|
172
177
|
self.on_status_message.emit("Disconnected")
|
|
173
178
|
|
|
174
179
|
def toggle_connection(self) -> None:
|
|
175
|
-
"""
|
|
180
|
+
"""
|
|
181
|
+
Toggle connection state between connected and disconnected.
|
|
176
182
|
|
|
177
|
-
Disconnects if currently connected or connecting.
|
|
178
|
-
|
|
183
|
+
Disconnects if currently connected or connecting. Connects if currently
|
|
184
|
+
disconnected or failed.
|
|
179
185
|
"""
|
|
180
186
|
if self._connection_state in (
|
|
181
187
|
ConnectionState.CONNECTED,
|
|
@@ -186,10 +192,12 @@ class StateMonitorService:
|
|
|
186
192
|
self.connect()
|
|
187
193
|
|
|
188
194
|
def refresh_all(self) -> None:
|
|
189
|
-
"""
|
|
195
|
+
"""
|
|
196
|
+
Refresh all module states.
|
|
190
197
|
|
|
191
|
-
Queries module_output_state datapoint for eligible modules (XP24, XP33LR,
|
|
192
|
-
Updates outputs column and last_update timestamp for each queried
|
|
198
|
+
Queries module_output_state datapoint for eligible modules (XP24, XP33LR,
|
|
199
|
+
XP33LED). Updates outputs column and last_update timestamp for each queried
|
|
200
|
+
module.
|
|
193
201
|
"""
|
|
194
202
|
self.on_status_message.emit("Refreshing module states...")
|
|
195
203
|
|
|
@@ -205,7 +213,8 @@ class StateMonitorService:
|
|
|
205
213
|
)
|
|
206
214
|
|
|
207
215
|
def _query_module_output_state(self, serial_number: str) -> None:
|
|
208
|
-
"""
|
|
216
|
+
"""
|
|
217
|
+
Query module output state datapoint.
|
|
209
218
|
|
|
210
219
|
Args:
|
|
211
220
|
serial_number: Module serial number to query.
|
|
@@ -228,7 +237,8 @@ class StateMonitorService:
|
|
|
228
237
|
self.on_module_list_updated.emit(self.module_states)
|
|
229
238
|
|
|
230
239
|
def _on_connection_failed(self, failure: Exception) -> None:
|
|
231
|
-
"""
|
|
240
|
+
"""
|
|
241
|
+
Handle connection failed event.
|
|
232
242
|
|
|
233
243
|
Args:
|
|
234
244
|
failure: Exception that caused the failure.
|
|
@@ -239,7 +249,8 @@ class StateMonitorService:
|
|
|
239
249
|
self.on_status_message.emit(f"Connection failed: {failure}")
|
|
240
250
|
|
|
241
251
|
def _on_telegram_received(self, event: TelegramReceivedEvent) -> None:
|
|
242
|
-
"""
|
|
252
|
+
"""
|
|
253
|
+
Handle telegram received event.
|
|
243
254
|
|
|
244
255
|
Routes telegrams to appropriate handlers based on type.
|
|
245
256
|
Processes reply telegrams for datapoint queries and event telegrams for state changes.
|
|
@@ -254,7 +265,8 @@ class StateMonitorService:
|
|
|
254
265
|
self._handle_event_telegram(event)
|
|
255
266
|
|
|
256
267
|
def _handle_reply_telegram(self, event: TelegramReceivedEvent) -> None:
|
|
257
|
-
"""
|
|
268
|
+
"""
|
|
269
|
+
Handle reply telegram for datapoint queries.
|
|
258
270
|
|
|
259
271
|
Args:
|
|
260
272
|
event: Telegram received event.
|
|
@@ -290,7 +302,8 @@ class StateMonitorService:
|
|
|
290
302
|
self.on_status_message.emit("Connection timeout")
|
|
291
303
|
|
|
292
304
|
def _on_failed(self, failure: Exception) -> None:
|
|
293
|
-
"""
|
|
305
|
+
"""
|
|
306
|
+
Handle protocol failure event.
|
|
294
307
|
|
|
295
308
|
Args:
|
|
296
309
|
failure: Exception that caused the failure.
|
|
@@ -301,7 +314,8 @@ class StateMonitorService:
|
|
|
301
314
|
self.on_status_message.emit(f"Protocol error: {failure}")
|
|
302
315
|
|
|
303
316
|
def _find_module_by_link(self, link_number: int) -> Optional[ModuleState]:
|
|
304
|
-
"""
|
|
317
|
+
"""
|
|
318
|
+
Find module state by link number.
|
|
305
319
|
|
|
306
320
|
Args:
|
|
307
321
|
link_number: Link number to search for.
|
|
@@ -317,7 +331,8 @@ class StateMonitorService:
|
|
|
317
331
|
def _update_output_bit(
|
|
318
332
|
self, module_state: ModuleState, output_number: int, output_state: bool
|
|
319
333
|
) -> None:
|
|
320
|
-
"""
|
|
334
|
+
"""
|
|
335
|
+
Update a single output bit in module state.
|
|
321
336
|
|
|
322
337
|
Args:
|
|
323
338
|
module_state: Module state to update.
|
|
@@ -338,7 +353,8 @@ class StateMonitorService:
|
|
|
338
353
|
module_state.outputs = " ".join(outputs)
|
|
339
354
|
|
|
340
355
|
def _handle_event_telegram(self, event: TelegramReceivedEvent) -> None:
|
|
341
|
-
"""
|
|
356
|
+
"""
|
|
357
|
+
Handle event telegram for output state changes.
|
|
342
358
|
|
|
343
359
|
Processes XP24 and XP33 output event telegrams to update module state in real-time.
|
|
344
360
|
- XP24 output events use input_number 80-83 to represent outputs 0-3.
|
|
@@ -415,7 +431,8 @@ class StateMonitorService:
|
|
|
415
431
|
self.logger.debug("StateMonitorService cleaned up")
|
|
416
432
|
|
|
417
433
|
def __enter__(self) -> "StateMonitorService":
|
|
418
|
-
"""
|
|
434
|
+
"""
|
|
435
|
+
Context manager entry.
|
|
419
436
|
|
|
420
437
|
Returns:
|
|
421
438
|
Self for context manager.
|
|
@@ -423,7 +440,8 @@ class StateMonitorService:
|
|
|
423
440
|
return self
|
|
424
441
|
|
|
425
442
|
def __exit__(self, _exc_type: object, _exc_val: object, _exc_tb: object) -> None:
|
|
426
|
-
"""
|
|
443
|
+
"""
|
|
444
|
+
Context manager exit.
|
|
427
445
|
|
|
428
446
|
Args:
|
|
429
447
|
_exc_type: Exception type.
|
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()
|