conson-xp 1.2.0__py3-none-any.whl → 1.4.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 (62) hide show
  1. {conson_xp-1.2.0.dist-info → conson_xp-1.4.0.dist-info}/METADATA +1 -5
  2. {conson_xp-1.2.0.dist-info → conson_xp-1.4.0.dist-info}/RECORD +43 -60
  3. xp/__init__.py +1 -1
  4. xp/cli/commands/__init__.py +0 -2
  5. xp/cli/commands/conbus/conbus_actiontable_commands.py +5 -3
  6. xp/cli/commands/conbus/conbus_autoreport_commands.py +39 -21
  7. xp/cli/commands/conbus/conbus_blink_commands.py +8 -8
  8. xp/cli/commands/conbus/conbus_config_commands.py +3 -1
  9. xp/cli/commands/conbus/conbus_custom_commands.py +3 -1
  10. xp/cli/commands/conbus/conbus_datapoint_commands.py +4 -2
  11. xp/cli/commands/conbus/conbus_discover_commands.py +5 -3
  12. xp/cli/commands/conbus/conbus_lightlevel_commands.py +68 -32
  13. xp/cli/commands/conbus/conbus_linknumber_commands.py +32 -17
  14. xp/cli/commands/conbus/conbus_msactiontable_commands.py +11 -4
  15. xp/cli/commands/conbus/conbus_output_commands.py +6 -2
  16. xp/cli/commands/conbus/conbus_receive_commands.py +5 -3
  17. xp/cli/commands/file_commands.py +9 -3
  18. xp/cli/commands/homekit/homekit_start_commands.py +3 -1
  19. xp/cli/commands/module_commands.py +12 -4
  20. xp/cli/commands/reverse_proxy_commands.py +3 -1
  21. xp/cli/main.py +0 -2
  22. xp/models/conbus/conbus_datapoint.py +3 -0
  23. xp/models/conbus/conbus_discover.py +19 -3
  24. xp/models/conbus/conbus_writeconfig.py +60 -0
  25. xp/models/telegram/system_telegram.py +4 -4
  26. xp/services/conbus/conbus_datapoint_service.py +9 -6
  27. xp/services/conbus/conbus_discover_service.py +120 -2
  28. xp/services/conbus/conbus_scan_service.py +1 -1
  29. xp/services/conbus/{conbus_linknumber_set_service.py → write_config_service.py} +78 -66
  30. xp/services/protocol/telegram_protocol.py +4 -4
  31. xp/services/server/base_server_service.py +9 -4
  32. xp/services/server/cp20_server_service.py +2 -1
  33. xp/services/server/server_service.py +75 -4
  34. xp/services/server/xp130_server_service.py +2 -1
  35. xp/services/server/xp20_server_service.py +2 -1
  36. xp/services/server/xp230_server_service.py +2 -1
  37. xp/services/server/xp24_server_service.py +123 -50
  38. xp/services/server/xp33_server_service.py +150 -20
  39. xp/services/telegram/telegram_datapoint_service.py +70 -0
  40. xp/utils/dependencies.py +4 -46
  41. xp/api/__init__.py +0 -1
  42. xp/api/main.py +0 -125
  43. xp/api/models/__init__.py +0 -1
  44. xp/api/models/api.py +0 -31
  45. xp/api/models/discover.py +0 -31
  46. xp/api/routers/__init__.py +0 -17
  47. xp/api/routers/conbus.py +0 -5
  48. xp/api/routers/conbus_blink.py +0 -117
  49. xp/api/routers/conbus_custom.py +0 -71
  50. xp/api/routers/conbus_datapoint.py +0 -74
  51. xp/api/routers/conbus_output.py +0 -167
  52. xp/api/routers/errors.py +0 -38
  53. xp/cli/commands/api.py +0 -12
  54. xp/cli/commands/api_start_commands.py +0 -132
  55. xp/services/conbus/conbus_autoreport_get_service.py +0 -94
  56. xp/services/conbus/conbus_autoreport_set_service.py +0 -141
  57. xp/services/conbus/conbus_lightlevel_get_service.py +0 -109
  58. xp/services/conbus/conbus_lightlevel_set_service.py +0 -225
  59. xp/services/conbus/conbus_linknumber_get_service.py +0 -94
  60. {conson_xp-1.2.0.dist-info → conson_xp-1.4.0.dist-info}/WHEEL +0 -0
  61. {conson_xp-1.2.0.dist-info → conson_xp-1.4.0.dist-info}/entry_points.txt +0 -0
  62. {conson_xp-1.2.0.dist-info → conson_xp-1.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -6,6 +6,7 @@ including response generation and device configuration handling.
6
6
 
7
7
  from typing import Dict
8
8
 
9
+ from xp.models import ModuleTypeCode
9
10
  from xp.services.server.base_server_service import BaseServerService
10
11
 
11
12
 
@@ -31,7 +32,7 @@ class XP230ServerService(BaseServerService):
31
32
  """
32
33
  super().__init__(serial_number)
33
34
  self.device_type = "XP230"
34
- self.module_type_code = 34 # XP230 module type from registry
35
+ self.module_type_code = ModuleTypeCode.XP230 # XP230 module type from registry
35
36
  self.firmware_version = "XP230_V1.00.04"
36
37
 
37
38
  def get_device_info(self) -> Dict:
@@ -6,6 +6,7 @@ including response generation and device configuration handling.
6
6
 
7
7
  from typing import Dict, Optional
8
8
 
9
+ from xp.models import ModuleTypeCode
9
10
  from xp.models.telegram.datapoint_type import DataPointType
10
11
  from xp.models.telegram.system_function import SystemFunction
11
12
  from xp.models.telegram.system_telegram import SystemTelegram
@@ -18,6 +19,16 @@ class XP24ServerError(Exception):
18
19
  pass
19
20
 
20
21
 
22
+ class XP24Output:
23
+ """Represents an XP24 output state.
24
+
25
+ Attributes:
26
+ state: Current state of the output (True=on, False=off).
27
+ """
28
+
29
+ state: bool = False
30
+
31
+
21
32
  class XP24ServerService(BaseServerService):
22
33
  """
23
34
  XP24 device emulation service.
@@ -34,30 +45,107 @@ class XP24ServerService(BaseServerService):
34
45
  """
35
46
  super().__init__(serial_number)
36
47
  self.device_type = "XP24"
37
- self.module_type_code = 7 # XP24 module type from registry
48
+ self.module_type_code = ModuleTypeCode.XP24
49
+ self.autoreport_status = True
38
50
  self.firmware_version = "XP24_V0.34.03"
51
+ self.output_0: XP24Output = XP24Output()
52
+ self.output_1: XP24Output = XP24Output()
53
+ self.output_2: XP24Output = XP24Output()
54
+ self.output_3: XP24Output = XP24Output()
55
+
56
+ def _handle_device_specific_action_request(
57
+ self, request: SystemTelegram
58
+ ) -> Optional[str]:
59
+ """Handle XP24-specific data requests."""
60
+ telegrams = self._handle_action_module_output_state(request.data)
61
+ self.logger.debug(
62
+ f"Generated {self.device_type} module type responses: {telegrams}"
63
+ )
64
+ return telegrams
65
+
66
+ def _handle_action_module_output_state(self, data_value: str) -> str:
67
+ """Handle XP24-specific module output state."""
68
+ output_number = int(data_value[:2])
69
+ output_state = data_value[2:]
70
+ if output_number not in range(0, 4):
71
+ return self._build_ack_nak_response_telegram(False)
72
+
73
+ if output_state not in ("AA", "AB"):
74
+ return self._build_ack_nak_response_telegram(False)
75
+
76
+ output = (self.output_0, self.output_1, self.output_2, self.output_3)[
77
+ output_number
78
+ ]
79
+ previous_state = output.state
80
+ output.state = True if output_state == "AB" else False
81
+ state_changed = previous_state != output.state
82
+
83
+ telegrams = self._build_ack_nak_response_telegram(True)
84
+ if state_changed and self.autoreport_status:
85
+ telegrams += self._build_make_break_response_telegram(
86
+ output.state, output_number
87
+ )
88
+
89
+ return telegrams
90
+
91
+ def _build_ack_nak_response_telegram(self, ack_or_nak: bool) -> str:
92
+ """Build a complete ACK or NAK response telegram with checksum.
93
+
94
+ Args:
95
+ ack_or_nak: true: ACK telegram response, false: NAK telegram response.
96
+
97
+ Returns:
98
+ The complete telegram with checksum enclosed in angle brackets.
99
+ """
100
+ data_value = (
101
+ SystemFunction.ACK.value if ack_or_nak else SystemFunction.NAK.value
102
+ )
103
+ data_part = f"R{self.serial_number}" f"F{data_value:02}D"
104
+ return self._build_response_telegram(data_part)
105
+
106
+ def _build_make_break_response_telegram(
107
+ self, make_or_break: bool, output_number: int
108
+ ) -> str:
109
+ """Build a complete ACK or NAK response telegram with checksum.
110
+
111
+ Args:
112
+ make_or_break: true: MAKE event response, false: BREAK event response.
113
+ output_number: output concerned
114
+
115
+ Returns:
116
+ The complete event telegram with checksum enclosed in angle brackets.
117
+ """
118
+ data_value = "M" if make_or_break else "B"
119
+ data_part = (
120
+ f"E{self.module_type_code.value:02}"
121
+ f"L{self.link_number:02}"
122
+ f"I{output_number:02}"
123
+ f"{data_value}"
124
+ )
125
+ return self._build_response_telegram(data_part)
39
126
 
40
127
  def _handle_device_specific_data_request(
41
128
  self, request: SystemTelegram
42
129
  ) -> Optional[str]:
43
130
  """Handle XP24-specific data requests."""
44
- if (
45
- request.system_function != SystemFunction.READ_DATAPOINT
46
- or not request.datapoint_type
47
- ):
131
+ if not request.datapoint_type:
48
132
  return None
49
133
 
50
134
  datapoint_type = request.datapoint_type
51
- datapoint_values = {
52
- DataPointType.MODULE_OUTPUT_STATE: "xxxx0001",
53
- DataPointType.MODULE_STATE: "OFF",
54
- DataPointType.MODULE_OPERATING_HOURS: "00:000[H],01:000[H],02:000[H],03:000[H]",
55
- }
135
+ handler = {
136
+ DataPointType.MODULE_OUTPUT_STATE: self._handle_read_module_output_state,
137
+ DataPointType.MODULE_STATE: self._handle_read_module_state,
138
+ DataPointType.MODULE_OPERATING_HOURS: self._handle_read_module_operating_hours,
139
+ }.get(datapoint_type)
140
+ if not handler:
141
+ return None
142
+
143
+ data_value = handler()
56
144
  data_part = (
57
145
  f"R{self.serial_number}"
58
- f"F02{datapoint_type.value}"
59
- f"{self.module_type_code}"
60
- f"{datapoint_values.get(datapoint_type)}"
146
+ f"F02D{datapoint_type.value}"
147
+ f"{self.module_type_code.value:02}"
148
+ f"{data_value}"
61
149
  )
62
150
  telegram = self._build_response_telegram(data_part)
63
151
 
@@ -66,21 +154,26 @@ class XP24ServerService(BaseServerService):
66
154
  )
67
155
  return telegram
68
156
 
69
- def _handle_device_specific_action_request(
70
- self, request: SystemTelegram
71
- ) -> Optional[str]:
72
- """Handle XP24-specific action requests.
73
-
74
- Args:
75
- request: The system telegram request.
76
-
77
- Returns:
78
- The response telegram string, or None if request cannot be handled.
79
- """
80
- if request.system_function != SystemFunction.ACTION:
81
- return None
82
-
83
- return self.generate_action_response(request)
157
+ def _handle_read_module_operating_hours(self) -> str:
158
+ """Handle XP24-specific module operating hours."""
159
+ return "00:000[H],01:000[H],02:000[H],03:000[H]"
160
+
161
+ def _handle_read_module_state(self) -> str:
162
+ """Handle XP24-specific module state."""
163
+ for output in (self.output_0, self.output_1, self.output_2, self.output_3):
164
+ if output.state:
165
+ return "ON"
166
+ return "OFF"
167
+
168
+ def _handle_read_module_output_state(self) -> str:
169
+ """Handle XP24-specific module output state."""
170
+ return (
171
+ f"xxxx"
172
+ f"{1 if self.output_0.state else 0}"
173
+ f"{1 if self.output_1.state else 0}"
174
+ f"{1 if self.output_2.state else 0}"
175
+ f"{1 if self.output_3.state else 0}"
176
+ )
84
177
 
85
178
  def get_device_info(self) -> Dict:
86
179
  """Get XP24 device information.
@@ -91,29 +184,9 @@ class XP24ServerService(BaseServerService):
91
184
  return {
92
185
  "serial_number": self.serial_number,
93
186
  "device_type": self.device_type,
187
+ "module_type_code": self.module_type_code.value,
94
188
  "firmware_version": self.firmware_version,
95
189
  "status": self.device_status,
96
190
  "link_number": self.link_number,
191
+ "autoreport_status": self.autoreport_status,
97
192
  }
98
-
99
- def generate_action_response(self, request: SystemTelegram) -> Optional[str]:
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
- """
108
- response = "F19D" # NAK
109
- if (
110
- request.system_function == SystemFunction.ACTION
111
- and request.data[:2] in ("00", "01", "02", "03")
112
- and request.data[2:] in ("AA", "AB")
113
- ):
114
- response = "F18D" # ACK
115
-
116
- data_part = f"R{self.serial_number}{response}"
117
- telegram = self._build_response_telegram(data_part)
118
- self._log_response("module_action_response", telegram)
119
- return telegram
@@ -7,11 +7,11 @@ including response generation and device configuration handling for
7
7
 
8
8
  from typing import Dict, Optional
9
9
 
10
+ from xp.models import ModuleTypeCode
10
11
  from xp.models.telegram.datapoint_type import DataPointType
11
12
  from xp.models.telegram.system_function import SystemFunction
12
13
  from xp.models.telegram.system_telegram import SystemTelegram
13
14
  from xp.services.server.base_server_service import BaseServerService
14
- from xp.utils import calculate_checksum
15
15
 
16
16
 
17
17
  class XP33ServerError(Exception):
@@ -38,27 +38,28 @@ class XP33ServerService(BaseServerService):
38
38
  super().__init__(serial_number)
39
39
  self.variant = variant # XP33 or XP33LR or XP33LED
40
40
  self.device_type = "XP33"
41
- self.module_type_code = 11 # XP33 module type
41
+ self.module_type_code = ModuleTypeCode.XP33 # XP33 module type
42
42
 
43
43
  # XP33 device characteristics (anonymized for interoperability testing)
44
44
  if variant == "XP33LED":
45
45
  self.firmware_version = "XP33LED_V0.00.00"
46
46
  self.ean_code = "1234567890123" # Test EAN - not a real product code
47
47
  self.max_power = 300 # 3 x 100VA
48
- self.module_type_code = 31 # XP33LR module type
48
+ self.module_type_code = ModuleTypeCode.XP33LED # XP33LR module type
49
49
  elif variant == "XP33LR": # XP33LR
50
50
  self.firmware_version = "XP33LR_V0.00.00"
51
51
  self.ean_code = "1234567890124" # Test EAN - not a real product code
52
52
  self.max_power = 640 # Total 640VA
53
- self.module_type_code = 30 # XP33LR module type
53
+ self.module_type_code = ModuleTypeCode.XP33LR # XP33LR module type
54
54
  else: # XP33
55
55
  self.firmware_version = "XP33_V0.04.02"
56
56
  self.ean_code = "1234567890125" # Test EAN - not a real product code
57
57
  self.max_power = 100 # Total 640VA
58
- self.module_type_code = 11 # XP33 module type
58
+ self.module_type_code = ModuleTypeCode.XP33 # XP33 module type
59
59
 
60
60
  self.device_status = "00" # Normal status
61
61
  self.link_number = 4 # 4 links configured
62
+ self.autoreport_status = True
62
63
 
63
64
  # Channel states (3 channels, 0-100% dimming)
64
65
  self.channel_states = [0, 0, 0] # All channels at 0%
@@ -71,36 +72,164 @@ class XP33ServerService(BaseServerService):
71
72
  4: [0, 0, 0], # Scene 4: Off
72
73
  }
73
74
 
75
+ def _handle_device_specific_action_request(
76
+ self, request: SystemTelegram
77
+ ) -> Optional[str]:
78
+ """Handle XP33-specific action requests."""
79
+ telegrams = self._handle_action_channel_dimming(request.data)
80
+ self.logger.debug(f"Generated {self.device_type} action responses: {telegrams}")
81
+ return telegrams
82
+
83
+ def _handle_action_channel_dimming(self, data_value: str) -> str:
84
+ """Handle XP33-specific channel dimming action.
85
+
86
+ Args:
87
+ data_value: Action data in format channel_number:dimming_level.
88
+ E.g., "00:050" means channel 0, 50% dimming.
89
+
90
+ Returns:
91
+ Response telegram(s) - ACK/NAK, optionally with event telegram.
92
+ """
93
+ if ":" not in data_value or len(data_value) < 6:
94
+ return self._build_ack_nak_response_telegram(False)
95
+
96
+ try:
97
+ parts = data_value.split(":")
98
+ channel_number = int(parts[0])
99
+ dimming_level = int(parts[1])
100
+ except (ValueError, IndexError):
101
+ return self._build_ack_nak_response_telegram(False)
102
+
103
+ if channel_number not in range(len(self.channel_states)):
104
+ return self._build_ack_nak_response_telegram(False)
105
+
106
+ if dimming_level not in range(0, 101):
107
+ return self._build_ack_nak_response_telegram(False)
108
+
109
+ previous_level = self.channel_states[channel_number]
110
+ self.channel_states[channel_number] = dimming_level
111
+ state_changed = (previous_level == 0 and dimming_level > 0) or (
112
+ previous_level > 0 and dimming_level == 0
113
+ )
114
+
115
+ telegrams = self._build_ack_nak_response_telegram(True)
116
+ if state_changed and self.autoreport_status:
117
+ # Report dimming change event
118
+ telegrams += self._build_dimming_event_telegram(
119
+ dimming_level, channel_number
120
+ )
121
+
122
+ return telegrams
123
+
124
+ def _build_ack_nak_response_telegram(self, ack_or_nak: bool) -> str:
125
+ """Build a complete ACK or NAK response telegram with checksum.
126
+
127
+ Args:
128
+ ack_or_nak: true: ACK telegram response, false: NAK telegram response.
129
+
130
+ Returns:
131
+ The complete telegram with checksum enclosed in angle brackets.
132
+ """
133
+ data_value = (
134
+ SystemFunction.ACK.value if ack_or_nak else SystemFunction.NAK.value
135
+ )
136
+ data_part = f"R{self.serial_number}" f"F{data_value:02}D"
137
+ return self._build_response_telegram(data_part)
138
+
139
+ def _build_dimming_event_telegram(
140
+ self, dimming_level: int, channel_number: int
141
+ ) -> str:
142
+ """Build a complete dimming event telegram with checksum.
143
+
144
+ Args:
145
+ dimming_level: Dimming level 0-100%.
146
+ channel_number: Channel concerned (0-2).
147
+
148
+ Returns:
149
+ The complete event telegram with checksum enclosed in angle brackets.
150
+ """
151
+ data_value = "M" if dimming_level > 0 else "B"
152
+ data_part = (
153
+ f"E{self.module_type_code.value:02}"
154
+ f"L{self.link_number:02}"
155
+ f"I{channel_number:02}"
156
+ f"{data_value}"
157
+ )
158
+ return self._build_response_telegram(data_part)
159
+
74
160
  def _handle_device_specific_data_request(
75
161
  self, request: SystemTelegram
76
162
  ) -> Optional[str]:
77
- """Handle XP24-specific data requests."""
78
- if (
79
- request.system_function != SystemFunction.READ_DATAPOINT
80
- or not request.datapoint_type
81
- ):
163
+ """Handle XP33-specific data requests."""
164
+ if not request.datapoint_type:
82
165
  return None
83
166
 
84
167
  datapoint_type = request.datapoint_type
85
- datapoint_values = {
86
- DataPointType.MODULE_OUTPUT_STATE: "xxxxx001",
87
- DataPointType.MODULE_STATE: "OFF",
88
- DataPointType.MODULE_OPERATING_HOURS: "00:000[H],01:000[H],02:000[H]",
89
- }
168
+ handler = {
169
+ DataPointType.MODULE_OUTPUT_STATE: self._handle_read_module_output_state,
170
+ DataPointType.MODULE_STATE: self._handle_read_module_state,
171
+ DataPointType.MODULE_OPERATING_HOURS: self._handle_read_module_operating_hours,
172
+ DataPointType.MODULE_LIGHT_LEVEL: self._handle_read_light_level,
173
+ }.get(datapoint_type)
174
+ if not handler:
175
+ return None
176
+
177
+ data_value = handler()
90
178
  data_part = (
91
179
  f"R{self.serial_number}"
92
- f"F02{datapoint_type.value}"
93
- f"{self.module_type_code}"
94
- f"{datapoint_values.get(datapoint_type)}"
180
+ f"F02D{datapoint_type.value}"
181
+ f"{self.module_type_code.value:02}"
182
+ f"{data_value}"
95
183
  )
96
- checksum = calculate_checksum(data_part)
97
- telegram = f"<{data_part}{checksum}>"
184
+ telegram = self._build_response_telegram(data_part)
98
185
 
99
186
  self.logger.debug(
100
187
  f"Generated {self.device_type} module type response: {telegram}"
101
188
  )
102
189
  return telegram
103
190
 
191
+ def _handle_read_module_output_state(self) -> str:
192
+ """Handle XP33-specific module output state.
193
+
194
+ Returns:
195
+ String representation of the output state for 3 channels.
196
+ """
197
+ return (
198
+ f"xxxxx"
199
+ f"{1 if self.channel_states[0] > 0 else 0}"
200
+ f"{1 if self.channel_states[1] > 0 else 0}"
201
+ f"{1 if self.channel_states[2] > 0 else 0}"
202
+ )
203
+
204
+ def _handle_read_module_state(self) -> str:
205
+ """Handle XP33-specific module state.
206
+
207
+ Returns:
208
+ 'ON' if any channel is active, 'OFF' otherwise.
209
+ """
210
+ if any(level > 0 for level in self.channel_states):
211
+ return "ON"
212
+ return "OFF"
213
+
214
+ def _handle_read_module_operating_hours(self) -> str:
215
+ """Handle XP33-specific module operating hours.
216
+
217
+ Returns:
218
+ Operating hours for all 3 channels.
219
+ """
220
+ return "00:000[H],01:000[H],02:000[H]"
221
+
222
+ def _handle_read_light_level(self) -> str:
223
+ """Handle XP33-specific light level reading.
224
+
225
+ Returns:
226
+ Light levels for all channels in format "00:000[%],01:000[%],02:000[%]".
227
+ """
228
+ levels = [
229
+ f"{i:02d}:{level:03d}[%]" for i, level in enumerate(self.channel_states)
230
+ ]
231
+ return ",".join(levels)
232
+
104
233
  def set_channel_dimming(self, channel: int, level: int) -> bool:
105
234
  """Set individual channel dimming level.
106
235
 
@@ -147,6 +276,7 @@ class XP33ServerService(BaseServerService):
147
276
  "max_power": self.max_power,
148
277
  "status": self.device_status,
149
278
  "link_number": self.link_number,
279
+ "autoreport_status": self.autoreport_status,
150
280
  "channel_states": self.channel_states.copy(),
151
281
  "available_scenes": list(self.scenes.keys()),
152
282
  }
