conson-xp 1.18.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 (176) hide show
  1. conson_xp-1.18.0.dist-info/METADATA +412 -0
  2. conson_xp-1.18.0.dist-info/RECORD +176 -0
  3. conson_xp-1.18.0.dist-info/WHEEL +4 -0
  4. conson_xp-1.18.0.dist-info/entry_points.txt +5 -0
  5. conson_xp-1.18.0.dist-info/licenses/LICENSE +29 -0
  6. xp/__init__.py +9 -0
  7. xp/cli/__init__.py +5 -0
  8. xp/cli/__main__.py +6 -0
  9. xp/cli/commands/__init__.py +153 -0
  10. xp/cli/commands/conbus/__init__.py +25 -0
  11. xp/cli/commands/conbus/conbus.py +128 -0
  12. xp/cli/commands/conbus/conbus_actiontable_commands.py +233 -0
  13. xp/cli/commands/conbus/conbus_autoreport_commands.py +108 -0
  14. xp/cli/commands/conbus/conbus_blink_commands.py +163 -0
  15. xp/cli/commands/conbus/conbus_config_commands.py +29 -0
  16. xp/cli/commands/conbus/conbus_custom_commands.py +57 -0
  17. xp/cli/commands/conbus/conbus_datapoint_commands.py +113 -0
  18. xp/cli/commands/conbus/conbus_discover_commands.py +61 -0
  19. xp/cli/commands/conbus/conbus_event_commands.py +81 -0
  20. xp/cli/commands/conbus/conbus_lightlevel_commands.py +207 -0
  21. xp/cli/commands/conbus/conbus_linknumber_commands.py +102 -0
  22. xp/cli/commands/conbus/conbus_modulenumber_commands.py +104 -0
  23. xp/cli/commands/conbus/conbus_msactiontable_commands.py +94 -0
  24. xp/cli/commands/conbus/conbus_output_commands.py +163 -0
  25. xp/cli/commands/conbus/conbus_raw_commands.py +62 -0
  26. xp/cli/commands/conbus/conbus_receive_commands.py +59 -0
  27. xp/cli/commands/conbus/conbus_scan_commands.py +58 -0
  28. xp/cli/commands/file_commands.py +186 -0
  29. xp/cli/commands/homekit/__init__.py +3 -0
  30. xp/cli/commands/homekit/homekit.py +118 -0
  31. xp/cli/commands/homekit/homekit_start_commands.py +43 -0
  32. xp/cli/commands/module_commands.py +187 -0
  33. xp/cli/commands/reverse_proxy_commands.py +178 -0
  34. xp/cli/commands/server/__init__.py +3 -0
  35. xp/cli/commands/server/server_commands.py +135 -0
  36. xp/cli/commands/telegram/__init__.py +5 -0
  37. xp/cli/commands/telegram/telegram.py +41 -0
  38. xp/cli/commands/telegram/telegram_blink_commands.py +79 -0
  39. xp/cli/commands/telegram/telegram_checksum_commands.py +112 -0
  40. xp/cli/commands/telegram/telegram_discover_commands.py +41 -0
  41. xp/cli/commands/telegram/telegram_linknumber_commands.py +86 -0
  42. xp/cli/commands/telegram/telegram_parse_commands.py +75 -0
  43. xp/cli/commands/telegram/telegram_version_commands.py +52 -0
  44. xp/cli/main.py +87 -0
  45. xp/cli/utils/__init__.py +1 -0
  46. xp/cli/utils/click_tree.py +57 -0
  47. xp/cli/utils/datapoint_type_choice.py +57 -0
  48. xp/cli/utils/decorators.py +351 -0
  49. xp/cli/utils/error_handlers.py +201 -0
  50. xp/cli/utils/formatters.py +312 -0
  51. xp/cli/utils/module_type_choice.py +56 -0
  52. xp/cli/utils/serial_number_type.py +52 -0
  53. xp/cli/utils/system_function_choice.py +57 -0
  54. xp/cli/utils/xp_module_type.py +53 -0
  55. xp/connection/__init__.py +13 -0
  56. xp/connection/exceptions.py +22 -0
  57. xp/models/__init__.py +36 -0
  58. xp/models/actiontable/__init__.py +1 -0
  59. xp/models/actiontable/actiontable.py +43 -0
  60. xp/models/actiontable/msactiontable_xp20.py +53 -0
  61. xp/models/actiontable/msactiontable_xp24.py +58 -0
  62. xp/models/actiontable/msactiontable_xp33.py +65 -0
  63. xp/models/conbus/__init__.py +1 -0
  64. xp/models/conbus/conbus.py +87 -0
  65. xp/models/conbus/conbus_autoreport.py +67 -0
  66. xp/models/conbus/conbus_blink.py +80 -0
  67. xp/models/conbus/conbus_client_config.py +55 -0
  68. xp/models/conbus/conbus_connection_status.py +40 -0
  69. xp/models/conbus/conbus_custom.py +58 -0
  70. xp/models/conbus/conbus_datapoint.py +89 -0
  71. xp/models/conbus/conbus_discover.py +64 -0
  72. xp/models/conbus/conbus_event_raw.py +47 -0
  73. xp/models/conbus/conbus_lightlevel.py +52 -0
  74. xp/models/conbus/conbus_linknumber.py +54 -0
  75. xp/models/conbus/conbus_output.py +57 -0
  76. xp/models/conbus/conbus_raw.py +45 -0
  77. xp/models/conbus/conbus_receive.py +42 -0
  78. xp/models/conbus/conbus_writeconfig.py +60 -0
  79. xp/models/homekit/__init__.py +1 -0
  80. xp/models/homekit/homekit_accessory.py +35 -0
  81. xp/models/homekit/homekit_config.py +106 -0
  82. xp/models/homekit/homekit_conson_config.py +86 -0
  83. xp/models/log_entry.py +130 -0
  84. xp/models/protocol/__init__.py +1 -0
  85. xp/models/protocol/conbus_protocol.py +312 -0
  86. xp/models/response.py +42 -0
  87. xp/models/telegram/__init__.py +1 -0
  88. xp/models/telegram/action_type.py +31 -0
  89. xp/models/telegram/datapoint_type.py +82 -0
  90. xp/models/telegram/event_telegram.py +140 -0
  91. xp/models/telegram/event_type.py +15 -0
  92. xp/models/telegram/input_action_type.py +69 -0
  93. xp/models/telegram/input_type.py +17 -0
  94. xp/models/telegram/module_type.py +188 -0
  95. xp/models/telegram/module_type_code.py +205 -0
  96. xp/models/telegram/output_telegram.py +103 -0
  97. xp/models/telegram/reply_telegram.py +297 -0
  98. xp/models/telegram/system_function.py +116 -0
  99. xp/models/telegram/system_telegram.py +94 -0
  100. xp/models/telegram/telegram.py +28 -0
  101. xp/models/telegram/telegram_type.py +19 -0
  102. xp/models/telegram/timeparam_type.py +51 -0
  103. xp/models/write_config_type.py +33 -0
  104. xp/services/__init__.py +26 -0
  105. xp/services/actiontable/__init__.py +1 -0
  106. xp/services/actiontable/actiontable_serializer.py +273 -0
  107. xp/services/actiontable/msactiontable_serializer.py +7 -0
  108. xp/services/actiontable/msactiontable_xp20_serializer.py +169 -0
  109. xp/services/actiontable/msactiontable_xp24_serializer.py +120 -0
  110. xp/services/actiontable/msactiontable_xp33_serializer.py +239 -0
  111. xp/services/conbus/__init__.py +1 -0
  112. xp/services/conbus/actiontable/__init__.py +1 -0
  113. xp/services/conbus/actiontable/actiontable_download_service.py +158 -0
  114. xp/services/conbus/actiontable/actiontable_list_service.py +91 -0
  115. xp/services/conbus/actiontable/actiontable_show_service.py +89 -0
  116. xp/services/conbus/actiontable/actiontable_upload_service.py +211 -0
  117. xp/services/conbus/actiontable/msactiontable_service.py +232 -0
  118. xp/services/conbus/conbus_blink_all_service.py +181 -0
  119. xp/services/conbus/conbus_blink_service.py +158 -0
  120. xp/services/conbus/conbus_custom_service.py +156 -0
  121. xp/services/conbus/conbus_datapoint_queryall_service.py +182 -0
  122. xp/services/conbus/conbus_datapoint_service.py +170 -0
  123. xp/services/conbus/conbus_discover_service.py +312 -0
  124. xp/services/conbus/conbus_event_raw_service.py +181 -0
  125. xp/services/conbus/conbus_output_service.py +194 -0
  126. xp/services/conbus/conbus_raw_service.py +122 -0
  127. xp/services/conbus/conbus_receive_service.py +115 -0
  128. xp/services/conbus/conbus_scan_service.py +150 -0
  129. xp/services/conbus/write_config_service.py +194 -0
  130. xp/services/homekit/__init__.py +1 -0
  131. xp/services/homekit/homekit_cache_service.py +307 -0
  132. xp/services/homekit/homekit_conbus_service.py +93 -0
  133. xp/services/homekit/homekit_config_validator.py +310 -0
  134. xp/services/homekit/homekit_conson_validator.py +121 -0
  135. xp/services/homekit/homekit_dimminglight.py +182 -0
  136. xp/services/homekit/homekit_dimminglight_service.py +148 -0
  137. xp/services/homekit/homekit_hap_service.py +342 -0
  138. xp/services/homekit/homekit_lightbulb.py +120 -0
  139. xp/services/homekit/homekit_lightbulb_service.py +86 -0
  140. xp/services/homekit/homekit_module_service.py +56 -0
  141. xp/services/homekit/homekit_outlet.py +168 -0
  142. xp/services/homekit/homekit_outlet_service.py +121 -0
  143. xp/services/homekit/homekit_service.py +359 -0
  144. xp/services/log_file_service.py +309 -0
  145. xp/services/module_type_service.py +257 -0
  146. xp/services/protocol/__init__.py +21 -0
  147. xp/services/protocol/conbus_event_protocol.py +360 -0
  148. xp/services/protocol/conbus_protocol.py +318 -0
  149. xp/services/protocol/protocol_factory.py +78 -0
  150. xp/services/protocol/telegram_protocol.py +264 -0
  151. xp/services/reverse_proxy_service.py +435 -0
  152. xp/services/server/__init__.py +1 -0
  153. xp/services/server/base_server_service.py +366 -0
  154. xp/services/server/cp20_server_service.py +65 -0
  155. xp/services/server/device_service_factory.py +94 -0
  156. xp/services/server/server_service.py +428 -0
  157. xp/services/server/xp130_server_service.py +67 -0
  158. xp/services/server/xp20_server_service.py +92 -0
  159. xp/services/server/xp230_server_service.py +58 -0
  160. xp/services/server/xp24_server_service.py +245 -0
  161. xp/services/server/xp33_server_service.py +535 -0
  162. xp/services/telegram/__init__.py +1 -0
  163. xp/services/telegram/telegram_blink_service.py +138 -0
  164. xp/services/telegram/telegram_checksum_service.py +149 -0
  165. xp/services/telegram/telegram_datapoint_service.py +82 -0
  166. xp/services/telegram/telegram_discover_service.py +277 -0
  167. xp/services/telegram/telegram_link_number_service.py +216 -0
  168. xp/services/telegram/telegram_output_service.py +322 -0
  169. xp/services/telegram/telegram_service.py +380 -0
  170. xp/services/telegram/telegram_version_service.py +288 -0
  171. xp/utils/__init__.py +12 -0
  172. xp/utils/checksum.py +61 -0
  173. xp/utils/dependencies.py +531 -0
  174. xp/utils/event_helper.py +31 -0
  175. xp/utils/serialization.py +205 -0
  176. xp/utils/time_utils.py +134 -0
