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.
Files changed (179) hide show
  1. {conson_xp-1.46.0.dist-info → conson_xp-1.48.0.dist-info}/METADATA +1 -1
  2. conson_xp-1.48.0.dist-info/RECORD +210 -0
  3. xp/__init__.py +3 -2
  4. xp/cli/commands/conbus/conbus.py +1 -1
  5. xp/cli/commands/conbus/conbus_actiontable_commands.py +33 -19
  6. xp/cli/commands/conbus/conbus_autoreport_commands.py +8 -4
  7. xp/cli/commands/conbus/conbus_blink_commands.py +20 -10
  8. xp/cli/commands/conbus/conbus_config_commands.py +2 -1
  9. xp/cli/commands/conbus/conbus_custom_commands.py +4 -2
  10. xp/cli/commands/conbus/conbus_datapoint_commands.py +10 -5
  11. xp/cli/commands/conbus/conbus_discover_commands.py +8 -4
  12. xp/cli/commands/conbus/conbus_event_commands.py +8 -4
  13. xp/cli/commands/conbus/conbus_export_commands.py +8 -4
  14. xp/cli/commands/conbus/conbus_lightlevel_commands.py +16 -8
  15. xp/cli/commands/conbus/conbus_linknumber_commands.py +8 -4
  16. xp/cli/commands/conbus/conbus_modulenumber_commands.py +8 -4
  17. xp/cli/commands/conbus/conbus_msactiontable_commands.py +78 -40
  18. xp/cli/commands/conbus/conbus_output_commands.py +16 -8
  19. xp/cli/commands/conbus/conbus_raw_commands.py +6 -3
  20. xp/cli/commands/conbus/conbus_receive_commands.py +6 -3
  21. xp/cli/commands/conbus/conbus_scan_commands.py +6 -3
  22. xp/cli/commands/file_commands.py +6 -3
  23. xp/cli/commands/homekit/homekit.py +4 -2
  24. xp/cli/commands/homekit/homekit_start_commands.py +2 -1
  25. xp/cli/commands/module_commands.py +8 -4
  26. xp/cli/commands/reverse_proxy_commands.py +8 -4
  27. xp/cli/commands/server/server_commands.py +6 -3
  28. xp/cli/commands/telegram/telegram_blink_commands.py +4 -2
  29. xp/cli/commands/telegram/telegram_checksum_commands.py +4 -2
  30. xp/cli/commands/telegram/telegram_discover_commands.py +2 -1
  31. xp/cli/commands/telegram/telegram_linknumber_commands.py +4 -2
  32. xp/cli/commands/telegram/telegram_parse_commands.py +4 -2
  33. xp/cli/commands/telegram/telegram_version_commands.py +2 -1
  34. xp/cli/commands/term/term_commands.py +4 -2
  35. xp/cli/main.py +2 -1
  36. xp/cli/utils/click_tree.py +6 -3
  37. xp/cli/utils/datapoint_type_choice.py +4 -2
  38. xp/cli/utils/decorators.py +42 -21
  39. xp/cli/utils/error_handlers.py +16 -8
  40. xp/cli/utils/formatters.py +22 -11
  41. xp/cli/utils/module_type_choice.py +4 -2
  42. xp/cli/utils/serial_number_type.py +4 -2
  43. xp/cli/utils/system_function_choice.py +4 -2
  44. xp/cli/utils/xp_module_type.py +4 -2
  45. xp/models/actiontable/actiontable.py +8 -8
  46. xp/models/actiontable/actiontable_type.py +20 -0
  47. xp/models/actiontable/msactiontable_xp20.py +8 -4
  48. xp/models/actiontable/msactiontable_xp24.py +12 -6
  49. xp/models/actiontable/msactiontable_xp33.py +20 -10
  50. xp/models/conbus/conbus.py +8 -4
  51. xp/models/conbus/conbus_autoreport.py +4 -2
  52. xp/models/conbus/conbus_blink.py +4 -2
  53. xp/models/conbus/conbus_client_config.py +6 -3
  54. xp/models/conbus/conbus_connection_status.py +4 -2
  55. xp/models/conbus/conbus_custom.py +4 -2
  56. xp/models/conbus/conbus_datapoint.py +4 -2
  57. xp/models/conbus/conbus_discover.py +6 -3
  58. xp/models/conbus/conbus_event_list.py +4 -2
  59. xp/models/conbus/conbus_event_raw.py +4 -2
  60. xp/models/conbus/conbus_export.py +2 -1
  61. xp/models/conbus/conbus_lightlevel.py +4 -2
  62. xp/models/conbus/conbus_linknumber.py +4 -2
  63. xp/models/conbus/conbus_logger_config.py +8 -4
  64. xp/models/conbus/conbus_output.py +4 -2
  65. xp/models/conbus/conbus_raw.py +4 -2
  66. xp/models/conbus/conbus_receive.py +4 -2
  67. xp/models/conbus/conbus_writeconfig.py +4 -2
  68. xp/models/config/conson_module_config.py +8 -4
  69. xp/models/homekit/homekit_accessory.py +4 -2
  70. xp/models/homekit/homekit_config.py +12 -6
  71. xp/models/log_entry.py +16 -8
  72. xp/models/protocol/conbus_protocol.py +36 -18
  73. xp/models/response.py +12 -8
  74. xp/models/telegram/action_type.py +4 -2
  75. xp/models/telegram/datapoint_type.py +4 -2
  76. xp/models/telegram/event_telegram.py +14 -7
  77. xp/models/telegram/event_type.py +2 -1
  78. xp/models/telegram/input_action_type.py +2 -1
  79. xp/models/telegram/input_type.py +2 -1
  80. xp/models/telegram/module_type.py +24 -12
  81. xp/models/telegram/module_type_code.py +2 -1
  82. xp/models/telegram/output_telegram.py +16 -10
  83. xp/models/telegram/reply_telegram.py +24 -13
  84. xp/models/telegram/system_function.py +6 -3
  85. xp/models/telegram/system_telegram.py +10 -6
  86. xp/models/telegram/telegram.py +2 -1
  87. xp/models/telegram/telegram_type.py +2 -1
  88. xp/models/telegram/timeparam_type.py +2 -1
  89. xp/models/term/connection_state.py +4 -2
  90. xp/models/term/module_state.py +2 -1
  91. xp/models/term/protocol_keys_config.py +6 -3
  92. xp/models/term/status_message.py +2 -1
  93. xp/models/term/telegram_display.py +2 -1
  94. xp/models/write_config_type.py +4 -2
  95. xp/services/actiontable/actiontable_serializer.py +34 -41
  96. xp/services/{conbus/actiontable/actiontable_download_state_machine.py → actiontable/download_state_machine.py} +13 -8
  97. xp/services/actiontable/msactiontable_xp20_serializer.py +73 -50
  98. xp/services/actiontable/msactiontable_xp24_serializer.py +73 -54
  99. xp/services/actiontable/msactiontable_xp33_serializer.py +44 -20
  100. xp/services/actiontable/serializer_protocol.py +76 -0
  101. xp/services/conbus/actiontable/actiontable_download_service.py +68 -31
  102. xp/services/conbus/actiontable/actiontable_list_service.py +17 -4
  103. xp/services/conbus/actiontable/actiontable_show_service.py +10 -6
  104. xp/services/conbus/actiontable/actiontable_upload_service.py +17 -9
  105. xp/services/conbus/conbus_blink_all_service.py +16 -8
  106. xp/services/conbus/conbus_blink_service.py +14 -7
  107. xp/services/conbus/conbus_custom_service.py +16 -8
  108. xp/services/conbus/conbus_datapoint_queryall_service.py +18 -9
  109. xp/services/conbus/conbus_datapoint_service.py +18 -9
  110. xp/services/conbus/conbus_discover_service.py +24 -13
  111. xp/services/conbus/conbus_event_list_service.py +11 -7
  112. xp/services/conbus/conbus_event_raw_service.py +18 -10
  113. xp/services/conbus/conbus_export_service.py +28 -14
  114. xp/services/conbus/conbus_output_service.py +18 -10
  115. xp/services/conbus/conbus_raw_service.py +16 -8
  116. xp/services/conbus/conbus_receive_service.py +18 -10
  117. xp/services/conbus/conbus_scan_service.py +18 -10
  118. xp/services/conbus/msactiontable/msactiontable_upload_service.py +18 -10
  119. xp/services/conbus/write_config_service.py +18 -9
  120. xp/services/homekit/homekit_cache_service.py +12 -6
  121. xp/services/homekit/homekit_conbus_service.py +12 -6
  122. xp/services/homekit/homekit_config_validator.py +34 -17
  123. xp/services/homekit/homekit_conson_validator.py +18 -9
  124. xp/services/homekit/homekit_dimminglight.py +14 -7
  125. xp/services/homekit/homekit_dimminglight_service.py +14 -7
  126. xp/services/homekit/homekit_hap_service.py +18 -9
  127. xp/services/homekit/homekit_lightbulb.py +10 -5
  128. xp/services/homekit/homekit_lightbulb_service.py +10 -5
  129. xp/services/homekit/homekit_module_service.py +8 -4
  130. xp/services/homekit/homekit_outlet.py +14 -7
  131. xp/services/homekit/homekit_outlet_service.py +12 -6
  132. xp/services/homekit/homekit_service.py +24 -12
  133. xp/services/log_file_service.py +16 -8
  134. xp/services/module_type_service.py +10 -5
  135. xp/services/protocol/conbus_event_protocol.py +51 -26
  136. xp/services/protocol/conbus_protocol.py +36 -19
  137. xp/services/protocol/protocol_factory.py +12 -6
  138. xp/services/protocol/telegram_protocol.py +12 -6
  139. xp/services/reverse_proxy_service.py +26 -14
  140. xp/services/server/base_server_service.py +42 -23
  141. xp/services/server/client_buffer_manager.py +12 -7
  142. xp/services/server/cp20_server_service.py +10 -7
  143. xp/services/server/device_service_factory.py +12 -8
  144. xp/services/server/server_service.py +18 -11
  145. xp/services/server/xp130_server_service.py +11 -8
  146. xp/services/server/xp20_server_service.py +16 -10
  147. xp/services/server/xp230_server_service.py +10 -7
  148. xp/services/server/xp24_server_service.py +22 -13
  149. xp/services/server/xp33_server_service.py +44 -25
  150. xp/services/telegram/telegram_blink_service.py +14 -8
  151. xp/services/telegram/telegram_checksum_service.py +12 -7
  152. xp/services/telegram/telegram_datapoint_service.py +14 -9
  153. xp/services/telegram/telegram_discover_service.py +28 -15
  154. xp/services/telegram/telegram_link_number_service.py +18 -10
  155. xp/services/telegram/telegram_output_service.py +24 -12
  156. xp/services/telegram/telegram_service.py +22 -11
  157. xp/services/telegram/telegram_version_service.py +14 -8
  158. xp/services/term/protocol_monitor_service.py +30 -16
  159. xp/services/term/state_monitor_service.py +39 -21
  160. xp/term/protocol.py +12 -6
  161. xp/term/state.py +12 -7
  162. xp/term/widgets/help_menu.py +6 -3
  163. xp/term/widgets/modules_list.py +20 -10
  164. xp/term/widgets/protocol_log.py +12 -6
  165. xp/term/widgets/status_footer.py +10 -5
  166. xp/utils/checksum.py +6 -3
  167. xp/utils/dependencies.py +25 -30
  168. xp/utils/event_helper.py +6 -4
  169. xp/utils/logging.py +6 -3
  170. xp/utils/serialization.py +30 -16
  171. xp/utils/state_machine.py +16 -9
  172. xp/utils/time_utils.py +6 -3
  173. conson_xp-1.46.0.dist-info/RECORD +0 -211
  174. xp/services/conbus/msactiontable/msactiontable_download_service.py +0 -275
  175. xp/services/conbus/msactiontable/msactiontable_list_service.py +0 -100
  176. xp/services/conbus/msactiontable/msactiontable_show_service.py +0 -89
  177. {conson_xp-1.46.0.dist-info → conson_xp-1.48.0.dist-info}/WHEEL +0 -0
  178. {conson_xp-1.46.0.dist-info → conson_xp-1.48.0.dist-info}/entry_points.txt +0 -0
  179. {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
- """Service for module state monitoring in terminal interface.
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
- """Initialize the State Monitor service.
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
- """Get current connection state.
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
- """Get server connection info (IP:port).
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
- """Get all module states.
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
- """Toggle connection state between connected and disconnected.
180
+ """
181
+ Toggle connection state between connected and disconnected.
176
182
 
177
- Disconnects if currently connected or connecting.
178
- Connects if currently disconnected or failed.
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
- """Refresh all module states.
195
+ """
196
+ Refresh all module states.
190
197
 
191
- Queries module_output_state datapoint for eligible modules (XP24, XP33LR, XP33LED).
192
- Updates outputs column and last_update timestamp for each queried module.
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
- """Query module output state datapoint.
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
- """Handle connection failed event.
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
- """Handle telegram received event.
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
- """Handle reply telegram for datapoint queries.
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
- """Handle protocol failure event.
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
- """Find module state by link number.
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
- """Update a single output bit in module state.
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
- """Handle event telegram for output state changes.
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
- """Context manager entry.
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
- """Context manager exit.
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
- """Textual app for real-time protocol monitoring.
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
- """Initialize the Protocol Monitor app.
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
- """Compose the app layout with widgets.
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
- """Initialize app after UI is mounted.
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
- """Toggle connection on 'c' key press.
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
- """Handle key press events for protocol keys.
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
- """Textual app for module state monitoring.
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
- """Initialize the State Monitor app.
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
- """Compose the app layout with widgets.
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
- """Initialize app after UI is mounted.
68
+ """
69
+ Initialize app after UI is mounted.
66
70
 
67
- Delays connection by 0.5s to let UI render first.
68
- Sets up automatic screen refresh every second to update elapsed times.
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
- """Toggle connection on 'c' key press.
89
+ """
90
+ Toggle connection on 'c' key press.
86
91
 
87
92
  Connects if disconnected/failed, disconnects if connected/connecting.
88
93
  """
@@ -11,7 +11,8 @@ if TYPE_CHECKING:
11
11
 
12
12
 
13
13
  class HelpMenuWidget(Vertical):
14
- """Help menu widget displaying keyboard shortcuts and protocol keys.
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
- """Initialize the Help Menu widget.
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
- """Compose the help menu layout.
47
+ """
48
+ Compose the help menu layout.
46
49
 
47
50
  Yields:
48
51
  DataTable widget with key mappings.
@@ -12,7 +12,8 @@ from xp.services.term.state_monitor_service import StateMonitorService
12
12
 
13
13
 
14
14
  class ModulesListWidget(Static):
15
- """Widget displaying module states in a data table.
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
- """Initialize the Modules List widget.
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
- """Compose the widget layout.
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
- """Update entire module list from service.
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
- """Update individual module state in table.
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
- """Add a module row to the table.
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
- """Format outputs for display.
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
- """Format auto-report status for display.
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
- """Format last update timestamp for display.
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
- """Refresh only the last_update column for all modules.
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
  """
@@ -11,7 +11,8 @@ from xp.services.term.protocol_monitor_service import ProtocolMonitorService
11
11
 
12
12
 
13
13
  class ProtocolLogWidget(Widget):
14
- """Widget for displaying protocol telegram stream.
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
- """Initialize the Protocol Log widget.
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
- """Compose the widget layout.
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
- """Initialize widget when mounted.
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
- """Handle telegram display event from service.
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
- """Clean up when widget unmounts.
77
+ """
78
+ Clean up when widget unmounts.
73
79
 
74
80
  Disconnects signals from service.
75
81
  """
@@ -12,7 +12,8 @@ from xp.services.term.state_monitor_service import StateMonitorService
12
12
 
13
13
 
14
14
  class StatusFooterWidget(Horizontal):
15
- """Footer widget with connection status indicator.
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
- """Initialize the Status Footer widget.
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
- """Compose the footer layout.
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
- """Update status indicator with connection state.
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
- """Update status text with message.
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
- """Checksum utility functions for protocol interoperability.
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
- """Calculate simple XOR checksum of a string buffer.
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
- """Calculate CRC32 checksum for protocol interoperability.
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
- wire up all services with their dependencies.
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
- MsActionTableDownloadService,
377
- factory=lambda: MsActionTableDownloadService(
376
+ ActionTableDownloadService,
377
+ factory=lambda: ActionTableDownloadService(
378
378
  conbus_protocol=self.container.resolve(ConbusEventProtocol),
379
- xp20ms_serializer=self.container.resolve(Xp20MsActionTableSerializer),
380
- xp24ms_serializer=self.container.resolve(Xp24MsActionTableSerializer),
381
- xp33ms_serializer=self.container.resolve(Xp33MsActionTableSerializer),
382
- telegram_service=self.container.resolve(TelegramService),
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
- """Load protocol keys from YAML config file.
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
- """Event handling utilities for PyDispatcher integration.
1
+ """
2
+ Event handling utilities for PyDispatcher integration.
2
3
 
3
- This module provides clean, reusable utilities for handling PyDispatcher
4
- responses across all HomeKit accessory classes.
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
- """Extract the first non-None response from PyDispatcher responses.
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()