conson-xp 1.1.0__py3-none-any.whl → 1.3.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 (174) hide show
  1. {conson_xp-1.1.0.dist-info → conson_xp-1.3.0.dist-info}/METADATA +1 -5
  2. conson_xp-1.3.0.dist-info/RECORD +164 -0
  3. xp/__init__.py +4 -3
  4. xp/cli/__init__.py +1 -1
  5. xp/cli/commands/__init__.py +1 -2
  6. xp/cli/commands/conbus/conbus.py +9 -37
  7. xp/cli/commands/conbus/conbus_actiontable_commands.py +26 -4
  8. xp/cli/commands/conbus/conbus_autoreport_commands.py +58 -30
  9. xp/cli/commands/conbus/conbus_blink_commands.py +61 -29
  10. xp/cli/commands/conbus/conbus_config_commands.py +10 -5
  11. xp/cli/commands/conbus/conbus_custom_commands.py +16 -5
  12. xp/cli/commands/conbus/conbus_datapoint_commands.py +32 -10
  13. xp/cli/commands/conbus/conbus_discover_commands.py +20 -7
  14. xp/cli/commands/conbus/conbus_lightlevel_commands.py +114 -39
  15. xp/cli/commands/conbus/conbus_linknumber_commands.py +50 -25
  16. xp/cli/commands/conbus/conbus_msactiontable_commands.py +36 -5
  17. xp/cli/commands/conbus/conbus_output_commands.py +52 -14
  18. xp/cli/commands/conbus/conbus_raw_commands.py +17 -6
  19. xp/cli/commands/conbus/conbus_receive_commands.py +20 -10
  20. xp/cli/commands/conbus/conbus_scan_commands.py +17 -4
  21. xp/cli/commands/file_commands.py +35 -18
  22. xp/cli/commands/homekit/homekit.py +14 -8
  23. xp/cli/commands/homekit/homekit_start_commands.py +8 -6
  24. xp/cli/commands/module_commands.py +38 -23
  25. xp/cli/commands/reverse_proxy_commands.py +27 -19
  26. xp/cli/commands/server/server_commands.py +18 -18
  27. xp/cli/commands/telegram/telegram.py +4 -12
  28. xp/cli/commands/telegram/telegram_blink_commands.py +10 -8
  29. xp/cli/commands/telegram/telegram_checksum_commands.py +19 -8
  30. xp/cli/commands/telegram/telegram_discover_commands.py +2 -4
  31. xp/cli/commands/telegram/telegram_linknumber_commands.py +11 -8
  32. xp/cli/commands/telegram/telegram_parse_commands.py +10 -9
  33. xp/cli/commands/telegram/telegram_version_commands.py +8 -4
  34. xp/cli/main.py +5 -3
  35. xp/cli/utils/click_tree.py +23 -3
  36. xp/cli/utils/datapoint_type_choice.py +20 -0
  37. xp/cli/utils/decorators.py +165 -14
  38. xp/cli/utils/error_handlers.py +49 -18
  39. xp/cli/utils/formatters.py +95 -10
  40. xp/cli/utils/serial_number_type.py +18 -0
  41. xp/cli/utils/system_function_choice.py +20 -0
  42. xp/cli/utils/xp_module_type.py +20 -0
  43. xp/connection/__init__.py +1 -1
  44. xp/connection/exceptions.py +5 -5
  45. xp/models/__init__.py +1 -1
  46. xp/models/actiontable/__init__.py +1 -0
  47. xp/models/actiontable/actiontable.py +17 -1
  48. xp/models/actiontable/msactiontable_xp20.py +10 -0
  49. xp/models/actiontable/msactiontable_xp24.py +20 -3
  50. xp/models/actiontable/msactiontable_xp33.py +27 -4
  51. xp/models/conbus/__init__.py +1 -0
  52. xp/models/conbus/conbus.py +34 -4
  53. xp/models/conbus/conbus_autoreport.py +20 -2
  54. xp/models/conbus/conbus_blink.py +22 -2
  55. xp/models/conbus/conbus_client_config.py +22 -1
  56. xp/models/conbus/conbus_connection_status.py +16 -2
  57. xp/models/conbus/conbus_custom.py +21 -2
  58. xp/models/conbus/conbus_datapoint.py +25 -2
  59. xp/models/conbus/conbus_discover.py +18 -2
  60. xp/models/conbus/conbus_lightlevel.py +20 -2
  61. xp/models/conbus/conbus_linknumber.py +20 -2
  62. xp/models/conbus/conbus_output.py +22 -2
  63. xp/models/conbus/conbus_raw.py +17 -2
  64. xp/models/conbus/conbus_receive.py +16 -2
  65. xp/models/conbus/conbus_writeconfig.py +60 -0
  66. xp/models/homekit/__init__.py +1 -0
  67. xp/models/homekit/homekit_accessory.py +15 -1
  68. xp/models/homekit/homekit_config.py +52 -0
  69. xp/models/homekit/homekit_conson_config.py +32 -0
  70. xp/models/log_entry.py +49 -9
  71. xp/models/protocol/__init__.py +1 -0
  72. xp/models/protocol/conbus_protocol.py +130 -21
  73. xp/models/telegram/__init__.py +1 -0
  74. xp/models/telegram/action_type.py +16 -2
  75. xp/models/telegram/datapoint_type.py +36 -2
  76. xp/models/telegram/event_telegram.py +46 -10
  77. xp/models/telegram/event_type.py +8 -1
  78. xp/models/telegram/input_action_type.py +34 -2
  79. xp/models/telegram/input_type.py +9 -1
  80. xp/models/telegram/module_type.py +69 -19
  81. xp/models/telegram/module_type_code.py +43 -1
  82. xp/models/telegram/output_telegram.py +30 -6
  83. xp/models/telegram/reply_telegram.py +56 -11
  84. xp/models/telegram/system_function.py +35 -3
  85. xp/models/telegram/system_telegram.py +18 -4
  86. xp/models/telegram/telegram.py +12 -3
  87. xp/models/telegram/telegram_type.py +8 -1
  88. xp/models/telegram/timeparam_type.py +27 -0
  89. xp/models/write_config_type.py +17 -2
  90. xp/services/__init__.py +1 -1
  91. xp/services/conbus/__init__.py +1 -0
  92. xp/services/conbus/actiontable/__init__.py +1 -0
  93. xp/services/conbus/actiontable/actiontable_service.py +33 -2
  94. xp/services/conbus/actiontable/msactiontable_service.py +40 -3
  95. xp/services/conbus/actiontable/msactiontable_xp24_serializer.py +36 -4
  96. xp/services/conbus/actiontable/msactiontable_xp33_serializer.py +45 -5
  97. xp/services/conbus/conbus_blink_all_service.py +40 -21
  98. xp/services/conbus/conbus_blink_service.py +37 -13
  99. xp/services/conbus/conbus_custom_service.py +29 -13
  100. xp/services/conbus/conbus_datapoint_queryall_service.py +40 -16
  101. xp/services/conbus/conbus_datapoint_service.py +42 -18
  102. xp/services/conbus/conbus_discover_service.py +43 -7
  103. xp/services/conbus/conbus_output_service.py +33 -13
  104. xp/services/conbus/conbus_raw_service.py +36 -16
  105. xp/services/conbus/conbus_receive_service.py +38 -6
  106. xp/services/conbus/conbus_scan_service.py +44 -18
  107. xp/services/conbus/write_config_service.py +193 -0
  108. xp/services/homekit/__init__.py +1 -0
  109. xp/services/homekit/homekit_cache_service.py +31 -6
  110. xp/services/homekit/homekit_conbus_service.py +33 -2
  111. xp/services/homekit/homekit_config_validator.py +97 -15
  112. xp/services/homekit/homekit_conson_validator.py +51 -7
  113. xp/services/homekit/homekit_dimminglight.py +47 -1
  114. xp/services/homekit/homekit_dimminglight_service.py +35 -1
  115. xp/services/homekit/homekit_hap_service.py +71 -18
  116. xp/services/homekit/homekit_lightbulb.py +35 -1
  117. xp/services/homekit/homekit_lightbulb_service.py +30 -2
  118. xp/services/homekit/homekit_module_service.py +23 -1
  119. xp/services/homekit/homekit_outlet.py +47 -1
  120. xp/services/homekit/homekit_outlet_service.py +44 -2
  121. xp/services/homekit/homekit_service.py +113 -19
  122. xp/services/log_file_service.py +37 -41
  123. xp/services/module_type_service.py +26 -5
  124. xp/services/protocol/__init__.py +1 -1
  125. xp/services/protocol/conbus_protocol.py +110 -16
  126. xp/services/protocol/protocol_factory.py +40 -0
  127. xp/services/protocol/telegram_protocol.py +38 -7
  128. xp/services/reverse_proxy_service.py +79 -14
  129. xp/services/server/__init__.py +1 -0
  130. xp/services/server/base_server_service.py +102 -14
  131. xp/services/server/cp20_server_service.py +12 -4
  132. xp/services/server/server_service.py +26 -11
  133. xp/services/server/xp130_server_service.py +11 -3
  134. xp/services/server/xp20_server_service.py +11 -3
  135. xp/services/server/xp230_server_service.py +11 -3
  136. xp/services/server/xp24_server_service.py +33 -6
  137. xp/services/server/xp33_server_service.py +41 -8
  138. xp/services/telegram/__init__.py +1 -0
  139. xp/services/telegram/telegram_blink_service.py +19 -31
  140. xp/services/telegram/telegram_checksum_service.py +10 -10
  141. xp/services/telegram/telegram_datapoint_service.py +70 -0
  142. xp/services/telegram/telegram_discover_service.py +58 -29
  143. xp/services/telegram/telegram_link_number_service.py +27 -40
  144. xp/services/telegram/telegram_output_service.py +46 -49
  145. xp/services/telegram/telegram_service.py +41 -41
  146. xp/services/telegram/telegram_version_service.py +4 -2
  147. xp/utils/__init__.py +1 -1
  148. xp/utils/dependencies.py +4 -47
  149. xp/utils/serialization.py +6 -0
  150. xp/utils/time_utils.py +6 -11
  151. conson_xp-1.1.0.dist-info/RECORD +0 -181
  152. xp/api/__init__.py +0 -1
  153. xp/api/main.py +0 -110
  154. xp/api/models/__init__.py +0 -1
  155. xp/api/models/api.py +0 -20
  156. xp/api/models/discover.py +0 -21
  157. xp/api/routers/__init__.py +0 -17
  158. xp/api/routers/conbus.py +0 -5
  159. xp/api/routers/conbus_blink.py +0 -105
  160. xp/api/routers/conbus_custom.py +0 -63
  161. xp/api/routers/conbus_datapoint.py +0 -67
  162. xp/api/routers/conbus_output.py +0 -147
  163. xp/api/routers/errors.py +0 -37
  164. xp/cli/commands/api.py +0 -16
  165. xp/cli/commands/api_start_commands.py +0 -126
  166. xp/services/conbus/conbus_autoreport_get_service.py +0 -85
  167. xp/services/conbus/conbus_autoreport_set_service.py +0 -128
  168. xp/services/conbus/conbus_lightlevel_get_service.py +0 -101
  169. xp/services/conbus/conbus_lightlevel_set_service.py +0 -205
  170. xp/services/conbus/conbus_linknumber_get_service.py +0 -86
  171. xp/services/conbus/conbus_linknumber_set_service.py +0 -155
  172. {conson_xp-1.1.0.dist-info → conson_xp-1.3.0.dist-info}/WHEEL +0 -0
  173. {conson_xp-1.1.0.dist-info → conson_xp-1.3.0.dist-info}/entry_points.txt +0 -0
  174. {conson_xp-1.1.0.dist-info → conson_xp-1.3.0.dist-info}/licenses/LICENSE +0 -0