@@ -0,0 +1,366 @@
1
+ """Base Server Service with shared functionality.
2
+
3
+ This module provides a base class for all XP device server services,
4
+ containing common functionality like module type response generation.
5
+ """
6
+
7
+ import logging
8
+ import threading
9
+ from abc import ABC
10
+ from typing import Any, Optional
11
+
12
+ from xp.models import ModuleTypeCode
13
+ from xp.models.telegram.datapoint_type import DataPointType
14
+ from xp.models.telegram.system_function import SystemFunction
15
+ from xp.models.telegram.system_telegram import SystemTelegram
16
+ from xp.utils.checksum import calculate_checksum
17
+
18
+
19
+ class BaseServerService(ABC):
20
+ """
21
+ Base class for all XP device server services.
22
+
23
+ Provides common functionality that is shared across all device types,
24
+ such as module type response generation.
25
+ """
26
+
27
+ def __init__(self, serial_number: str):
28
+ """Initialize base server service.
29
+
30
+ Args:
31
+ serial_number: The device serial number.
32
+ """
33
+ self.serial_number = serial_number
34
+ self.logger = logging.getLogger(__name__)
35
+
36
+ # Must be set by subclasses
37
+ self.device_type: str = ""
38
+ self.module_type_code: ModuleTypeCode = ModuleTypeCode.NOMOD
39
+ self.hardware_version: str = ""
40
+ self.software_version: str = ""
41
+ self.device_status: str = "OK"
42
+ self.link_number: int = 1
43
+ self.temperature: str = "+23,5§C"
44
+ self.voltage: str = "+12,5§V"
45
+
46
+ self.telegram_buffer: list[str] = []
47
+ self.telegram_buffer_lock = threading.Lock() # Lock for socket set
48
+
49
+ # MsActionTable download state (None, "ack_sent", "data_sent")
50
+ self.msactiontable_download_state: Optional[str] = None
51
+
52
+ def generate_datapoint_type_response(
53
+ self, datapoint_type: DataPointType
54
+ ) -> Optional[str]:
55
+ """Generate datapoint_type response telegram.
56
+
57
+ Args:
58
+ datapoint_type: The type of datapoint to query.
59
+
60
+ Returns:
61
+ The response telegram string, or None if generation fails.
62
+ """
63
+ datapoint_values = {
64
+ DataPointType.TEMPERATURE: self.temperature,
65
+ DataPointType.MODULE_TYPE_CODE: f"{self.module_type_code.value:02}",
66
+ DataPointType.SW_VERSION: self.software_version,
67
+ DataPointType.MODULE_STATE: self.device_status,
68
+ DataPointType.MODULE_TYPE: self.device_type,
69
+ DataPointType.LINK_NUMBER: f"{self.link_number:02}",
70
+ DataPointType.VOLTAGE: self.voltage,
71
+ DataPointType.HW_VERSION: self.hardware_version,
72
+ DataPointType.MODULE_ERROR_CODE: "00",
73
+ }
74
+ data_value = datapoint_values.get(datapoint_type) or "00"
75
+ data_part = f"R{self.serial_number}F02D{datapoint_type.value}{data_value}"
76
+ telegram = self._build_response_telegram(data_part)
77
+
78
+ self.logger.debug(
79
+ f"Generated {self.device_type} module type response: {telegram}"
80
+ )
81
+ return telegram
82
+
83
+ def _check_request_for_device(self, request: SystemTelegram) -> bool:
84
+ """Check if request is for this device (including broadcast).
85
+
86
+ Args:
87
+ request: The system telegram request to check.
88
+
89
+ Returns:
90
+ True if request is for this device or broadcast, False otherwise.
91
+ """
92
+ return request.serial_number in (self.serial_number, "0000000000")
93
+
94
+ @staticmethod
95
+ def _build_response_telegram(data_part: str) -> str:
96
+ """Build a complete response telegram with checksum.
97
+
98
+ Args:
99
+ data_part: The data part of the telegram without checksum.
100
+
101
+ Returns:
102
+ The complete telegram with checksum enclosed in angle brackets.
103
+ """
104
+ checksum = calculate_checksum(data_part)
105
+ return f"<{data_part}{checksum}>"
106
+
107
+ def _log_response(self, response_type: str, telegram: str) -> None:
108
+ """Log response generation.
109
+
110
+ Args:
111
+ response_type: The type of response being generated.
112
+ telegram: The telegram string being logged.
113
+ """
114
+ self.logger.debug(
115
+ f"Generated {self.device_type} {response_type} response: {telegram}"
116
+ )
117
+
118
+ def generate_discover_response(self) -> str:
119
+ """Generate discover response telegram.
120
+
121
+ Returns:
122
+ The discover response telegram string.
123
+ """
124
+ data_part = f"R{self.serial_number}F01D"
125
+ telegram = self._build_response_telegram(data_part)
126
+ self._log_response("discover", telegram)
127
+ return telegram
128
+
129
+ def set_link_number(
130
+ self, request: SystemTelegram, new_link_number: int
131
+ ) -> Optional[str]:
132
+ """Set link number and generate ACK response.
133
+
134
+ Args:
135
+ request: The system telegram request.
136
+ new_link_number: The new link number to set.
137
+
138
+ Returns:
139
+ The ACK response telegram string, or None if request is invalid.
140
+ """
141
+ if (
142
+ request.system_function == SystemFunction.WRITE_CONFIG
143
+ and request.datapoint_type == DataPointType.LINK_NUMBER
144
+ ):
145
+ # Update internal link number
146
+ self.link_number = new_link_number
147
+
148
+ # Generate ACK response
149
+ data_part = f"R{self.serial_number}F18D"
150
+ telegram = self._build_response_telegram(data_part)
151
+
152
+ self.logger.info(f"{self.device_type} link number set to {new_link_number}")
153
+ return telegram
154
+
155
+ return None
156
+
157
+ def _get_msactiontable_serializer(self) -> Optional[Any]:
158
+ """Get the MsActionTable serializer for this device.
159
+
160
+ Subclasses should override this to return their specific serializer.
161
+
162
+ Returns:
163
+ The serializer instance, or None if not supported.
164
+ """
165
+ return None
166
+
167
+ def _get_msactiontable(self) -> Optional[Any]:
168
+ """Get the MsActionTable for this device.
169
+
170
+ Subclasses should override this to return their msactiontable instance.
171
+
172
+ Returns:
173
+ The msactiontable instance, or None if not supported.
174
+ """
175
+ return None
176
+
177
+ def _handle_download_msactiontable_request(
178
+ self, request: SystemTelegram
179
+ ) -> Optional[str]:
180
+ """Handle F13D - DOWNLOAD_MSACTIONTABLE request.
181
+
182
+ Args:
183
+ request: The system telegram request to process.
184
+
185
+ Returns:
186
+ ACK telegram if request is valid, NAK otherwise.
187
+ """
188
+ serializer = self._get_msactiontable_serializer()
189
+ msactiontable = self._get_msactiontable()
190
+
191
+ # Only handle if serializer and msactiontable are available
192
+ if not serializer or msactiontable is None:
193
+ return None
194
+
195
+ # Send ACK and queue data telegram
196
+ ack_data = self._build_response_telegram(f"R{self.serial_number}F18D") # ACK
197
+
198
+ # Send MsActionTable data
199
+ encoded_data = serializer.to_data(msactiontable)
200
+ data_telegram = self._build_response_telegram(
201
+ f"R{self.serial_number}F17D{encoded_data}"
202
+ )
203
+ self.msactiontable_download_state = "data_sent"
204
+
205
+ # Return ACK and TABLE
206
+ return ack_data + data_telegram
207
+
208
+ def _handle_download_msactiontable_ack_request(
209
+ self, _request: SystemTelegram
210
+ ) -> Optional[str]:
211
+ """Handle MsActionTable download ACK protocol.
212
+
213
+ Args:
214
+ _request: The system telegram request (unused, kept for signature consistency).
215
+
216
+ Returns:
217
+ Data telegram, EOF telegram, or NAK if state is invalid.
218
+ """
219
+ if self.msactiontable_download_state == "data_sent":
220
+ # Send EOF
221
+ eof_telegram = self._build_response_telegram(f"R{self.serial_number}F16D")
222
+ self.msactiontable_download_state = None
223
+ return eof_telegram
224
+
225
+ return self._build_response_telegram(f"R{self.serial_number}F19D") # NAK
226
+
227
+ def process_system_telegram(self, request: SystemTelegram) -> Optional[str]:
228
+ """Template method for processing system telegrams.
229
+
230
+ Args:
231
+ request: The system telegram request to process.
232
+
233
+ Returns:
234
+ The response telegram string, or None if request cannot be handled.
235
+ """
236
+ # Check if request is for this device
237
+ if not self._check_request_for_device(request):
238
+ return None
239
+
240
+ # Handle different system functions
241
+ if request.system_function == SystemFunction.DISCOVERY:
242
+ return self.generate_discover_response()
243
+
244
+ elif request.system_function == SystemFunction.READ_DATAPOINT:
245
+ return self._handle_return_data_request(request)
246
+
247
+ elif request.system_function == SystemFunction.WRITE_CONFIG:
248
+ return self._handle_write_config_request(request)
249
+
250
+ elif request.system_function == SystemFunction.ACTION:
251
+ return self._handle_action_request(request)
252
+
253
+ elif request.system_function == SystemFunction.DOWNLOAD_MSACTIONTABLE:
254
+ return self._handle_download_msactiontable_request(request)
255
+
256
+ elif (
257
+ request.system_function == SystemFunction.ACK
258
+ and self.msactiontable_download_state
259
+ ):
260
+ return self._handle_download_msactiontable_ack_request(request)
261
+
262
+ self.logger.warning(f"Unhandled {self.device_type} request: {request}")
263
+ return None
264
+
265
+ def _handle_return_data_request(self, request: SystemTelegram) -> Optional[str]:
266
+ """Handle RETURN_DATA requests - can be overridden by subclasses.
267
+
268
+ Args:
269
+ request: The system telegram request.
270
+
271
+ Returns:
272
+ The response telegram string, or None if request cannot be handled.
273
+ """
274
+ self.logger.debug(
275
+ f"_handle_return_data_request {self.device_type} request: {request}"
276
+ )
277
+ module_specific = self._handle_device_specific_data_request(request)
278
+ if module_specific:
279
+ return module_specific
280
+
281
+ if request.datapoint_type:
282
+ return self.generate_datapoint_type_response(request.datapoint_type)
283
+
284
+ # Allow device-specific handlers
285
+ return None
286
+
287
+ def _handle_device_specific_data_request(
288
+ self, request: SystemTelegram
289
+ ) -> Optional[str]:
290
+ """Override in subclasses for device-specific data requests.
291
+
292
+ Args:
293
+ request: The system telegram request.
294
+
295
+ Returns:
296
+ The response telegram string, or None if request cannot be handled.
297
+ """
298
+ return None
299
+
300
+ def _handle_write_config_request(self, request: SystemTelegram) -> Optional[str]:
301
+ """Handle WRITE_CONFIG requests.
302
+
303
+ Args:
304
+ request: The system telegram request.
305
+
306
+ Returns:
307
+ The response telegram string, or None if request cannot be handled.
308
+ """
309
+ if request.datapoint_type == DataPointType.LINK_NUMBER:
310
+ return self.set_link_number(request, 1) # Default implementation
311
+
312
+ return self._handle_device_specific_config_request()
313
+
314
+ def _handle_action_request(self, request: SystemTelegram) -> Optional[str]:
315
+ """Handle ACTION requests.
316
+
317
+ Args:
318
+ request: The system telegram request.
319
+
320
+ Returns:
321
+ The response telegram string, or None if request cannot be handled.
322
+ """
323
+ return self._handle_device_specific_action_request(request)
324
+
325
+ def _handle_device_specific_action_request(
326
+ self, request: SystemTelegram
327
+ ) -> Optional[str]:
328
+ """Override in subclasses for device-specific data requests.
329
+
330
+ Args:
331
+ request: The system telegram request.
332
+
333
+ Returns:
334
+ The response telegram string, or None if request cannot be handled.
335
+ """
336
+ return None
337
+
338
+ @staticmethod
339
+ def _handle_device_specific_config_request() -> Optional[str]:
340
+ """Override in subclasses for device-specific config requests.
341
+
342
+ Returns:
343
+ The response telegram string, or None if request cannot be handled.
344
+ """
345
+ return None
346
+
347
+ def add_telegram_buffer(self, telegram: str) -> None:
348
+ """Add telegram to the buffer.
349
+
350
+ Args:
351
+ telegram: The telegram string to add to the buffer.
352
+ """
353
+ self.logger.debug(f"Add telegram to the buffer: {telegram}")
354
+ with self.telegram_buffer_lock:
355
+ self.telegram_buffer.append(telegram)
356
+
357
+ def collect_telegram_buffer(self) -> list[str]:
358
+ """Collecting telegrams from the buffer.
359
+
360
+ Returns:
361
+ List of telegram strings from the buffer. The buffer is cleared after collection.
362
+ """
363
+ with self.telegram_buffer_lock:
364
+ result = self.telegram_buffer.copy()
365
+ self.telegram_buffer.clear()
366
+ return result
@@ -0,0 +1,65 @@
1
+ """CP20 Server Service for device emulation.
2
+
3
+ This service provides CP20-specific device emulation functionality,
4
+ including response generation and device configuration handling.
5
+ """
6
+
7
+ from typing import Dict, Optional
8
+
9
+ from xp.models import ModuleTypeCode
10
+ from xp.models.telegram.system_telegram import SystemTelegram
11
+ from xp.services.actiontable.msactiontable_serializer import MsActionTableSerializer
12
+ from xp.services.server.base_server_service import BaseServerService
13
+
14
+
15
+ class CP20ServerError(Exception):
16
+ """Raised when CP20 server operations fail."""
17
+
18
+ pass
19
+
20
+
21
+ class CP20ServerService(BaseServerService):
22
+ """
23
+ CP20 device emulation service.
24
+
25
+ Generates CP20-specific responses, handles CP20 device configuration,
26
+ and implements CP20 telegram format.
27
+ """
28
+
29
+ def __init__(
30
+ self,
31
+ serial_number: str,
32
+ _variant: str = "",
33
+ _msactiontable_serializer: Optional[MsActionTableSerializer] = None,
34
+ ):
35
+ """Initialize CP20 server service.
36
+
37
+ Args:
38
+ serial_number: The device serial number.
39
+ _variant: Reserved parameter for consistency (unused).
40
+ _msactiontable_serializer: Generic MsActionTable serializer (unused).
41
+ """
42
+ super().__init__(serial_number)
43
+ self.device_type = "CP20"
44
+ self.module_type_code = ModuleTypeCode.CP20 # CP20 module type from registry
45
+ self.firmware_version = "CP20_V0.01.05"
46
+
47
+ def _handle_device_specific_data_request(
48
+ self, request: SystemTelegram
49
+ ) -> Optional[str]:
50
+ """Handle CP20-specific data requests."""
51
+ return None
52
+
53
+ def get_device_info(self) -> Dict:
54
+ """Get CP20 device information.
55
+
56
+ Returns:
57
+ Dictionary containing device information.
58
+ """
59
+ return {
60
+ "serial_number": self.serial_number,
61
+ "device_type": self.device_type,
62
+ "firmware_version": self.firmware_version,
63
+ "status": self.device_status,
64
+ "link_number": self.link_number,
65
+ }
@@ -0,0 +1,94 @@
1
+ """Device Service Factory for creating device instances.
2
+
3
+ This module provides a factory for creating device service instances
4
+ with proper dependency injection of serializers.
5
+ """
6
+
7
+ from xp.services.actiontable.msactiontable_serializer import MsActionTableSerializer
8
+ from xp.services.actiontable.msactiontable_xp20_serializer import (
9
+ Xp20MsActionTableSerializer,
10
+ )
11
+ from xp.services.actiontable.msactiontable_xp24_serializer import (
12
+ Xp24MsActionTableSerializer,
13
+ )
14
+ from xp.services.actiontable.msactiontable_xp33_serializer import (
15
+ Xp33MsActionTableSerializer,
16
+ )
17
+ from xp.services.server.base_server_service import BaseServerService
18
+ from xp.services.server.cp20_server_service import CP20ServerService
19
+ from xp.services.server.xp20_server_service import XP20ServerService
20
+ from xp.services.server.xp24_server_service import XP24ServerService
21
+ from xp.services.server.xp33_server_service import XP33ServerService
22
+ from xp.services.server.xp130_server_service import XP130ServerService
23
+ from xp.services.server.xp230_server_service import XP230ServerService
24
+
25
+
26
+ class DeviceServiceFactory:
27
+ """Factory for creating device service instances.
28
+
29
+ Encapsulates device creation logic and handles serializer injection
30
+ for different device types.
31
+ """
32
+
33
+ def __init__(
34
+ self,
35
+ xp20ms_serializer: Xp20MsActionTableSerializer,
36
+ xp24ms_serializer: Xp24MsActionTableSerializer,
37
+ xp33ms_serializer: Xp33MsActionTableSerializer,
38
+ ms_serializer: MsActionTableSerializer,
39
+ ):
40
+ """Initialize device service factory.
41
+
42
+ Args:
43
+ xp20ms_serializer: XP20 MsActionTable serializer (injected via DI).
44
+ xp24ms_serializer: XP24 MsActionTable serializer (injected via DI).
45
+ xp33ms_serializer: XP33 MsActionTable serializer (injected via DI).
46
+ ms_serializer: Generic MsActionTable serializer (injected via DI).
47
+ """
48
+ self.xp20ms_serializer = xp20ms_serializer
49
+ self.xp24ms_serializer = xp24ms_serializer
50
+ self.xp33ms_serializer = xp33ms_serializer
51
+ self.ms_serializer = ms_serializer
52
+
53
+ def create_device(self, module_type: str, serial_number: str) -> BaseServerService:
54
+ """Create device instance for given module type.
55
+
56
+ Args:
57
+ module_type: Module type code (e.g., "XP20", "XP33LR").
58
+ serial_number: Device serial number.
59
+
60
+ Returns:
61
+ Device service instance configured with appropriate serializer.
62
+
63
+ Raises:
64
+ ValueError: If module_type is unknown or unsupported.
65
+ """
66
+ # Map module types to their constructors and parameters
67
+ if module_type == "CP20":
68
+ return CP20ServerService(serial_number, "CP20", self.ms_serializer)
69
+
70
+ elif module_type == "XP24":
71
+ return XP24ServerService(serial_number, "XP24", self.xp24ms_serializer)
72
+
73
+ elif module_type == "XP33":
74
+ return XP33ServerService(serial_number, "XP33", self.xp33ms_serializer)
75
+
76
+ elif module_type == "XP33LR":
77
+ return XP33ServerService(serial_number, "XP33LR", self.xp33ms_serializer)
78
+
79
+ elif module_type == "XP33LED":
80
+ return XP33ServerService(serial_number, "XP33LED", self.xp33ms_serializer)
81
+
82
+ elif module_type == "XP20":
83
+ return XP20ServerService(serial_number, "XP20", self.xp20ms_serializer)
84
+
85
+ elif module_type == "XP130":
86
+ return XP130ServerService(serial_number, "XP130", self.ms_serializer)
87
+
88
+ elif module_type == "XP230":
89
+ return XP230ServerService(serial_number, "XP230", self.ms_serializer)
90
+
91
+ else:
92
+ raise ValueError(
93
+ f"Unknown device type '{module_type}' for serial {serial_number}"
94
+ )