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
@@ -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,
@@ -13,7 +13,7 @@ from xp.services.server.base_server_service import BaseServerService
13
13
 
14
14
 
15
15
  class XP24ServerError(Exception):
16
- """Raised when XP24 server operations fail"""
16
+ """Raised when XP24 server operations fail."""
17
17
 
18
18
  pass
19
19
 
@@ -27,7 +27,11 @@ class XP24ServerService(BaseServerService):
27
27
  """
28
28
 
29
29
  def __init__(self, serial_number: str):
30
- """Initialize XP24 server service"""
30
+ """Initialize XP24 server service.
31
+
32
+ Args:
33
+ serial_number: The device serial number.
34
+ """
31
35
  super().__init__(serial_number)
32
36
  self.device_type = "XP24"
33
37
  self.module_type_code = 7 # XP24 module type from registry
@@ -36,7 +40,7 @@ class XP24ServerService(BaseServerService):
36
40
  def _handle_device_specific_data_request(
37
41
  self, request: SystemTelegram
38
42
  ) -> Optional[str]:
39
- """Handle XP24-specific data requests"""
43
+ """Handle XP24-specific data requests."""
40
44
  if (
41
45
  request.system_function != SystemFunction.READ_DATAPOINT
42
46
  or not request.datapoint_type
@@ -49,7 +53,12 @@ class XP24ServerService(BaseServerService):
49
53
  DataPointType.MODULE_STATE: "OFF",
50
54
  DataPointType.MODULE_OPERATING_HOURS: "00:000[H],01:000[H],02:000[H],03:000[H]",
51
55
  }
52
- data_part = f"R{self.serial_number}F02{datapoint_type.value}{self.module_type_code}{datapoint_values.get(datapoint_type)}"
56
+ data_part = (
57
+ f"R{self.serial_number}"
58
+ f"F02{datapoint_type.value}"
59
+ f"{self.module_type_code}"
60
+ f"{datapoint_values.get(datapoint_type)}"
61
+ )
53
62
  telegram = self._build_response_telegram(data_part)
54
63
 
55
64
  self.logger.debug(
@@ -60,14 +69,25 @@ class XP24ServerService(BaseServerService):
60
69
  def _handle_device_specific_action_request(
61
70
  self, request: SystemTelegram
62
71
  ) -> Optional[str]:
72
+ """Handle XP24-specific action requests.
73
+
74
+ Args:
75
+ request: The system telegram request.
63
76
 
77
+ Returns:
78
+ The response telegram string, or None if request cannot be handled.
79
+ """
64
80
  if request.system_function != SystemFunction.ACTION:
65
81
  return None
66
82
 
67
83
  return self.generate_action_response(request)
68
84
 
69
85
  def get_device_info(self) -> Dict:
70
- """Get XP24 device information"""
86
+ """Get XP24 device information.
87
+
88
+ Returns:
89
+ Dictionary containing device information.
90
+ """
71
91
  return {
72
92
  "serial_number": self.serial_number,
73
93
  "device_type": self.device_type,
@@ -77,7 +97,14 @@ class XP24ServerService(BaseServerService):
77
97
  }
78
98
 
79
99
  def generate_action_response(self, request: SystemTelegram) -> Optional[str]:
80
- """Generate action response telegram (simulated)"""
100
+ """Generate action response telegram (simulated).
101
+
102
+ Args:
103
+ request: The system telegram request.
104
+
105
+ Returns:
106
+ The ACK or NAK response telegram string.
107
+ """
81
108
  response = "F19D" # NAK
82
109
  if (
83
110
  request.system_function == SystemFunction.ACTION
@@ -15,7 +15,7 @@ from xp.utils import calculate_checksum
15
15
 
16
16
 
17
17
  class XP33ServerError(Exception):
18
- """Raised when XP33 server operations fail"""
18
+ """Raised when XP33 server operations fail."""
19
19
 
20
20
  pass
21
21
 
@@ -29,7 +29,12 @@ class XP33ServerService(BaseServerService):
29
29
  """
30
30
 
