conson-xp 1.5.0__py3-none-any.whl → 1.7.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.5.0.dist-info → conson_xp-1.7.0.dist-info}/METADATA +1 -1
- {conson_xp-1.5.0.dist-info → conson_xp-1.7.0.dist-info}/RECORD +27 -24
- xp/__init__.py +1 -1
- xp/cli/commands/conbus/conbus_msactiontable_commands.py +11 -2
- xp/services/actiontable/__init__.py +1 -0
- xp/services/actiontable/msactiontable_serializer.py +7 -0
- xp/services/{conbus/actiontable → actiontable}/msactiontable_xp20_serializer.py +13 -25
- xp/services/{conbus/actiontable → actiontable}/msactiontable_xp24_serializer.py +25 -39
- xp/services/{conbus/actiontable → actiontable}/msactiontable_xp33_serializer.py +4 -21
- xp/services/conbus/actiontable/actiontable_service.py +1 -1
- xp/services/conbus/actiontable/msactiontable_service.py +44 -11
- xp/services/protocol/conbus_protocol.py +0 -2
- xp/services/server/base_server_service.py +83 -5
- xp/services/server/cp20_server_service.py +9 -1
- xp/services/server/device_service_factory.py +94 -0
- xp/services/server/server_service.py +16 -61
- xp/services/server/xp130_server_service.py +10 -2
- xp/services/server/xp20_server_service.py +44 -2
- xp/services/server/xp230_server_service.py +10 -2
- xp/services/server/xp24_server_service.py +54 -1
- xp/services/server/xp33_server_service.py +42 -1
- xp/services/telegram/telegram_service.py +4 -1
- xp/utils/dependencies.py +27 -6
- {conson_xp-1.5.0.dist-info → conson_xp-1.7.0.dist-info}/WHEEL +0 -0
- {conson_xp-1.5.0.dist-info → conson_xp-1.7.0.dist-info}/entry_points.txt +0 -0
- {conson_xp-1.5.0.dist-info → conson_xp-1.7.0.dist-info}/licenses/LICENSE +0 -0
- /xp/services/{conbus/actiontable → actiontable}/actiontable_serializer.py +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
conson_xp-1.
|
|
2
|
-
conson_xp-1.
|
|
3
|
-
conson_xp-1.
|
|
4
|
-
conson_xp-1.
|
|
5
|
-
xp/__init__.py,sha256=
|
|
1
|
+
conson_xp-1.7.0.dist-info/METADATA,sha256=zJcFXnX0j-cYc8sCjdUkPuDsGp0-ji8KCGwE79oxZNQ,9274
|
|
2
|
+
conson_xp-1.7.0.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90
|
|
3
|
+
conson_xp-1.7.0.dist-info/entry_points.txt,sha256=1OcdIcDM1hz3ljCXgybaPUh1IOFEwkaTgLIW9u9zGeg,50
|
|
4
|
+
conson_xp-1.7.0.dist-info/licenses/LICENSE,sha256=rxj6woMM-r3YCyGq_UHFtbh7kHTAJgrccH6O-33IDE4,1419
|
|
5
|
+
xp/__init__.py,sha256=IyP3xj9rnVfGLnpDwGvX8oBbLbm-WT5Qx42Tf8OD9I4,180
|
|
6
6
|
xp/cli/__init__.py,sha256=QjnKB1KaI2aIyKlzrnvCwfbBuUj8HNgwNMvNJVQofbI,81
|
|
7
7
|
xp/cli/__main__.py,sha256=l2iKwMdat5rTGd3JWs-uGksnYYDDffp_Npz05QdKEeU,117
|
|
8
8
|
xp/cli/commands/__init__.py,sha256=02CbZoKmNX-fn5etX4Hdgg2lUt1MsLFPYx2VkXZyFJ8,4394
|
|
@@ -17,7 +17,7 @@ xp/cli/commands/conbus/conbus_datapoint_commands.py,sha256=r36OuTjREtbGKL-bskAGa
|
|
|
17
17
|
xp/cli/commands/conbus/conbus_discover_commands.py,sha256=Jt-OjARc-QXCr453bhKG0zXRqvOMVKZr328zNxSDulY,1373
|
|
18
18
|
xp/cli/commands/conbus/conbus_lightlevel_commands.py,sha256=FpCwogdxa7yFUjlrxM7e8Q2Ut32tKAHabngQQChvtJI,6763
|
|
19
19
|
xp/cli/commands/conbus/conbus_linknumber_commands.py,sha256=pn491Iy_LcwZ5XLtvNTWu8qG_Vdaaxr7le1tTuVK7Qw,3392
|
|
20
|
-
xp/cli/commands/conbus/conbus_msactiontable_commands.py,sha256=
|
|
20
|
+
xp/cli/commands/conbus/conbus_msactiontable_commands.py,sha256=fb9MQ4O04H0Dinpt7vSF5GtfntTZHelQ5TuUmSBbCTg,2899
|
|
21
21
|
xp/cli/commands/conbus/conbus_output_commands.py,sha256=zdRVbHzVhMbZpG2x5WXtujc3wKTsoQUV4IgkVIbJbCc,5019
|
|
22
22
|
xp/cli/commands/conbus/conbus_raw_commands.py,sha256=8BKUarwvHgz-sxML7n99YVsb8B1HJNExjQpRsuY_tQw,1829
|
|
23
23
|
xp/cli/commands/conbus/conbus_receive_commands.py,sha256=WdH7fYcbjweIGxD2uxrTRD8lJzViMSVdsaHTvSDJNCQ,1757
|
|
@@ -97,14 +97,16 @@ xp/models/telegram/telegram_type.py,sha256=GhqKP63oNMyh2tIvCPcsC5RFp4s4JjhmEqCLC
|
|
|
97
97
|
xp/models/telegram/timeparam_type.py,sha256=Ar8xvSfPmOAgR2g2Je0FgvP01SL7bPvZn5_HrVDpmJM,1137
|
|
98
98
|
xp/models/write_config_type.py,sha256=T2RaO52RpzoJ4782uMHE-fX7Ymx3CaIQAEwByydXq1M,881
|
|
99
99
|
xp/services/__init__.py,sha256=W9YZyrkh7vm--ZHhAXNQiOYQs5yhhmUHXP5I0Lf1XBg,782
|
|
100
|
+
xp/services/actiontable/__init__.py,sha256=z6js4EuJ6xKHaseTEhuEvKo1tr9K1XyQiruReJtBiPY,26
|
|
101
|
+
xp/services/actiontable/actiontable_serializer.py,sha256=x45-8d5Ba9l3hX2TFC5nqKv-g_244g-VTWhXvVXL8Jg,5159
|
|
102
|
+
xp/services/actiontable/msactiontable_serializer.py,sha256=RRL6TZ1gpSQw81kAiw2BV3jTqm4fCJC0pWIcO26Cmos,174
|
|
103
|
+
xp/services/actiontable/msactiontable_xp20_serializer.py,sha256=3Lz6t3uRYhoeMRhjDAO1XuWPJzH-ML13t05UQLFUW-s,6057
|
|
104
|
+
xp/services/actiontable/msactiontable_xp24_serializer.py,sha256=zdKzcrKqD41POqj_1c4B4why_Jp9mNXncajsnXXBtPw,4215
|
|
105
|
+
xp/services/actiontable/msactiontable_xp33_serializer.py,sha256=xoZBA38pBSUPA9nn7HgaH1ZM5sR2heQbJ6JVlPVbzUY,8400
|
|
100
106
|
xp/services/conbus/__init__.py,sha256=Hi35sMKu9o6LpYoi2tQDaQoMb8M5sOt_-LUTxxaCU_0,28
|
|
101
107
|
xp/services/conbus/actiontable/__init__.py,sha256=oD6vRk_Ye-eZ9s_hldAgtRJFu4mfAnODqpkJUGHHszk,40
|
|
102
|
-
xp/services/conbus/actiontable/
|
|
103
|
-
xp/services/conbus/actiontable/
|
|
104
|
-
xp/services/conbus/actiontable/msactiontable_service.py,sha256=u64nejKvHzMdmlK9VoM7P3uMGIfjyfo2xp9dXXlgvjc,7451
|
|
105
|
-
xp/services/conbus/actiontable/msactiontable_xp20_serializer.py,sha256=EYspooOdi0Z8oaXGxpazwnUoTmh-d7U9auhu11iBgmU,6527
|
|
106
|
-
xp/services/conbus/actiontable/msactiontable_xp24_serializer.py,sha256=30qsk9UKje1n32PPc4YoGV1lw_ZvgxNqqd8ZDgzMJpg,4504
|
|
107
|
-
xp/services/conbus/actiontable/msactiontable_xp33_serializer.py,sha256=nuWfka4U9W4lpTcS8uD6azXFcryPb0CUO5O7Z28G1k8,8901
|
|
108
|
+
xp/services/conbus/actiontable/actiontable_service.py,sha256=uy-BFCsjDoe1ZuZy9cTwRSIfMSxznLEN-iMtTsPW3EI,5626
|
|
109
|
+
xp/services/conbus/actiontable/msactiontable_service.py,sha256=yeMJRHVJe2s_-MquOdZDDym3g7135J3CzhxfEa6Qmkc,8357
|
|
108
110
|
xp/services/conbus/conbus_blink_all_service.py,sha256=OaEg4b8AEiEruHSkZ5jDtaoI81vwwxLq4KWXO7zBdD0,6582
|
|
109
111
|
xp/services/conbus/conbus_blink_service.py,sha256=x9uM-sLnIEV8wSNsvJgo08E042g-Hh2ZF3rXkz-k_9s,5824
|
|
110
112
|
xp/services/conbus/conbus_custom_service.py,sha256=4aneYdPObiZOGxPFYg5Wr70cl_xFxlQIdJBPQSa0enI,5826
|
|
@@ -133,19 +135,20 @@ xp/services/homekit/homekit_service.py,sha256=0lW-hg40ETco3gDBEYkR_sX-UIYsLSKCD4
|
|
|
133
135
|
xp/services/log_file_service.py,sha256=fvPcZQj8XOKUA-4ep5R8n0PelvwvRlTLlVxvIWM5KR4,10506
|
|
134
136
|
xp/services/module_type_service.py,sha256=xWhr1EAZMykL5uNWHWdpa5T8yNruGKH43XRTOS8GwZg,7477
|
|
135
137
|
xp/services/protocol/__init__.py,sha256=WuYn2iEcvsOIXnn5HCrU9kD3PjuMX1sIh0ljKISDoJw,720
|
|
136
|
-
xp/services/protocol/conbus_protocol.py,sha256=
|
|
138
|
+
xp/services/protocol/conbus_protocol.py,sha256=JO7yLkD_ohPT0ETjnAIx4CGpZyobf4LdbuozM_43btE,10276
|
|
137
139
|
xp/services/protocol/protocol_factory.py,sha256=PmjN9AtW9sxNo3voqUiNgQA-pTvX1RW4XXFlHKfFr5Q,2470
|
|
138
140
|
xp/services/protocol/telegram_protocol.py,sha256=Ki5DrXsKxiaqLcdP9WWUuhUI7cPu2DfwyZkh-Gv9Lb8,9496
|
|
139
141
|
xp/services/reverse_proxy_service.py,sha256=BUOlcLlTU-R5iuC_96rasug21xo19wK9_4fMQXxc0QM,15061
|
|
140
142
|
xp/services/server/__init__.py,sha256=QEcCj-jK0goAukJCe15TKYFQfSAzWsduPT_wW0HxZU8,48
|
|
141
|
-
xp/services/server/base_server_service.py,sha256=
|
|
142
|
-
xp/services/server/cp20_server_service.py,sha256=
|
|
143
|
-
xp/services/server/
|
|
144
|
-
xp/services/server/
|
|
145
|
-
xp/services/server/
|
|
146
|
-
xp/services/server/
|
|
147
|
-
xp/services/server/
|
|
148
|
-
xp/services/server/
|
|
143
|
+
xp/services/server/base_server_service.py,sha256=B-ntxp3swbwuri-9_2EuvBDi-4Uo9AH-AA4iAFGWIS4,12682
|
|
144
|
+
xp/services/server/cp20_server_service.py,sha256=SXdI6Jt400T9sLdw86ovEqKRGeV3nYVaHEA9Gcj6W2A,2041
|
|
145
|
+
xp/services/server/device_service_factory.py,sha256=Y4TvSFALeq0zYzHfCwcbikSpmIyYbLcvm9756n5Jm7Q,3744
|
|
146
|
+
xp/services/server/server_service.py,sha256=JPRFMto2l956dW7vfSclQugu2vdF0fssxxUOYjHNtA4,15833
|
|
147
|
+
xp/services/server/xp130_server_service.py,sha256=YnvetDp72-QzkyDGB4qfZZIwFs03HuibUOz2zb9XR0c,2191
|
|
148
|
+
xp/services/server/xp20_server_service.py,sha256=1wJ7A-bRkN9O5Spu3q3LNDW31mNtNF2eNMQ5E6O2ltA,2928
|
|
149
|
+
xp/services/server/xp230_server_service.py,sha256=k9ftCY5tjLFP31mKVCspq283RVaPkGx-Yq61Urk8JLs,1815
|
|
150
|
+
xp/services/server/xp24_server_service.py,sha256=S4kDZHf6SsFTwIzk1PwkWntFHtmOuVcz6UclkRdTGsc,8670
|
|
151
|
+
xp/services/server/xp33_server_service.py,sha256=X5BJr7RYueHAPNrfW-HnqV7ZN-OAouKxH1qMdDADqhk,19745
|
|
149
152
|
xp/services/telegram/__init__.py,sha256=kv0JgMg13Fp18WgGQpalNRAWwiWbrz18X4kZAP9xpSQ,48
|
|
150
153
|
xp/services/telegram/telegram_blink_service.py,sha256=Xctc9mCSZiiW1YTh8cA-4jlc8fTioS5OxT6ymhSqiYI,4487
|
|
151
154
|
xp/services/telegram/telegram_checksum_service.py,sha256=rp_C5PlraOOIyqZDp9XjBBNZLUeBLdQNNHVpN6D-1v8,4729
|
|
@@ -153,12 +156,12 @@ xp/services/telegram/telegram_datapoint_service.py,sha256=A3ERuFSWc22uJUoymH-2dW
|
|
|
153
156
|
xp/services/telegram/telegram_discover_service.py,sha256=oTpiq-yzP_UmC0xVOMMFeHO-rIlK1pF3aG-Kq4SeiBI,9025
|
|
154
157
|
xp/services/telegram/telegram_link_number_service.py,sha256=1_c-_QCRPTHYn3BmMElrBJqGG6vnoIst8CB-N42hazk,6862
|
|
155
158
|
xp/services/telegram/telegram_output_service.py,sha256=UaUv_14fR8o5K2PxQBXrCzx-Hohnk-gzbev_oLw_Clc,10799
|
|
156
|
-
xp/services/telegram/telegram_service.py,sha256=
|
|
159
|
+
xp/services/telegram/telegram_service.py,sha256=XrP1CPi0ckxoKBaNwLA6lo-TogWxXgmXDOsU4Xl8BlY,13237
|
|
157
160
|
xp/services/telegram/telegram_version_service.py,sha256=M5HdOTsLdcwo122FP-jW6R740ktLrtKf2TiMDVz23h8,10528
|
|
158
161
|
xp/utils/__init__.py,sha256=_avMF_UOkfR3tNeDIPqQ5odmbq5raKkaq1rZ9Cn1CJs,332
|
|
159
162
|
xp/utils/checksum.py,sha256=HDpiQxmdIedbCbZ4o_Box0i_Zig417BtCV_46ZyhiTk,1711
|
|
160
|
-
xp/utils/dependencies.py,sha256=
|
|
163
|
+
xp/utils/dependencies.py,sha256=1XDwIg3OsmLvOazMQ3qaktcsitYW8E400RxihNWgyt0,18894
|
|
161
164
|
xp/utils/event_helper.py,sha256=W-A_xmoXlpWZBbJH6qdaN50o3-XrwFsDgvAGMJDiAgo,1001
|
|
162
165
|
xp/utils/serialization.py,sha256=RWHHk86feaB4ZP7rjE4qOWK0900yg2joUBDkP76gfOY,4618
|
|
163
166
|
xp/utils/time_utils.py,sha256=dEyViDlAG9GWU-J3D_YVa-sGma6yiyyMTgN4h2x3PY4,3781
|
|
164
|
-
conson_xp-1.
|
|
167
|
+
conson_xp-1.7.0.dist-info/RECORD,,
|
xp/__init__.py
CHANGED
|
@@ -49,13 +49,22 @@ def conbus_download_msactiontable(
|
|
|
49
49
|
click.echo(progress, nl=False)
|
|
50
50
|
|
|
51
51
|
def on_finish(
|
|
52
|
-
action_table: Union[
|
|
52
|
+
action_table: Union[
|
|
53
|
+
Xp20MsActionTable, Xp24MsActionTable, Xp33MsActionTable, None
|
|
54
|
+
],
|
|
53
55
|
) -> None:
|
|
54
56
|
"""Handle successful completion of MS action table download.
|
|
55
57
|
|
|
56
58
|
Args:
|
|
57
|
-
action_table: Downloaded MS action table object.
|
|
59
|
+
action_table: Downloaded MS action table object or None if failed.
|
|
60
|
+
|
|
61
|
+
Raises:
|
|
62
|
+
Abort: If action table download failed.
|
|
58
63
|
"""
|
|
64
|
+
if action_table is None:
|
|
65
|
+
click.echo("Error: Failed to download MS action table")
|
|
66
|
+
raise click.Abort()
|
|
67
|
+
|
|
59
68
|
output = {
|
|
60
69
|
"serial_number": serial_number,
|
|
61
70
|
"xpmoduletype": xpmoduletype,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Action table utils."""
|
|
@@ -46,8 +46,9 @@ class Xp20MsActionTableSerializer:
|
|
|
46
46
|
input_channel, input_index, raw_bytes
|
|
47
47
|
)
|
|
48
48
|
|
|
49
|
+
encoded_data = nibbles(raw_bytes)
|
|
49
50
|
# Convert raw bytes to hex string with A-P encoding
|
|
50
|
-
return
|
|
51
|
+
return "AAAA" + encoded_data
|
|
51
52
|
|
|
52
53
|
@staticmethod
|
|
53
54
|
def from_data(msactiontable_rawdata: str) -> Xp20MsActionTable:
|
|
@@ -62,13 +63,20 @@ class Xp20MsActionTableSerializer:
|
|
|
62
63
|
Raises:
|
|
63
64
|
ValueError: If input length is not 64 characters
|
|
64
65
|
"""
|
|
65
|
-
|
|
66
|
+
raw_length = len(msactiontable_rawdata)
|
|
67
|
+
if raw_length < 68: # Minimum: 4 char prefix + 64 chars data
|
|
66
68
|
raise ValueError(
|
|
67
|
-
f"XP20 action table data must be
|
|
69
|
+
f"XP20 action table data must be 68 characters long, got {len(msactiontable_rawdata)}"
|
|
68
70
|
)
|
|
69
71
|
|
|
70
|
-
#
|
|
71
|
-
|
|
72
|
+
# Remove action table count prefix (first 4 characters: AAAA, AAAB, etc.)
|
|
73
|
+
data = msactiontable_rawdata[4:]
|
|
74
|
+
|
|
75
|
+
# Take first 64 chars (32 bytes) as per pseudocode
|
|
76
|
+
hex_data = data[:64]
|
|
77
|
+
|
|
78
|
+
# Convert hex string to bytes using deNibble (A-P encoding)
|
|
79
|
+
raw_bytes = de_nibbles(hex_data)
|
|
72
80
|
|
|
73
81
|
# Decode input channels
|
|
74
82
|
input_channels = []
|
|
@@ -159,23 +167,3 @@ class Xp20MsActionTableSerializer:
|
|
|
159
167
|
and_functions_byte |= 1 << bit_index
|
|
160
168
|
|
|
161
169
|
raw_bytes[AND_FUNCTIONS_INDEX + input_index] = and_functions_byte
|
|
162
|
-
|
|
163
|
-
@staticmethod
|
|
164
|
-
def from_telegrams(ms_telegrams: str) -> Xp20MsActionTable:
|
|
165
|
-
"""Legacy method for backward compatibility. Use from_data() instead.
|
|
166
|
-
|
|
167
|
-
Args:
|
|
168
|
-
ms_telegrams: Full telegram string
|
|
169
|
-
|
|
170
|
-
Returns:
|
|
171
|
-
Decoded XP20 action table
|
|
172
|
-
"""
|
|
173
|
-
# Extract data portion from telegram (skip header, take action table data)
|
|
174
|
-
# Based on XP24 pattern: telegram[16:84] gives us the 68-char data portion
|
|
175
|
-
# For XP20, we need 64 chars, so we take the first 64 chars after removing count
|
|
176
|
-
data_parts = ms_telegrams[16:84]
|
|
177
|
-
|
|
178
|
-
# Remove action table count (first 4 chars: AAAA, AAAB, etc.)
|
|
179
|
-
hex_data = data_parts[4:68] # Take 64 chars after count
|
|
180
|
-
|
|
181
|
-
return Xp20MsActionTableSerializer.from_data(hex_data)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from xp.models.actiontable.msactiontable_xp24 import InputAction, Xp24MsActionTable
|
|
4
4
|
from xp.models.telegram.input_action_type import InputActionType
|
|
5
5
|
from xp.models.telegram.timeparam_type import TimeParam
|
|
6
|
-
from xp.utils.serialization import de_nibbles
|
|
6
|
+
from xp.utils.serialization import de_nibbles, nibbles
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class Xp24MsActionTableSerializer:
|
|
@@ -17,11 +17,12 @@ class Xp24MsActionTableSerializer:
|
|
|
17
17
|
action_table: XP24 MS action table to serialize.
|
|
18
18
|
|
|
19
19
|
Returns:
|
|
20
|
-
Serialized action table data string.
|
|
20
|
+
Serialized action table data string (68 characters).
|
|
21
21
|
"""
|
|
22
|
-
|
|
22
|
+
# Build byte array for the action table (32 bytes total)
|
|
23
|
+
raw_bytes = bytearray()
|
|
23
24
|
|
|
24
|
-
# Encode all 4 input actions
|
|
25
|
+
# Encode all 4 input actions (2 bytes each = 8 bytes total)
|
|
25
26
|
input_actions = [
|
|
26
27
|
action_table.input1_action,
|
|
27
28
|
action_table.input2_action,
|
|
@@ -30,26 +31,24 @@ class Xp24MsActionTableSerializer:
|
|
|
30
31
|
]
|
|
31
32
|
|
|
32
33
|
for action in input_actions:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
]
|
|
49
|
-
)
|
|
34
|
+
raw_bytes.append(action.type.value)
|
|
35
|
+
raw_bytes.append(action.param.value)
|
|
36
|
+
|
|
37
|
+
# Add settings (5 bytes)
|
|
38
|
+
raw_bytes.append(0x01 if action_table.mutex12 else 0x00)
|
|
39
|
+
raw_bytes.append(0x01 if action_table.mutex34 else 0x00)
|
|
40
|
+
raw_bytes.append(action_table.mutual_deadtime)
|
|
41
|
+
raw_bytes.append(0x01 if action_table.curtain12 else 0x00)
|
|
42
|
+
raw_bytes.append(0x01 if action_table.curtain34 else 0x00)
|
|
43
|
+
|
|
44
|
+
# Add padding to reach 32 bytes (19 more bytes needed)
|
|
45
|
+
raw_bytes.extend([0x00] * 19)
|
|
46
|
+
|
|
47
|
+
# Encode to A-P nibbles (32 bytes -> 64 chars)
|
|
48
|
+
encoded_data = nibbles(bytes(raw_bytes))
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
return
|
|
50
|
+
# Prepend action table count "AAAA" (4 chars) -> total 68 chars
|
|
51
|
+
return "AAAA" + encoded_data
|
|
53
52
|
|
|
54
53
|
@staticmethod
|
|
55
54
|
def from_data(msactiontable_rawdata: str) -> Xp24MsActionTable:
|
|
@@ -66,7 +65,9 @@ class Xp24MsActionTableSerializer:
|
|
|
66
65
|
"""
|
|
67
66
|
raw_length = len(msactiontable_rawdata)
|
|
68
67
|
if raw_length != 68:
|
|
69
|
-
raise ValueError(
|
|
68
|
+
raise ValueError(
|
|
69
|
+
f"Msactiontable is not 68 bytes long ({raw_length}): {msactiontable_rawdata}"
|
|
70
|
+
)
|
|
70
71
|
|
|
71
72
|
# Remove action table count AAAA, AAAB .
|
|
72
73
|
data = msactiontable_rawdata[4:]
|
|
@@ -117,18 +118,3 @@ class Xp24MsActionTableSerializer:
|
|
|
117
118
|
param_type = TimeParam(param_id)
|
|
118
119
|
|
|
119
120
|
return InputAction(action_type, param_type)
|
|
120
|
-
|
|
121
|
-
@staticmethod
|
|
122
|
-
def from_telegrams(ms_telegrams: str) -> Xp24MsActionTable:
|
|
123
|
-
"""Legacy method for backward compatibility. Use from_data() instead.
|
|
124
|
-
|
|
125
|
-
Args:
|
|
126
|
-
ms_telegrams: Telegram data string.
|
|
127
|
-
|
|
128
|
-
Returns:
|
|
129
|
-
Deserialized XP24 MS action table.
|
|
130
|
-
"""
|
|
131
|
-
# For backward compatibility, assume full telegrams and extract data
|
|
132
|
-
data_parts = ms_telegrams[16:84]
|
|
133
|
-
|
|
134
|
-
return Xp24MsActionTableSerializer.from_data(data_parts)
|
|
@@ -6,7 +6,7 @@ from xp.models.actiontable.msactiontable_xp33 import (
|
|
|
6
6
|
Xp33Scene,
|
|
7
7
|
)
|
|
8
8
|
from xp.models.telegram.timeparam_type import TimeParam
|
|
9
|
-
from xp.utils.serialization import bits_to_byte, byte_to_bits, de_nibbles,
|
|
9
|
+
from xp.utils.serialization import bits_to_byte, byte_to_bits, de_nibbles, nibbles
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class Xp33MsActionTableSerializer:
|
|
@@ -96,13 +96,11 @@ class Xp33MsActionTableSerializer:
|
|
|
96
96
|
raw_bytes[24] = bits_to_byte(leading_edge_bits)
|
|
97
97
|
|
|
98
98
|
# Bytes 25-31 are padding (already 0)
|
|
99
|
-
|
|
100
99
|
# Convert to hex string using nibble encoding
|
|
101
|
-
|
|
102
|
-
for byte_val in raw_bytes:
|
|
103
|
-
hex_data += nibble(byte_val)
|
|
100
|
+
encoded_data = nibbles(raw_bytes)
|
|
104
101
|
|
|
105
|
-
|
|
102
|
+
# Convert raw bytes to hex string with A-P encoding
|
|
103
|
+
return "AAAA" + encoded_data
|
|
106
104
|
|
|
107
105
|
@staticmethod
|
|
108
106
|
def from_data(msactiontable_rawdata: str) -> Xp33MsActionTable:
|
|
@@ -239,18 +237,3 @@ class Xp33MsActionTableSerializer:
|
|
|
239
237
|
output3_level=output3_level,
|
|
240
238
|
time=time_param,
|
|
241
239
|
)
|
|
242
|
-
|
|
243
|
-
@staticmethod
|
|
244
|
-
def from_telegrams(ms_telegrams: str) -> Xp33MsActionTable:
|
|
245
|
-
"""Legacy method for backward compatibility. Use from_data() instead.
|
|
246
|
-
|
|
247
|
-
Args:
|
|
248
|
-
ms_telegrams: Telegram data string.
|
|
249
|
-
|
|
250
|
-
Returns:
|
|
251
|
-
Deserialized XP33 MS action table.
|
|
252
|
-
"""
|
|
253
|
-
# For backward compatibility, assume full telegrams and extract data
|
|
254
|
-
data_parts = ms_telegrams[16:152] # Adjusted for XP33 length
|
|
255
|
-
|
|
256
|
-
return Xp33MsActionTableSerializer.from_data(data_parts)
|
|
@@ -10,7 +10,7 @@ from xp.models.actiontable.actiontable import ActionTable
|
|
|
10
10
|
from xp.models.protocol.conbus_protocol import TelegramReceivedEvent
|
|
11
11
|
from xp.models.telegram.system_function import SystemFunction
|
|
12
12
|
from xp.models.telegram.telegram_type import TelegramType
|
|
13
|
-
from xp.services.
|
|
13
|
+
from xp.services.actiontable.actiontable_serializer import ActionTableSerializer
|
|
14
14
|
from xp.services.protocol import ConbusProtocol
|
|
15
15
|
from xp.services.telegram.telegram_service import TelegramService
|
|
16
16
|
|
|
@@ -12,13 +12,13 @@ from xp.models.actiontable.msactiontable_xp33 import Xp33MsActionTable
|
|
|
12
12
|
from xp.models.protocol.conbus_protocol import TelegramReceivedEvent
|
|
13
13
|
from xp.models.telegram.system_function import SystemFunction
|
|
14
14
|
from xp.models.telegram.telegram_type import TelegramType
|
|
15
|
-
from xp.services.
|
|
15
|
+
from xp.services.actiontable.msactiontable_xp20_serializer import (
|
|
16
16
|
Xp20MsActionTableSerializer,
|
|
17
17
|
)
|
|
18
|
-
from xp.services.
|
|
18
|
+
from xp.services.actiontable.msactiontable_xp24_serializer import (
|
|
19
19
|
Xp24MsActionTableSerializer,
|
|
20
20
|
)
|
|
21
|
-
from xp.services.
|
|
21
|
+
from xp.services.actiontable.msactiontable_xp33_serializer import (
|
|
22
22
|
Xp33MsActionTableSerializer,
|
|
23
23
|
)
|
|
24
24
|
from xp.services.protocol import ConbusProtocol
|
|
@@ -74,7 +74,8 @@ class MsActionTableService(ConbusProtocol):
|
|
|
74
74
|
self.error_callback: Optional[Callable[[str], None]] = None
|
|
75
75
|
self.finish_callback: Optional[
|
|
76
76
|
Callable[
|
|
77
|
-
[Union[Xp20MsActionTable, Xp24MsActionTable, Xp33MsActionTable]],
|
|
77
|
+
[Union[Xp20MsActionTable, Xp24MsActionTable, Xp33MsActionTable, None]],
|
|
78
|
+
None,
|
|
78
79
|
]
|
|
79
80
|
] = None
|
|
80
81
|
self.msactiontable_data: list[str] = []
|
|
@@ -114,17 +115,32 @@ class MsActionTableService(ConbusProtocol):
|
|
|
114
115
|
self.logger.debug("Not a reply response")
|
|
115
116
|
return
|
|
116
117
|
|
|
117
|
-
reply_telegram = self.telegram_service.parse_reply_telegram(
|
|
118
|
+
reply_telegram = self.telegram_service.parse_reply_telegram(
|
|
119
|
+
telegram_received.frame
|
|
120
|
+
)
|
|
118
121
|
if reply_telegram.system_function not in (
|
|
119
122
|
SystemFunction.MSACTIONTABLE,
|
|
123
|
+
SystemFunction.ACK,
|
|
124
|
+
SystemFunction.NAK,
|
|
120
125
|
SystemFunction.EOF,
|
|
121
126
|
):
|
|
122
127
|
self.logger.debug("Not a msactiontable response")
|
|
123
128
|
return
|
|
124
129
|
|
|
125
|
-
if reply_telegram.system_function == SystemFunction.
|
|
126
|
-
self.logger.debug("
|
|
127
|
-
|
|
130
|
+
if reply_telegram.system_function == SystemFunction.ACK:
|
|
131
|
+
self.logger.debug("Received ACK")
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
if reply_telegram.system_function == SystemFunction.NAK:
|
|
135
|
+
self.logger.debug("Received NAK")
|
|
136
|
+
self.failed("Received NAK")
|
|
137
|
+
return
|
|
138
|
+
|
|
139
|
+
if reply_telegram.system_function == SystemFunction.MSACTIONTABLE:
|
|
140
|
+
self.logger.debug("Received MSACTIONTABLE")
|
|
141
|
+
self.msactiontable_data.extend(
|
|
142
|
+
(reply_telegram.data, reply_telegram.data_value)
|
|
143
|
+
)
|
|
128
144
|
if self.progress_callback:
|
|
129
145
|
self.progress_callback(".")
|
|
130
146
|
|
|
@@ -137,11 +153,14 @@ class MsActionTableService(ConbusProtocol):
|
|
|
137
153
|
return
|
|
138
154
|
|
|
139
155
|
if reply_telegram.system_function == SystemFunction.EOF:
|
|
156
|
+
self.logger.debug("Received EOF")
|
|
140
157
|
all_data = "".join(self.msactiontable_data)
|
|
141
158
|
# Deserialize from received data
|
|
142
159
|
msactiontable = self.serializer.from_data(all_data)
|
|
143
|
-
|
|
144
|
-
|
|
160
|
+
self.succeed(msactiontable)
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
self.logger.debug("Invalid msactiontable response")
|
|
145
164
|
|
|
146
165
|
def failed(self, message: str) -> None:
|
|
147
166
|
"""Handle failed connection event.
|
|
@@ -152,6 +171,20 @@ class MsActionTableService(ConbusProtocol):
|
|
|
152
171
|
self.logger.debug(f"Failed: {message}")
|
|
153
172
|
if self.error_callback:
|
|
154
173
|
self.error_callback(message)
|
|
174
|
+
self._stop_reactor()
|
|
175
|
+
|
|
176
|
+
def succeed(
|
|
177
|
+
self,
|
|
178
|
+
msactiontable: Union[Xp20MsActionTable, Xp24MsActionTable, Xp33MsActionTable],
|
|
179
|
+
) -> None:
|
|
180
|
+
"""Handle succeed connection event.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
msactiontable: result.
|
|
184
|
+
"""
|
|
185
|
+
if self.finish_callback:
|
|
186
|
+
self.finish_callback(msactiontable)
|
|
187
|
+
self._stop_reactor()
|
|
155
188
|
|
|
156
189
|
def start(
|
|
157
190
|
self,
|
|
@@ -160,7 +193,7 @@ class MsActionTableService(ConbusProtocol):
|
|
|
160
193
|
progress_callback: Callable[[str], None],
|
|
161
194
|
error_callback: Callable[[str], None],
|
|
162
195
|
finish_callback: Callable[
|
|
163
|
-
[Union[Xp20MsActionTable, Xp24MsActionTable, Xp33MsActionTable]], None
|
|
196
|
+
[Union[Xp20MsActionTable, Xp24MsActionTable, Xp33MsActionTable, None]], None
|
|
164
197
|
],
|
|
165
198
|
timeout_seconds: Optional[float] = None,
|
|
166
199
|
) -> None:
|
|
@@ -230,13 +230,11 @@ class ConbusProtocol(protocol.Protocol, protocol.ClientFactory):
|
|
|
230
230
|
self.timeout_call = self.reactor.callLater(
|
|
231
231
|
self.timeout_seconds, self._on_timeout
|
|
232
232
|
)
|
|
233
|
-
self.logger.debug(f"Timeout set for {self.timeout_seconds} seconds")
|
|
234
233
|
|
|
235
234
|
def _cancel_timeout(self) -> None:
|
|
236
235
|
"""Cancel the inactivity timeout."""
|
|
237
236
|
if self.timeout_call and self.timeout_call.active():
|
|
238
237
|
self.timeout_call.cancel()
|
|
239
|
-
self.logger.debug("Timeout cancelled")
|
|
240
238
|
|
|
241
239
|
def _on_timeout(self) -> None:
|
|
242
240
|
"""Handle inactivity timeout expiration."""
|
|
@@ -7,7 +7,7 @@ containing common functionality like module type response generation.
|
|
|
7
7
|
import logging
|
|
8
8
|
import threading
|
|
9
9
|
from abc import ABC
|
|
10
|
-
from typing import Optional
|
|
10
|
+
from typing import Any, Optional
|
|
11
11
|
|
|
12
12
|
from xp.models import ModuleTypeCode
|
|
13
13
|
from xp.models.telegram.datapoint_type import DataPointType
|
|
@@ -46,6 +46,9 @@ class BaseServerService(ABC):
|
|
|
46
46
|
self.telegram_buffer: list[str] = []
|
|
47
47
|
self.telegram_buffer_lock = threading.Lock() # Lock for socket set
|
|
48
48
|
|
|
49
|
+
# MsActionTable download state (None, "ack_sent", "data_sent")
|
|
50
|
+
self.msactiontable_download_state: Optional[str] = None
|
|
51
|
+
|
|
49
52
|
def generate_datapoint_type_response(
|
|
50
53
|
self, datapoint_type: DataPointType
|
|
51
54
|
) -> Optional[str]:
|
|
@@ -151,6 +154,76 @@ class BaseServerService(ABC):
|
|
|
151
154
|
|
|
152
155
|
return None
|
|
153
156
|
|
|
157
|
+
def _get_msactiontable_serializer(self) -> Optional[Any]:
|
|
158
|
+
"""Get the MsActionTable serializer for this device.
|
|
159
|
+
|
|
160
|
+
Subclasses should override this to return their specific serializer.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
The serializer instance, or None if not supported.
|
|
164
|
+
"""
|
|
165
|
+
return None
|
|
166
|
+
|
|
167
|
+
def _get_msactiontable(self) -> Optional[Any]:
|
|
168
|
+
"""Get the MsActionTable for this device.
|
|
169
|
+
|
|
170
|
+
Subclasses should override this to return their msactiontable instance.
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
The msactiontable instance, or None if not supported.
|
|
174
|
+
"""
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
def _handle_download_msactiontable_request(
|
|
178
|
+
self, request: SystemTelegram
|
|
179
|
+
) -> Optional[str]:
|
|
180
|
+
"""Handle F13D - DOWNLOAD_MSACTIONTABLE request.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
request: The system telegram request to process.
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
ACK telegram if request is valid, NAK otherwise.
|
|
187
|
+
"""
|
|
188
|
+
serializer = self._get_msactiontable_serializer()
|
|
189
|
+
msactiontable = self._get_msactiontable()
|
|
190
|
+
|
|
191
|
+
# Only handle if serializer and msactiontable are available
|
|
192
|
+
if not serializer or msactiontable is None:
|
|
193
|
+
return None
|
|
194
|
+
|
|
195
|
+
# Send ACK and queue data telegram
|
|
196
|
+
ack_data = self._build_response_telegram(f"R{self.serial_number}F18D") # ACK
|
|
197
|
+
|
|
198
|
+
# Send MsActionTable data
|
|
199
|
+
encoded_data = serializer.to_data(msactiontable)
|
|
200
|
+
data_telegram = self._build_response_telegram(
|
|
201
|
+
f"R{self.serial_number}F17D{encoded_data}"
|
|
202
|
+
)
|
|
203
|
+
self.msactiontable_download_state = "data_sent"
|
|
204
|
+
|
|
205
|
+
# Return ACK and TABLE
|
|
206
|
+
return ack_data + data_telegram
|
|
207
|
+
|
|
208
|
+
def _handle_download_msactiontable_ack_request(
|
|
209
|
+
self, _request: SystemTelegram
|
|
210
|
+
) -> Optional[str]:
|
|
211
|
+
"""Handle MsActionTable download ACK protocol.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
_request: The system telegram request (unused, kept for signature consistency).
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
Data telegram, EOF telegram, or NAK if state is invalid.
|
|
218
|
+
"""
|
|
219
|
+
if self.msactiontable_download_state == "data_sent":
|
|
220
|
+
# Send EOF
|
|
221
|
+
eof_telegram = self._build_response_telegram(f"R{self.serial_number}F16D")
|
|
222
|
+
self.msactiontable_download_state = None
|
|
223
|
+
return eof_telegram
|
|
224
|
+
|
|
225
|
+
return self._build_response_telegram(f"R{self.serial_number}F19D") # NAK
|
|
226
|
+
|
|
154
227
|
def process_system_telegram(self, request: SystemTelegram) -> Optional[str]:
|
|
155
228
|
"""Template method for processing system telegrams.
|
|
156
229
|
|
|
@@ -177,6 +250,15 @@ class BaseServerService(ABC):
|
|
|
177
250
|
elif request.system_function == SystemFunction.ACTION:
|
|
178
251
|
return self._handle_action_request(request)
|
|
179
252
|
|
|
253
|
+
elif request.system_function == SystemFunction.DOWNLOAD_MSACTIONTABLE:
|
|
254
|
+
return self._handle_download_msactiontable_request(request)
|
|
255
|
+
|
|
256
|
+
elif (
|
|
257
|
+
request.system_function == SystemFunction.ACK
|
|
258
|
+
and self.msactiontable_download_state
|
|
259
|
+
):
|
|
260
|
+
return self._handle_download_msactiontable_ack_request(request)
|
|
261
|
+
|
|
180
262
|
self.logger.warning(f"Unhandled {self.device_type} request: {request}")
|
|
181
263
|
return None
|
|
182
264
|
|
|
@@ -278,11 +360,7 @@ class BaseServerService(ABC):
|
|
|
278
360
|
Returns:
|
|
279
361
|
List of telegram strings from the buffer. The buffer is cleared after collection.
|
|
280
362
|
"""
|
|
281
|
-
self.logger.debug(
|
|
282
|
-
f"Collecting {self.serial_number} telegrams from buffer: {len(self.telegram_buffer)}"
|
|
283
|
-
)
|
|
284
363
|
with self.telegram_buffer_lock:
|
|
285
364
|
result = self.telegram_buffer.copy()
|
|
286
|
-
self.logger.debug(f"Resetting {self.serial_number} buffer")
|
|
287
365
|
self.telegram_buffer.clear()
|
|
288
366
|
return result
|
|
@@ -8,6 +8,7 @@ from typing import Dict, Optional
|
|
|
8
8
|
|
|
9
9
|
from xp.models import ModuleTypeCode
|
|
10
10
|
from xp.models.telegram.system_telegram import SystemTelegram
|
|
11
|
+
from xp.services.actiontable.msactiontable_serializer import MsActionTableSerializer
|
|
11
12
|
from xp.services.server.base_server_service import BaseServerService
|
|
12
13
|
|
|
13
14
|
|
|
@@ -25,11 +26,18 @@ class CP20ServerService(BaseServerService):
|
|
|
25
26
|
and implements CP20 telegram format.
|
|
26
27
|
"""
|
|
27
28
|
|
|
28
|
-
def __init__(
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
serial_number: str,
|
|
32
|
+
_variant: str = "",
|
|
33
|
+
_msactiontable_serializer: Optional[MsActionTableSerializer] = None,
|
|
34
|
+
):
|
|
29
35
|
"""Initialize CP20 server service.
|
|
30
36
|
|
|
31
37
|
Args:
|
|
32
38
|
serial_number: The device serial number.
|
|
39
|
+
_variant: Reserved parameter for consistency (unused).
|
|
40
|
+
_msactiontable_serializer: Generic MsActionTable serializer (unused).
|
|
33
41
|
"""
|
|
34
42
|
super().__init__(serial_number)
|
|
35
43
|
self.device_type = "CP20"
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Device Service Factory for creating device instances.
|
|
2
|
+
|
|
3
|
+
This module provides a factory for creating device service instances
|
|
4
|
+
with proper dependency injection of serializers.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from xp.services.actiontable.msactiontable_serializer import MsActionTableSerializer
|
|
8
|
+
from xp.services.actiontable.msactiontable_xp20_serializer import (
|
|
9
|
+
Xp20MsActionTableSerializer,
|
|
10
|
+
)
|
|
11
|
+
from xp.services.actiontable.msactiontable_xp24_serializer import (
|
|
12
|
+
Xp24MsActionTableSerializer,
|
|
13
|
+
)
|
|
14
|
+
from xp.services.actiontable.msactiontable_xp33_serializer import (
|
|
15
|
+
Xp33MsActionTableSerializer,
|
|
16
|
+
)
|
|
17
|
+
from xp.services.server.base_server_service import BaseServerService
|
|
18
|
+
from xp.services.server.cp20_server_service import CP20ServerService
|
|
19
|
+
from xp.services.server.xp20_server_service import XP20ServerService
|
|
20
|
+
from xp.services.server.xp24_server_service import XP24ServerService
|
|
21
|
+
from xp.services.server.xp33_server_service import XP33ServerService
|
|
22
|
+
from xp.services.server.xp130_server_service import XP130ServerService
|
|
23
|
+
from xp.services.server.xp230_server_service import XP230ServerService
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DeviceServiceFactory:
|
|
27
|
+
"""Factory for creating device service instances.
|
|
28
|
+
|
|
29
|
+
Encapsulates device creation logic and handles serializer injection
|
|
30
|
+
for different device types.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
xp20ms_serializer: Xp20MsActionTableSerializer,
|
|
36
|
+
xp24ms_serializer: Xp24MsActionTableSerializer,
|
|
37
|
+
xp33ms_serializer: Xp33MsActionTableSerializer,
|
|
38
|
+
ms_serializer: MsActionTableSerializer,
|
|
39
|
+
):
|
|
40
|
+
"""Initialize device service factory.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
xp20ms_serializer: XP20 MsActionTable serializer (injected via DI).
|
|
44
|
+
xp24ms_serializer: XP24 MsActionTable serializer (injected via DI).
|
|
45
|
+
xp33ms_serializer: XP33 MsActionTable serializer (injected via DI).
|
|
46
|
+
ms_serializer: Generic MsActionTable serializer (injected via DI).
|
|
47
|
+
"""
|
|
48
|
+
self.xp20ms_serializer = xp20ms_serializer
|
|
49
|
+
self.xp24ms_serializer = xp24ms_serializer
|
|
50
|
+
self.xp33ms_serializer = xp33ms_serializer
|
|
51
|
+
self.ms_serializer = ms_serializer
|
|
52
|
+
|
|
53
|
+
def create_device(self, module_type: str, serial_number: str) -> BaseServerService:
|
|
54
|
+
"""Create device instance for given module type.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
module_type: Module type code (e.g., "XP20", "XP33LR").
|
|
58
|
+
serial_number: Device serial number.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Device service instance configured with appropriate serializer.
|
|
62
|
+
|
|
63
|
+
Raises:
|
|
64
|
+
ValueError: If module_type is unknown or unsupported.
|
|
65
|
+
"""
|
|
66
|
+
# Map module types to their constructors and parameters
|
|
67
|
+
if module_type == "CP20":
|
|
68
|
+
return CP20ServerService(serial_number, "CP20", self.ms_serializer)
|
|
69
|
+
|
|
70
|
+
elif module_type == "XP24":
|
|
71
|
+
return XP24ServerService(serial_number, "XP24", self.xp24ms_serializer)
|
|
72
|
+
|
|
73
|
+
elif module_type == "XP33":
|
|
74
|
+
return XP33ServerService(serial_number, "XP33", self.xp33ms_serializer)
|
|
75
|
+
|
|
76
|
+
elif module_type == "XP33LR":
|
|
77
|
+
return XP33ServerService(serial_number, "XP33LR", self.xp33ms_serializer)
|
|
78
|
+
|
|
79
|
+
elif module_type == "XP33LED":
|
|
80
|
+
return XP33ServerService(serial_number, "XP33LED", self.xp33ms_serializer)
|
|
81
|
+
|
|
82
|
+
elif module_type == "XP20":
|
|
83
|
+
return XP20ServerService(serial_number, "XP20", self.xp20ms_serializer)
|
|
84
|
+
|
|
85
|
+
elif module_type == "XP130":
|
|
86
|
+
return XP130ServerService(serial_number, "XP130", self.ms_serializer)
|
|
87
|
+
|
|
88
|
+
elif module_type == "XP230":
|
|
89
|
+
return XP230ServerService(serial_number, "XP230", self.ms_serializer)
|
|
90
|
+
|
|
91
|
+
else:
|
|
92
|
+
raise ValueError(
|
|
93
|
+
f"Unknown device type '{module_type}' for serial {serial_number}"
|
|
94
|
+
)
|
|
@@ -8,19 +8,14 @@ import logging
|
|
|
8
8
|
import socket
|
|
9
9
|
import threading
|
|
10
10
|
from pathlib import Path
|
|
11
|
-
from typing import Dict, List, Optional
|
|
11
|
+
from typing import Dict, List, Optional
|
|
12
12
|
|
|
13
13
|
from xp.models.homekit.homekit_conson_config import (
|
|
14
14
|
ConsonModuleConfig,
|
|
15
15
|
ConsonModuleListConfig,
|
|
16
16
|
)
|
|
17
17
|
from xp.services.server.base_server_service import BaseServerService
|
|
18
|
-
from xp.services.server.
|
|
19
|
-
from xp.services.server.xp20_server_service import XP20ServerService
|
|
20
|
-
from xp.services.server.xp24_server_service import XP24ServerService
|
|
21
|
-
from xp.services.server.xp33_server_service import XP33ServerService
|
|
22
|
-
from xp.services.server.xp130_server_service import XP130ServerService
|
|
23
|
-
from xp.services.server.xp230_server_service import XP230ServerService
|
|
18
|
+
from xp.services.server.device_service_factory import DeviceServiceFactory
|
|
24
19
|
from xp.services.telegram.telegram_discover_service import TelegramDiscoverService
|
|
25
20
|
from xp.services.telegram.telegram_service import TelegramService
|
|
26
21
|
|
|
@@ -43,6 +38,7 @@ class ServerService:
|
|
|
43
38
|
self,
|
|
44
39
|
telegram_service: TelegramService,
|
|
45
40
|
discover_service: TelegramDiscoverService,
|
|
41
|
+
device_factory: DeviceServiceFactory,
|
|
46
42
|
config_path: str = "server.yml",
|
|
47
43
|
port: int = 10001,
|
|
48
44
|
):
|
|
@@ -51,25 +47,21 @@ class ServerService:
|
|
|
51
47
|
Args:
|
|
52
48
|
telegram_service: Service for parsing system telegrams.
|
|
53
49
|
discover_service: Service for handling discover requests.
|
|
50
|
+
device_factory: Factory for creating device service instances (injected via DI).
|
|
54
51
|
config_path: Path to the server configuration file.
|
|
55
52
|
port: TCP port to listen on.
|
|
56
53
|
"""
|
|
57
54
|
self.telegram_service = telegram_service
|
|
58
55
|
self.discover_service = discover_service
|
|
56
|
+
self.device_factory = device_factory
|
|
59
57
|
self.config_path = config_path
|
|
60
58
|
self.port = port
|
|
61
59
|
self.server_socket: Optional[socket.socket] = None
|
|
62
60
|
self.is_running = False
|
|
63
61
|
self.devices: List[ConsonModuleConfig] = []
|
|
64
|
-
self.device_services: Dict[
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
BaseServerService,
|
|
68
|
-
XP33ServerService,
|
|
69
|
-
XP20ServerService,
|
|
70
|
-
XP130ServerService,
|
|
71
|
-
],
|
|
72
|
-
] = {} # serial -> device service instance
|
|
62
|
+
self.device_services: Dict[str, BaseServerService] = (
|
|
63
|
+
{}
|
|
64
|
+
) # serial -> device service instance
|
|
73
65
|
|
|
74
66
|
# Collect device buffer to broadcast to client
|
|
75
67
|
self.collector_thread: Optional[threading.Thread] = (
|
|
@@ -112,44 +104,14 @@ class ServerService:
|
|
|
112
104
|
serial_number = module.serial_number
|
|
113
105
|
|
|
114
106
|
try:
|
|
107
|
+
# Use factory to create device instance
|
|
108
|
+
self.device_services[serial_number] = self.device_factory.create_device(
|
|
109
|
+
module_type, serial_number
|
|
110
|
+
)
|
|
115
111
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
serial_number
|
|
120
|
-
)
|
|
121
|
-
if module_type == "XP24":
|
|
122
|
-
self.device_services[serial_number] = XP24ServerService(
|
|
123
|
-
serial_number
|
|
124
|
-
)
|
|
125
|
-
elif module_type == "XP33":
|
|
126
|
-
self.device_services[serial_number] = XP33ServerService(
|
|
127
|
-
serial_number, "XP33"
|
|
128
|
-
)
|
|
129
|
-
elif module_type == "XP33LR":
|
|
130
|
-
self.device_services[serial_number] = XP33ServerService(
|
|
131
|
-
serial_number, "XP33LR"
|
|
132
|
-
)
|
|
133
|
-
elif module_type == "XP33LED":
|
|
134
|
-
self.device_services[serial_number] = XP33ServerService(
|
|
135
|
-
serial_number, "XP33LED"
|
|
136
|
-
)
|
|
137
|
-
elif module_type == "XP20":
|
|
138
|
-
self.device_services[serial_number] = XP20ServerService(
|
|
139
|
-
serial_number
|
|
140
|
-
)
|
|
141
|
-
elif module_type == "XP130":
|
|
142
|
-
self.device_services[serial_number] = XP130ServerService(
|
|
143
|
-
serial_number
|
|
144
|
-
)
|
|
145
|
-
elif module_type == "XP230":
|
|
146
|
-
self.device_services[serial_number] = XP230ServerService(
|
|
147
|
-
serial_number
|
|
148
|
-
)
|
|
149
|
-
else:
|
|
150
|
-
self.logger.warning(
|
|
151
|
-
f"Unknown device type '{module_type}' for serial {serial_number}"
|
|
152
|
-
)
|
|
112
|
+
except ValueError as e:
|
|
113
|
+
# Factory raises ValueError for unknown device types
|
|
114
|
+
self.logger.warning(str(e))
|
|
153
115
|
|
|
154
116
|
except Exception as e:
|
|
155
117
|
self.logger.error(
|
|
@@ -247,14 +209,11 @@ class ServerService:
|
|
|
247
209
|
self.logger.debug(f"Sent buffer to {client_address}")
|
|
248
210
|
|
|
249
211
|
# Receive data from client
|
|
250
|
-
self.logger.debug(f"Receiving data {client_address}")
|
|
251
212
|
data = None
|
|
252
213
|
try:
|
|
253
214
|
data = client_socket.recv(1024)
|
|
254
215
|
except socket.timeout:
|
|
255
|
-
|
|
256
|
-
f"Timeout receiving data {client_address} ({timeout})"
|
|
257
|
-
)
|
|
216
|
+
pass
|
|
258
217
|
finally:
|
|
259
218
|
timeout -= 1
|
|
260
219
|
|
|
@@ -459,9 +418,6 @@ class ServerService:
|
|
|
459
418
|
self.logger.info("Collector thread starting")
|
|
460
419
|
|
|
461
420
|
while True:
|
|
462
|
-
self.logger.debug(
|
|
463
|
-
f"Collector thread collecting ({len(self.collector_buffer)})"
|
|
464
|
-
)
|
|
465
421
|
collected = 0
|
|
466
422
|
for device_service in self.device_services.values():
|
|
467
423
|
telegram_buffer = device_service.collect_telegram_buffer()
|
|
@@ -469,5 +425,4 @@ class ServerService:
|
|
|
469
425
|
collected += len(telegram_buffer)
|
|
470
426
|
|
|
471
427
|
# Wait a bit before checking again
|
|
472
|
-
self.logger.debug(f"Collector thread collected ({collected})")
|
|
473
428
|
self.collector_stop_event.wait(timeout=1)
|
|
@@ -5,9 +5,10 @@ including response generation and device configuration handling.
|
|
|
5
5
|
XP130 is an Ethernet/TCPIP interface module.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
from typing import Dict
|
|
8
|
+
from typing import Dict, Optional
|
|
9
9
|
|
|
10
10
|
from xp.models import ModuleTypeCode
|
|
11
|
+
from xp.services.actiontable.msactiontable_serializer import MsActionTableSerializer
|
|
11
12
|
from xp.services.server.base_server_service import BaseServerService
|
|
12
13
|
|
|
13
14
|
|
|
@@ -25,11 +26,18 @@ class XP130ServerService(BaseServerService):
|
|
|
25
26
|
and implements XP130 telegram format for Ethernet/TCPIP interface module.
|
|
26
27
|
"""
|
|
27
28
|
|
|
28
|
-
def __init__(
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
serial_number: str,
|
|
32
|
+
_variant: str = "",
|
|
33
|
+
_msactiontable_serializer: Optional[MsActionTableSerializer] = None,
|
|
34
|
+
):
|
|
29
35
|
"""Initialize XP130 server service.
|
|
30
36
|
|
|
31
37
|
Args:
|
|
32
38
|
serial_number: The device serial number.
|
|
39
|
+
_variant: Reserved parameter for consistency (unused).
|
|
40
|
+
_msactiontable_serializer: Generic MsActionTable serializer (unused).
|
|
33
41
|
"""
|
|
34
42
|
super().__init__(serial_number)
|
|
35
43
|
self.device_type = "XP130"
|
|
@@ -4,9 +4,13 @@ This service provides XP20-specific device emulation functionality,
|
|
|
4
4
|
including response generation and device configuration handling.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from typing import Dict
|
|
7
|
+
from typing import Dict, Optional
|
|
8
8
|
|
|
9
9
|
from xp.models import ModuleTypeCode
|
|
10
|
+
from xp.models.actiontable.msactiontable_xp20 import Xp20MsActionTable
|
|
11
|
+
from xp.services.actiontable.msactiontable_xp20_serializer import (
|
|
12
|
+
Xp20MsActionTableSerializer,
|
|
13
|
+
)
|
|
10
14
|
from xp.services.server.base_server_service import BaseServerService
|
|
11
15
|
|
|
12
16
|
|
|
@@ -24,17 +28,55 @@ class XP20ServerService(BaseServerService):
|
|
|
24
28
|
and implements XP20 telegram format.
|
|
25
29
|
"""
|
|
26
30
|
|
|
27
|
-
def __init__(
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
serial_number: str,
|
|
34
|
+
_variant: str = "",
|
|
35
|
+
msactiontable_serializer: Optional[Xp20MsActionTableSerializer] = None,
|
|
36
|
+
):
|
|
28
37
|
"""Initialize XP20 server service.
|
|
29
38
|
|
|
30
39
|
Args:
|
|
31
40
|
serial_number: The device serial number.
|
|
41
|
+
_variant: Reserved parameter for consistency (unused).
|
|
42
|
+
msactiontable_serializer: MsActionTable serializer (injected via DI).
|
|
32
43
|
"""
|
|
33
44
|
super().__init__(serial_number)
|
|
34
45
|
self.device_type = "XP20"
|
|
35
46
|
self.module_type_code = ModuleTypeCode.XP20 # XP20 module type from registry
|
|
36
47
|
self.firmware_version = "XP20_V0.01.05"
|
|
37
48
|
|
|
49
|
+
# MsActionTable support
|
|
50
|
+
self.msactiontable_serializer = (
|
|
51
|
+
msactiontable_serializer or Xp20MsActionTableSerializer()
|
|
52
|
+
)
|
|
53
|
+
self.msactiontable = self._get_default_msactiontable()
|
|
54
|
+
|
|
55
|
+
def _get_msactiontable_serializer(self) -> Optional[Xp20MsActionTableSerializer]:
|
|
56
|
+
"""Get the MsActionTable serializer for XP20.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
The XP20 MsActionTable serializer instance.
|
|
60
|
+
"""
|
|
61
|
+
return self.msactiontable_serializer
|
|
62
|
+
|
|
63
|
+
def _get_msactiontable(self) -> Optional[Xp20MsActionTable]:
|
|
64
|
+
"""Get the MsActionTable for XP20.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
The XP20 MsActionTable instance.
|
|
68
|
+
"""
|
|
69
|
+
return self.msactiontable
|
|
70
|
+
|
|
71
|
+
def _get_default_msactiontable(self) -> Xp20MsActionTable:
|
|
72
|
+
"""Generate default MsActionTable configuration.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Default XP20 MsActionTable with all inputs unconfigured.
|
|
76
|
+
"""
|
|
77
|
+
# All inputs unconfigured (all flags False, AND functions empty)
|
|
78
|
+
return Xp20MsActionTable()
|
|
79
|
+
|
|
38
80
|
def get_device_info(self) -> Dict:
|
|
39
81
|
"""Get XP20 device information.
|
|
40
82
|
|
|
@@ -4,9 +4,10 @@ This service provides XP230-specific device emulation functionality,
|
|
|
4
4
|
including response generation and device configuration handling.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from typing import Dict
|
|
7
|
+
from typing import Dict, Optional
|
|
8
8
|
|
|
9
9
|
from xp.models import ModuleTypeCode
|
|
10
|
+
from xp.services.actiontable.msactiontable_serializer import MsActionTableSerializer
|
|
10
11
|
from xp.services.server.base_server_service import BaseServerService
|
|
11
12
|
|
|
12
13
|
|
|
@@ -24,11 +25,18 @@ class XP230ServerService(BaseServerService):
|
|
|
24
25
|
and implements XP230 telegram format.
|
|
25
26
|
"""
|
|
26
27
|
|
|
27
|
-
def __init__(
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
serial_number: str,
|
|
31
|
+
_variant: str = "",
|
|
32
|
+
_msactiontable_serializer: Optional[MsActionTableSerializer] = None,
|
|
33
|
+
):
|
|
28
34
|
"""Initialize XP230 server service.
|
|
29
35
|
|
|
30
36
|
Args:
|
|
31
37
|
serial_number: The device serial number.
|
|
38
|
+
_variant: Reserved parameter for consistency (unused).
|
|
39
|
+
_msactiontable_serializer: Generic MsActionTable serializer (unused).
|
|
32
40
|
"""
|
|
33
41
|
super().__init__(serial_number)
|
|
34
42
|
self.device_type = "XP230"
|
|
@@ -7,9 +7,15 @@ including response generation and device configuration handling.
|
|
|
7
7
|
from typing import Dict, Optional
|
|
8
8
|
|
|
9
9
|
from xp.models import ModuleTypeCode
|
|
10
|
+
from xp.models.actiontable.msactiontable_xp24 import InputAction, Xp24MsActionTable
|
|
10
11
|
from xp.models.telegram.datapoint_type import DataPointType
|
|
12
|
+
from xp.models.telegram.input_action_type import InputActionType
|
|
11
13
|
from xp.models.telegram.system_function import SystemFunction
|
|
12
14
|
from xp.models.telegram.system_telegram import SystemTelegram
|
|
15
|
+
from xp.models.telegram.timeparam_type import TimeParam
|
|
16
|
+
from xp.services.actiontable.msactiontable_xp24_serializer import (
|
|
17
|
+
Xp24MsActionTableSerializer,
|
|
18
|
+
)
|
|
13
19
|
from xp.services.server.base_server_service import BaseServerService
|
|
14
20
|
|
|
15
21
|
|
|
@@ -37,11 +43,18 @@ class XP24ServerService(BaseServerService):
|
|
|
37
43
|
and implements XP24 telegram format.
|
|
38
44
|
"""
|
|
39
45
|
|
|
40
|
-
def __init__(
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
serial_number: str,
|
|
49
|
+
_variant: str = "",
|
|
50
|
+
msactiontable_serializer: Optional[Xp24MsActionTableSerializer] = None,
|
|
51
|
+
):
|
|
41
52
|
"""Initialize XP24 server service.
|
|
42
53
|
|
|
43
54
|
Args:
|
|
44
55
|
serial_number: The device serial number.
|
|
56
|
+
_variant: Reserved parameter for consistency (unused).
|
|
57
|
+
msactiontable_serializer: MsActionTable serializer (injected via DI).
|
|
45
58
|
"""
|
|
46
59
|
super().__init__(serial_number)
|
|
47
60
|
self.device_type = "XP24"
|
|
@@ -53,6 +66,12 @@ class XP24ServerService(BaseServerService):
|
|
|
53
66
|
self.output_2: XP24Output = XP24Output()
|
|
54
67
|
self.output_3: XP24Output = XP24Output()
|
|
55
68
|
|
|
69
|
+
# MsActionTable support
|
|
70
|
+
self.msactiontable_serializer = (
|
|
71
|
+
msactiontable_serializer or Xp24MsActionTableSerializer()
|
|
72
|
+
)
|
|
73
|
+
self.msactiontable = self._get_default_msactiontable()
|
|
74
|
+
|
|
56
75
|
def _handle_device_specific_action_request(
|
|
57
76
|
self, request: SystemTelegram
|
|
58
77
|
) -> Optional[str]:
|
|
@@ -175,6 +194,40 @@ class XP24ServerService(BaseServerService):
|
|
|
175
194
|
f"{1 if self.output_3.state else 0}"
|
|
176
195
|
)
|
|
177
196
|
|
|
197
|
+
def _get_msactiontable_serializer(self) -> Optional[Xp24MsActionTableSerializer]:
|
|
198
|
+
"""Get the MsActionTable serializer for XP24.
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
The XP24 MsActionTable serializer instance.
|
|
202
|
+
"""
|
|
203
|
+
return self.msactiontable_serializer
|
|
204
|
+
|
|
205
|
+
def _get_msactiontable(self) -> Optional[Xp24MsActionTable]:
|
|
206
|
+
"""Get the MsActionTable for XP24.
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
The XP24 MsActionTable instance.
|
|
210
|
+
"""
|
|
211
|
+
return self.msactiontable
|
|
212
|
+
|
|
213
|
+
def _get_default_msactiontable(self) -> Xp24MsActionTable:
|
|
214
|
+
"""Generate default MsActionTable configuration.
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
Default XP24 MsActionTable with all inputs set to VOID.
|
|
218
|
+
"""
|
|
219
|
+
return Xp24MsActionTable(
|
|
220
|
+
input1_action=InputAction(type=InputActionType.VOID, param=TimeParam.NONE),
|
|
221
|
+
input2_action=InputAction(type=InputActionType.VOID, param=TimeParam.NONE),
|
|
222
|
+
input3_action=InputAction(type=InputActionType.VOID, param=TimeParam.NONE),
|
|
223
|
+
input4_action=InputAction(type=InputActionType.VOID, param=TimeParam.NONE),
|
|
224
|
+
mutex12=False,
|
|
225
|
+
mutex34=False,
|
|
226
|
+
curtain12=False,
|
|
227
|
+
curtain34=False,
|
|
228
|
+
mutual_deadtime=12, # MS300
|
|
229
|
+
)
|
|
230
|
+
|
|
178
231
|
def get_device_info(self) -> Dict:
|
|
179
232
|
"""Get XP24 device information.
|
|
180
233
|
|
|
@@ -10,9 +10,13 @@ import threading
|
|
|
10
10
|
from typing import Dict, Optional
|
|
11
11
|
|
|
12
12
|
from xp.models import ModuleTypeCode
|
|
13
|
+
from xp.models.actiontable.msactiontable_xp33 import Xp33MsActionTable
|
|
13
14
|
from xp.models.telegram.datapoint_type import DataPointType
|
|
14
15
|
from xp.models.telegram.system_function import SystemFunction
|
|
15
16
|
from xp.models.telegram.system_telegram import SystemTelegram
|
|
17
|
+
from xp.services.actiontable.msactiontable_xp33_serializer import (
|
|
18
|
+
Xp33MsActionTableSerializer,
|
|
19
|
+
)
|
|
16
20
|
from xp.services.server.base_server_service import BaseServerService
|
|
17
21
|
|
|
18
22
|
|
|
@@ -30,12 +34,18 @@ class XP33ServerService(BaseServerService):
|
|
|
30
34
|
and implements XP33 telegram format for 3-channel dimmer modules.
|
|
31
35
|
"""
|
|
32
36
|
|
|
33
|
-
def __init__(
|
|
37
|
+
def __init__(
|
|
38
|
+
self,
|
|
39
|
+
serial_number: str,
|
|
40
|
+
variant: str = "XP33LR",
|
|
41
|
+
msactiontable_serializer: Optional[Xp33MsActionTableSerializer] = None,
|
|
42
|
+
):
|
|
34
43
|
"""Initialize XP33 server service.
|
|
35
44
|
|
|
36
45
|
Args:
|
|
37
46
|
serial_number: The device serial number.
|
|
38
47
|
variant: Device variant (XP33, XP33LR, or XP33LED).
|
|
48
|
+
msactiontable_serializer: MsActionTable serializer (injected via DI).
|
|
39
49
|
"""
|
|
40
50
|
super().__init__(serial_number)
|
|
41
51
|
self.variant = variant # XP33 or XP33LR or XP33LED
|
|
@@ -85,6 +95,12 @@ class XP33ServerService(BaseServerService):
|
|
|
85
95
|
self.client_sockets_lock = threading.Lock() # Lock for socket set
|
|
86
96
|
self.storm_packets_sent = 0 # Counter for packets sent during storm
|
|
87
97
|
|
|
98
|
+
# MsActionTable support
|
|
99
|
+
self.msactiontable_serializer = (
|
|
100
|
+
msactiontable_serializer or Xp33MsActionTableSerializer()
|
|
101
|
+
)
|
|
102
|
+
self.msactiontable = self._get_default_msactiontable()
|
|
103
|
+
|
|
88
104
|
def _handle_device_specific_action_request(
|
|
89
105
|
self, request: SystemTelegram
|
|
90
106
|
) -> Optional[str]:
|
|
@@ -449,6 +465,31 @@ class XP33ServerService(BaseServerService):
|
|
|
449
465
|
return True
|
|
450
466
|
return False
|
|
451
467
|
|
|
468
|
+
def _get_msactiontable_serializer(self) -> Optional[Xp33MsActionTableSerializer]:
|
|
469
|
+
"""Get the MsActionTable serializer for XP33.
|
|
470
|
+
|
|
471
|
+
Returns:
|
|
472
|
+
The XP33 MsActionTable serializer instance.
|
|
473
|
+
"""
|
|
474
|
+
return self.msactiontable_serializer
|
|
475
|
+
|
|
476
|
+
def _get_msactiontable(self) -> Optional[Xp33MsActionTable]:
|
|
477
|
+
"""Get the MsActionTable for XP33.
|
|
478
|
+
|
|
479
|
+
Returns:
|
|
480
|
+
The XP33 MsActionTable instance.
|
|
481
|
+
"""
|
|
482
|
+
return self.msactiontable
|
|
483
|
+
|
|
484
|
+
def _get_default_msactiontable(self) -> Xp33MsActionTable:
|
|
485
|
+
"""Generate default MsActionTable configuration.
|
|
486
|
+
|
|
487
|
+
Returns:
|
|
488
|
+
Default XP33 MsActionTable with all outputs at 0-100% range, no scenes configured.
|
|
489
|
+
"""
|
|
490
|
+
# All outputs at 0-100% range, no scenes configured
|
|
491
|
+
return Xp33MsActionTable()
|
|
492
|
+
|
|
452
493
|
def get_device_info(self) -> Dict:
|
|
453
494
|
"""Get XP33 device information.
|
|
454
495
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
This module provides telegram parsing functionality for event, system, and reply telegrams.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
import logging
|
|
6
7
|
import re
|
|
7
8
|
from typing import Union
|
|
8
9
|
|
|
@@ -48,7 +49,8 @@ class TelegramService:
|
|
|
48
49
|
|
|
49
50
|
def __init__(self) -> None:
|
|
50
51
|
"""Initialize the telegram service."""
|
|
51
|
-
|
|
52
|
+
# Set up logging
|
|
53
|
+
self.logger = logging.getLogger(__name__)
|
|
52
54
|
|
|
53
55
|
def parse_event_telegram(self, raw_telegram: str) -> EventTelegram:
|
|
54
56
|
"""Parse a raw telegram string into an EventTelegram object.
|
|
@@ -242,6 +244,7 @@ class TelegramService:
|
|
|
242
244
|
raise TelegramParsingError("Empty telegram string")
|
|
243
245
|
|
|
244
246
|
# Validate and parse using regex
|
|
247
|
+
self.logger.debug(f"Parsing reply telegram {raw_telegram}")
|
|
245
248
|
match = self.REPLY_TELEGRAM_PATTERN.match(raw_telegram.strip())
|
|
246
249
|
if not match:
|
|
247
250
|
raise TelegramParsingError(f"Invalid reply telegram format: {raw_telegram}")
|
xp/utils/dependencies.py
CHANGED
|
@@ -9,18 +9,19 @@ from twisted.internet.posixbase import PosixReactorBase
|
|
|
9
9
|
from xp.models import ConbusClientConfig
|
|
10
10
|
from xp.models.homekit.homekit_config import HomekitConfig
|
|
11
11
|
from xp.models.homekit.homekit_conson_config import ConsonModuleListConfig
|
|
12
|
-
from xp.services.
|
|
13
|
-
from xp.services.
|
|
14
|
-
from xp.services.
|
|
15
|
-
from xp.services.conbus.actiontable.msactiontable_xp20_serializer import (
|
|
12
|
+
from xp.services.actiontable.actiontable_serializer import ActionTableSerializer
|
|
13
|
+
from xp.services.actiontable.msactiontable_serializer import MsActionTableSerializer
|
|
14
|
+
from xp.services.actiontable.msactiontable_xp20_serializer import (
|
|
16
15
|
Xp20MsActionTableSerializer,
|
|
17
16
|
)
|
|
18
|
-
from xp.services.
|
|
17
|
+
from xp.services.actiontable.msactiontable_xp24_serializer import (
|
|
19
18
|
Xp24MsActionTableSerializer,
|
|
20
19
|
)
|
|
21
|
-
from xp.services.
|
|
20
|
+
from xp.services.actiontable.msactiontable_xp33_serializer import (
|
|
22
21
|
Xp33MsActionTableSerializer,
|
|
23
22
|
)
|
|
23
|
+
from xp.services.conbus.actiontable.actiontable_service import ActionTableService
|
|
24
|
+
from xp.services.conbus.actiontable.msactiontable_service import MsActionTableService
|
|
24
25
|
from xp.services.conbus.conbus_blink_all_service import ConbusBlinkAllService
|
|
25
26
|
from xp.services.conbus.conbus_blink_service import ConbusBlinkService
|
|
26
27
|
from xp.services.conbus.conbus_custom_service import ConbusCustomService
|
|
@@ -49,6 +50,7 @@ from xp.services.module_type_service import ModuleTypeService
|
|
|
49
50
|
from xp.services.protocol.protocol_factory import TelegramFactory
|
|
50
51
|
from xp.services.protocol.telegram_protocol import TelegramProtocol
|
|
51
52
|
from xp.services.reverse_proxy_service import ReverseProxyService
|
|
53
|
+
from xp.services.server.device_service_factory import DeviceServiceFactory
|
|
52
54
|
from xp.services.server.server_service import ServerService
|
|
53
55
|
from xp.services.telegram.telegram_blink_service import TelegramBlinkService
|
|
54
56
|
from xp.services.telegram.telegram_discover_service import TelegramDiscoverService
|
|
@@ -324,12 +326,31 @@ class ServiceContainer:
|
|
|
324
326
|
# Module type services layer
|
|
325
327
|
self.container.register(ModuleTypeService, scope=punq.Scope.singleton)
|
|
326
328
|
|
|
329
|
+
# MsActionTable serializers
|
|
330
|
+
self.container.register(MsActionTableSerializer, scope=punq.Scope.singleton)
|
|
331
|
+
self.container.register(Xp20MsActionTableSerializer, scope=punq.Scope.singleton)
|
|
332
|
+
self.container.register(Xp24MsActionTableSerializer, scope=punq.Scope.singleton)
|
|
333
|
+
self.container.register(Xp33MsActionTableSerializer, scope=punq.Scope.singleton)
|
|
334
|
+
|
|
335
|
+
# Device service factory
|
|
336
|
+
self.container.register(
|
|
337
|
+
DeviceServiceFactory,
|
|
338
|
+
factory=lambda: DeviceServiceFactory(
|
|
339
|
+
xp20ms_serializer=self.container.resolve(Xp20MsActionTableSerializer),
|
|
340
|
+
xp24ms_serializer=self.container.resolve(Xp24MsActionTableSerializer),
|
|
341
|
+
xp33ms_serializer=self.container.resolve(Xp33MsActionTableSerializer),
|
|
342
|
+
ms_serializer=self.container.resolve(MsActionTableSerializer),
|
|
343
|
+
),
|
|
344
|
+
scope=punq.Scope.singleton,
|
|
345
|
+
)
|
|
346
|
+
|
|
327
347
|
# Server services layer
|
|
328
348
|
self.container.register(
|
|
329
349
|
ServerService,
|
|
330
350
|
factory=lambda: ServerService(
|
|
331
351
|
telegram_service=self.container.resolve(TelegramService),
|
|
332
352
|
discover_service=self.container.resolve(TelegramDiscoverService),
|
|
353
|
+
device_factory=self.container.resolve(DeviceServiceFactory),
|
|
333
354
|
config_path="server.yml",
|
|
334
355
|
port=self._server_port,
|
|
335
356
|
),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|