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.
- {conson_xp-0.11.16.dist-info → conson_xp-0.11.21.dist-info}/METADATA +1 -1
- {conson_xp-0.11.16.dist-info → conson_xp-0.11.21.dist-info}/RECORD +19 -17
- xp/__init__.py +1 -1
- xp/cli/commands/__init__.py +0 -1
- xp/cli/commands/conbus/conbus_blink_commands.py +38 -20
- xp/cli/commands/conbus/conbus_lightlevel_commands.py +35 -16
- xp/cli/commands/conbus/conbus_raw_commands.py +21 -26
- xp/models/conbus/conbus_blink.py +24 -4
- xp/services/conbus/conbus_autoreport_get_service.py +17 -10
- xp/services/conbus/conbus_blink_all_service.py +162 -0
- xp/services/conbus/conbus_blink_service.py +95 -157
- xp/services/conbus/conbus_discover_service.py +2 -2
- xp/services/conbus/conbus_lightlevel_get_service.py +149 -0
- xp/services/conbus/conbus_lightlevel_set_service.py +205 -0
- xp/services/conbus/conbus_raw_service.py +77 -54
- xp/utils/dependencies.py +20 -12
- xp/services/conbus/conbus_lightlevel_service.py +0 -205
- {conson_xp-0.11.16.dist-info → conson_xp-0.11.21.dist-info}/WHEEL +0 -0
- {conson_xp-0.11.16.dist-info → conson_xp-0.11.21.dist-info}/entry_points.txt +0 -0
- {conson_xp-0.11.16.dist-info → conson_xp-0.11.21.dist-info}/licenses/LICENSE +0 -0
|
@@ -4,76 +4,99 @@ This service handles sending raw telegram strings without prior validation.
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
import logging
|
|
7
|
-
from
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
|
20
|
+
Service for querying datapoints from Conbus modules.
|
|
22
21
|
|
|
23
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
|
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
|
-
|
|
84
|
+
Query a specific datapoint from a module.
|
|
37
85
|
|
|
38
86
|
Args:
|
|
39
|
-
|
|
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
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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.
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
220
|
-
factory=lambda:
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
|
|
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
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|