@@ -16,7 +16,7 @@ from xp.models.response import Response
16
16
 
17
17
 
18
18
  class ReverseProxyError(Exception):
19
- """Raised when Conbus reverse proxy operations fail"""
19
+ """Raised when Conbus reverse proxy operations fail."""
20
20
 
21
21
  pass
22
22
 
@@ -28,6 +28,17 @@ class ReverseProxyService:
28
28
  Accepts client connections on port 10001 and forwards all telegrams
29
29
  to the target server configured in cli.yml. Monitors and prints all
30
30
  bidirectional traffic with timestamps.
31
+
32
+ Attributes:
33
+ logger: Logger instance for the service.
34
+ listen_port: Port to listen on for client connections.
35
+ server_socket: Main server socket for accepting connections.
36
+ is_running: Flag indicating if proxy is running.
37
+ active_connections: Dictionary of active connection information.
38
+ connection_counter: Counter for connection IDs.
39
+ cli_config: Conbus client configuration.
40
+ target_ip: Target server IP address.
41
+ target_port: Target server port number.
31
42
  """
32
43
 
33
44
  def __init__(
@@ -35,7 +46,12 @@ class ReverseProxyService:
35
46
  cli_config: ConbusClientConfig,
36
47
  listen_port: int,
37
48
  ):
38
- """Initialize the Conbus reverse proxy service"""
49
+ """Initialize the Conbus reverse proxy service.
50
+
51
+ Args:
52
+ cli_config: Conbus client configuration.
53
+ listen_port: Port to listen on for client connections.
54
+ """
39
55
  # Set up logging first
40
56
  self.logger = logging.getLogger(__name__)
41
57
 
@@ -50,16 +66,28 @@ class ReverseProxyService:
50
66
 
51
67
  @property
52
68
  def target_ip(self) -> str:
53
- """Get target server IP"""
69
+ """Get target server IP.
70
+
71
+ Returns:
72
+ Target server IP address.
73
+ """
54
74
  return self.cli_config.conbus.ip
55
75
 
56
76
  @property
57
77
  def target_port(self) -> int:
58
- """Get target server port"""
78
+ """Get target server port.
79
+
80
+ Returns:
81
+ Target server port number.
82
+ """
59
83
  return self.cli_config.conbus.port
60
84
 
61
85
  def start_proxy(self) -> Response:
62
- """Start the reverse proxy server"""
86
+ """Start the reverse proxy server.
87
+
88
+ Returns:
89
+ Response object with success status and proxy details.
90
+ """
63
91
  if self.is_running:
64
92
  return Response(
65
93
  success=False, data=None, error="Reverse proxy is already running"
@@ -111,7 +139,11 @@ class ReverseProxyService:
111
139
  )
112
140
 
113
141
  def stop_proxy(self) -> Response:
114
- """Stop the reverse proxy server"""
142
+ """Stop the reverse proxy server.
143
+
144
+ Returns:
145
+ Response object with success status.
146
+ """
115
147
  if not self.is_running:
116
148
  return Response(
117
149
  success=False, data=None, error="Reverse proxy is not running"
@@ -139,7 +171,11 @@ class ReverseProxyService:
139
171
  )
140
172
 
141
173
  def get_status(self) -> Response:
142
- """Get current proxy status and active connections"""
174
+ """Get current proxy status and active connections.
175
+
176
+ Returns:
177
+ Response object with proxy status and connection details.
178
+ """
143
179
  return Response(
144
180
  success=True,
145
181
  data={
@@ -161,7 +197,7 @@ class ReverseProxyService:
161
197
  )
162
198
 
163
199
  def _accept_connections(self) -> None:
164
- """Accept and handle client connections"""
200
+ """Accept and handle client connections."""
165
201
  while self.is_running:
166
202
  try:
167
203
  # Accept connection
@@ -194,7 +230,13 @@ class ReverseProxyService:
194
230
  def _handle_client(
195
231
  self, client_socket: socket.socket, client_address: tuple, conn_id: str
196
232
  ) -> None:
197
- """Handle individual client connection with server relay"""
233
+ """Handle individual client connection with server relay.
234
+
235
+ Args:
236
+ client_socket: Client socket connection.
237
+ client_address: Client address tuple (ip, port).
238
+ conn_id: Connection identifier.
239
+ """
198
240
  try:
199
241
  # Connect to target server
200
242
  server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -272,7 +314,15 @@ class ReverseProxyService:
272
314
  dest_label: str,
273
315
  conn_id: str,
274
316
  ) -> None:
275
- """Relay data between sockets with telegram monitoring"""
317
+ """Relay data between sockets with telegram monitoring.
318
+
319
+ Args:
320
+ source_socket: Source socket to receive from.
321
+ dest_socket: Destination socket to send to.
322
+ source_label: Label for source in logs.
323
+ dest_label: Label for destination in logs.
324
+ conn_id: Connection identifier.
325
+ """
276
326
  try:
277
327
  while self.is_running:
278
328
  # Receive data from source
@@ -316,7 +366,11 @@ class ReverseProxyService:
316
366
  self.logger.error(f"Error in data relay: {e} [{conn_id}]")
317
367
 
318
368
  def _close_connection_pair(self, conn_id: str) -> None:
319
- """Close both client and server sockets for a connection"""
369
+ """Close both client and server sockets for a connection.
370
+
371
+ Args:
372
+ conn_id: Connection identifier.
373
+ """
320
374
  if conn_id not in self.active_connections:
321
375
  return
322
376
 
@@ -344,7 +398,10 @@ class ReverseProxyService:
344
398
  f"Client {client_address} disconnected [{conn_id}] - {bytes_relayed} bytes relayed"
345
399
  )
346
400
  print(
347
- f"{self.timestamp()} [DISCONNECTION] Client {client_address} disconnected [{conn_id}] - {bytes_relayed} bytes relayed"
401
+ f"{self.timestamp()} [DISCONNECTION] "
402
+ f"Client {client_address} "
403
+ f"disconnected [{conn_id}] - "
404
+ f"{bytes_relayed} bytes relayed"
348
405
  )
349
406
 
350
407
  # Remove from active connections
@@ -352,11 +409,19 @@ class ReverseProxyService:
352
409
 
353
410
  @staticmethod
354
411
  def timestamp() -> str:
355
- """Generate timestamp string for logging"""
412
+ """Generate timestamp string for logging.
413
+
414
+ Returns:
415
+ Timestamp string in HH:MM:SS,mmm format.
416
+ """
356
417
  return datetime.now().strftime("%H:%M:%S,%f")[:-3]
357
418
 
358
419
  def run_blocking(self) -> None:
359
- """Run the proxy in blocking mode (for CLI usage)"""
420
+ """Run the proxy in blocking mode (for CLI usage).
421
+
422
+ Raises:
423
+ ReverseProxyError: If proxy fails to start.
424
+ """
360
425
  result = self.start_proxy()
361
426
  if not result.success:
362
427
  raise ReverseProxyError(result.error)
@@ -0,0 +1 @@
1
+ """Server services for XP protocol variants."""
@@ -23,7 +23,11 @@ class BaseServerService(ABC):
23
23
  """
