conson-xp 0.11.16__py3-none-any.whl → 0.11.21__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.
@@ -4,76 +4,99 @@ This service handles sending raw telegram strings without prior validation.
4
4
  """
5
5
 
6
6
  import logging
7
- from typing import Any, Optional
8
-
9
- from xp.models.conbus.conbus_raw import ConbusRawResponse
10
- from xp.services.conbus.conbus_service import ConbusService
7
+ from datetime import datetime
8
+ from typing import Callable, Optional
11
9
 
10
+ from twisted.internet.posixbase import PosixReactorBase
12
11
 
13
- class ConbusRawError(Exception):
14
- """Raised when Conbus raw operations fail"""
15
-
16
- pass
12
+ from xp.models import ConbusClientConfig
13
+ from xp.models.conbus.conbus_raw import ConbusRawResponse
14
+ from xp.models.protocol.conbus_protocol import TelegramReceivedEvent
15
+ from xp.services.protocol import ConbusProtocol
17
16
 
18
17
 
19
- class ConbusRawService:
18
+ class ConbusRawService(ConbusProtocol):
20
19
  """
21
- Service for sending raw telegram sequences to Conbus servers.
20
+ Service for querying datapoints from Conbus modules.
22
21
 
23
- Handles parsing and sending of raw telegram strings without validation.
22
+ Uses ConbusProtocol to provide datapoint query functionality
23
+ for reading sensor data and module information.
24
24
  """
25
25
 
26
26
  def __init__(
27
27
  self,
28
- conbus_service: ConbusService,
29
- ):
30
- """Initialize the Conbus raw service"""
31
- self.conbus_service = conbus_service
28
+ cli_config: ConbusClientConfig,
29
+ reactor: PosixReactorBase,
30
+ ) -> None:
31
+ """Initialize the Conbus datapoint service"""
32
+ super().__init__(cli_config, reactor)
33
+ self.raw_input: str = ""
34
+ self.progress_callback: Optional[Callable[[str], None]] = None
35
+ self.finish_callback: Optional[Callable[[ConbusRawResponse], None]] = None
36
+ self.service_response: ConbusRawResponse = ConbusRawResponse(
37
+ success=False,
38
+ )
39
+ # Set up logging
32
40
  self.logger = logging.getLogger(__name__)
33
41
 
34
- def send_raw_telegrams(self, raw_input: str) -> ConbusRawResponse:
42
+ def connection_established(self) -> None:
43
+ self.logger.debug(f"Connection established, sending {self.raw_input}")
44
+ self.sendFrame(self.raw_input.encode())
45
+
46
+ def telegram_sent(self, telegram_sent: str) -> None:
47
+ self.service_response.success = True
48
+ self.service_response.sent_telegrams = telegram_sent
49
+ self.service_response.timestamp = datetime.now()
50
+ self.service_response.received_telegrams = []
51
+ pass
52
+
53
+ def telegram_received(self, telegram_received: TelegramReceivedEvent) -> None:
54
+ self.logger.debug(f"Telegram received: {telegram_received}")
55
+ if not self.service_response.received_telegrams:
56
+ self.service_response.received_telegrams = []
57
+ self.service_response.received_telegrams.append(telegram_received.frame)
58
+
59
+ if self.progress_callback:
60
+ self.progress_callback(telegram_received.frame)
61
+
62
+ def timeout(self) -> bool:
63
+ self.logger.debug(f"Timeout: {self.timeout_seconds}s")
64
+ if self.finish_callback:
65
+ self.finish_callback(self.service_response)
66
+ return False
67
+
68
+ def failed(self, message: str) -> None:
69
+ self.logger.debug(f"Failed with message: {message}")
70
+ self.service_response.success = False
71
+ self.service_response.timestamp = datetime.now()
72
+ self.service_response.error = message
73
+ if self.finish_callback:
74
+ self.finish_callback(self.service_response)
75
+
76
+ def send_raw_telegram(
77
+ self,
78
+ raw_input: str,
79
+ progress_callback: Callable[[str], None],
80
+ finish_callback: Callable[[ConbusRawResponse], None],
81
+ timeout_seconds: Optional[float] = None,
82
+ ) -> None:
35
83
  """
36
- Send raw telegram sequence to Conbus server.
84
+ Query a specific datapoint from a module.
37
85
 
38
86
  Args:
