conson-xp 1.1.0__py3-none-any.whl → 1.3.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.1.0.dist-info → conson_xp-1.3.0.dist-info}/METADATA +1 -5
- conson_xp-1.3.0.dist-info/RECORD +164 -0
- xp/__init__.py +4 -3
- xp/cli/__init__.py +1 -1
- xp/cli/commands/__init__.py +1 -2
- xp/cli/commands/conbus/conbus.py +9 -37
- xp/cli/commands/conbus/conbus_actiontable_commands.py +26 -4
- xp/cli/commands/conbus/conbus_autoreport_commands.py +58 -30
- xp/cli/commands/conbus/conbus_blink_commands.py +61 -29
- xp/cli/commands/conbus/conbus_config_commands.py +10 -5
- xp/cli/commands/conbus/conbus_custom_commands.py +16 -5
- xp/cli/commands/conbus/conbus_datapoint_commands.py +32 -10
- xp/cli/commands/conbus/conbus_discover_commands.py +20 -7
- xp/cli/commands/conbus/conbus_lightlevel_commands.py +114 -39
- xp/cli/commands/conbus/conbus_linknumber_commands.py +50 -25
- xp/cli/commands/conbus/conbus_msactiontable_commands.py +36 -5
- xp/cli/commands/conbus/conbus_output_commands.py +52 -14
- xp/cli/commands/conbus/conbus_raw_commands.py +17 -6
- xp/cli/commands/conbus/conbus_receive_commands.py +20 -10
- xp/cli/commands/conbus/conbus_scan_commands.py +17 -4
- xp/cli/commands/file_commands.py +35 -18
- xp/cli/commands/homekit/homekit.py +14 -8
- xp/cli/commands/homekit/homekit_start_commands.py +8 -6
- xp/cli/commands/module_commands.py +38 -23
- xp/cli/commands/reverse_proxy_commands.py +27 -19
- xp/cli/commands/server/server_commands.py +18 -18
- xp/cli/commands/telegram/telegram.py +4 -12
- xp/cli/commands/telegram/telegram_blink_commands.py +10 -8
- xp/cli/commands/telegram/telegram_checksum_commands.py +19 -8
- xp/cli/commands/telegram/telegram_discover_commands.py +2 -4
- xp/cli/commands/telegram/telegram_linknumber_commands.py +11 -8
- xp/cli/commands/telegram/telegram_parse_commands.py +10 -9
- xp/cli/commands/telegram/telegram_version_commands.py +8 -4
- xp/cli/main.py +5 -3
- xp/cli/utils/click_tree.py +23 -3
- xp/cli/utils/datapoint_type_choice.py +20 -0
- xp/cli/utils/decorators.py +165 -14
- xp/cli/utils/error_handlers.py +49 -18
- xp/cli/utils/formatters.py +95 -10
- xp/cli/utils/serial_number_type.py +18 -0
- xp/cli/utils/system_function_choice.py +20 -0
- xp/cli/utils/xp_module_type.py +20 -0
- xp/connection/__init__.py +1 -1
- xp/connection/exceptions.py +5 -5
- xp/models/__init__.py +1 -1
- xp/models/actiontable/__init__.py +1 -0
- xp/models/actiontable/actiontable.py +17 -1
- xp/models/actiontable/msactiontable_xp20.py +10 -0
- xp/models/actiontable/msactiontable_xp24.py +20 -3
- xp/models/actiontable/msactiontable_xp33.py +27 -4
- xp/models/conbus/__init__.py +1 -0
- xp/models/conbus/conbus.py +34 -4
- xp/models/conbus/conbus_autoreport.py +20 -2
- xp/models/conbus/conbus_blink.py +22 -2
- xp/models/conbus/conbus_client_config.py +22 -1
- xp/models/conbus/conbus_connection_status.py +16 -2
- xp/models/conbus/conbus_custom.py +21 -2
- xp/models/conbus/conbus_datapoint.py +25 -2
- xp/models/conbus/conbus_discover.py +18 -2
- xp/models/conbus/conbus_lightlevel.py +20 -2
- xp/models/conbus/conbus_linknumber.py +20 -2
- xp/models/conbus/conbus_output.py +22 -2
- xp/models/conbus/conbus_raw.py +17 -2
- xp/models/conbus/conbus_receive.py +16 -2
- xp/models/conbus/conbus_writeconfig.py +60 -0
- xp/models/homekit/__init__.py +1 -0
- xp/models/homekit/homekit_accessory.py +15 -1
- xp/models/homekit/homekit_config.py +52 -0
- xp/models/homekit/homekit_conson_config.py +32 -0
- xp/models/log_entry.py +49 -9
- xp/models/protocol/__init__.py +1 -0
- xp/models/protocol/conbus_protocol.py +130 -21
- xp/models/telegram/__init__.py +1 -0
- xp/models/telegram/action_type.py +16 -2
- xp/models/telegram/datapoint_type.py +36 -2
- xp/models/telegram/event_telegram.py +46 -10
- xp/models/telegram/event_type.py +8 -1
- xp/models/telegram/input_action_type.py +34 -2
- xp/models/telegram/input_type.py +9 -1
- xp/models/telegram/module_type.py +69 -19
- xp/models/telegram/module_type_code.py +43 -1
- xp/models/telegram/output_telegram.py +30 -6
- xp/models/telegram/reply_telegram.py +56 -11
- xp/models/telegram/system_function.py +35 -3
- xp/models/telegram/system_telegram.py +18 -4
- xp/models/telegram/telegram.py +12 -3
- xp/models/telegram/telegram_type.py +8 -1
- xp/models/telegram/timeparam_type.py +27 -0
- xp/models/write_config_type.py +17 -2
- xp/services/__init__.py +1 -1
- xp/services/conbus/__init__.py +1 -0
- xp/services/conbus/actiontable/__init__.py +1 -0
- xp/services/conbus/actiontable/actiontable_service.py +33 -2
- xp/services/conbus/actiontable/msactiontable_service.py +40 -3
- xp/services/conbus/actiontable/msactiontable_xp24_serializer.py +36 -4
- xp/services/conbus/actiontable/msactiontable_xp33_serializer.py +45 -5
- xp/services/conbus/conbus_blink_all_service.py +40 -21
- xp/services/conbus/conbus_blink_service.py +37 -13
- xp/services/conbus/conbus_custom_service.py +29 -13
- xp/services/conbus/conbus_datapoint_queryall_service.py +40 -16
- xp/services/conbus/conbus_datapoint_service.py +42 -18
- xp/services/conbus/conbus_discover_service.py +43 -7
- xp/services/conbus/conbus_output_service.py +33 -13
- xp/services/conbus/conbus_raw_service.py +36 -16
- xp/services/conbus/conbus_receive_service.py +38 -6
- xp/services/conbus/conbus_scan_service.py +44 -18
- xp/services/conbus/write_config_service.py +193 -0
- xp/services/homekit/__init__.py +1 -0
- xp/services/homekit/homekit_cache_service.py +31 -6
- xp/services/homekit/homekit_conbus_service.py +33 -2
- xp/services/homekit/homekit_config_validator.py +97 -15
- xp/services/homekit/homekit_conson_validator.py +51 -7
- xp/services/homekit/homekit_dimminglight.py +47 -1
- xp/services/homekit/homekit_dimminglight_service.py +35 -1
- xp/services/homekit/homekit_hap_service.py +71 -18
- xp/services/homekit/homekit_lightbulb.py +35 -1
- xp/services/homekit/homekit_lightbulb_service.py +30 -2
- xp/services/homekit/homekit_module_service.py +23 -1
- xp/services/homekit/homekit_outlet.py +47 -1
- xp/services/homekit/homekit_outlet_service.py +44 -2
- xp/services/homekit/homekit_service.py +113 -19
- xp/services/log_file_service.py +37 -41
- xp/services/module_type_service.py +26 -5
- xp/services/protocol/__init__.py +1 -1
- xp/services/protocol/conbus_protocol.py +110 -16
- xp/services/protocol/protocol_factory.py +40 -0
- xp/services/protocol/telegram_protocol.py +38 -7
- xp/services/reverse_proxy_service.py +79 -14
- xp/services/server/__init__.py +1 -0
- xp/services/server/base_server_service.py +102 -14
- xp/services/server/cp20_server_service.py +12 -4
- xp/services/server/server_service.py +26 -11
- xp/services/server/xp130_server_service.py +11 -3
- xp/services/server/xp20_server_service.py +11 -3
- xp/services/server/xp230_server_service.py +11 -3
- xp/services/server/xp24_server_service.py +33 -6
- xp/services/server/xp33_server_service.py +41 -8
- xp/services/telegram/__init__.py +1 -0
- xp/services/telegram/telegram_blink_service.py +19 -31
- xp/services/telegram/telegram_checksum_service.py +10 -10
- xp/services/telegram/telegram_datapoint_service.py +70 -0
- xp/services/telegram/telegram_discover_service.py +58 -29
- xp/services/telegram/telegram_link_number_service.py +27 -40
- xp/services/telegram/telegram_output_service.py +46 -49
- xp/services/telegram/telegram_service.py +41 -41
- xp/services/telegram/telegram_version_service.py +4 -2
- xp/utils/__init__.py +1 -1
- xp/utils/dependencies.py +4 -47
- xp/utils/serialization.py +6 -0
- xp/utils/time_utils.py +6 -11
- conson_xp-1.1.0.dist-info/RECORD +0 -181
- xp/api/__init__.py +0 -1
- xp/api/main.py +0 -110
- xp/api/models/__init__.py +0 -1
- xp/api/models/api.py +0 -20
- xp/api/models/discover.py +0 -21
- xp/api/routers/__init__.py +0 -17
- xp/api/routers/conbus.py +0 -5
- xp/api/routers/conbus_blink.py +0 -105
- xp/api/routers/conbus_custom.py +0 -63
- xp/api/routers/conbus_datapoint.py +0 -67
- xp/api/routers/conbus_output.py +0 -147
- xp/api/routers/errors.py +0 -37
- xp/cli/commands/api.py +0 -16
- xp/cli/commands/api_start_commands.py +0 -126
- xp/services/conbus/conbus_autoreport_get_service.py +0 -85
- xp/services/conbus/conbus_autoreport_set_service.py +0 -128
- xp/services/conbus/conbus_lightlevel_get_service.py +0 -101
- xp/services/conbus/conbus_lightlevel_set_service.py +0 -205
- xp/services/conbus/conbus_linknumber_get_service.py +0 -86
- xp/services/conbus/conbus_linknumber_set_service.py +0 -155
- {conson_xp-1.1.0.dist-info → conson_xp-1.3.0.dist-info}/WHEEL +0 -0
- {conson_xp-1.1.0.dist-info → conson_xp-1.3.0.dist-info}/entry_points.txt +0 -0
- {conson_xp-1.1.0.dist-info → conson_xp-1.3.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Conbus Scan Service for TCP communication with Conbus servers.
|
|
2
2
|
|
|
3
|
-
This service implements a TCP client that
|
|
4
|
-
|
|
3
|
+
This service implements a TCP client that scans Conbus servers and sends
|
|
4
|
+
telegrams to scan modules for all datapoints by function code.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import logging
|
|
@@ -20,10 +20,10 @@ from xp.services.protocol import ConbusProtocol
|
|
|
20
20
|
|
|
21
21
|
class ConbusScanService(ConbusProtocol):
|
|
22
22
|
"""
|
|
23
|
-
Service for
|
|
23
|
+
Service for scanning modules for all datapoints by function code.
|
|
24
24
|
|
|
25
|
-
Uses ConbusProtocol to provide
|
|
26
|
-
|
|
25
|
+
Uses ConbusProtocol to provide scan functionality for discovering
|
|
26
|
+
all available datapoints on a module.
|
|
27
27
|
"""
|
|
28
28
|
|
|
29
29
|
def __init__(
|
|
@@ -31,7 +31,12 @@ class ConbusScanService(ConbusProtocol):
|
|
|
31
31
|
cli_config: ConbusClientConfig,
|
|
32
32
|
reactor: PosixReactorBase,
|
|
33
33
|
) -> None:
|
|
34
|
-
"""Initialize the Conbus
|
|
34
|
+
"""Initialize the Conbus scan service.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
cli_config: Conbus client configuration.
|
|
38
|
+
reactor: Twisted reactor instance.
|
|
39
|
+
"""
|
|
35
40
|
super().__init__(cli_config, reactor)
|
|
36
41
|
self.serial_number: str = ""
|
|
37
42
|
self.function_code: str = ""
|
|
@@ -49,10 +54,16 @@ class ConbusScanService(ConbusProtocol):
|
|
|
49
54
|
self.logger = logging.getLogger(__name__)
|
|
50
55
|
|
|
51
56
|
def connection_established(self) -> None:
|
|
57
|
+
"""Handle connection established event."""
|
|
52
58
|
self.logger.debug("Connection established, starting scan")
|
|
53
59
|
self.scan_next_datacode()
|
|
54
60
|
|
|
55
61
|
def scan_next_datacode(self) -> bool:
|
|
62
|
+
"""Scan the next data code.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
True if scanning should continue, False if complete.
|
|
66
|
+
"""
|
|
56
67
|
self.datapoint_value += 1
|
|
57
68
|
if self.datapoint_value >= 100:
|
|
58
69
|
if self.finish_callback:
|
|
@@ -66,10 +77,20 @@ class ConbusScanService(ConbusProtocol):
|
|
|
66
77
|
return True
|
|
67
78
|
|
|
68
79
|
def telegram_sent(self, telegram_sent: str) -> None:
|
|
80
|
+
"""Handle telegram sent event.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
telegram_sent: The telegram that was sent.
|
|
84
|
+
"""
|
|
69
85
|
self.service_response.success = True
|
|
70
86
|
self.service_response.sent_telegrams.append(telegram_sent)
|
|
71
87
|
|
|
72
88
|
def telegram_received(self, telegram_received: TelegramReceivedEvent) -> None:
|
|
89
|
+
"""Handle telegram received event.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
telegram_received: The telegram received event.
|
|
93
|
+
"""
|
|
73
94
|
self.logger.debug(f"Telegram received: {telegram_received}")
|
|
74
95
|
if not self.service_response.received_telegrams:
|
|
75
96
|
self.service_response.received_telegrams = []
|
|
@@ -79,11 +100,21 @@ class ConbusScanService(ConbusProtocol):
|
|
|
79
100
|
self.progress_callback(telegram_received.frame)
|
|
80
101
|
|
|
81
102
|
def timeout(self) -> bool:
|
|
103
|
+
"""Handle timeout event by scanning next data code.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
True to continue scanning, False to stop.
|
|
107
|
+
"""
|
|
82
108
|
self.logger.debug(f"Timeout: {self.timeout_seconds}s")
|
|
83
109
|
continue_scan = self.scan_next_datacode()
|
|
84
110
|
return continue_scan
|
|
85
111
|
|
|
86
112
|
def failed(self, message: str) -> None:
|
|
113
|
+
"""Handle failed connection event.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
message: Failure message.
|
|
117
|
+
"""
|
|
87
118
|
self.logger.debug(f"Failed with message: {message}")
|
|
88
119
|
self.service_response.success = False
|
|
89
120
|
self.service_response.timestamp = datetime.now()
|
|
@@ -99,21 +130,16 @@ class ConbusScanService(ConbusProtocol):
|
|
|
99
130
|
finish_callback: Callable[[ConbusResponse], None],
|
|
100
131
|
timeout_seconds: Optional[float] = None,
|
|
101
132
|
) -> None:
|
|
102
|
-
"""
|
|
103
|
-
Query a specific datapoint from a module.
|
|
133
|
+
"""Scan a module for all datapoints by function code.
|
|
104
134
|
|
|
105
135
|
Args:
|
|
106
|
-
serial_number: 10-digit module serial number
|
|
107
|
-
function_code:
|
|
108
|
-
progress_callback:
|
|
109
|
-
finish_callback:
|
|
110
|
-
timeout_seconds:
|
|
111
|
-
|
|
112
|
-
Returns:
|
|
113
|
-
ConbusDatapointResponse with operation result and datapoint value
|
|
136
|
+
serial_number: 10-digit module serial number.
|
|
137
|
+
function_code: The function code to scan.
|
|
138
|
+
progress_callback: Callback to handle progress.
|
|
139
|
+
finish_callback: Callback function to call when the scan is complete.
|
|
140
|
+
timeout_seconds: Timeout in seconds.
|
|
114
141
|
"""
|
|
115
|
-
|
|
116
|
-
self.logger.info("Starting query_datapoint")
|
|
142
|
+
self.logger.info("Starting scan_module")
|
|
117
143
|
if timeout_seconds:
|
|
118
144
|
self.timeout_seconds = timeout_seconds
|
|
119
145
|
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""Conbus Link Number Service for setting module link numbers.
|
|
2
|
+
|
|
3
|
+
This service handles setting link numbers 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_writeconfig import ConbusWriteConfigResponse
|
|
14
|
+
from xp.models.protocol.conbus_protocol import TelegramReceivedEvent
|
|
15
|
+
from xp.models.telegram.datapoint_type import DataPointType
|
|
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 WriteConfigService(ConbusProtocol):
|
|
23
|
+
"""
|
|
24
|
+
Service for writing module settings via Conbus telegrams.
|
|
25
|
+
|
|
26
|
+
Handles setting assignment by sending F04DXX telegrams and processing
|
|
27
|
+
ACK/NAK responses from 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 link number set 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.datapoint_type: Optional[DataPointType] = None
|
|
46
|
+
self.serial_number: str = ""
|
|
47
|
+
self.data_value: str = ""
|
|
48
|
+
self.write_config_finished_callback: Optional[
|
|
49
|
+
Callable[[ConbusWriteConfigResponse], None]
|
|
50
|
+
] = None
|
|
51
|
+
self.write_config_response: ConbusWriteConfigResponse = (
|
|
52
|
+
ConbusWriteConfigResponse(
|
|
53
|
+
success=False,
|
|
54
|
+
serial_number=self.serial_number,
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Set up logging
|
|
59
|
+
self.logger = logging.getLogger(__name__)
|
|
60
|
+
|
|
61
|
+
def connection_established(self) -> None:
|
|
62
|
+
"""Handle connection established event."""
|
|
63
|
+
self.logger.debug(f"Connection established, writing config {self.data_value}.")
|
|
64
|
+
|
|
65
|
+
# Validate parameters before sending
|
|
66
|
+
if not self.serial_number or len(self.serial_number) != 10:
|
|
67
|
+
self.failed(f"Serial number must be 10 digits, got: {self.serial_number}")
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
if len(self.data_value) < 2:
|
|
71
|
+
self.failed(f"data_value must be at least 2 bytes, got: {self.data_value}")
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
if not self.datapoint_type:
|
|
75
|
+
self.failed(f"datapoint_type must be defined, got: {self.datapoint_type}")
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
# Send WRITE_CONFIG telegram
|
|
79
|
+
# Function F04 = WRITE_CONFIG,
|
|
80
|
+
# Datapoint = D datapoint_type
|
|
81
|
+
# Data = XX
|
|
82
|
+
self.send_telegram(
|
|
83
|
+
telegram_type=TelegramType.SYSTEM,
|
|
84
|
+
serial_number=self.serial_number,
|
|
85
|
+
system_function=SystemFunction.WRITE_CONFIG,
|
|
86
|
+
data_value=f"{self.datapoint_type.value}{self.data_value}",
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
def telegram_sent(self, telegram_sent: str) -> None:
|
|
90
|
+
"""Handle telegram sent event.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
telegram_sent: The telegram that was sent.
|
|
94
|
+
"""
|
|
95
|
+
self.write_config_response.sent_telegram = telegram_sent
|
|
96
|
+
|
|
97
|
+
def telegram_received(self, telegram_received: TelegramReceivedEvent) -> None:
|
|
98
|
+
"""Handle telegram received event.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
telegram_received: The telegram received event.
|
|
102
|
+
"""
|
|
103
|
+
self.logger.debug(f"Telegram received: {telegram_received}")
|
|
104
|
+
|
|
105
|
+
if not self.write_config_response.received_telegrams:
|
|
106
|
+
self.write_config_response.received_telegrams = []
|
|
107
|
+
self.write_config_response.received_telegrams.append(telegram_received.frame)
|
|
108
|
+
|
|
109
|
+
if (
|
|
110
|
+
not telegram_received.checksum_valid
|
|
111
|
+
or telegram_received.telegram_type != TelegramType.REPLY
|
|
112
|
+
or telegram_received.serial_number != self.serial_number
|
|
113
|
+
):
|
|
114
|
+
self.logger.debug("Not a reply for our serial number")
|
|
115
|
+
return
|
|
116
|
+
|
|
117
|
+
# Parse the reply telegram
|
|
118
|
+
reply_telegram = self.telegram_service.parse_reply_telegram(
|
|
119
|
+
telegram_received.frame
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
if not reply_telegram or reply_telegram.system_function not in (
|
|
123
|
+
SystemFunction.ACK,
|
|
124
|
+
SystemFunction.NAK,
|
|
125
|
+
):
|
|
126
|
+
self.logger.debug("Not a write config reply")
|
|
127
|
+
return
|
|
128
|
+
|
|
129
|
+
succeed = (
|
|
130
|
+
True if reply_telegram.system_function == SystemFunction.ACK else False
|
|
131
|
+
)
|
|
132
|
+
self.finished(
|
|
133
|
+
succeed_or_failed=succeed, system_function=reply_telegram.system_function
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
def failed(self, message: str) -> None:
|
|
137
|
+
"""Handle telegram failed event.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
message: The error message.
|
|
141
|
+
"""
|
|
142
|
+
self.logger.debug("Failed to send telegram")
|
|
143
|
+
self.finished(succeed_or_failed=False, message=message)
|
|
144
|
+
|
|
145
|
+
def finished(
|
|
146
|
+
self,
|
|
147
|
+
succeed_or_failed: bool,
|
|
148
|
+
message: Optional[str] = None,
|
|
149
|
+
system_function: Optional[SystemFunction] = None,
|
|
150
|
+
) -> None:
|
|
151
|
+
"""Handle successful link number set operation.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
succeed_or_failed: succeed true, failed false.
|
|
155
|
+
message: error message if any.
|
|
156
|
+
system_function: The system function from the reply telegram.
|
|
157
|
+
"""
|
|
158
|
+
self.logger.debug("finished writing config")
|
|
159
|
+
self.write_config_response.success = succeed_or_failed
|
|
160
|
+
self.write_config_response.error = message
|
|
161
|
+
self.write_config_response.timestamp = datetime.now()
|
|
162
|
+
self.write_config_response.serial_number = self.serial_number
|
|
163
|
+
self.write_config_response.system_function = system_function
|
|
164
|
+
self.write_config_response.datapoint_type = self.datapoint_type
|
|
165
|
+
self.write_config_response.data_value = self.data_value
|
|
166
|
+
if self.write_config_finished_callback:
|
|
167
|
+
self.write_config_finished_callback(self.write_config_response)
|
|
168
|
+
|
|
169
|
+
def write_config(
|
|
170
|
+
self,
|
|
171
|
+
serial_number: str,
|
|
172
|
+
datapoint_type: DataPointType,
|
|
173
|
+
data_value: str,
|
|
174
|
+
finish_callback: Callable[[ConbusWriteConfigResponse], None],
|
|
175
|
+
timeout_seconds: Optional[float] = None,
|
|
176
|
+
) -> None:
|
|
177
|
+
"""Write config to a specific module.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
serial_number: 10-digit module serial number.
|
|
181
|
+
datapoint_type: the datapoint type to write to.
|
|
182
|
+
data_value: the data to write.
|
|
183
|
+
finish_callback: Callback function to call when operation completes.
|
|
184
|
+
timeout_seconds: Optional timeout in seconds.
|
|
185
|
+
"""
|
|
186
|
+
self.logger.info("Starting write_config")
|
|
187
|
+
if timeout_seconds:
|
|
188
|
+
self.timeout_seconds = timeout_seconds
|
|
189
|
+
self.serial_number = serial_number
|
|
190
|
+
self.datapoint_type = datapoint_type
|
|
191
|
+
self.data_value = data_value
|
|
192
|
+
self.write_config_finished_callback = finish_callback
|
|
193
|
+
self.start_reactor()
|
xp/services/homekit/__init__.py
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""HomeKit integration services."""
|
|
@@ -22,7 +22,12 @@ CACHE_FILE = CACHE_DIR / "homekit_cache.json"
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
class CacheEntry(TypedDict):
|
|
25
|
-
"""Cache entry type definition.
|
|
25
|
+
"""Cache entry type definition.
|
|
26
|
+
|
|
27
|
+
Attributes:
|
|
28
|
+
event: The cached event (OutputStateReceivedEvent or LightLevelReceivedEvent).
|
|
29
|
+
timestamp: When the event was cached.
|
|
30
|
+
"""
|
|
26
31
|
|
|
27
32
|
event: Union[OutputStateReceivedEvent, LightLevelReceivedEvent]
|
|
28
33
|
timestamp: datetime
|
|
@@ -39,6 +44,12 @@ class HomeKitCacheService:
|
|
|
39
44
|
"""
|
|
40
45
|
|
|
41
46
|
def __init__(self, event_bus: EventBus, enable_persistence: bool = True):
|
|
47
|
+
"""Initialize the HomeKit cache service.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
event_bus: Event bus for inter-service communication.
|
|
51
|
+
enable_persistence: Whether to persist cache to disk.
|
|
52
|
+
"""
|
|
42
53
|
self.logger = logging.getLogger(__name__)
|
|
43
54
|
self.event_bus = event_bus
|
|
44
55
|
self.cache: dict[tuple[str, DataPointType], CacheEntry] = {}
|
|
@@ -192,12 +203,14 @@ class HomeKitCacheService:
|
|
|
192
203
|
return None
|
|
193
204
|
|
|
194
205
|
def handle_read_datapoint_event(self, event: ReadDatapointEvent) -> None:
|
|
195
|
-
"""
|
|
196
|
-
Handle ReadDatapointEvent by checking cache or refresh flag.
|
|
206
|
+
"""Handle ReadDatapointEvent by checking cache or refresh flag.
|
|
197
207
|
|
|
198
208
|
On refresh_cache=True: invalidate cache and force protocol query
|
|
199
209
|
On cache hit: dispatch cached response event
|
|
200
210
|
On cache miss: forward to protocol via ReadDatapointFromProtocolEvent
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
event: Read datapoint event with serial number, datapoint type, and refresh flag.
|
|
201
214
|
"""
|
|
202
215
|
self.logger.debug(
|
|
203
216
|
f"Handling ReadDatapointEvent: "
|
|
@@ -250,7 +263,11 @@ class HomeKitCacheService:
|
|
|
250
263
|
def handle_output_state_received_event(
|
|
251
264
|
self, event: OutputStateReceivedEvent
|
|
252
265
|
) -> None:
|
|
253
|
-
"""Cache OutputStateReceivedEvent for future queries.
|
|
266
|
+
"""Cache OutputStateReceivedEvent for future queries.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
event: Output state received event to cache.
|
|
270
|
+
"""
|
|
254
271
|
self.logger.debug(
|
|
255
272
|
f"Caching OutputStateReceivedEvent: "
|
|
256
273
|
f"serial={event.serial_number}, "
|
|
@@ -260,7 +277,11 @@ class HomeKitCacheService:
|
|
|
260
277
|
self._cache_event(event)
|
|
261
278
|
|
|
262
279
|
def handle_light_level_received_event(self, event: LightLevelReceivedEvent) -> None:
|
|
263
|
-
"""Cache LightLevelReceivedEvent for future queries.
|
|
280
|
+
"""Cache LightLevelReceivedEvent for future queries.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
event: Light level received event to cache.
|
|
284
|
+
"""
|
|
264
285
|
self.logger.debug(
|
|
265
286
|
f"Caching LightLevelReceivedEvent: "
|
|
266
287
|
f"serial={event.serial_number}, "
|
|
@@ -276,7 +297,11 @@ class HomeKitCacheService:
|
|
|
276
297
|
self._save_cache()
|
|
277
298
|
|
|
278
299
|
def get_cache_stats(self) -> dict[str, int]:
|
|
279
|
-
"""Get cache statistics.
|
|
300
|
+
"""Get cache statistics.
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
Dictionary with cache statistics including total_entries.
|
|
304
|
+
"""
|
|
280
305
|
return {
|
|
281
306
|
"total_entries": len(self.cache),
|
|
282
307
|
}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
"""HomeKit Conbus Service for protocol communication.
|
|
2
|
+
|
|
3
|
+
This module bridges HomeKit events with the Conbus protocol for device control.
|
|
4
|
+
"""
|
|
5
|
+
|
|
1
6
|
import logging
|
|
2
7
|
|
|
3
8
|
from bubus import EventBus
|
|
@@ -14,11 +19,23 @@ from xp.services.protocol.telegram_protocol import TelegramProtocol
|
|
|
14
19
|
|
|
15
20
|
|
|
16
21
|
class HomeKitConbusService:
|
|
17
|
-
"""
|
|
22
|
+
"""Service for bridging HomeKit events with Conbus protocol.
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
event_bus: Event bus for inter-service communication.
|
|
26
|
+
telegram_protocol: Protocol for sending telegrams.
|
|
27
|
+
logger: Logger instance.
|
|
28
|
+
"""
|
|
18
29
|
|
|
19
30
|
event_bus: EventBus
|
|
20
31
|
|
|
21
32
|
def __init__(self, event_bus: EventBus, telegram_protocol: TelegramProtocol):
|
|
33
|
+
"""Initialize the HomeKit Conbus service.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
event_bus: Event bus instance.
|
|
37
|
+
telegram_protocol: Telegram protocol instance.
|
|
38
|
+
"""
|
|
22
39
|
self.logger = logging.getLogger(__name__)
|
|
23
40
|
self.event_bus = event_bus
|
|
24
41
|
self.telegram_protocol = telegram_protocol
|
|
@@ -33,7 +50,11 @@ class HomeKitConbusService:
|
|
|
33
50
|
def handle_read_datapoint_request(
|
|
34
51
|
self, event: ReadDatapointFromProtocolEvent
|
|
35
52
|
) -> None:
|
|
36
|
-
"""Handle request to read datapoint from protocol.
|
|
53
|
+
"""Handle request to read datapoint from protocol.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
event: Read datapoint event with serial number and datapoint type.
|
|
57
|
+
"""
|
|
37
58
|
self.logger.debug(f"read_datapoint_request {event}")
|
|
38
59
|
|
|
39
60
|
system_function = SystemFunction.READ_DATAPOINT.value
|
|
@@ -42,6 +63,11 @@ class HomeKitConbusService:
|
|
|
42
63
|
self.telegram_protocol.sendFrame(telegram.encode())
|
|
43
64
|
|
|
44
65
|
def handle_send_write_config_event(self, event: SendWriteConfigEvent) -> None:
|
|
66
|
+
"""Handle send write config event.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
event: Write config event with configuration data.
|
|
70
|
+
"""
|
|
45
71
|
self.logger.debug(f"send_write_config_event {event}")
|
|
46
72
|
|
|
47
73
|
# Format data as output_number:level (e.g., "02:050")
|
|
@@ -54,6 +80,11 @@ class HomeKitConbusService:
|
|
|
54
80
|
self.telegram_protocol.sendFrame(telegram.encode())
|
|
55
81
|
|
|
56
82
|
def handle_send_action_event(self, event: SendActionEvent) -> None:
|
|
83
|
+
"""Handle send action event.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
event: Send action event with action data.
|
|
87
|
+
"""
|
|
57
88
|
self.logger.debug(f"send_action_event {event}")
|
|
58
89
|
|
|
59
90
|
action_value = (
|