@@ -0,0 +1,70 @@
1
+ """Service for processing Telegram protocol datapoint values."""
2
+
3
+
4
+ class TelegramDatapointService:
5
+ """Service for processing Telegram protocol datapoint values.
6
+
7
+ Provides methods to parse and extract values from different types of
8
+ Telegram datapoints including autoreport status, light level outputs,
9
+ and link number values.
10
+ """
11
+
12
+ def get_autoreport_status(self, data_value: str) -> bool:
13
+ """Get the autoreport status value.
14
+
15
+ Args:
16
+ data_value: The raw autoreport status data value (PP or AA).
17
+
18
+ Returns:
19
+ The autoreport status: Enable (True) or disable (False).
20
+ """
21
+ status_value = True if data_value == "PP" else False
22
+ return status_value
23
+
24
+ def get_autoreport_status_data_value(self, status_value: bool) -> str:
25
+ """Get the autoreport status data_value.
26
+
27
+ Args:
28
+ status_value: Enable (True) or disable (False).
29
+
30
+ Returns:
31
+ data_value: The raw autoreport status data value (PP or AA).
32
+ """
33
+ data_value = "PP" if status_value else "AA"
34
+ return data_value
35
+
36
+ def get_lightlevel(self, data_value: str, output_number: int) -> int:
37
+ """Extract the light level for a specific output number.
38
+
39
+ Parses comma-separated output data in the format "output:level[%]"
40
+ and returns the level for the requested output number.
41
+
42
+ Args:
43
+ data_value: Comma-separated string of output:level pairs
44
+ (e.g., "1:50[%],2:75[%]").
45
+ output_number: The output number to get the level for.
46
+
47
+ Returns:
48
+ The light level as an integer (0 if output not found).
49
+ """
50
+ level = 0
51
+ for output_data in data_value.split(","):
52
+ if ":" in output_data:
53
+ output_str, level_str = output_data.split(":")
54
+ if int(output_str) == output_number:
55
+ level_str = level_str.replace("[%]", "")
56
+ level = int(level_str)
57
+ break
58
+ return level
59
+
60
+ def get_linknumber(self, data_value: str) -> int:
61
+ """Parse and return the link number value.
62
+
63
+ Args:
64
+ data_value: The raw link number data value as a string.
65
+
66
+ Returns:
67
+ The link number as an integer.
68
+ """
69
+ link_number_value = int(data_value)
70
+ return link_number_value
xp/utils/dependencies.py CHANGED
@@ -21,8 +21,6 @@ from xp.services.conbus.actiontable.msactiontable_xp24_serializer import (
21
21
  from xp.services.conbus.actiontable.msactiontable_xp33_serializer import (
22
22
  Xp33MsActionTableSerializer,
23
23
  )
24
- from xp.services.conbus.conbus_autoreport_get_service import ConbusAutoreportGetService
25
- from xp.services.conbus.conbus_autoreport_set_service import ConbusAutoreportSetService
26
24
  from xp.services.conbus.conbus_blink_all_service import ConbusBlinkAllService
27
25
  from xp.services.conbus.conbus_blink_service import ConbusBlinkService
28
26
  from xp.services.conbus.conbus_custom_service import ConbusCustomService
@@ -33,13 +31,11 @@ from xp.services.conbus.conbus_datapoint_service import (
33
31
  ConbusDatapointService,
34
32
  )
35
33
  from xp.services.conbus.conbus_discover_service import ConbusDiscoverService
36
- from xp.services.conbus.conbus_lightlevel_set_service import ConbusLightlevelSetService
37
- from xp.services.conbus.conbus_linknumber_get_service import ConbusLinknumberGetService
38
- from xp.services.conbus.conbus_linknumber_set_service import ConbusLinknumberSetService
39
34
  from xp.services.conbus.conbus_output_service import ConbusOutputService
40
35
  from xp.services.conbus.conbus_raw_service import ConbusRawService
41
36
  from xp.services.conbus.conbus_receive_service import ConbusReceiveService
42
37
  from xp.services.conbus.conbus_scan_service import ConbusScanService
38
+ from xp.services.conbus.write_config_service import WriteConfigService
43
39
  from xp.services.homekit.homekit_cache_service import HomeKitCacheService
44
40
  from xp.services.homekit.homekit_conbus_service import HomeKitConbusService
45
41
  from xp.services.homekit.homekit_dimminglight_service import HomeKitDimmingLightService
@@ -191,8 +187,9 @@ class ServiceContainer:
191
187
  )