39
- raw_input: Raw string containing one or more telegrams
87
+ serial_number: 10-digit module serial number
88
+ datapoint_type: Type of datapoint to query
89
+ finish_callback: callback function to call when the datapoint is received
90
+ timeout_seconds: timeout in seconds
40
91
 
41
92
  Returns:
42
- ConbusRawResponse containing results
93
+ ConbusDatapointResponse with operation result and datapoint value
43
94
  """
44
- try:
45
-
46
- self.logger.info(f"Sending raw telegram: {raw_input}")
47
95
 
48
- response = self.conbus_service.send_raw_telegram(raw_input)
49
-
50
- if not response.success:
51
- return ConbusRawResponse(
52
- success=False,
53
- sent_telegrams=raw_input,
54
- error=f"Failed to send telegram {raw_input}: {response.error}",
55
- )
56
-
57
- return ConbusRawResponse(
58
- success=True,
59
- sent_telegrams=raw_input,
60
- received_telegrams=response.received_telegrams,
61
- )
62
-
63
- except Exception as e:
64
- error_msg = f"Failed to send raw telegrams: {e}"
65
- self.logger.error(error_msg)
66
- return ConbusRawResponse(success=False, error=error_msg)
67
-
68
- def __enter__(self) -> "ConbusRawService":
69
- """Context manager entry"""
70
- return self
71
-
72
- def __exit__(
73
- self,
74
- _exc_type: Optional[type],
75
- _exc_val: Optional[BaseException],
76
- _exc_tb: Optional[Any],
77
- ) -> None:
78
- """Context manager exit - ensure connection is closed"""
79
- self.conbus_service.disconnect()
96
+ self.logger.info("Starting query_datapoint")
97
+ if timeout_seconds:
98
+ self.timeout_seconds = timeout_seconds
99
+ self.progress_callback = progress_callback
100
+ self.finish_callback = finish_callback
101
+ self.raw_input = raw_input
102
+ self.start_reactor()
xp/utils/dependencies.py CHANGED
@@ -23,6 +23,7 @@ from xp.services.conbus.actiontable.msactiontable_xp33_serializer import (
23
23
  )
24
24
  from xp.services.conbus.conbus_autoreport_get_service import ConbusAutoreportGetService
25
25
  from xp.services.conbus.conbus_autoreport_set_service import ConbusAutoreportSetService
26
+ from xp.services.conbus.conbus_blink_all_service import ConbusBlinkAllService
26
27
  from xp.services.conbus.conbus_blink_service import ConbusBlinkService
27
28
  from xp.services.conbus.conbus_connection_pool import (
28
29
  ConbusConnectionPool,
@@ -36,7 +37,7 @@ from xp.services.conbus.conbus_datapoint_service import (
36
37
  ConbusDatapointService,
37
38
  )
38
39
  from xp.services.conbus.conbus_discover_service import ConbusDiscoverService
39
- from xp.services.conbus.conbus_lightlevel_service import ConbusLightlevelService
40
+ from xp.services.conbus.conbus_lightlevel_set_service import ConbusLightlevelSetService
40
41
  from xp.services.conbus.conbus_linknumber_service import ConbusLinknumberService
41
42
  from xp.services.conbus.conbus_output_service import ConbusOutputService
42
43
  from xp.services.conbus.conbus_raw_service import ConbusRawService
@@ -194,11 +195,18 @@ class ServiceContainer:
194
195
  self.container.register(
195
196
  ConbusBlinkService,
196
197
  factory=lambda: ConbusBlinkService(
197
- conbus_service=self.container.resolve(ConbusService),
198
- telegram_discover_service=self.container.resolve(
199
- TelegramDiscoverService
200
- ),
201
- telegram_blink_service=self.container.resolve(TelegramBlinkService),
198
+ cli_config=self.container.resolve(ConbusClientConfig),
199
+ reactor=self.container.resolve(PosixReactorBase),
200
+ telegram_service=self.container.resolve(TelegramService),
201
+ ),
202
+ scope=punq.Scope.singleton,
203
+ )
204
+
205
+ self.container.register(
206
+ ConbusBlinkAllService,
207
+ factory=lambda: ConbusBlinkAllService(
208
+ cli_config=self.container.resolve(ConbusClientConfig),
209
+ reactor=self.container.resolve(PosixReactorBase),
202
210
  telegram_service=self.container.resolve(TelegramService),
203
211
  ),
204
212
  scope=punq.Scope.singleton,
@@ -216,11 +224,10 @@ class ServiceContainer:
216
224
  )
217
225
 
218
226
  self.container.register(
219
- ConbusLightlevelService,
220
- factory=lambda: ConbusLightlevelService(
221
- telegram_service=self.container.resolve(TelegramService),
222
- conbus_service=self.container.resolve(ConbusService),
223
- datapoint_service=self.container.resolve(ConbusDatapointService),
227
+ ConbusLightlevelSetService,
228
+ factory=lambda: ConbusLightlevelSetService(
229
+ cli_config=self.container.resolve(ConbusClientConfig),
230
+ reactor=self.container.resolve(PosixReactorBase),
224
231
  ),
225
232
  scope=punq.Scope.singleton,
226
233
  )
@@ -316,7 +323,8 @@ class ServiceContainer:
316
323
  self.container.register(
317
324
  ConbusRawService,
318
325
  factory=lambda: ConbusRawService(
319
- conbus_service=self.container.resolve(ConbusService),
326
+ cli_config=self.container.resolve(ConbusClientConfig),
327
+ reactor=self.container.resolve(PosixReactorBase),
320
328
  ),
321
329
  scope=punq.Scope.singleton,
322
330
  )
@@ -1,205 +0,0 @@
1
- """Conbus Lightlevel Service for controlling light levels on Conbus modules.
2
-
3
- This service implements lightlevel control operations for XP modules,
4
- including setting specific light levels, turning lights on/off, and
5
- querying current light levels.
6
- """
7
-
8
- import logging
9
- from datetime import datetime
10
- from typing import Any, Optional
11
-
12
- from xp.models.conbus.conbus_lightlevel import ConbusLightlevelResponse
13
- from xp.models.telegram.datapoint_type import DataPointType
14
- from xp.models.telegram.system_function import SystemFunction
15
- from xp.services.conbus.conbus_datapoint_service import ConbusDatapointService
16
- from xp.services.conbus.conbus_service import ConbusService
17
- from xp.services.telegram.telegram_service import TelegramService
18
-
19
-
20
- class ConbusLightlevelError(Exception):
21
- """Raised when Conbus lightlevel operations fail"""
22
-
23
- pass
24
-
25
-
26
- class ConbusLightlevelService:
27
- """
28
- Service for controlling light levels on Conbus modules.
29
-
30
- Manages lightlevel operations including setting specific levels,
31
- turning lights on/off, and querying current states.
32
- """
33
-
34
- def __init__(
35
- self,
36
- telegram_service: TelegramService,
37
- conbus_service: ConbusService,
38
- datapoint_service: ConbusDatapointService,
39
- ):
40
- """Initialize the Conbus lightlevel service"""
41
-
42
- # Service dependencies
43
- self.telegram_service = telegram_service
44
- self.conbus_service = conbus_service
45
- self.datapoint_service = datapoint_service
46
-
47
- # Set up logging
48
- self.logger = logging.getLogger(__name__)
49
-
50
- def __enter__(self) -> "ConbusLightlevelService":
51
- return self
52
-
53
- def __exit__(
54
- self,
55
- _exc_type: Optional[type],
56
- _exc_val: Optional[Exception],
57
- _exc_tb: Optional[Any],
58
- ) -> None:
59
- # Cleanup logic if needed
60
- pass
61
-
62
- def set_lightlevel(
63
- self, serial_number: str, output_number: int, level: int
64
- ) -> ConbusLightlevelResponse:
65
- """Set light level for a specific output on a module.
66
-
67
- Args:
68
- serial_number: Module serial number
69
- output_number: Output number (0-based)
70
- level: Light level percentage (0-100)
71
-
72
- Returns:
73
- ConbusLightlevelResponse with operation result
74
- """
75
-
76
- # Validate output_number range (0-8)
77
- if not 0 <= output_number <= 8:
78
- return ConbusLightlevelResponse(
79
- success=False,
80
- serial_number=serial_number,
81
- output_number=output_number,
82
- level=level,
83
- timestamp=datetime.now(),
84
- error=f"Output number must be between 0 and 8, got {output_number}",
85
- )
86
-
87
- # Validate level range
88
- if not 0 <= level <= 100:
89
- return ConbusLightlevelResponse(
90
- success=False,
91
- serial_number=serial_number,
92
- output_number=output_number,
93
- level=level,
94
- timestamp=datetime.now(),
95
- error=f"Light level must be between 0 and 100, got {level}",
96
- )
97
-
98
- # Format data as output_number:level (e.g., "02:050")
99
- data = f"{output_number:02d}:{level:03d}"
100
-
101
- # Send telegram using WRITE_CONFIG function with MODULE_LIGHT_LEVEL datapoint
102
- response = self.conbus_service.send_telegram(
103
- serial_number,
104
- SystemFunction.WRITE_CONFIG, # "04"
105
- f"{DataPointType.MODULE_LIGHT_LEVEL.value}{data}", # "15" + "02:050"
106
- )
107
-
108
- return ConbusLightlevelResponse(
109
- success=response.success,
110
- serial_number=serial_number,
111
- output_number=output_number,
112
- level=level,
113
- timestamp=response.timestamp or datetime.now(),
114
- sent_telegram=response.sent_telegram,
115
- received_telegrams=response.received_telegrams,
116
- error=response.error,
117
- )
118
-
119
- def turn_off(
120
- self, serial_number: str, output_number: int
121
- ) -> ConbusLightlevelResponse:
122
- """Turn off light (set level to 0) for a specific output.
123
-
124
- Args:
125
- serial_number: Module serial number
126
- output_number: Output number (0-8)
127
-
128
- Returns:
129
- ConbusLightlevelResponse with operation result
130
- """
131
- return self.set_lightlevel(serial_number, output_number, 0)
132
-
133
- def turn_on(
134
- self, serial_number: str, output_number: int
135
- ) -> ConbusLightlevelResponse:
136
- """Turn on light (set level to 80%) for a specific output.
137
-
138
- Args:
139
- serial_number: Module serial number
140
- output_number: Output number (0-8)
141
-
142
- Returns:
143
- ConbusLightlevelResponse with operation result
144
- """
145
- return self.set_lightlevel(serial_number, output_number, 80)
146
-
147
- def get_lightlevel(
148
- self, serial_number: str, output_number: int
149
- ) -> ConbusLightlevelResponse:
150
- """Query current light level for a specific output.
151
-
152
- Args:
153
- serial_number: Module serial number
154
- output_number: Output number (0-8)
155
-
156
- Returns:
157
- ConbusLightlevelResponse with current light level
158
- """
159
-
160
- # TODO: Migrate to new ConbusDatapointService callback-based API
161
- # Query MODULE_LIGHT_LEVEL datapoint
162
- datapoint_response = self.datapoint_service.query_datapoint( # type: ignore[call-arg,func-returns-value]
163
- serial_number=serial_number,
164
- datapoint_type=DataPointType.MODULE_LIGHT_LEVEL,
165
- )
166
-
167
- if not datapoint_response.success:
168
- return ConbusLightlevelResponse(
169
- success=False,
170
- serial_number=serial_number,
171
- output_number=output_number,
172
- level=None,
173
- timestamp=datetime.now(),
174
- error=datapoint_response.error or "Failed to query light level",
175
- )
176
-
177
- # Parse the response to extract level for specific output
178
- level = None
179
- if (
180
- datapoint_response.datapoint_telegram
181
- and datapoint_response.datapoint_telegram.data_value
182
- ):
183
- try:
184
- # Parse response format like "00:050,01:025,02:100"
185
- data_value = str(datapoint_response.datapoint_telegram.data_value)
186
- for output_data in data_value.split(","):
187
- if ":" in output_data:
188
- output_str, level_str = output_data.split(":")
189
- if int(output_str) == output_number:
190
- level_str = level_str.replace("[%]", "")
191
- level = int(level_str)
192
- break
193
- except (ValueError, AttributeError) as e:
194
- self.logger.debug(f"Failed to parse light level data: {e}")
195
-
196
- return ConbusLightlevelResponse(
197
- success=datapoint_response.success,
198
- serial_number=serial_number,
199
- output_number=output_number,
200
- level=level,
201
- timestamp=datetime.now(),
202
- sent_telegram=datapoint_response.sent_telegram,
203
- received_telegrams=datapoint_response.received_telegrams,
204
- error=datapoint_response.error if level is None else None,
205
- )