31
31
  def __init__(self, serial_number: str, variant: str = "XP33LR"):
32
- """Initialize XP33 server service"""
32
+ """Initialize XP33 server service.
33
+
34
+ Args:
35
+ serial_number: The device serial number.
36
+ variant: Device variant (XP33, XP33LR, or XP33LED).
37
+ """
33
38
  super().__init__(serial_number)
34
39
  self.variant = variant # XP33 or XP33LR or XP33LED
35
40
  self.device_type = "XP33"
@@ -69,7 +74,7 @@ class XP33ServerService(BaseServerService):
69
74
  def _handle_device_specific_data_request(
70
75
  self, request: SystemTelegram
71
76
  ) -> Optional[str]:
72
- """Handle XP24-specific data requests"""
77
+ """Handle XP24-specific data requests."""
73
78
  if (
74
79
  request.system_function != SystemFunction.READ_DATAPOINT
75
80
  or not request.datapoint_type
@@ -82,7 +87,12 @@ class XP33ServerService(BaseServerService):
82
87
  DataPointType.MODULE_STATE: "OFF",
83
88
  DataPointType.MODULE_OPERATING_HOURS: "00:000[H],01:000[H],02:000[H]",
84
89
  }
85
- data_part = f"R{self.serial_number}F02{datapoint_type.value}{self.module_type_code}{datapoint_values.get(datapoint_type)}"
90
+ data_part = (
91
+ f"R{self.serial_number}"
92
+ f"F02{datapoint_type.value}"
93
+ f"{self.module_type_code}"
94
+ f"{datapoint_values.get(datapoint_type)}"
95
+ )
86
96
  checksum = calculate_checksum(data_part)
87
97
  telegram = f"<{data_part}{checksum}>"
88
98
 
@@ -92,7 +102,15 @@ class XP33ServerService(BaseServerService):
92
102
  return telegram
93
103
 
94
104
  def set_channel_dimming(self, channel: int, level: int) -> bool:
95
- """Set individual channel dimming level"""
105
+ """Set individual channel dimming level.
106
+
107
+ Args:
108
+ channel: Channel number (1-3).
109
+ level: Dimming level (0-100 percent).
110
+
111
+ Returns:
112
+ True if channel was set successfully, False otherwise.
113
+ """
96
114
  if 1 <= channel <= 3 and 0 <= level <= 100:
97
115
  self.channel_states[channel - 1] = level
98
116
  self.logger.info(f"XP33 channel {channel} set to {level}%")
@@ -100,7 +118,14 @@ class XP33ServerService(BaseServerService):
100
118
  return False
101
119
 
102
120
  def activate_scene(self, scene: int) -> bool:
103
- """Activate a pre-programmed scene"""
121
+ """Activate a pre-programmed scene.
122
+
123
+ Args:
124
+ scene: Scene number (1-4).
125
+
126
+ Returns:
127
+ True if scene was activated successfully, False otherwise.
128
+ """
104
129
  if scene in self.scenes:
105
130
  self.channel_states = self.scenes[scene].copy()
106
131
  self.logger.info(f"XP33 scene {scene} activated: {self.channel_states}")
@@ -108,7 +133,11 @@ class XP33ServerService(BaseServerService):
108
133
  return False
109
134
 
110
135
  def get_device_info(self) -> Dict:
111
- """Get XP33 device information"""
136
+ """Get XP33 device information.
137
+
138
+ Returns:
139
+ Dictionary containing device information.
140
+ """
112
141
  return {
113
142
  "serial_number": self.serial_number,
114
143
  "device_type": self.device_type,
@@ -123,7 +152,11 @@ class XP33ServerService(BaseServerService):
123
152
  }
124
153
 
125
154
  def get_technical_specs(self) -> Dict:
126
- """Get technical specifications"""
155
+ """Get technical specifications.
156
+
157
+ Returns:
158
+ Dictionary containing technical specifications.
159
+ """
127
160
  if self.variant == "XP33LED":
128
161
  return {
129
162
  "power_per_channel": "100VA",
@@ -0,0 +1 @@
1
+ """Telegram parsing and processing services."""