conson-xp 1.18.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- conson_xp-1.18.0.dist-info/METADATA +412 -0
- conson_xp-1.18.0.dist-info/RECORD +176 -0
- conson_xp-1.18.0.dist-info/WHEEL +4 -0
- conson_xp-1.18.0.dist-info/entry_points.txt +5 -0
- conson_xp-1.18.0.dist-info/licenses/LICENSE +29 -0
- xp/__init__.py +9 -0
- xp/cli/__init__.py +5 -0
- xp/cli/__main__.py +6 -0
- xp/cli/commands/__init__.py +153 -0
- xp/cli/commands/conbus/__init__.py +25 -0
- xp/cli/commands/conbus/conbus.py +128 -0
- xp/cli/commands/conbus/conbus_actiontable_commands.py +233 -0
- xp/cli/commands/conbus/conbus_autoreport_commands.py +108 -0
- xp/cli/commands/conbus/conbus_blink_commands.py +163 -0
- xp/cli/commands/conbus/conbus_config_commands.py +29 -0
- xp/cli/commands/conbus/conbus_custom_commands.py +57 -0
- xp/cli/commands/conbus/conbus_datapoint_commands.py +113 -0
- xp/cli/commands/conbus/conbus_discover_commands.py +61 -0
- xp/cli/commands/conbus/conbus_event_commands.py +81 -0
- xp/cli/commands/conbus/conbus_lightlevel_commands.py +207 -0
- xp/cli/commands/conbus/conbus_linknumber_commands.py +102 -0
- xp/cli/commands/conbus/conbus_modulenumber_commands.py +104 -0
- xp/cli/commands/conbus/conbus_msactiontable_commands.py +94 -0
- xp/cli/commands/conbus/conbus_output_commands.py +163 -0
- xp/cli/commands/conbus/conbus_raw_commands.py +62 -0
- xp/cli/commands/conbus/conbus_receive_commands.py +59 -0
- xp/cli/commands/conbus/conbus_scan_commands.py +58 -0
- xp/cli/commands/file_commands.py +186 -0
- xp/cli/commands/homekit/__init__.py +3 -0
- xp/cli/commands/homekit/homekit.py +118 -0
- xp/cli/commands/homekit/homekit_start_commands.py +43 -0
- xp/cli/commands/module_commands.py +187 -0
- xp/cli/commands/reverse_proxy_commands.py +178 -0
- xp/cli/commands/server/__init__.py +3 -0
- xp/cli/commands/server/server_commands.py +135 -0
- xp/cli/commands/telegram/__init__.py +5 -0
- xp/cli/commands/telegram/telegram.py +41 -0
- xp/cli/commands/telegram/telegram_blink_commands.py +79 -0
- xp/cli/commands/telegram/telegram_checksum_commands.py +112 -0
- xp/cli/commands/telegram/telegram_discover_commands.py +41 -0
- xp/cli/commands/telegram/telegram_linknumber_commands.py +86 -0
- xp/cli/commands/telegram/telegram_parse_commands.py +75 -0
- xp/cli/commands/telegram/telegram_version_commands.py +52 -0
- xp/cli/main.py +87 -0
- xp/cli/utils/__init__.py +1 -0
- xp/cli/utils/click_tree.py +57 -0
- xp/cli/utils/datapoint_type_choice.py +57 -0
- xp/cli/utils/decorators.py +351 -0
- xp/cli/utils/error_handlers.py +201 -0
- xp/cli/utils/formatters.py +312 -0
- xp/cli/utils/module_type_choice.py +56 -0
- xp/cli/utils/serial_number_type.py +52 -0
- xp/cli/utils/system_function_choice.py +57 -0
- xp/cli/utils/xp_module_type.py +53 -0
- xp/connection/__init__.py +13 -0
- xp/connection/exceptions.py +22 -0
- xp/models/__init__.py +36 -0
- xp/models/actiontable/__init__.py +1 -0
- xp/models/actiontable/actiontable.py +43 -0
- xp/models/actiontable/msactiontable_xp20.py +53 -0
- xp/models/actiontable/msactiontable_xp24.py +58 -0
- xp/models/actiontable/msactiontable_xp33.py +65 -0
- xp/models/conbus/__init__.py +1 -0
- xp/models/conbus/conbus.py +87 -0
- xp/models/conbus/conbus_autoreport.py +67 -0
- xp/models/conbus/conbus_blink.py +80 -0
- xp/models/conbus/conbus_client_config.py +55 -0
- xp/models/conbus/conbus_connection_status.py +40 -0
- xp/models/conbus/conbus_custom.py +58 -0
- xp/models/conbus/conbus_datapoint.py +89 -0
- xp/models/conbus/conbus_discover.py +64 -0
- xp/models/conbus/conbus_event_raw.py +47 -0
- xp/models/conbus/conbus_lightlevel.py +52 -0
- xp/models/conbus/conbus_linknumber.py +54 -0
- xp/models/conbus/conbus_output.py +57 -0
- xp/models/conbus/conbus_raw.py +45 -0
- xp/models/conbus/conbus_receive.py +42 -0
- xp/models/conbus/conbus_writeconfig.py +60 -0
- xp/models/homekit/__init__.py +1 -0
- xp/models/homekit/homekit_accessory.py +35 -0
- xp/models/homekit/homekit_config.py +106 -0
- xp/models/homekit/homekit_conson_config.py +86 -0
- xp/models/log_entry.py +130 -0
- xp/models/protocol/__init__.py +1 -0
- xp/models/protocol/conbus_protocol.py +312 -0
- xp/models/response.py +42 -0
- xp/models/telegram/__init__.py +1 -0
- xp/models/telegram/action_type.py +31 -0
- xp/models/telegram/datapoint_type.py +82 -0
- xp/models/telegram/event_telegram.py +140 -0
- xp/models/telegram/event_type.py +15 -0
- xp/models/telegram/input_action_type.py +69 -0
- xp/models/telegram/input_type.py +17 -0
- xp/models/telegram/module_type.py +188 -0
- xp/models/telegram/module_type_code.py +205 -0
- xp/models/telegram/output_telegram.py +103 -0
- xp/models/telegram/reply_telegram.py +297 -0
- xp/models/telegram/system_function.py +116 -0
- xp/models/telegram/system_telegram.py +94 -0
- xp/models/telegram/telegram.py +28 -0
- xp/models/telegram/telegram_type.py +19 -0
- xp/models/telegram/timeparam_type.py +51 -0
- xp/models/write_config_type.py +33 -0
- xp/services/__init__.py +26 -0
- xp/services/actiontable/__init__.py +1 -0
- xp/services/actiontable/actiontable_serializer.py +273 -0
- xp/services/actiontable/msactiontable_serializer.py +7 -0
- xp/services/actiontable/msactiontable_xp20_serializer.py +169 -0
- xp/services/actiontable/msactiontable_xp24_serializer.py +120 -0
- xp/services/actiontable/msactiontable_xp33_serializer.py +239 -0
- xp/services/conbus/__init__.py +1 -0
- xp/services/conbus/actiontable/__init__.py +1 -0
- xp/services/conbus/actiontable/actiontable_download_service.py +158 -0
- xp/services/conbus/actiontable/actiontable_list_service.py +91 -0
- xp/services/conbus/actiontable/actiontable_show_service.py +89 -0
- xp/services/conbus/actiontable/actiontable_upload_service.py +211 -0
- xp/services/conbus/actiontable/msactiontable_service.py +232 -0
- xp/services/conbus/conbus_blink_all_service.py +181 -0
- xp/services/conbus/conbus_blink_service.py +158 -0
- xp/services/conbus/conbus_custom_service.py +156 -0
- xp/services/conbus/conbus_datapoint_queryall_service.py +182 -0
- xp/services/conbus/conbus_datapoint_service.py +170 -0
- xp/services/conbus/conbus_discover_service.py +312 -0
- xp/services/conbus/conbus_event_raw_service.py +181 -0
- xp/services/conbus/conbus_output_service.py +194 -0
- xp/services/conbus/conbus_raw_service.py +122 -0
- xp/services/conbus/conbus_receive_service.py +115 -0
- xp/services/conbus/conbus_scan_service.py +150 -0
- xp/services/conbus/write_config_service.py +194 -0
- xp/services/homekit/__init__.py +1 -0
- xp/services/homekit/homekit_cache_service.py +307 -0
- xp/services/homekit/homekit_conbus_service.py +93 -0
- xp/services/homekit/homekit_config_validator.py +310 -0
- xp/services/homekit/homekit_conson_validator.py +121 -0
- xp/services/homekit/homekit_dimminglight.py +182 -0
- xp/services/homekit/homekit_dimminglight_service.py +148 -0
- xp/services/homekit/homekit_hap_service.py +342 -0
- xp/services/homekit/homekit_lightbulb.py +120 -0
- xp/services/homekit/homekit_lightbulb_service.py +86 -0
- xp/services/homekit/homekit_module_service.py +56 -0
- xp/services/homekit/homekit_outlet.py +168 -0
- xp/services/homekit/homekit_outlet_service.py +121 -0
- xp/services/homekit/homekit_service.py +359 -0
- xp/services/log_file_service.py +309 -0
- xp/services/module_type_service.py +257 -0
- xp/services/protocol/__init__.py +21 -0
- xp/services/protocol/conbus_event_protocol.py +360 -0
- xp/services/protocol/conbus_protocol.py +318 -0
- xp/services/protocol/protocol_factory.py +78 -0
- xp/services/protocol/telegram_protocol.py +264 -0
- xp/services/reverse_proxy_service.py +435 -0
- xp/services/server/__init__.py +1 -0
- xp/services/server/base_server_service.py +366 -0
- xp/services/server/cp20_server_service.py +65 -0
- xp/services/server/device_service_factory.py +94 -0
- xp/services/server/server_service.py +428 -0
- xp/services/server/xp130_server_service.py +67 -0
- xp/services/server/xp20_server_service.py +92 -0
- xp/services/server/xp230_server_service.py +58 -0
- xp/services/server/xp24_server_service.py +245 -0
- xp/services/server/xp33_server_service.py +535 -0
- xp/services/telegram/__init__.py +1 -0
- xp/services/telegram/telegram_blink_service.py +138 -0
- xp/services/telegram/telegram_checksum_service.py +149 -0
- xp/services/telegram/telegram_datapoint_service.py +82 -0
- xp/services/telegram/telegram_discover_service.py +277 -0
- xp/services/telegram/telegram_link_number_service.py +216 -0
- xp/services/telegram/telegram_output_service.py +322 -0
- xp/services/telegram/telegram_service.py +380 -0
- xp/services/telegram/telegram_version_service.py +288 -0
- xp/utils/__init__.py +12 -0
- xp/utils/checksum.py +61 -0
- xp/utils/dependencies.py +531 -0
- xp/utils/event_helper.py +31 -0
- xp/utils/serialization.py +205 -0
- xp/utils/time_utils.py +134 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""Conbus Blink Service for TCP communication with Conbus servers.
|
|
2
|
+
|
|
3
|
+
This service implements a TCP client that connects to Conbus servers and sends
|
|
4
|
+
blink/unblink telegrams to control module LED indicators.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from typing import Callable, Optional
|
|
10
|
+
|
|
11
|
+
from twisted.internet.posixbase import PosixReactorBase
|
|
12
|
+
|
|
13
|
+
from xp.models import ConbusClientConfig
|
|
14
|
+
from xp.models.conbus.conbus_blink import ConbusBlinkResponse
|
|
15
|
+
from xp.models.protocol.conbus_protocol import TelegramReceivedEvent
|
|
16
|
+
from xp.models.telegram.system_function import SystemFunction
|
|
17
|
+
from xp.models.telegram.telegram_type import TelegramType
|
|
18
|
+
from xp.services.protocol import ConbusProtocol
|
|
19
|
+
from xp.services.telegram.telegram_service import TelegramService
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ConbusBlinkService(ConbusProtocol):
|
|
23
|
+
"""
|
|
24
|
+
Service for blinking module LEDs on Conbus servers.
|
|
25
|
+
|
|
26
|
+
Uses ConbusProtocol to provide blink/unblink functionality
|
|
27
|
+
for controlling module LED indicators.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
telegram_service: TelegramService,
|
|
33
|
+
cli_config: ConbusClientConfig,
|
|
34
|
+
reactor: PosixReactorBase,
|
|
35
|
+
) -> None:
|
|
36
|
+
"""Initialize the Conbus blink service.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
telegram_service: Service for parsing telegrams.
|
|
40
|
+
cli_config: Configuration for Conbus client connection.
|
|
41
|
+
reactor: Twisted reactor for event loop.
|
|
42
|
+
"""
|
|
43
|
+
super().__init__(cli_config, reactor)
|
|
44
|
+
self.telegram_service = telegram_service
|
|
45
|
+
self.serial_number: str = ""
|
|
46
|
+
self.on_or_off = "none"
|
|
47
|
+
self.finish_callback: Optional[Callable[[ConbusBlinkResponse], None]] = None
|
|
48
|
+
self.service_response: ConbusBlinkResponse = ConbusBlinkResponse(
|
|
49
|
+
success=False,
|
|
50
|
+
serial_number=self.serial_number,
|
|
51
|
+
system_function=SystemFunction.NONE,
|
|
52
|
+
operation=self.on_or_off,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Set up logging
|
|
56
|
+
self.logger = logging.getLogger(__name__)
|
|
57
|
+
|
|
58
|
+
def connection_established(self) -> None:
|
|
59
|
+
"""Handle connection established event."""
|
|
60
|
+
self.logger.debug("Connection established, sending blink command.")
|
|
61
|
+
# Blink is 05, Unblink is 06
|
|
62
|
+
system_function = SystemFunction.UNBLINK
|
|
63
|
+
if self.on_or_off.lower() == "on":
|
|
64
|
+
system_function = SystemFunction.BLINK
|
|
65
|
+
|
|
66
|
+
self.send_telegram(
|
|
67
|
+
telegram_type=TelegramType.SYSTEM,
|
|
68
|
+
serial_number=self.serial_number,
|
|
69
|
+
system_function=system_function,
|
|
70
|
+
data_value="00",
|
|
71
|
+
)
|
|
72
|
+
self.service_response.system_function = system_function
|
|
73
|
+
self.service_response.operation = self.on_or_off
|
|
74
|
+
|
|
75
|
+
def telegram_sent(self, telegram_sent: str) -> None:
|
|
76
|
+
"""Handle telegram sent event.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
telegram_sent: The telegram that was sent.
|
|
80
|
+
"""
|
|
81
|
+
system_telegram = self.telegram_service.parse_system_telegram(telegram_sent)
|
|
82
|
+
self.service_response.sent_telegram = system_telegram
|
|
83
|
+
|
|
84
|
+
def telegram_received(self, telegram_received: TelegramReceivedEvent) -> None:
|
|
85
|
+
"""Handle telegram received event.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
telegram_received: The telegram received event.
|
|
89
|
+
"""
|
|
90
|
+
self.logger.debug(f"Telegram received: {telegram_received}")
|
|
91
|
+
if not self.service_response.received_telegrams:
|
|
92
|
+
self.service_response.received_telegrams = []
|
|
93
|
+
self.service_response.received_telegrams.append(telegram_received.frame)
|
|
94
|
+
|
|
95
|
+
if (
|
|
96
|
+
not telegram_received.checksum_valid
|
|
97
|
+
or telegram_received.telegram_type != TelegramType.REPLY
|
|
98
|
+
or telegram_received.serial_number != self.serial_number
|
|
99
|
+
):
|
|
100
|
+
self.logger.debug("Not a reply")
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
reply_telegram = self.telegram_service.parse_reply_telegram(
|
|
104
|
+
telegram_received.frame
|
|
105
|
+
)
|
|
106
|
+
if reply_telegram is not None and reply_telegram.system_function in (
|
|
107
|
+
SystemFunction.ACK,
|
|
108
|
+
SystemFunction.NAK,
|
|
109
|
+
):
|
|
110
|
+
self.logger.debug("Received blink response")
|
|
111
|
+
self.service_response.success = True
|
|
112
|
+
self.service_response.timestamp = datetime.now()
|
|
113
|
+
self.service_response.serial_number = self.serial_number
|
|
114
|
+
self.service_response.reply_telegram = reply_telegram
|
|
115
|
+
|
|
116
|
+
if self.finish_callback:
|
|
117
|
+
self.finish_callback(self.service_response)
|
|
118
|
+
|
|
119
|
+
def failed(self, message: str) -> None:
|
|
120
|
+
"""Handle failed connection event.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
message: Failure message.
|
|
124
|
+
"""
|
|
125
|
+
self.logger.debug(f"Failed with message: {message}")
|
|
126
|
+
self.service_response.success = False
|
|
127
|
+
self.service_response.timestamp = datetime.now()
|
|
128
|
+
self.service_response.error = message
|
|
129
|
+
if self.finish_callback:
|
|
130
|
+
self.finish_callback(self.service_response)
|
|
131
|
+
|
|
132
|
+
def send_blink_telegram(
|
|
133
|
+
self,
|
|
134
|
+
serial_number: str,
|
|
135
|
+
on_or_off: str,
|
|
136
|
+
finish_callback: Callable[[ConbusBlinkResponse], None],
|
|
137
|
+
timeout_seconds: Optional[float] = None,
|
|
138
|
+
) -> None:
|
|
139
|
+
r"""Send blink command to start blinking module LED.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
serial_number: 10-digit module serial number.
|
|
143
|
+
on_or_off: "on" to blink or "off" to unblink.
|
|
144
|
+
finish_callback: Callback function to call when the reply is received.
|
|
145
|
+
timeout_seconds: Timeout in seconds.
|
|
146
|
+
|
|
147
|
+
Examples:
|
|
148
|
+
\b
|
|
149
|
+
xp conbus blink 0012345008 on
|
|
150
|
+
xp conbus blink 0012345008 off
|
|
151
|
+
"""
|
|
152
|
+
self.logger.info("Starting send_blink_telegram")
|
|
153
|
+
if timeout_seconds:
|
|
154
|
+
self.timeout_seconds = timeout_seconds
|
|
155
|
+
self.finish_callback = finish_callback
|
|
156
|
+
self.serial_number = serial_number
|
|
157
|
+
self.on_or_off = on_or_off
|
|
158
|
+
self.start_reactor()
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""Conbus Custom Service for sending custom telegrams to modules.
|
|
2
|
+
|
|
3
|
+
This service handles custom telegram operations for modules through Conbus telegrams.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import Callable, Optional
|
|
9
|
+
|
|
10
|
+
from twisted.internet.posixbase import PosixReactorBase
|
|
11
|
+
|
|
12
|
+
from xp.models import ConbusClientConfig
|
|
13
|
+
from xp.models.conbus.conbus_custom import ConbusCustomResponse
|
|
14
|
+
from xp.models.protocol.conbus_protocol import TelegramReceivedEvent
|
|
15
|
+
from xp.models.telegram.reply_telegram import ReplyTelegram
|
|
16
|
+
from xp.models.telegram.system_function import SystemFunction
|
|
17
|
+
from xp.models.telegram.telegram_type import TelegramType
|
|
18
|
+
from xp.services.protocol import ConbusProtocol
|
|
19
|
+
from xp.services.telegram.telegram_service import TelegramService
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ConbusCustomService(ConbusProtocol):
|
|
23
|
+
"""
|
|
24
|
+
Service for sending custom telegrams to Conbus modules.
|
|
25
|
+
|
|
26
|
+
Uses ConbusProtocol to provide custom telegram functionality
|
|
27
|
+
for sending arbitrary function codes and data to modules.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
telegram_service: TelegramService,
|
|
33
|
+
cli_config: ConbusClientConfig,
|
|
34
|
+
reactor: PosixReactorBase,
|
|
35
|
+
) -> None:
|
|
36
|
+
"""Initialize the Conbus custom service.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
telegram_service: Service for parsing telegrams.
|
|
40
|
+
cli_config: Configuration for Conbus client connection.
|
|
41
|
+
reactor: Twisted reactor for event loop.
|
|
42
|
+
"""
|
|
43
|
+
super().__init__(cli_config, reactor)
|
|
44
|
+
self.telegram_service = telegram_service
|
|
45
|
+
self.serial_number: str = ""
|
|
46
|
+
self.function_code: str = ""
|
|
47
|
+
self.data: str = ""
|
|
48
|
+
self.finish_callback: Optional[Callable[[ConbusCustomResponse], None]] = None
|
|
49
|
+
self.service_response: ConbusCustomResponse = ConbusCustomResponse(
|
|
50
|
+
success=False,
|
|
51
|
+
serial_number=self.serial_number,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Set up logging
|
|
55
|
+
self.logger = logging.getLogger(__name__)
|
|
56
|
+
|
|
57
|
+
def connection_established(self) -> None:
|
|
58
|
+
"""Handle connection established event."""
|
|
59
|
+
self.logger.debug(
|
|
60
|
+
f"Connection established, sending custom telegram F{self.function_code}D{self.data}."
|
|
61
|
+
)
|
|
62
|
+
system_function = SystemFunction.from_code(self.function_code)
|
|
63
|
+
if not system_function:
|
|
64
|
+
self.logger.debug(f"Invalid function code F{self.function_code}")
|
|
65
|
+
self.failed(f"Invalid function code {self.function_code}")
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
self.send_telegram(
|
|
69
|
+
serial_number=self.serial_number,
|
|
70
|
+
telegram_type=TelegramType.SYSTEM,
|
|
71
|
+
system_function=system_function,
|
|
72
|
+
data_value=self.data,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
def telegram_sent(self, telegram_sent: str) -> None:
|
|
76
|
+
"""Handle telegram sent event.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
telegram_sent: The telegram that was sent.
|
|
80
|
+
"""
|
|
81
|
+
self.service_response.sent_telegram = telegram_sent
|
|
82
|
+
|
|
83
|
+
def telegram_received(self, telegram_received: TelegramReceivedEvent) -> None:
|
|
84
|
+
"""Handle telegram received event.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
telegram_received: The telegram received event.
|
|
88
|
+
"""
|
|
89
|
+
self.logger.debug(f"Telegram received: {telegram_received}")
|
|
90
|
+
if not self.service_response.received_telegrams:
|
|
91
|
+
self.service_response.received_telegrams = []
|
|
92
|
+
self.service_response.received_telegrams.append(telegram_received.frame)
|
|
93
|
+
|
|
94
|
+
if (
|
|
95
|
+
not telegram_received.checksum_valid
|
|
96
|
+
or telegram_received.telegram_type != TelegramType.REPLY
|
|
97
|
+
or telegram_received.serial_number != self.serial_number
|
|
98
|
+
):
|
|
99
|
+
self.logger.debug("Not a reply for our serial number")
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
# Parse the reply telegram
|
|
103
|
+
parsed_telegram = self.telegram_service.parse_telegram(telegram_received.frame)
|
|
104
|
+
reply_telegram = None
|
|
105
|
+
if isinstance(parsed_telegram, ReplyTelegram):
|
|
106
|
+
reply_telegram = parsed_telegram
|
|
107
|
+
|
|
108
|
+
self.logger.debug("Received reply telegram")
|
|
109
|
+
self.service_response.success = True
|
|
110
|
+
self.service_response.timestamp = datetime.now()
|
|
111
|
+
self.service_response.serial_number = self.serial_number
|
|
112
|
+
self.service_response.function_code = self.function_code
|
|
113
|
+
self.service_response.data = self.data
|
|
114
|
+
self.service_response.reply_telegram = reply_telegram
|
|
115
|
+
|
|
116
|
+
if self.finish_callback:
|
|
117
|
+
self.finish_callback(self.service_response)
|
|
118
|
+
|
|
119
|
+
def failed(self, message: str) -> None:
|
|
120
|
+
"""Handle failed connection event.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
message: Failure message.
|
|
124
|
+
"""
|
|
125
|
+
self.logger.debug(f"Failed with message: {message}")
|
|
126
|
+
self.service_response.success = False
|
|
127
|
+
self.service_response.timestamp = datetime.now()
|
|
128
|
+
self.service_response.error = message
|
|
129
|
+
if self.finish_callback:
|
|
130
|
+
self.finish_callback(self.service_response)
|
|
131
|
+
|
|
132
|
+
def send_custom_telegram(
|
|
133
|
+
self,
|
|
134
|
+
serial_number: str,
|
|
135
|
+
function_code: str,
|
|
136
|
+
data: str,
|
|
137
|
+
finish_callback: Callable[[ConbusCustomResponse], None],
|
|
138
|
+
timeout_seconds: Optional[float] = None,
|
|
139
|
+
) -> None:
|
|
140
|
+
"""Send a custom telegram to a module.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
serial_number: 10-digit module serial number.
|
|
144
|
+
function_code: Function code (e.g., "02", "17").
|
|
145
|
+
data: Data code (e.g., "E2", "AA").
|
|
146
|
+
finish_callback: Callback function to call when the reply is received.
|
|
147
|
+
timeout_seconds: Timeout in seconds.
|
|
148
|
+
"""
|
|
149
|
+
self.logger.info("Starting send_custom_telegram")
|
|
150
|
+
if timeout_seconds:
|
|
151
|
+
self.timeout_seconds = timeout_seconds
|
|
152
|
+
self.finish_callback = finish_callback
|
|
153
|
+
self.serial_number = serial_number
|
|
154
|
+
self.function_code = function_code
|
|
155
|
+
self.data = data
|
|
156
|
+
self.start_reactor()
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""Conbus DataPoint Query All Service.
|
|
2
|
+
|
|
3
|
+
This module provides service for querying all datapoint types from a module.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import Callable, Optional
|
|
9
|
+
|
|
10
|
+
from twisted.internet.posixbase import PosixReactorBase
|
|
11
|
+
|
|
12
|
+
from xp.models import ConbusClientConfig, ConbusDatapointResponse
|
|
13
|
+
from xp.models.protocol.conbus_protocol import TelegramReceivedEvent
|
|
14
|
+
from xp.models.telegram.datapoint_type import DataPointType
|
|
15
|
+
from xp.models.telegram.reply_telegram import ReplyTelegram
|
|
16
|
+
from xp.models.telegram.system_function import SystemFunction
|
|
17
|
+
from xp.models.telegram.telegram_type import TelegramType
|
|
18
|
+
from xp.services import TelegramService
|
|
19
|
+
from xp.services.protocol import ConbusProtocol
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ConbusDatapointQueryAllService(ConbusProtocol):
|
|
23
|
+
"""
|
|
24
|
+
Utility service for querying all datapoints from a module.
|
|
25
|
+
|
|
26
|
+
This service orchestrates multiple ConbusDatapointService calls to query
|
|
27
|
+
all available datapoint types sequentially.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
telegram_service: TelegramService,
|
|
33
|
+
cli_config: ConbusClientConfig,
|
|
34
|
+
reactor: PosixReactorBase,
|
|
35
|
+
) -> None:
|
|
36
|
+
"""Initialize the query all service.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
telegram_service: TelegramService for dependency injection.
|
|
40
|
+
cli_config: ConbusClientConfig for connection settings.
|
|
41
|
+
reactor: PosixReactorBase for async operations.
|
|
42
|
+
"""
|
|
43
|
+
super().__init__(cli_config, reactor)
|
|
44
|
+
self.telegram_service = telegram_service
|
|
45
|
+
self.serial_number: str = ""
|
|
46
|
+
self.finish_callback: Optional[Callable[[ConbusDatapointResponse], None]] = None
|
|
47
|
+
self.progress_callback: Optional[Callable[[ReplyTelegram], None]] = None
|
|
48
|
+
self.service_response: ConbusDatapointResponse = ConbusDatapointResponse(
|
|
49
|
+
success=False,
|
|
50
|
+
serial_number=self.serial_number,
|
|
51
|
+
)
|
|
52
|
+
self.datapoint_types = list(DataPointType)
|
|
53
|
+
self.current_index = 0
|
|
54
|
+
|
|
55
|
+
# Set up logging
|
|
56
|
+
self.logger = logging.getLogger(__name__)
|
|
57
|
+
|
|
58
|
+
def connection_established(self) -> None:
|
|
59
|
+
"""Handle connection established event."""
|
|
60
|
+
self.logger.debug("Connection established, querying datapoints.")
|
|
61
|
+
self.next_datapoint()
|
|
62
|
+
|
|
63
|
+
def next_datapoint(self) -> bool:
|
|
64
|
+
"""Query the next datapoint type.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
True if there are more datapoints to query, False otherwise.
|
|
68
|
+
"""
|
|
69
|
+
self.logger.debug("Querying next datapoint")
|
|
70
|
+
|
|
71
|
+
if self.current_index >= len(self.datapoint_types):
|
|
72
|
+
return False
|
|
73
|
+
|
|
74
|
+
datapoint_type_code = self.datapoint_types[self.current_index]
|
|
75
|
+
datapoint_type = DataPointType(datapoint_type_code)
|
|
76
|
+
|
|
77
|
+
self.logger.debug(f"Datapoint: {datapoint_type}")
|
|
78
|
+
self.send_telegram(
|
|
79
|
+
telegram_type=TelegramType.SYSTEM,
|
|
80
|
+
serial_number=self.serial_number,
|
|
81
|
+
system_function=SystemFunction.READ_DATAPOINT,
|
|
82
|
+
data_value=str(datapoint_type.value),
|
|
83
|
+
)
|
|
84
|
+
self.current_index += 1
|
|
85
|
+
return True
|
|
86
|
+
|
|
87
|
+
def timeout(self) -> bool:
|
|
88
|
+
"""Handle timeout event by querying next datapoint.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
True to continue, False to stop the reactor.
|
|
92
|
+
"""
|
|
93
|
+
self.logger.debug("Timeout, querying next datapoint")
|
|
94
|
+
query_next_datapoint = self.next_datapoint()
|
|
95
|
+
if not query_next_datapoint:
|
|
96
|
+
if self.finish_callback:
|
|
97
|
+
self.logger.debug("Received all datapoints telegram")
|
|
98
|
+
self.service_response.success = True
|
|
99
|
+
self.service_response.timestamp = datetime.now()
|
|
100
|
+
self.service_response.serial_number = self.serial_number
|
|
101
|
+
self.service_response.system_function = SystemFunction.READ_DATAPOINT
|
|
102
|
+
self.finish_callback(self.service_response)
|
|
103
|
+
return False
|
|
104
|
+
return True
|
|
105
|
+
|
|
106
|
+
def telegram_sent(self, telegram_sent: str) -> None:
|
|
107
|
+
"""Handle telegram sent event.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
telegram_sent: The telegram that was sent.
|
|
111
|
+
"""
|
|
112
|
+
self.service_response.sent_telegram = telegram_sent
|
|
113
|
+
|
|
114
|
+
def telegram_received(self, telegram_received: TelegramReceivedEvent) -> None:
|
|
115
|
+
"""Handle telegram received event.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
telegram_received: The telegram received event.
|
|
119
|
+
"""
|
|
120
|
+
self.logger.debug(f"Telegram received: {telegram_received}")
|
|
121
|
+
if not self.service_response.received_telegrams:
|
|
122
|
+
self.service_response.received_telegrams = []
|
|
123
|
+
self.service_response.received_telegrams.append(telegram_received.frame)
|
|
124
|
+
|
|
125
|
+
if (
|
|
126
|
+
not telegram_received.checksum_valid
|
|
127
|
+
or telegram_received.telegram_type != TelegramType.REPLY
|
|
128
|
+
or telegram_received.serial_number != self.serial_number
|
|
129
|
+
):
|
|
130
|
+
self.logger.debug("Not a reply for our serial number")
|
|
131
|
+
return
|
|
132
|
+
|
|
133
|
+
# Parse the reply telegram
|
|
134
|
+
datapoint_telegram = self.telegram_service.parse_reply_telegram(
|
|
135
|
+
telegram_received.frame
|
|
136
|
+
)
|
|
137
|
+
if (
|
|
138
|
+
not datapoint_telegram
|
|
139
|
+
or datapoint_telegram.system_function != SystemFunction.READ_DATAPOINT
|
|
140
|
+
):
|
|
141
|
+
self.logger.debug("Not a reply for our datapoint type")
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
self.logger.debug("Received a datapoint telegram")
|
|
145
|
+
if self.progress_callback:
|
|
146
|
+
self.progress_callback(datapoint_telegram)
|
|
147
|
+
|
|
148
|
+
def failed(self, message: str) -> None:
|
|
149
|
+
"""Handle failed connection event.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
message: Failure message.
|
|
153
|
+
"""
|
|
154
|
+
self.logger.debug(f"Failed with message: {message}")
|
|
155
|
+
self.service_response.success = False
|
|
156
|
+
self.service_response.timestamp = datetime.now()
|
|
157
|
+
self.service_response.error = message
|
|
158
|
+
if self.finish_callback:
|
|
159
|
+
self.finish_callback(self.service_response)
|
|
160
|
+
|
|
161
|
+
def query_all_datapoints(
|
|
162
|
+
self,
|
|
163
|
+
serial_number: str,
|
|
164
|
+
finish_callback: Callable[[ConbusDatapointResponse], None],
|
|
165
|
+
progress_callback: Callable[[ReplyTelegram], None],
|
|
166
|
+
timeout_seconds: Optional[float] = None,
|
|
167
|
+
) -> None:
|
|
168
|
+
"""Query all datapoints from a module.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
serial_number: 10-digit module serial number.
|
|
172
|
+
finish_callback: Callback function to call when all datapoints are received.
|
|
173
|
+
progress_callback: Callback function to call when each datapoint is received.
|
|
174
|
+
timeout_seconds: Timeout in seconds.
|
|
175
|
+
"""
|
|
176
|
+
self.logger.info("Starting query_all_datapoints")
|
|
177
|
+
if timeout_seconds:
|
|
178
|
+
self.timeout_seconds = timeout_seconds
|
|
179
|
+
self.finish_callback = finish_callback
|
|
180
|
+
self.progress_callback = progress_callback
|
|
181
|
+
self.serial_number = serial_number
|
|
182
|
+
self.start_reactor()
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""Conbus Datapoint Service for querying module datapoints.
|
|
2
|
+
|
|
3
|
+
This service handles datapoint query operations for modules through Conbus telegrams.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import Callable, Optional
|
|
9
|
+
|
|
10
|
+
from twisted.internet.posixbase import PosixReactorBase
|
|
11
|
+
|
|
12
|
+
from xp.models import ConbusClientConfig, ConbusDatapointResponse
|
|
13
|
+
from xp.models.protocol.conbus_protocol import TelegramReceivedEvent
|
|
14
|
+
from xp.models.telegram.datapoint_type import DataPointType
|
|
15
|
+
from xp.models.telegram.reply_telegram import ReplyTelegram
|
|
16
|
+
from xp.models.telegram.system_function import SystemFunction
|
|
17
|
+
from xp.models.telegram.telegram_type import TelegramType
|
|
18
|
+
from xp.services.protocol import ConbusProtocol
|
|
19
|
+
from xp.services.telegram.telegram_service import TelegramService
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ConbusDatapointService(ConbusProtocol):
|
|
23
|
+
"""
|
|
24
|
+
Service for querying datapoints from Conbus modules.
|
|
25
|
+
|
|
26
|
+
Uses ConbusProtocol to provide datapoint query functionality
|
|
27
|
+
for reading sensor data and module information.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
telegram_service: TelegramService,
|
|
33
|
+
cli_config: ConbusClientConfig,
|
|
34
|
+
reactor: PosixReactorBase,
|
|
35
|
+
) -> None:
|
|
36
|
+
"""Initialize the Conbus datapoint service.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
telegram_service: Service for parsing telegrams.
|
|
40
|
+
cli_config: Configuration for Conbus client connection.
|
|
41
|
+
reactor: Twisted reactor for event loop.
|
|
42
|
+
"""
|
|
43
|
+
super().__init__(cli_config, reactor)
|
|
44
|
+
self.telegram_service = telegram_service
|
|
45
|
+
self.serial_number: str = ""
|
|
46
|
+
self.datapoint_type: Optional[DataPointType] = None
|
|
47
|
+
self.datapoint_finished_callback: Optional[
|
|
48
|
+
Callable[[ConbusDatapointResponse], None]
|
|
49
|
+
] = None
|
|
50
|
+
self.service_response: ConbusDatapointResponse = ConbusDatapointResponse(
|
|
51
|
+
success=False,
|
|
52
|
+
serial_number=self.serial_number,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Set up logging
|
|
56
|
+
self.logger = logging.getLogger(__name__)
|
|
57
|
+
|
|
58
|
+
def connection_established(self) -> None:
|
|
59
|
+
"""Handle connection established event."""
|
|
60
|
+
self.logger.debug(
|
|
61
|
+
f"Connection established, querying datapoint {self.datapoint_type}."
|
|
62
|
+
)
|
|
63
|
+
if self.datapoint_type is None:
|
|
64
|
+
self.failed("Datapoint type not set")
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
self.send_telegram(
|
|
68
|
+
telegram_type=TelegramType.SYSTEM,
|
|
69
|
+
serial_number=self.serial_number,
|
|
70
|
+
system_function=SystemFunction.READ_DATAPOINT,
|
|
71
|
+
data_value=str(self.datapoint_type.value),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def telegram_sent(self, telegram_sent: str) -> None:
|
|
75
|
+
"""Handle telegram sent event.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
telegram_sent: The telegram that was sent.
|
|
79
|
+
"""
|
|
80
|
+
self.service_response.sent_telegram = telegram_sent
|
|
81
|
+
|
|
82
|
+
def telegram_received(self, telegram_received: TelegramReceivedEvent) -> None:
|
|
83
|
+
"""Handle telegram received event.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
telegram_received: The telegram received event.
|
|
87
|
+
"""
|
|
88
|
+
self.logger.debug(f"Telegram received: {telegram_received}")
|
|
89
|
+
if not self.service_response.received_telegrams:
|
|
90
|
+
self.service_response.received_telegrams = []
|
|
91
|
+
self.service_response.received_telegrams.append(telegram_received.frame)
|
|
92
|
+
|
|
93
|
+
if (
|
|
94
|
+
not telegram_received.checksum_valid
|
|
95
|
+
or telegram_received.telegram_type != TelegramType.REPLY
|
|
96
|
+
or telegram_received.serial_number != self.serial_number
|
|
97
|
+
):
|
|
98
|
+
self.logger.debug("Not a reply for our serial number")
|
|
99
|
+
return
|
|
100
|
+
|
|
101
|
+
# Parse the reply telegram
|
|
102
|
+
datapoint_telegram = self.telegram_service.parse_reply_telegram(
|
|
103
|
+
telegram_received.frame
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
if (
|
|
107
|
+
not datapoint_telegram
|
|
108
|
+
or datapoint_telegram.system_function != SystemFunction.READ_DATAPOINT
|
|
109
|
+
or datapoint_telegram.datapoint_type != self.datapoint_type
|
|
110
|
+
):
|
|
111
|
+
self.logger.debug("Not a reply for our datapoint type")
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
self.logger.debug("Received datapoint telegram")
|
|
115
|
+
self.succeed(datapoint_telegram)
|
|
116
|
+
|
|
117
|
+
def succeed(self, datapoint_telegram: ReplyTelegram) -> None:
|
|
118
|
+
"""Handle successful datapoint query.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
datapoint_telegram: The parsed datapoint telegram.
|
|
122
|
+
"""
|
|
123
|
+
self.logger.debug("Succeed querying datapoint")
|
|
124
|
+
self.service_response.success = True
|
|
125
|
+
self.service_response.timestamp = datetime.now()
|
|
126
|
+
self.service_response.serial_number = self.serial_number
|
|
127
|
+
self.service_response.system_function = SystemFunction.READ_DATAPOINT
|
|
128
|
+
self.service_response.datapoint_type = self.datapoint_type
|
|
129
|
+
self.service_response.datapoint_telegram = datapoint_telegram
|
|
130
|
+
self.service_response.data_value = datapoint_telegram.data_value
|
|
131
|
+
if self.datapoint_finished_callback:
|
|
132
|
+
self.datapoint_finished_callback(self.service_response)
|
|
133
|
+
self._stop_reactor()
|
|
134
|
+
|
|
135
|
+
def failed(self, message: str) -> None:
|
|
136
|
+
"""Handle failed connection event.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
message: Failure message.
|
|
140
|
+
"""
|
|
141
|
+
self.logger.debug(f"Failed with message: {message}")
|
|
142
|
+
self.service_response.success = False
|
|
143
|
+
self.service_response.timestamp = datetime.now()
|
|
144
|
+
self.service_response.serial_number = self.serial_number
|
|
145
|
+
self.service_response.error = message
|
|
146
|
+
if self.datapoint_finished_callback:
|
|
147
|
+
self.datapoint_finished_callback(self.service_response)
|
|
148
|
+
|
|
149
|
+
def query_datapoint(
|
|
150
|
+
self,
|
|
151
|
+
serial_number: str,
|
|
152
|
+
datapoint_type: DataPointType,
|
|
153
|
+
finish_callback: Callable[[ConbusDatapointResponse], None],
|
|
154
|
+
timeout_seconds: Optional[float] = None,
|
|
155
|
+
) -> None:
|
|
156
|
+
"""Query a specific datapoint from a module.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
serial_number: 10-digit module serial number.
|
|
160
|
+
datapoint_type: Type of datapoint to query.
|
|
161
|
+
finish_callback: Callback function to call when the datapoint is received.
|
|
162
|
+
timeout_seconds: Timeout in seconds.
|
|
163
|
+
"""
|
|
164
|
+
self.logger.info("Starting query_datapoint")
|
|
165
|
+
if timeout_seconds:
|
|
166
|
+
self.timeout_seconds = timeout_seconds
|
|
167
|
+
self.datapoint_finished_callback = finish_callback
|
|
168
|
+
self.serial_number = serial_number
|
|
169
|
+
self.datapoint_type = datapoint_type
|
|
170
|
+
self.start_reactor()
|