192
188
 
193
189
  self.container.register(
194
- ConbusLightlevelSetService,
195
- factory=lambda: ConbusLightlevelSetService(
190
+ WriteConfigService,
191
+ factory=lambda: WriteConfigService(
192
+ telegram_service=self.container.resolve(TelegramService),
196
193
  cli_config=self.container.resolve(ConbusClientConfig),
197
194
  reactor=self.container.resolve(PosixReactorBase),
198
195
  ),
@@ -247,45 +244,6 @@ class ServiceContainer:
247
244
  scope=punq.Scope.singleton,
248
245
  )
249
246
 
250
- self.container.register(
251
- ConbusAutoreportSetService,
252
- factory=lambda: ConbusAutoreportSetService(
253
- cli_config=self.container.resolve(ConbusClientConfig),
254
- reactor=self.container.resolve(PosixReactorBase),
255
- ),
256
- scope=punq.Scope.singleton,
257
- )
258
-
259
- self.container.register(
260
- ConbusAutoreportGetService,
261
- factory=lambda: ConbusAutoreportGetService(
262
- telegram_service=self.container.resolve(TelegramService),
263
- cli_config=self.container.resolve(ConbusClientConfig),
264
- reactor=self.container.resolve(PosixReactorBase),
265
- ),
266
- scope=punq.Scope.singleton,
267
- )
268
-
269
- self.container.register(
270
- ConbusLinknumberGetService,
271
- factory=lambda: ConbusLinknumberGetService(
272
- telegram_service=self.container.resolve(TelegramService),
273
- cli_config=self.container.resolve(ConbusClientConfig),
274
- reactor=self.container.resolve(PosixReactorBase),
275
- ),
276
- scope=punq.Scope.singleton,
277
- )
278
-
279
- self.container.register(
280
- ConbusLinknumberSetService,
281
- factory=lambda: ConbusLinknumberSetService(
282
- telegram_service=self.container.resolve(TelegramService),
283
- cli_config=self.container.resolve(ConbusClientConfig),
284
- reactor=self.container.resolve(PosixReactorBase),
285
- ),
286
- scope=punq.Scope.singleton,
287
- )
288
-
289
247
  self.container.register(
290
248
  ConbusCustomService,
291
249
  factory=lambda: ConbusCustomService(
xp/api/__init__.py DELETED
@@ -1 +0,0 @@
1
- """API module for FastAPI endpoints and models."""