conson-xp 1.0.1__py3-none-any.whl → 1.2.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 (167) hide show
  1. {conson_xp-1.0.1.dist-info → conson_xp-1.2.0.dist-info}/METADATA +1 -1
  2. conson_xp-1.2.0.dist-info/RECORD +181 -0
  3. xp/__init__.py +4 -3
  4. xp/api/main.py +18 -3
  5. xp/api/models/api.py +13 -2
  6. xp/api/models/discover.py +12 -2
  7. xp/api/routers/conbus_blink.py +18 -6
  8. xp/api/routers/conbus_custom.py +11 -3
  9. xp/api/routers/conbus_datapoint.py +10 -3
  10. xp/api/routers/conbus_output.py +29 -9
  11. xp/api/routers/errors.py +6 -5
  12. xp/cli/__init__.py +1 -1
  13. xp/cli/commands/__init__.py +1 -0
  14. xp/cli/commands/api.py +1 -5
  15. xp/cli/commands/api_start_commands.py +14 -8
  16. xp/cli/commands/conbus/conbus.py +9 -37
  17. xp/cli/commands/conbus/conbus_actiontable_commands.py +21 -1
  18. xp/cli/commands/conbus/conbus_autoreport_commands.py +21 -11
  19. xp/cli/commands/conbus/conbus_blink_commands.py +53 -21
  20. xp/cli/commands/conbus/conbus_config_commands.py +7 -4
  21. xp/cli/commands/conbus/conbus_custom_commands.py +13 -4
  22. xp/cli/commands/conbus/conbus_datapoint_commands.py +28 -8
  23. xp/cli/commands/conbus/conbus_discover_commands.py +15 -4
  24. xp/cli/commands/conbus/conbus_lightlevel_commands.py +50 -11
  25. xp/cli/commands/conbus/conbus_linknumber_commands.py +21 -11
  26. xp/cli/commands/conbus/conbus_msactiontable_commands.py +25 -1
  27. xp/cli/commands/conbus/conbus_output_commands.py +46 -12
  28. xp/cli/commands/conbus/conbus_raw_commands.py +17 -6
  29. xp/cli/commands/conbus/conbus_receive_commands.py +15 -7
  30. xp/cli/commands/conbus/conbus_scan_commands.py +35 -102
  31. xp/cli/commands/file_commands.py +26 -15
  32. xp/cli/commands/homekit/homekit.py +14 -8
  33. xp/cli/commands/homekit/homekit_start_commands.py +5 -5
  34. xp/cli/commands/module_commands.py +26 -19
  35. xp/cli/commands/reverse_proxy_commands.py +24 -18
  36. xp/cli/commands/server/server_commands.py +18 -18
  37. xp/cli/commands/telegram/telegram.py +4 -12
  38. xp/cli/commands/telegram/telegram_blink_commands.py +10 -8
  39. xp/cli/commands/telegram/telegram_checksum_commands.py +19 -8
  40. xp/cli/commands/telegram/telegram_discover_commands.py +2 -4
  41. xp/cli/commands/telegram/telegram_linknumber_commands.py +11 -8
  42. xp/cli/commands/telegram/telegram_parse_commands.py +10 -9
  43. xp/cli/commands/telegram/telegram_version_commands.py +8 -4
  44. xp/cli/main.py +5 -1
  45. xp/cli/utils/click_tree.py +23 -3
  46. xp/cli/utils/datapoint_type_choice.py +20 -0
  47. xp/cli/utils/decorators.py +165 -14
  48. xp/cli/utils/error_handlers.py +49 -18
  49. xp/cli/utils/formatters.py +95 -10
  50. xp/cli/utils/serial_number_type.py +18 -0
  51. xp/cli/utils/system_function_choice.py +20 -0
  52. xp/cli/utils/xp_module_type.py +20 -0
  53. xp/connection/__init__.py +1 -1
  54. xp/connection/exceptions.py +5 -5
  55. xp/models/__init__.py +1 -1
  56. xp/models/actiontable/__init__.py +1 -0
  57. xp/models/actiontable/actiontable.py +17 -1
  58. xp/models/actiontable/msactiontable_xp20.py +10 -0
  59. xp/models/actiontable/msactiontable_xp24.py +20 -3
  60. xp/models/actiontable/msactiontable_xp33.py +27 -4
  61. xp/models/conbus/__init__.py +1 -0
  62. xp/models/conbus/conbus.py +34 -4
  63. xp/models/conbus/conbus_autoreport.py +20 -2
  64. xp/models/conbus/conbus_blink.py +22 -2
  65. xp/models/conbus/conbus_client_config.py +22 -1
  66. xp/models/conbus/conbus_connection_status.py +16 -2
  67. xp/models/conbus/conbus_custom.py +21 -2
  68. xp/models/conbus/conbus_datapoint.py +22 -2
  69. xp/models/conbus/conbus_discover.py +18 -2
  70. xp/models/conbus/conbus_lightlevel.py +20 -2
  71. xp/models/conbus/conbus_linknumber.py +20 -2
  72. xp/models/conbus/conbus_output.py +22 -2
  73. xp/models/conbus/conbus_raw.py +17 -2
  74. xp/models/conbus/conbus_receive.py +16 -2
  75. xp/models/homekit/__init__.py +1 -0
  76. xp/models/homekit/homekit_accessory.py +15 -1
  77. xp/models/homekit/homekit_config.py +52 -0
  78. xp/models/homekit/homekit_conson_config.py +32 -0
  79. xp/models/log_entry.py +49 -9
  80. xp/models/protocol/__init__.py +1 -0
  81. xp/models/protocol/conbus_protocol.py +130 -21
  82. xp/models/telegram/__init__.py +1 -0
  83. xp/models/telegram/action_type.py +16 -2
  84. xp/models/telegram/datapoint_type.py +36 -2
  85. xp/models/telegram/event_telegram.py +46 -10
  86. xp/models/telegram/event_type.py +8 -1
  87. xp/models/telegram/input_action_type.py +34 -2
  88. xp/models/telegram/input_type.py +9 -1
  89. xp/models/telegram/module_type.py +69 -19
  90. xp/models/telegram/module_type_code.py +43 -1
  91. xp/models/telegram/output_telegram.py +30 -6
  92. xp/models/telegram/reply_telegram.py +56 -11
  93. xp/models/telegram/system_function.py +35 -3
  94. xp/models/telegram/system_telegram.py +18 -4
  95. xp/models/telegram/telegram.py +12 -3
  96. xp/models/telegram/telegram_type.py +8 -1
  97. xp/models/telegram/timeparam_type.py +27 -0
  98. xp/models/write_config_type.py +17 -2
  99. xp/services/__init__.py +1 -1
  100. xp/services/conbus/__init__.py +1 -0
  101. xp/services/conbus/actiontable/__init__.py +1 -0
  102. xp/services/conbus/actiontable/actiontable_service.py +33 -2
  103. xp/services/conbus/actiontable/msactiontable_service.py +40 -3
  104. xp/services/conbus/actiontable/msactiontable_xp24_serializer.py +36 -4
  105. xp/services/conbus/actiontable/msactiontable_xp33_serializer.py +45 -5
  106. xp/services/conbus/conbus_autoreport_get_service.py +17 -8
  107. xp/services/conbus/conbus_autoreport_set_service.py +29 -16
  108. xp/services/conbus/conbus_blink_all_service.py +40 -21
  109. xp/services/conbus/conbus_blink_service.py +37 -13
  110. xp/services/conbus/conbus_custom_service.py +29 -13
  111. xp/services/conbus/conbus_datapoint_queryall_service.py +40 -16
  112. xp/services/conbus/conbus_datapoint_service.py +33 -12
  113. xp/services/conbus/conbus_discover_service.py +43 -7
  114. xp/services/conbus/conbus_lightlevel_get_service.py +22 -14
  115. xp/services/conbus/conbus_lightlevel_set_service.py +40 -20
  116. xp/services/conbus/conbus_linknumber_get_service.py +18 -10
  117. xp/services/conbus/conbus_linknumber_set_service.py +34 -8
  118. xp/services/conbus/conbus_output_service.py +33 -13
  119. xp/services/conbus/conbus_raw_service.py +36 -16
  120. xp/services/conbus/conbus_receive_service.py +38 -6
  121. xp/services/conbus/conbus_scan_service.py +45 -19
  122. xp/services/homekit/__init__.py +1 -0
  123. xp/services/homekit/homekit_cache_service.py +31 -6
  124. xp/services/homekit/homekit_conbus_service.py +33 -2
  125. xp/services/homekit/homekit_config_validator.py +97 -15
  126. xp/services/homekit/homekit_conson_validator.py +51 -7
  127. xp/services/homekit/homekit_dimminglight.py +47 -1
  128. xp/services/homekit/homekit_dimminglight_service.py +35 -1
  129. xp/services/homekit/homekit_hap_service.py +71 -18
  130. xp/services/homekit/homekit_lightbulb.py +35 -1
  131. xp/services/homekit/homekit_lightbulb_service.py +30 -2
  132. xp/services/homekit/homekit_module_service.py +23 -1
  133. xp/services/homekit/homekit_outlet.py +47 -1
  134. xp/services/homekit/homekit_outlet_service.py +44 -2
  135. xp/services/homekit/homekit_service.py +113 -19
  136. xp/services/log_file_service.py +37 -41
  137. xp/services/module_type_service.py +26 -5
  138. xp/services/protocol/__init__.py +1 -1
  139. xp/services/protocol/conbus_protocol.py +115 -20
  140. xp/services/protocol/protocol_factory.py +40 -0
  141. xp/services/protocol/telegram_protocol.py +38 -7
  142. xp/services/reverse_proxy_service.py +79 -14
  143. xp/services/server/__init__.py +1 -0
  144. xp/services/server/base_server_service.py +102 -14
  145. xp/services/server/cp20_server_service.py +12 -4
  146. xp/services/server/server_service.py +26 -11
  147. xp/services/server/xp130_server_service.py +11 -3
  148. xp/services/server/xp20_server_service.py +11 -3
  149. xp/services/server/xp230_server_service.py +11 -3
  150. xp/services/server/xp24_server_service.py +33 -6
  151. xp/services/server/xp33_server_service.py +41 -8
  152. xp/services/telegram/__init__.py +1 -0
  153. xp/services/telegram/telegram_blink_service.py +19 -31
  154. xp/services/telegram/telegram_checksum_service.py +10 -10
  155. xp/services/telegram/telegram_discover_service.py +58 -29
  156. xp/services/telegram/telegram_link_number_service.py +27 -40
  157. xp/services/telegram/telegram_output_service.py +46 -49
  158. xp/services/telegram/telegram_service.py +41 -41
  159. xp/services/telegram/telegram_version_service.py +4 -2
  160. xp/utils/__init__.py +1 -1
  161. xp/utils/dependencies.py +0 -1
  162. xp/utils/serialization.py +6 -0
  163. xp/utils/time_utils.py +6 -11
  164. conson_xp-1.0.1.dist-info/RECORD +0 -181
  165. {conson_xp-1.0.1.dist-info → conson_xp-1.2.0.dist-info}/WHEEL +0 -0
  166. {conson_xp-1.0.1.dist-info → conson_xp-1.2.0.dist-info}/entry_points.txt +0 -0
  167. {conson_xp-1.0.1.dist-info → conson_xp-1.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,3 +1,8 @@
1
+ """HomeKit Service for Apple HomeKit integration.
2
+
3
+ This module provides the main service for HomeKit integration.
4
+ """
5
+
1
6
  # Install asyncio reactor before importing reactor
2
7
 
3
8
  import asyncio
@@ -29,6 +34,23 @@ from xp.services.protocol.protocol_factory import TelegramFactory
29
34
 
30
35
 
31
36
  class HomeKitService:
37
+ """Main HomeKit service for Apple HomeKit integration.
38
+
39
+ Attributes:
40
+ cli_config: Conbus client configuration.
41
+ reactor: Twisted reactor instance.
42
+ telegram_factory: Telegram factory for protocol.
43
+ protocol: Telegram protocol instance.
44
+ event_bus: Event bus for inter-service communication.
45
+ lightbulb_service: Lightbulb service instance.
46
+ dimminglight_service: Dimming light service instance.
47
+ outlet_service: Outlet service instance.
48
+ cache_service: Cache service instance.
49
+ conbus_service: Conbus service instance.
50
+ module_factory: HAP service instance.
51
+ telegram_service: Telegram service instance.
52
+ logger: Logger instance.
53
+ """
32
54
 
33
55
  def __init__(
34
56
  self,
@@ -44,7 +66,21 @@ class HomeKitService:
44
66
  module_factory: HomekitHapService,
45
67
  telegram_service: TelegramService,
46
68
  ):
47
-
69
+ """Initialize the HomeKit service.
70
+
71
+ Args:
72
+ cli_config: Conbus client configuration.
73
+ event_bus: Event bus instance.
74
+ telegram_factory: Telegram factory instance.
75
+ reactor: Twisted reactor instance.
76
+ lightbulb_service: Lightbulb service instance.
77
+ outlet_service: Outlet service instance.
78
+ dimminglight_service: Dimming light service instance.
79
+ cache_service: Cache service instance.
80
+ conbus_service: Conbus service instance.
81
+ module_factory: HAP service instance.
82
+ telegram_service: Telegram service instance.
83
+ """
48
84
  self.cli_config = cli_config.conbus
49
85
  self.reactor = reactor
50
86
  self.telegram_factory = telegram_factory
@@ -67,23 +103,24 @@ class HomeKitService:
67
103
  self.event_bus.on(ModuleDiscoveredEvent, self.handle_module_discovered)
68
104
 
69
105
  def start(self) -> None:
70
- self.logger.info("Starting HomeKit service...")
106
+ """Start the HomeKit service."""
107
+ self.logger.info("Starting HomeKit service.")
71
108
  self.logger.debug("start")
72
109
 
73
110
  # Run reactor in its own dedicated thread
74
- self.logger.info("Starting reactor in dedicated thread...")
111
+ self.logger.info("Starting reactor in dedicated thread.")
75
112
  reactor_thread = threading.Thread(
76
113
  target=self._run_reactor_in_thread, daemon=True, name="ReactorThread"
77
114
  )
78
115
  reactor_thread.start()
79
116
 
80
117
  # Keep MainThread alive while reactor thread runs
81
- self.logger.info("Reactor thread started, MainThread waiting...")
118
+ self.logger.info("Reactor thread started, MainThread waiting.")
82
119
  reactor_thread.join()
83
120
 
84
121
  def _run_reactor_in_thread(self) -> None:
85
- """Run reactor in dedicated thread with its own event loop"""
86
- self.logger.info("Reactor thread starting...")
122
+ """Run reactor in dedicated thread with its own event loop."""
123
+ self.logger.info("Reactor thread starting.")
87
124
 
88
125
  # The asyncio reactor already has an event loop set up
89
126
  # We just need to use it
@@ -101,17 +138,21 @@ class HomeKitService:
101
138
  self.reactor.callLater(0, self._start_module_factory)
102
139
 
103
140
  # Run the reactor (which now uses asyncio underneath)
104
- self.logger.info("Starting reactor event loop...")
141
+ self.logger.info("Starting reactor event loop.")
105
142
  self.reactor.run()
106
143
 
107
144
  def _start_module_factory(self) -> None:
108
- """Start module factory after reactor starts"""
109
- self.logger.info("Starting module factory...")
145
+ """Start module factory after reactor starts.
146
+
147
+ Creates and schedules an async task to start the HAP service.
148
+ """
149
+ self.logger.info("Starting module factory.")
110
150
  self.logger.debug("callWhenRunning executed, scheduling async task")
111
151
 
112
152
  # Run HAP-python driver asynchronously in the reactor's event loop
113
153
  async def async_start() -> None:
114
- self.logger.info("async_start executing...")
154
+ """Start the HAP service asynchronously."""
155
+ self.logger.info("async_start executing.")
115
156
  try:
116
157
  await self.module_factory.async_start()
117
158
  self.logger.info("Module factory started successfully")
@@ -130,23 +171,42 @@ class HomeKitService:
130
171
 
131
172
  # Event handlers
132
173
  def handle_connection_made(self, event: ConnectionMadeEvent) -> None:
133
- """Handle connection established - send initial telegram"""
174
+ """Handle connection established - send initial telegram.
175
+
176
+ Args:
177
+ event: Connection made event.
178
+ """
134
179
  self.logger.debug("Connection established successfully")
135
180
  self.logger.debug("Sending initial discovery telegram: S0000000000F01D00")
136
181
  event.protocol.sendFrame(b"S0000000000F01D00")
137
182
 
138
183
  def handle_connection_failed(self, event: ConnectionFailedEvent) -> None:
139
- """Handle connection failed"""
184
+ """Handle connection failed.
185
+
186
+ Args:
187
+ event: Connection failed event.
188
+ """
140
189
  self.logger.error(f"Connection failed: {event.reason}")
141
190
 
142
191
  def handle_connection_lost(self, event: ConnectionLostEvent) -> None:
143
- """Handle connection lost"""
192
+ """Handle connection lost.
193
+
194
+ Args:
195
+ event: Connection lost event.
196
+ """
144
197
  self.logger.warning(
145
198
  f"Connection lost: {event.reason if hasattr(event, 'reason') else 'Unknown reason'}"
146
199
  )
147
200
 
148
201
  def handle_telegram_received(self, event: TelegramReceivedEvent) -> str:
149
- """Handle received telegram events"""
202
+ """Handle received telegram events.
203
+
204
+ Args:
205
+ event: Telegram received event.
206
+
207
+ Returns:
208
+ Frame data from the event.
209
+ """
150
210
  self.logger.debug(
151
211
  f"handle_telegram_received ENTERED with telegram: {event.telegram}"
152
212
  )
@@ -176,10 +236,18 @@ class HomeKitService:
176
236
  return event.frame
177
237
 
178
238
  def dispatch_light_level_event(self, event: TelegramReceivedEvent) -> None:
179
- self.logger.debug("Light level Datapoint, parsing telegram...")
239
+ """Dispatch light level received event.
240
+
241
+ Args:
242
+ event: Telegram received event.
243
+ """
244
+ self.logger.debug("Light level Datapoint, parsing telegram.")
180
245
  reply_telegram = self.telegram_service.parse_reply_telegram(event.frame)
181
246
  self.logger.debug(
182
- f"Parsed telegram: serial={reply_telegram.serial_number}, type={reply_telegram.datapoint_type}, value={reply_telegram.data_value}"
247
+ f"Parsed telegram: "
248
+ f"serial={reply_telegram.serial_number}, "
249
+ f"type={reply_telegram.datapoint_type}, "
250
+ f"value={reply_telegram.data_value}"
183
251
  )
184
252
  self.logger.debug("About to dispatch LightLevelReceivedEvent")
185
253
  self.event_bus.dispatch(
@@ -192,10 +260,18 @@ class HomeKitService:
192
260
  self.logger.debug("LightLevelReceivedEvent dispatched successfully")
193
261
 
194
262
  def dispatch_output_state_event(self, event: TelegramReceivedEvent) -> None:
195
- self.logger.debug("Module Read Datapoint, parsing telegram...")
263
+ """Dispatch output state received event.
264
+
265
+ Args:
266
+ event: Telegram received event.
267
+ """
268
+ self.logger.debug("Module Read Datapoint, parsing telegram.")
196
269
  reply_telegram = self.telegram_service.parse_reply_telegram(event.frame)
197
270
  self.logger.debug(
198
- f"Parsed telegram: serial={reply_telegram.serial_number}, type={reply_telegram.datapoint_type}, value={reply_telegram.data_value}"
271
+ f"Parsed telegram: "
272
+ f"serial={reply_telegram.serial_number}, "
273
+ f"type={reply_telegram.datapoint_type}, "
274
+ f"value={reply_telegram.data_value}"
199
275
  )
200
276
  self.logger.debug("About to dispatch OutputStateReceivedEvent")
201
277
  self.event_bus.dispatch(
@@ -210,7 +286,12 @@ class HomeKitService:
210
286
  def dispatch_event_telegram_received_event(
211
287
  self, event: TelegramReceivedEvent
212
288
  ) -> None:
213
- self.logger.debug("Event telegram received, parsing...")
289
+ """Dispatch event telegram received event.
290
+
291
+ Args:
292
+ event: Telegram received event.
293
+ """
294
+ self.logger.debug("Event telegram received, parsing.")
214
295
 
215
296
  # Parse event telegram to extract module information
216
297
  event_telegram = self.telegram_service.parse_event_telegram(event.frame)
@@ -238,6 +319,11 @@ class HomeKitService:
238
319
  self.logger.debug("ModuleStateChangedEvent dispatched successfully")
239
320
 
240
321
  def dispatch_module_discovered_event(self, event: TelegramReceivedEvent) -> None:
322
+ """Dispatch module discovered event.
323
+
324
+ Args:
325
+ event: Telegram received event.
326
+ """
241
327
  self.logger.debug("Module discovered, dispatching ModuleDiscoveredEvent")
242
328
  self.event_bus.dispatch(
243
329
  ModuleDiscoveredEvent(
@@ -253,6 +339,14 @@ class HomeKitService:
253
339
  self.logger.debug("ModuleDiscoveredEvent dispatched successfully")
254
340
 
255
341
  def handle_module_discovered(self, event: ModuleDiscoveredEvent) -> str:
342
+ """Handle module discovered event.
343
+
344
+ Args:
345
+ event: Module discovered event.
346
+
347
+ Returns:
348
+ Serial number of the discovered module.
349
+ """
256
350
  self.logger.debug("Handling module discovered event")
257
351
 
258
352
  # Replace R with S and F01D with F02D00
@@ -15,7 +15,7 @@ from xp.utils.time_utils import (
15
15
 
16
16
 
17
17
  class LogFileParsingError(Exception):
18
- """Raised when log file parsing fails"""
18
+ """Raised when log file parsing fails."""
19
19
 
20
20
  pass
21
21
 
@@ -26,6 +26,10 @@ class LogFileService:
26
26
 
27
27
  Handles parsing of log files containing timestamped telegram transmissions
28
28
  and receptions with automatic telegram parsing and validation.
29
+
30
+ Attributes:
31
+ telegram_service: Telegram service for parsing telegrams.
32
+ LOG_LINE_PATTERN: Regex pattern for log line format.
29
33
  """
30
34
 
31
35
  # Regex pattern for log line format: HH:MM:SS,mmm [TX/RX] <telegram>
@@ -34,29 +38,27 @@ class LogFileService:
34
38
  )
35
39
 
36
40
  def __init__(self, telegram_service: TelegramService):
37
- """
38
- Initialize the log file service
41
+ """Initialize the log file service.
39
42
 
40
43
  Args:
41
- telegram_service: Telegram service for parsing telegrams
44
+ telegram_service: Telegram service for parsing telegrams.
42
45
  """
43
46
  self.telegram_service = telegram_service
44
47
 
45
48
  def parse_log_file(
46
49
  self, file_path: str, base_date: Optional[datetime] = None
47
50
  ) -> List[LogEntry]:
48
- """
49
- Parse a console bus log file into LogEntry objects
51
+ """Parse a console bus log file into LogEntry objects.
50
52
 
51
53
  Args:
52
- file_path: Path to the log file
53
- base_date: Base date for timestamps (defaults to today)
54
+ file_path: Path to the log file.
55
+ base_date: Base date for timestamps (defaults to today).
54
56
 
55
57
  Returns:
56
- List of parsed LogEntry objects
58
+ List of parsed LogEntry objects.
57
59
 
58
60
  Raises:
59
- LogFileParsingError: If file cannot be read or parsed
61
+ LogFileParsingError: If file cannot be read or parsed.
60
62
  """
61
63
  try:
62
64
  path = Path(file_path)
@@ -77,15 +79,14 @@ class LogFileService:
77
79
  def parse_log_lines(
78
80
  self, lines: List[str], base_date: Optional[datetime] = None
79
81
  ) -> List[LogEntry]:
80
- """
81
- Parse log lines into LogEntry objects
82
+ """Parse log lines into LogEntry objects.
82
83
 
83
84
  Args:
84
- lines: List of log lines to parse
85
- base_date: Base date for timestamps
85
+ lines: List of log lines to parse.
86
+ base_date: Base date for timestamps.
86
87
 
87
88
  Returns:
88
- List of parsed LogEntry objects
89
+ List of parsed LogEntry objects.
89
90
  """
90
91
  entries = []
91
92
 
@@ -114,16 +115,15 @@ class LogFileService:
114
115
  def _parse_log_line(
115
116
  self, line: str, line_number: int, base_date: Optional[datetime] = None
116
117
  ) -> Optional[LogEntry]:
117
- """
118
- Parse a single log line into a LogEntry
118
+ """Parse a single log line into a LogEntry.
119
119
 
120
120
  Args:
121
- line: Log line to parse
122
- line_number: Line number in the file
123
- base_date: Base date for timestamp
121
+ line: Log line to parse.
122
+ line_number: Line number in the file.
123
+ base_date: Base date for timestamp.
124
124
 
125
125
  Returns:
126
- LogEntry object or None if line format is invalid
126
+ LogEntry object or None if line format is invalid.
127
127
  """
128
128
  match = self.LOG_LINE_PATTERN.match(line)
129
129
  if not match:
@@ -157,14 +157,13 @@ class LogFileService:
157
157
  return entry
158
158
 
159
159
  def validate_log_format(self, file_path: str) -> bool:
160
- """
161
- Validate that a file follows the expected log format
160
+ """Validate that a file follows the expected log format.
162
161
 
163
162
  Args:
164
- file_path: Path to the log file
163
+ file_path: Path to the log file.
165
164
 
166
165
  Returns:
167
- True if format is valid, False otherwise
166
+ True if format is valid, False otherwise.
168
167
  """
169
168
  try:
170
169
  entries = self.parse_log_file(file_path)
@@ -175,28 +174,26 @@ class LogFileService:
175
174
  return False
176
175
 
177
176
  def extract_telegrams(self, file_path: str) -> List[str]:
178
- """
179
- Extract all telegram strings from a log file
177
+ """Extract all telegram strings from a log file.
180
178
 
181
179
  Args:
182
- file_path: Path to the log file
180
+ file_path: Path to the log file.
183
181
 
184
182
  Returns:
185
- List of telegram strings
183
+ List of telegram strings.
186
184
  """
187
185
  entries = self.parse_log_file(file_path)
188
186
  return [entry.raw_telegram for entry in entries]
189
187
 
190
188
  @staticmethod
191
189
  def get_file_statistics(entries: List[LogEntry]) -> Dict[str, Any]:
192
- """
193
- Generate statistics for a list of log entries
190
+ """Generate statistics for a list of log entries.
194
191
 
195
192
  Args:
196
- entries: List of LogEntry objects
193
+ entries: List of LogEntry objects.
197
194
 
198
195
  Returns:
199
- Dictionary containing statistics
196
+ Dictionary containing statistics.
200
197
  """
201
198
  if not entries:
202
199
  return {"total_entries": 0}
@@ -283,18 +280,17 @@ class LogFileService:
283
280
  start_time: Optional[datetime] = None,
284
281
  end_time: Optional[datetime] = None,
285
282
  ) -> List[LogEntry]:
286
- """
287
- Filter log entries based on criteria
283
+ """Filter log entries based on criteria.
288
284
 
289
285
  Args:
290
- entries: List of LogEntry objects to filter
291
- telegram_type: Filter by telegram type (event, system, reply)
292
- direction: Filter by direction (TX, RX)
293
- start_time: Filter entries after this time
294
- end_time: Filter entries before this time
286
+ entries: List of LogEntry objects to filter.
287
+ telegram_type: Filter by telegram type (event, system, reply).
288
+ direction: Filter by direction (TX, RX).
289
+ start_time: Filter entries after this time.
290
+ end_time: Filter entries before this time.
295
291
 
296
292
  Returns:
297
- Filtered list of LogEntry objects
293
+ Filtered list of LogEntry objects.
298
294
  """
299
295
  filtered = entries.copy()
300
296
 
@@ -1,3 +1,8 @@
1
+ """Module Type Service for XP module management.
2
+
3
+ This module provides lookup, validation, and search functionality for XP system module types.
4
+ """
5
+
1
6
  from typing import Dict, List, Optional, Union
2
7
 
3
8
  from xp.models.telegram.module_type import (
@@ -9,7 +14,7 @@ from xp.models.telegram.module_type import (
9
14
 
10
15
 
11
16
  class ModuleTypeNotFoundError(Exception):
12
- """Raised when a module type cannot be found"""
17
+ """Raised when a module type cannot be found."""
13
18
 
14
19
  pass
15
20
 
@@ -17,11 +22,12 @@ class ModuleTypeNotFoundError(Exception):
17
22
  class ModuleTypeService:
18
23
  """
19
24
  Service for managing module type operations.
25
+
20
26
  Provides lookup, validation, and search functionality for XP system module types.
21
27
  """
22
28
 
23
29
  def __init__(self) -> None:
24
- """Initialize the module type service"""
30
+ """Initialize the module type service."""
25
31
  pass
26
32
 
27
33
  @staticmethod
@@ -193,7 +199,14 @@ class ModuleTypeService:
193
199
 
194
200
  @staticmethod
195
201
  def _format_module_summary(module_type: ModuleType) -> str:
196
- """Format a single module type for display"""
202
+ """Format a single module type for display.
203
+
204
+ Args:
205
+ module_type: The module type to format.
206
+
207
+ Returns:
208
+ Formatted string with module information.
209
+ """
197
210
  summary = f"Module: {module_type.name} (Code {module_type.code})\n"
198
211
  summary += f"Description: {module_type.description}\n"
199
212
  summary += f"Category: {module_type.category}\n"
@@ -213,7 +226,11 @@ class ModuleTypeService:
213
226
 
214
227
  @staticmethod
215
228
  def _format_all_modules() -> str:
216
- """Format all modules in a simple list"""
229
+ """Format all modules in a simple list.
230
+
231
+ Returns:
232
+ Formatted string with all modules.
233
+ """
217
234
  modules = get_all_module_types()
218
235
  lines = ["Code | Name | Description", "-" * 60]
219
236
 
@@ -224,7 +241,11 @@ class ModuleTypeService:
224
241
 
225
242
  @staticmethod
226
243
  def _format_modules_by_category() -> str:
227
- """Format modules grouped by category"""
244
+ """Format modules grouped by category.
245
+
246
+ Returns:
247
+ Formatted string with modules grouped by category.
248
+ """
228
249
  categories = get_module_types_by_category()
229
250
  lines = []
230
251
 
@@ -1,4 +1,4 @@
1
- """Protocol layer services for XP"""
1
+ """Protocol layer services for XP."""
2
2
 
3
3
  from xp.models.protocol.conbus_protocol import (
4
4
  ConnectionMadeEvent,