24
24
 
25
25
  def __init__(self, serial_number: str):
26
- """Initialize base server service"""
26
+ """Initialize base server service.
27
+
28
+ Args:
29
+ serial_number: The device serial number.
30
+ """
27
31
  self.serial_number = serial_number
28
32
  self.logger = logging.getLogger(__name__)
29
33
 
@@ -40,7 +44,14 @@ class BaseServerService(ABC):
40
44
  def generate_datapoint_type_response(
41
45
  self, datapoint_type: DataPointType
42
46
  ) -> Optional[str]:
43
- """Generate datapoint_type response telegram"""
47
+ """Generate datapoint_type response telegram.
48
+
49
+ Args:
50
+ datapoint_type: The type of datapoint to query.
51
+
52
+ Returns:
53
+ The response telegram string, or None if generation fails.
54
+ """
44
55
  datapoint_values = {
45
56
  DataPointType.TEMPERATURE: self.temperature,
46
57
  DataPointType.MODULE_TYPE_CODE: f"{self.module_type_code:02X}",
@@ -62,23 +73,46 @@ class BaseServerService(ABC):
62
73
  return telegram
63
74
 
64
75
  def _check_request_for_device(self, request: SystemTelegram) -> bool:
65
- """Check if request is for this device (including broadcast)"""
76
+ """Check if request is for this device (including broadcast).
77
+
78
+ Args:
79
+ request: The system telegram request to check.
80
+
81
+ Returns:
82
+ True if request is for this device or broadcast, False otherwise.
83
+ """
66
84
  return request.serial_number in (self.serial_number, "0000000000")
67
85
 
68
86
  @staticmethod
69
87
  def _build_response_telegram(data_part: str) -> str:
70
- """Build a complete response telegram with checksum"""
88
+ """Build a complete response telegram with checksum.
89
+
90
+ Args:
91
+ data_part: The data part of the telegram without checksum.
92
+
93
+ Returns:
94
+ The complete telegram with checksum enclosed in angle brackets.
95
+ """
71
96
  checksum = calculate_checksum(data_part)
72
97
  return f"<{data_part}{checksum}>"
73
98
 
74
99
  def _log_response(self, response_type: str, telegram: str) -> None:
75
- """Log response generation"""
100
+ """Log response generation.
101
+
102
+ Args:
103
+ response_type: The type of response being generated.
104
+ telegram: The telegram string being logged.
105
+ """
76
106
  self.logger.debug(
77
107
  f"Generated {self.device_type} {response_type} response: {telegram}"
78
108
  )
79
109
 
80
110
  def generate_discover_response(self) -> str:
81
- """Generate discover response telegram"""
111
+ """Generate discover response telegram.
112
+
113
+ Returns:
114
+ The discover response telegram string.
115
+ """
82
116
  data_part = f"R{self.serial_number}F01D"
83
117
  telegram = self._build_response_telegram(data_part)
84
118
  self._log_response("discover", telegram)
@@ -87,7 +121,15 @@ class BaseServerService(ABC):
87
121
  def set_link_number(
88
122
  self, request: SystemTelegram, new_link_number: int
89
123
  ) -> Optional[str]:
90
- """Set link number and generate ACK response"""
124
+ """Set link number and generate ACK response.
125
+
126
+ Args:
127
+ request: The system telegram request.
128
+ new_link_number: The new link number to set.
129
+
130
+ Returns:
131
+ The ACK response telegram string, or None if request is invalid.
132
+ """
91
133
  if (
92
134
  request.system_function == SystemFunction.WRITE_CONFIG
93
135
  and request.datapoint_type == DataPointType.LINK_NUMBER
@@ -105,7 +147,14 @@ class BaseServerService(ABC):
105
147
  return None
106
148
 
107
149
  def process_system_telegram(self, request: SystemTelegram) -> Optional[str]:
108
- """Template method for processing system telegrams"""
150
+ """Template method for processing system telegrams.
151
+
152
+ Args:
153
+ request: The system telegram request to process.
154
+
155
+ Returns:
156
+ The response telegram string, or None if request cannot be handled.
157
+ """
109
158
  # Check if request is for this device
110
159
  if not self._check_request_for_device(request):
111
160
  return None
@@ -127,7 +176,14 @@ class BaseServerService(ABC):
127
176
  return None
128
177
 
129
178
  def _handle_return_data_request(self, request: SystemTelegram) -> Optional[str]:
130
- """Handle RETURN_DATA requests - can be overridden by subclasses"""
179
+ """Handle RETURN_DATA requests - can be overridden by subclasses.
180
+
181
+ Args:
182
+ request: The system telegram request.
183
+
184
+ Returns:
185
+ The response telegram string, or None if request cannot be handled.
186
+ """
131
187
  self.logger.debug(
132
188
  f"_handle_return_data_request {self.device_type} request: {request}"
133
189
  )
@@ -140,27 +196,59 @@ class BaseServerService(ABC):
140
196
  def _handle_device_specific_data_request(
141
197
  self, request: SystemTelegram
142
198
  ) -> Optional[str]:
143
- """Override in subclasses for device-specific data requests"""
199
+ """Override in subclasses for device-specific data requests.
200
+
201
+ Args:
202
+ request: The system telegram request.
203
+
204
+ Returns:
205
+ The response telegram string, or None if request cannot be handled.
206
+ """
144
207
  return None
145
208
 
146
209
  def _handle_write_config_request(self, request: SystemTelegram) -> Optional[str]:
147
- """Handle WRITE_CONFIG requests"""
210
+ """Handle WRITE_CONFIG requests.
211
+
212
+ Args:
213
+ request: The system telegram request.
214
+
215
+ Returns:
216
+ The response telegram string, or None if request cannot be handled.
217
+ """
148
218
  if request.datapoint_type == DataPointType.LINK_NUMBER:
149
219
  return self.set_link_number(request, 1) # Default implementation
150
220
 
151
221
  return self._handle_device_specific_config_request()
152
222
 
153
223
  def _handle_action_request(self, request: SystemTelegram) -> Optional[str]:
154
- """Handle ACTION requests"""
224
+ """Handle ACTION requests.
225
+
226
+ Args:
227
+ request: The system telegram request.
228
+
229
+ Returns:
230
+ The response telegram string, or None if request cannot be handled.
231
+ """
155
232
  return self._handle_device_specific_action_request(request)
156
233
 
157
234
  def _handle_device_specific_action_request(
158
235
  self, request: SystemTelegram
159
236
  ) -> Optional[str]:
160
- """Override in subclasses for device-specific data requests"""
237
+ """Override in subclasses for device-specific data requests.
238
+
239
+ Args:
240
+ request: The system telegram request.
241
+
242
+ Returns:
243
+ The response telegram string, or None if request cannot be handled.
244
+ """
161
245
  return None
162
246
 
163
247
  @staticmethod
164
248
  def _handle_device_specific_config_request() -> Optional[str]:
165
- """Override in subclasses for device-specific config requests"""
249
+ """Override in subclasses for device-specific config requests.
250
+
251
+ Returns:
252
+ The response telegram string, or None if request cannot be handled.
253
+ """
166
254
  return None
@@ -11,7 +11,7 @@ from xp.services.server.base_server_service import BaseServerService
11
11
 
12
12
 
13
13
  class CP20ServerError(Exception):
14
- """Raised when CP20 server operations fail"""
14
+ """Raised when CP20 server operations fail."""
15
15
 
16
16
  pass
17
17
 
@@ -25,7 +25,11 @@ class CP20ServerService(BaseServerService):
25
25
  """
26
26
 
27
27
  def __init__(self, serial_number: str):
28
- """Initialize CP20 server service"""
28
+ """Initialize CP20 server service.
29
+
30
+ Args:
31
+ serial_number: The device serial number.
32
+ """
29
33
  super().__init__(serial_number)
30
34
  self.device_type = "CP20"
31
35
  self.module_type_code = 2 # CP20 module type from registry
@@ -34,11 +38,15 @@ class CP20ServerService(BaseServerService):
34
38
  def _handle_device_specific_data_request(
35
39
  self, request: SystemTelegram
36
40
  ) -> Optional[str]:
37
- """Handle CP20-specific data requests"""
41
+ """Handle CP20-specific data requests."""
38
42
  return None
39
43
 
40
44
  def get_device_info(self) -> Dict:
41
- """Get CP20 device information"""
45
+ """Get CP20 device information.
46
+
47
+ Returns:
48
+ Dictionary containing device information.
49
+ """
42
50
  return {
43
51
  "serial_number": self.serial_number,
44
52
  "device_type": self.device_type,
@@ -26,7 +26,7 @@ from xp.services.telegram.telegram_service import TelegramService
26
26
 
27
27
 
28
28
  class ServerError(Exception):
29
- """Raised when Conbus server operations fail"""
29
+ """Raised when Conbus server operations fail."""
30
30
 
31
31
  pass
32
32
 
@@ -46,7 +46,14 @@ class ServerService:
46
46
  config_path: str = "server.yml",
47
47
  port: int = 10001,
48
48
  ):
49
- """Initialize the Conbus server service"""
49
+ """Initialize the Conbus server service.
50
+
51
+ Args:
52
+ telegram_service: Service for parsing system telegrams.
53
+ discover_service: Service for handling discover requests.
54
+ config_path: Path to the server configuration file.
55
+ port: TCP port to listen on.
56
+ """
50
57
  self.telegram_service = telegram_service
51
58
  self.discover_service = discover_service
52
59
  self.config_path = config_path
@@ -71,7 +78,7 @@ class ServerService:
71
78
  self._load_device_config()
72
79
 
73
80
  def _load_device_config(self) -> None:
74
- """Load device configurations from server.yml"""
81
+ """Load device configurations from server.yml."""
75
82
  try:
76
83
  if Path(self.config_path).exists():
77
84
  config = ConsonModuleListConfig.from_yaml(self.config_path)
@@ -90,7 +97,7 @@ class ServerService:
90
97
  self.device_services = {}
91
98
 
92
99
  def _create_device_services(self) -> None:
93
- """Create device service instances based on device configuration"""
100
+ """Create device service instances based on device configuration."""
94
101
  self.device_services = {}
95
102
 
96
103
  for module in self.devices:
@@ -143,7 +150,11 @@ class ServerService:
143
150
  )
144
151
 
145
152
  def start_server(self) -> None:
146
- """Start the TCP server on port 10001"""
153
+ """Start the TCP server on port 10001.
154
+
155
+ Raises:
156
+ ServerError: If server is already running or fails to start.
157
+ """
147
158
  if self.is_running:
148
159
  raise ServerError("Server is already running")
149
160
 
@@ -170,7 +181,7 @@ class ServerService:
170
181
  raise ServerError(f"Failed to start server: {e}")
171
182
 
172
183
  def stop_server(self) -> None:
173
- """Stop the TCP server"""
184
+ """Stop the TCP server."""
174
185
  if not self.is_running:
175
186
  return
176
187
 
@@ -184,7 +195,7 @@ class ServerService:
184
195
  self.logger.error(f"Error closing server socket: {e}")
185
196
 
186
197
  def _accept_connections(self) -> None:
187
- """Accept and handle client connections"""
198
+ """Accept and handle client connections."""
188
199
  while self.is_running:
189
200
  try:
190
201
  # Accept connection
@@ -208,7 +219,7 @@ class ServerService:
208
219
  def _handle_client(
209
220
  self, client_socket: socket.socket, client_address: tuple[str, int]
210
221
  ) -> None:
211
- """Handle individual client connection"""
222
+ """Handle individual client connection."""
212
223
  try:
213
224
  # Set timeout for idle connections (30 seconds as per spec)
214
225
  client_socket.settimeout(300.0)
@@ -242,7 +253,7 @@ class ServerService:
242
253
  self.logger.error(f"Error closing client socket: {e}")
243
254
 
244
255
  def _process_request(self, message: str) -> List[str]:
245
- """Process incoming request and generate responses"""
256
+ """Process incoming request and generate responses."""
246
257
  responses: list[str] = []
247
258
 
248
259
  try:
@@ -290,7 +301,11 @@ class ServerService:
290
301
  return responses
291
302
 
292
303
  def get_server_status(self) -> dict:
293
- """Get current server status"""
304
+ """Get current server status.
305
+
306
+ Returns:
307
+ Dictionary containing server status information.
308
+ """
294
309
  return {
295
310
  "running": self.is_running,
296
311
  "port": self.port,
@@ -299,7 +314,7 @@ class ServerService:
299
314
  }
300
315
 
301
316
  def reload_config(self) -> None:
302
- """Reload device configuration from file"""
317
+ """Reload device configuration from file."""
303
318
  self._load_device_config()
304
319
  self.logger.info(
305
320
  f"Configuration reloaded: {len(self.devices)} devices, {len(self.device_services)} services"
@@ -11,7 +11,7 @@ from xp.services.server.base_server_service import BaseServerService
11
11
 
12
12
 
13
13
  class XP130ServerError(Exception):
14
- """Raised when XP130 server operations fail"""
14
+ """Raised when XP130 server operations fail."""
15
15
 
16
16
  pass
17
17
 
@@ -25,7 +25,11 @@ class XP130ServerService(BaseServerService):
25
25
  """
26
26
 
27
27
  def __init__(self, serial_number: str):
28
- """Initialize XP130 server service"""
28
+ """Initialize XP130 server service.
29
+
30
+ Args:
31
+ serial_number: The device serial number.
32
+ """
29
33
  super().__init__(serial_number)
30
34
  self.device_type = "XP130"
31
35
  self.module_type_code = 13 # XP130 module type from registry
@@ -37,7 +41,11 @@ class XP130ServerService(BaseServerService):
37
41
  self.gateway = "192.168.1.1"
38
42
 
39
43
  def get_device_info(self) -> Dict:
40
- """Get XP130 device information"""
44
+ """Get XP130 device information.
45
+
46
+ Returns:
47
+ Dictionary containing device information.
48
+ """
41
49
  return {
42
50
  "serial_number": self.serial_number,
43
51
  "device_type": self.device_type,
@@ -10,7 +10,7 @@ from xp.services.server.base_server_service import BaseServerService
10
10
 
11
11
 
12
12
  class XP20ServerError(Exception):
13
- """Raised when XP20 server operations fail"""
13
+ """Raised when XP20 server operations fail."""
14
14
 
15
15
  pass
16
16
 
@@ -24,14 +24,22 @@ class XP20ServerService(BaseServerService):
24
24
  """
25
25
 
26
26
  def __init__(self, serial_number: str):
27
- """Initialize XP20 server service"""
27
+ """Initialize XP20 server service.
28
+
29
+ Args:
30
+ serial_number: The device serial number.
31
+ """
28
32
  super().__init__(serial_number)
29
33
  self.device_type = "XP20"
30
34
  self.module_type_code = 33 # XP20 module type from registry
31
35
  self.firmware_version = "XP20_V0.01.05"
32
36
 
33
37
  def get_device_info(self) -> Dict:
34
- """Get XP20 device information"""
38
+ """Get XP20 device information.
39
+
40
+ Returns:
41
+ Dictionary containing device information.
42
+ """
35
43
  return {
36
44
  "serial_number": self.serial_number,
37
45
  "device_type": self.device_type,
@@ -10,7 +10,7 @@ from xp.services.server.base_server_service import BaseServerService
10
10
 
11
11
 
12
12
  class XP230ServerError(Exception):
13
- """Raised when XP230 server operations fail"""
13
+ """Raised when XP230 server operations fail."""
14
14
 
15
15
  pass
16
16
 
@@ -24,14 +24,22 @@ class XP230ServerService(BaseServerService):
24
24
  """
25
25
 
26
26
  def __init__(self, serial_number: str):
27
- """Initialize XP230 server service"""
27
+ """Initialize XP230 server service.
28
+
29
+ Args:
30
+ serial_number: The device serial number.
31
+ """
28
32
  super().__init__(serial_number)
29
33
  self.device_type = "XP230"
30
34
  self.module_type_code = 34 # XP230 module type from registry
31
35
  self.firmware_version = "XP230_V1.00.04"
32
36
 
33
37
  def get_device_info(self) -> Dict:
34
- """Get XP230 device information"""
38
+ """Get XP230 device information.
39
+
40
+ Returns:
41
+ Dictionary containing device information.
42
+ """
35
43
  return {
36
44
  "serial_number": self.serial_number,
37
45
  "device_type": self.device_type,