conson-xp 1.46.0__py3-none-any.whl → 1.48.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.46.0.dist-info → conson_xp-1.48.0.dist-info}/METADATA +1 -1
- conson_xp-1.48.0.dist-info/RECORD +210 -0
- xp/__init__.py +3 -2
- xp/cli/commands/conbus/conbus.py +1 -1
- xp/cli/commands/conbus/conbus_actiontable_commands.py +33 -19
- xp/cli/commands/conbus/conbus_autoreport_commands.py +8 -4
- xp/cli/commands/conbus/conbus_blink_commands.py +20 -10
- xp/cli/commands/conbus/conbus_config_commands.py +2 -1
- xp/cli/commands/conbus/conbus_custom_commands.py +4 -2
- xp/cli/commands/conbus/conbus_datapoint_commands.py +10 -5
- xp/cli/commands/conbus/conbus_discover_commands.py +8 -4
- xp/cli/commands/conbus/conbus_event_commands.py +8 -4
- xp/cli/commands/conbus/conbus_export_commands.py +8 -4
- xp/cli/commands/conbus/conbus_lightlevel_commands.py +16 -8
- xp/cli/commands/conbus/conbus_linknumber_commands.py +8 -4
- xp/cli/commands/conbus/conbus_modulenumber_commands.py +8 -4
- xp/cli/commands/conbus/conbus_msactiontable_commands.py +78 -40
- xp/cli/commands/conbus/conbus_output_commands.py +16 -8
- xp/cli/commands/conbus/conbus_raw_commands.py +6 -3
- xp/cli/commands/conbus/conbus_receive_commands.py +6 -3
- xp/cli/commands/conbus/conbus_scan_commands.py +6 -3
- xp/cli/commands/file_commands.py +6 -3
- xp/cli/commands/homekit/homekit.py +4 -2
- xp/cli/commands/homekit/homekit_start_commands.py +2 -1
- xp/cli/commands/module_commands.py +8 -4
- xp/cli/commands/reverse_proxy_commands.py +8 -4
- xp/cli/commands/server/server_commands.py +6 -3
- xp/cli/commands/telegram/telegram_blink_commands.py +4 -2
- xp/cli/commands/telegram/telegram_checksum_commands.py +4 -2
- xp/cli/commands/telegram/telegram_discover_commands.py +2 -1
- xp/cli/commands/telegram/telegram_linknumber_commands.py +4 -2
- xp/cli/commands/telegram/telegram_parse_commands.py +4 -2
- xp/cli/commands/telegram/telegram_version_commands.py +2 -1
- xp/cli/commands/term/term_commands.py +4 -2
- xp/cli/main.py +2 -1
- xp/cli/utils/click_tree.py +6 -3
- xp/cli/utils/datapoint_type_choice.py +4 -2
- xp/cli/utils/decorators.py +42 -21
- xp/cli/utils/error_handlers.py +16 -8
- xp/cli/utils/formatters.py +22 -11
- xp/cli/utils/module_type_choice.py +4 -2
- xp/cli/utils/serial_number_type.py +4 -2
- xp/cli/utils/system_function_choice.py +4 -2
- xp/cli/utils/xp_module_type.py +4 -2
- xp/models/actiontable/actiontable.py +8 -8
- xp/models/actiontable/actiontable_type.py +20 -0
- xp/models/actiontable/msactiontable_xp20.py +8 -4
- xp/models/actiontable/msactiontable_xp24.py +12 -6
- xp/models/actiontable/msactiontable_xp33.py +20 -10
- xp/models/conbus/conbus.py +8 -4
- xp/models/conbus/conbus_autoreport.py +4 -2
- xp/models/conbus/conbus_blink.py +4 -2
- xp/models/conbus/conbus_client_config.py +6 -3
- xp/models/conbus/conbus_connection_status.py +4 -2
- xp/models/conbus/conbus_custom.py +4 -2
- xp/models/conbus/conbus_datapoint.py +4 -2
- xp/models/conbus/conbus_discover.py +6 -3
- xp/models/conbus/conbus_event_list.py +4 -2
- xp/models/conbus/conbus_event_raw.py +4 -2
- xp/models/conbus/conbus_export.py +2 -1
- xp/models/conbus/conbus_lightlevel.py +4 -2
- xp/models/conbus/conbus_linknumber.py +4 -2
- xp/models/conbus/conbus_logger_config.py +8 -4
- xp/models/conbus/conbus_output.py +4 -2
- xp/models/conbus/conbus_raw.py +4 -2
- xp/models/conbus/conbus_receive.py +4 -2
- xp/models/conbus/conbus_writeconfig.py +4 -2
- xp/models/config/conson_module_config.py +8 -4
- xp/models/homekit/homekit_accessory.py +4 -2
- xp/models/homekit/homekit_config.py +12 -6
- xp/models/log_entry.py +16 -8
- xp/models/protocol/conbus_protocol.py +36 -18
- xp/models/response.py +12 -8
- xp/models/telegram/action_type.py +4 -2
- xp/models/telegram/datapoint_type.py +4 -2
- xp/models/telegram/event_telegram.py +14 -7
- xp/models/telegram/event_type.py +2 -1
- xp/models/telegram/input_action_type.py +2 -1
- xp/models/telegram/input_type.py +2 -1
- xp/models/telegram/module_type.py +24 -12
- xp/models/telegram/module_type_code.py +2 -1
- xp/models/telegram/output_telegram.py +16 -10
- xp/models/telegram/reply_telegram.py +24 -13
- xp/models/telegram/system_function.py +6 -3
- xp/models/telegram/system_telegram.py +10 -6
- xp/models/telegram/telegram.py +2 -1
- xp/models/telegram/telegram_type.py +2 -1
- xp/models/telegram/timeparam_type.py +2 -1
- xp/models/term/connection_state.py +4 -2
- xp/models/term/module_state.py +2 -1
- xp/models/term/protocol_keys_config.py +6 -3
- xp/models/term/status_message.py +2 -1
- xp/models/term/telegram_display.py +2 -1
- xp/models/write_config_type.py +4 -2
- xp/services/actiontable/actiontable_serializer.py +34 -41
- xp/services/{conbus/actiontable/actiontable_download_state_machine.py → actiontable/download_state_machine.py} +13 -8
- xp/services/actiontable/msactiontable_xp20_serializer.py +73 -50
- xp/services/actiontable/msactiontable_xp24_serializer.py +73 -54
- xp/services/actiontable/msactiontable_xp33_serializer.py +44 -20
- xp/services/actiontable/serializer_protocol.py +76 -0
- xp/services/conbus/actiontable/actiontable_download_service.py +68 -31
- xp/services/conbus/actiontable/actiontable_list_service.py +17 -4
- xp/services/conbus/actiontable/actiontable_show_service.py +10 -6
- xp/services/conbus/actiontable/actiontable_upload_service.py +17 -9
- xp/services/conbus/conbus_blink_all_service.py +16 -8
- xp/services/conbus/conbus_blink_service.py +14 -7
- xp/services/conbus/conbus_custom_service.py +16 -8
- xp/services/conbus/conbus_datapoint_queryall_service.py +18 -9
- xp/services/conbus/conbus_datapoint_service.py +18 -9
- xp/services/conbus/conbus_discover_service.py +24 -13
- xp/services/conbus/conbus_event_list_service.py +11 -7
- xp/services/conbus/conbus_event_raw_service.py +18 -10
- xp/services/conbus/conbus_export_service.py +28 -14
- xp/services/conbus/conbus_output_service.py +18 -10
- xp/services/conbus/conbus_raw_service.py +16 -8
- xp/services/conbus/conbus_receive_service.py +18 -10
- xp/services/conbus/conbus_scan_service.py +18 -10
- xp/services/conbus/msactiontable/msactiontable_upload_service.py +18 -10
- xp/services/conbus/write_config_service.py +18 -9
- xp/services/homekit/homekit_cache_service.py +12 -6
- xp/services/homekit/homekit_conbus_service.py +12 -6
- xp/services/homekit/homekit_config_validator.py +34 -17
- xp/services/homekit/homekit_conson_validator.py +18 -9
- xp/services/homekit/homekit_dimminglight.py +14 -7
- xp/services/homekit/homekit_dimminglight_service.py +14 -7
- xp/services/homekit/homekit_hap_service.py +18 -9
- xp/services/homekit/homekit_lightbulb.py +10 -5
- xp/services/homekit/homekit_lightbulb_service.py +10 -5
- xp/services/homekit/homekit_module_service.py +8 -4
- xp/services/homekit/homekit_outlet.py +14 -7
- xp/services/homekit/homekit_outlet_service.py +12 -6
- xp/services/homekit/homekit_service.py +24 -12
- xp/services/log_file_service.py +16 -8
- xp/services/module_type_service.py +10 -5
- xp/services/protocol/conbus_event_protocol.py +51 -26
- xp/services/protocol/conbus_protocol.py +36 -19
- xp/services/protocol/protocol_factory.py +12 -6
- xp/services/protocol/telegram_protocol.py +12 -6
- xp/services/reverse_proxy_service.py +26 -14
- xp/services/server/base_server_service.py +42 -23
- xp/services/server/client_buffer_manager.py +12 -7
- xp/services/server/cp20_server_service.py +10 -7
- xp/services/server/device_service_factory.py +12 -8
- xp/services/server/server_service.py +18 -11
- xp/services/server/xp130_server_service.py +11 -8
- xp/services/server/xp20_server_service.py +16 -10
- xp/services/server/xp230_server_service.py +10 -7
- xp/services/server/xp24_server_service.py +22 -13
- xp/services/server/xp33_server_service.py +44 -25
- xp/services/telegram/telegram_blink_service.py +14 -8
- xp/services/telegram/telegram_checksum_service.py +12 -7
- xp/services/telegram/telegram_datapoint_service.py +14 -9
- xp/services/telegram/telegram_discover_service.py +28 -15
- xp/services/telegram/telegram_link_number_service.py +18 -10
- xp/services/telegram/telegram_output_service.py +24 -12
- xp/services/telegram/telegram_service.py +22 -11
- xp/services/telegram/telegram_version_service.py +14 -8
- xp/services/term/protocol_monitor_service.py +30 -16
- xp/services/term/state_monitor_service.py +39 -21
- xp/term/protocol.py +12 -6
- xp/term/state.py +12 -7
- xp/term/widgets/help_menu.py +6 -3
- xp/term/widgets/modules_list.py +20 -10
- xp/term/widgets/protocol_log.py +12 -6
- xp/term/widgets/status_footer.py +10 -5
- xp/utils/checksum.py +6 -3
- xp/utils/dependencies.py +25 -30
- xp/utils/event_helper.py +6 -4
- xp/utils/logging.py +6 -3
- xp/utils/serialization.py +30 -16
- xp/utils/state_machine.py +16 -9
- xp/utils/time_utils.py +6 -3
- conson_xp-1.46.0.dist-info/RECORD +0 -211
- xp/services/conbus/msactiontable/msactiontable_download_service.py +0 -275
- xp/services/conbus/msactiontable/msactiontable_list_service.py +0 -100
- xp/services/conbus/msactiontable/msactiontable_show_service.py +0 -89
- {conson_xp-1.46.0.dist-info → conson_xp-1.48.0.dist-info}/WHEEL +0 -0
- {conson_xp-1.46.0.dist-info → conson_xp-1.48.0.dist-info}/entry_points.txt +0 -0
- {conson_xp-1.46.0.dist-info → conson_xp-1.48.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,7 +5,9 @@ import re
|
|
|
5
5
|
from xp.models import ModuleTypeCode
|
|
6
6
|
from xp.models.actiontable.actiontable import ActionTable, ActionTableEntry
|
|
7
7
|
from xp.models.telegram.input_action_type import InputActionType
|
|
8
|
+
from xp.models.telegram.system_function import SystemFunction
|
|
8
9
|
from xp.models.telegram.timeparam_type import TimeParam
|
|
10
|
+
from xp.services.actiontable.serializer_protocol import ActionTableSerializerProtocol
|
|
9
11
|
from xp.utils.serialization import (
|
|
10
12
|
byte_to_unsigned,
|
|
11
13
|
de_bcd,
|
|
@@ -19,8 +21,9 @@ from xp.utils.serialization import (
|
|
|
19
21
|
)
|
|
20
22
|
|
|
21
23
|
|
|
22
|
-
class ActionTableSerializer:
|
|
23
|
-
"""
|
|
24
|
+
class ActionTableSerializer(ActionTableSerializerProtocol):
|
|
25
|
+
"""
|
|
26
|
+
Handles serialization/deserialization of ActionTable to/from telegrams.
|
|
24
27
|
|
|
25
28
|
Attributes:
|
|
26
29
|
MAX_ENTRIES: Maximum number of entries in an ActionTable (96).
|
|
@@ -29,15 +32,27 @@ class ActionTableSerializer:
|
|
|
29
32
|
MAX_ENTRIES = 96 # ActionTable must always contain exactly 96 entries
|
|
30
33
|
|
|
31
34
|
@staticmethod
|
|
32
|
-
def
|
|
33
|
-
"""
|
|
35
|
+
def download_type() -> SystemFunction:
|
|
36
|
+
"""
|
|
37
|
+
Get the download system function type.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
The download system function: DOWNLOAD_ACTIONTABLE
|
|
41
|
+
"""
|
|
42
|
+
return SystemFunction.DOWNLOAD_ACTIONTABLE
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def from_encoded_string(encoded_data: str) -> ActionTable:
|
|
46
|
+
"""
|
|
47
|
+
Deserialize telegram data to ActionTable.
|
|
34
48
|
|
|
35
49
|
Args:
|
|
36
|
-
|
|
50
|
+
encoded_data: Raw byte data from telegram
|
|
37
51
|
|
|
38
52
|
Returns:
|
|
39
53
|
Decoded ActionTable
|
|
40
54
|
"""
|
|
55
|
+
data = de_nibbles(encoded_data)
|
|
41
56
|
entries = []
|
|
42
57
|
|
|
43
58
|
# Process data in 5-byte chunks
|
|
@@ -92,14 +107,15 @@ class ActionTableSerializer:
|
|
|
92
107
|
return ActionTable(entries=entries)
|
|
93
108
|
|
|
94
109
|
@staticmethod
|
|
95
|
-
def
|
|
96
|
-
"""
|
|
110
|
+
def to_encoded_string(action_table: ActionTable) -> str:
|
|
111
|
+
"""
|
|
112
|
+
Convert ActionTable to base64-encoded string format.
|
|
97
113
|
|
|
98
114
|
Args:
|
|
99
|
-
action_table: ActionTable to
|
|
115
|
+
action_table: ActionTable to encode
|
|
100
116
|
|
|
101
117
|
Returns:
|
|
102
|
-
|
|
118
|
+
Base64-encoded string representation
|
|
103
119
|
"""
|
|
104
120
|
data = bytearray()
|
|
105
121
|
|
|
@@ -128,37 +144,12 @@ class ActionTableSerializer:
|
|
|
128
144
|
for _ in range(ActionTableSerializer.MAX_ENTRIES - current_entries):
|
|
129
145
|
data.extend(padding_bytes)
|
|
130
146
|
|
|
131
|
-
return bytes(data)
|
|
132
|
-
|
|
133
|
-
@staticmethod
|
|
134
|
-
def to_encoded_string(action_table: ActionTable) -> str:
|
|
135
|
-
"""Convert ActionTable to base64-encoded string format.
|
|
136
|
-
|
|
137
|
-
Args:
|
|
138
|
-
action_table: ActionTable to encode
|
|
139
|
-
|
|
140
|
-
Returns:
|
|
141
|
-
Base64-encoded string representation
|
|
142
|
-
"""
|
|
143
|
-
data = ActionTableSerializer.to_data(action_table)
|
|
144
147
|
return nibbles(data)
|
|
145
148
|
|
|
146
149
|
@staticmethod
|
|
147
|
-
def
|
|
148
|
-
"""Convert base64-encoded string to ActionTable.
|
|
149
|
-
|
|
150
|
-
Args:
|
|
151
|
-
encoded_data: Base64-encoded string
|
|
152
|
-
|
|
153
|
-
Returns:
|
|
154
|
-
Decoded ActionTable
|
|
150
|
+
def to_short_string(action_table: ActionTable) -> list[str]:
|
|
155
151
|
"""
|
|
156
|
-
|
|
157
|
-
return ActionTableSerializer.from_data(data)
|
|
158
|
-
|
|
159
|
-
@staticmethod
|
|
160
|
-
def format_decoded_output(action_table: ActionTable) -> list[str]:
|
|
161
|
-
"""Format ActionTable as human-readable decoded output.
|
|
152
|
+
Format ActionTable as human-readable decoded output.
|
|
162
153
|
|
|
163
154
|
Args:
|
|
164
155
|
action_table: ActionTable to format
|
|
@@ -194,8 +185,9 @@ class ActionTableSerializer:
|
|
|
194
185
|
return lines
|
|
195
186
|
|
|
196
187
|
@staticmethod
|
|
197
|
-
def
|
|
198
|
-
"""
|
|
188
|
+
def _parse_action_string(action_str: str) -> ActionTableEntry:
|
|
189
|
+
"""
|
|
190
|
+
Parse action table entry from string format.
|
|
199
191
|
|
|
200
192
|
Args:
|
|
201
193
|
action_str: String in format "CP20 0 0 > 1 OFF" or "CP20 0 1 > 1 ~ON"
|
|
@@ -257,8 +249,9 @@ class ActionTableSerializer:
|
|
|
257
249
|
)
|
|
258
250
|
|
|
259
251
|
@staticmethod
|
|
260
|
-
def
|
|
261
|
-
"""
|
|
252
|
+
def from_short_string(action_strings: list[str]) -> ActionTable:
|
|
253
|
+
"""
|
|
254
|
+
Parse action table from short string representation.
|
|
262
255
|
|
|
263
256
|
Args:
|
|
264
257
|
action_strings: List of action strings from conson.yml
|
|
@@ -267,7 +260,7 @@ class ActionTableSerializer:
|
|
|
267
260
|
Parsed ActionTable
|
|
268
261
|
"""
|
|
269
262
|
entries = [
|
|
270
|
-
ActionTableSerializer.
|
|
263
|
+
ActionTableSerializer._parse_action_string(action_str)
|
|
271
264
|
for action_str in action_strings
|
|
272
265
|
]
|
|
273
266
|
return ActionTable(entries=entries)
|
|
@@ -9,10 +9,11 @@ from statemachine.factory import StateMachineMetaclass
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class AbstractStateMachineMeta(StateMachineMetaclass, ABCMeta):
|
|
12
|
-
"""
|
|
12
|
+
"""
|
|
13
|
+
Combined metaclass for abstract state machines.
|
|
13
14
|
|
|
14
|
-
Combines StateMachineMetaclass (for state machine introspection) with
|
|
15
|
-
|
|
15
|
+
Combines StateMachineMetaclass (for state machine introspection) with ABCMeta (for
|
|
16
|
+
abstract method enforcement).
|
|
16
17
|
"""
|
|
17
18
|
|
|
18
19
|
pass
|
|
@@ -23,7 +24,8 @@ MAX_ERROR_RETRIES = 3 # Max retries for error_status_received before giving up
|
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
class Phase(Enum):
|
|
26
|
-
"""
|
|
27
|
+
"""
|
|
28
|
+
Download workflow phases.
|
|
27
29
|
|
|
28
30
|
The download workflow consists of three sequential phases:
|
|
29
31
|
- INIT: Drain pending telegrams, query error status → proceed to DOWNLOAD
|
|
@@ -41,8 +43,9 @@ class Phase(Enum):
|
|
|
41
43
|
CLEANUP = "cleanup"
|
|
42
44
|
|
|
43
45
|
|
|
44
|
-
class
|
|
45
|
-
"""
|
|
46
|
+
class DownloadStateMachine(StateMachine, metaclass=AbstractStateMachineMeta):
|
|
47
|
+
"""
|
|
48
|
+
State machine for ActionTable download workflow.
|
|
46
49
|
|
|
47
50
|
Pure state machine with states, transitions, and guards. Subclasses can
|
|
48
51
|
override on_enter_* methods to add protocol-specific behavior.
|
|
@@ -146,7 +149,8 @@ class ActionTableDownloadStateMachine(StateMachine, metaclass=AbstractStateMachi
|
|
|
146
149
|
|
|
147
150
|
@phase.setter
|
|
148
151
|
def phase(self, value: Phase) -> None:
|
|
149
|
-
"""
|
|
152
|
+
"""
|
|
153
|
+
Set current phase.
|
|
150
154
|
|
|
151
155
|
Args:
|
|
152
156
|
value: The phase value to set.
|
|
@@ -160,7 +164,8 @@ class ActionTableDownloadStateMachine(StateMachine, metaclass=AbstractStateMachi
|
|
|
160
164
|
|
|
161
165
|
@error_retry_count.setter
|
|
162
166
|
def error_retry_count(self, value: int) -> None:
|
|
163
|
-
"""
|
|
167
|
+
"""
|
|
168
|
+
Set error retry count.
|
|
164
169
|
|
|
165
170
|
Args:
|
|
166
171
|
value: The error retry count value to set.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"""Serializer for XP20 Action Table telegram encoding/decoding."""
|
|
2
2
|
|
|
3
3
|
from xp.models.actiontable.msactiontable_xp20 import InputChannel, Xp20MsActionTable
|
|
4
|
+
from xp.models.telegram.system_function import SystemFunction
|
|
5
|
+
from xp.services.actiontable.serializer_protocol import ActionTableSerializerProtocol
|
|
4
6
|
from xp.utils.serialization import byte_to_bits, de_nibbles, nibbles
|
|
5
7
|
|
|
6
8
|
# Index constants for clarity in implementation
|
|
@@ -12,24 +14,65 @@ SA_FUNCTION_INDEX: int = 11
|
|
|
12
14
|
TA_FUNCTION_INDEX: int = 12
|
|
13
15
|
|
|
14
16
|
|
|
15
|
-
class Xp20MsActionTableSerializer:
|
|
17
|
+
class Xp20MsActionTableSerializer(ActionTableSerializerProtocol):
|
|
16
18
|
"""Handles serialization/deserialization of XP20 action tables to/from telegrams."""
|
|
17
19
|
|
|
18
20
|
@staticmethod
|
|
19
|
-
def
|
|
20
|
-
"""
|
|
21
|
+
def download_type() -> SystemFunction:
|
|
22
|
+
"""
|
|
23
|
+
Get the download system function type.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
The download system function: DOWNLOAD_MSACTIONTABLE
|
|
27
|
+
"""
|
|
28
|
+
return SystemFunction.DOWNLOAD_MSACTIONTABLE
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def from_encoded_string(encoded_data: str) -> Xp20MsActionTable:
|
|
32
|
+
"""
|
|
33
|
+
Deserialize telegram data to XP20 action table.
|
|
21
34
|
|
|
22
35
|
Args:
|
|
23
|
-
|
|
36
|
+
encoded_data: 64-character hex string with A-P encoding
|
|
24
37
|
|
|
25
38
|
Returns:
|
|
26
|
-
|
|
39
|
+
Decoded XP20 action table
|
|
40
|
+
|
|
41
|
+
Raises:
|
|
42
|
+
ValueError: If input length is not 64 characters
|
|
27
43
|
"""
|
|
28
|
-
|
|
44
|
+
raw_length = len(encoded_data)
|
|
45
|
+
if raw_length < 64: # Minimum: 4 char prefix + 64 chars data
|
|
46
|
+
raise ValueError(
|
|
47
|
+
f"XP20 action table data must be 64 characters long, got {len(encoded_data)}"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
raw_bytes = de_nibbles(encoded_data)
|
|
51
|
+
|
|
52
|
+
# Decode input channels
|
|
53
|
+
input_channels = []
|
|
54
|
+
for input_index in range(8):
|
|
55
|
+
input_channel = Xp20MsActionTableSerializer._decode_input_channel(
|
|
56
|
+
raw_bytes, input_index
|
|
57
|
+
)
|
|
58
|
+
input_channels.append(input_channel)
|
|
59
|
+
|
|
60
|
+
# Create and return XP20 action table
|
|
61
|
+
return Xp20MsActionTable(
|
|
62
|
+
input1=input_channels[0],
|
|
63
|
+
input2=input_channels[1],
|
|
64
|
+
input3=input_channels[2],
|
|
65
|
+
input4=input_channels[3],
|
|
66
|
+
input5=input_channels[4],
|
|
67
|
+
input6=input_channels[5],
|
|
68
|
+
input7=input_channels[6],
|
|
69
|
+
input8=input_channels[7],
|
|
70
|
+
)
|
|
29
71
|
|
|
30
72
|
@staticmethod
|
|
31
|
-
def
|
|
32
|
-
"""
|
|
73
|
+
def to_encoded_string(action_table: Xp20MsActionTable) -> str:
|
|
74
|
+
"""
|
|
75
|
+
Serialize XP20 action table to telegram hex string format.
|
|
33
76
|
|
|
34
77
|
Args:
|
|
35
78
|
action_table: XP20 action table to serialize
|
|
@@ -60,59 +103,38 @@ class Xp20MsActionTableSerializer:
|
|
|
60
103
|
|
|
61
104
|
encoded_data = nibbles(raw_bytes)
|
|
62
105
|
# Convert raw bytes to hex string with A-P encoding
|
|
63
|
-
return
|
|
106
|
+
return encoded_data
|
|
64
107
|
|
|
65
108
|
@staticmethod
|
|
66
|
-
def
|
|
67
|
-
"""
|
|
109
|
+
def to_short_string(action_table: Xp20MsActionTable) -> list[str]:
|
|
110
|
+
"""
|
|
111
|
+
Serialize XP20 action table to humane compact readable format.
|
|
68
112
|
|
|
69
113
|
Args:
|
|
70
|
-
|
|
114
|
+
action_table: XP20 action table to serialize
|
|
71
115
|
|
|
72
116
|
Returns:
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
Raises:
|
|
76
|
-
ValueError: If input length is not 64 characters
|
|
117
|
+
Human-readable string describing XP20 action table
|
|
77
118
|
"""
|
|
78
|
-
|
|
79
|
-
if raw_length < 68: # Minimum: 4 char prefix + 64 chars data
|
|
80
|
-
raise ValueError(
|
|
81
|
-
f"XP20 action table data must be 68 characters long, got {len(msactiontable_rawdata)}"
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
# Remove action table count prefix (first 4 characters: AAAA, AAAB, etc.)
|
|
85
|
-
data = msactiontable_rawdata[4:]
|
|
86
|
-
|
|
87
|
-
# Take first 64 chars (32 bytes) as per pseudocode
|
|
88
|
-
hex_data = data[:64]
|
|
119
|
+
return action_table.to_short_format()
|
|
89
120
|
|
|
90
|
-
|
|
91
|
-
|
|
121
|
+
@staticmethod
|
|
122
|
+
def from_short_string(action_strings: list[str]) -> Xp20MsActionTable:
|
|
123
|
+
"""
|
|
124
|
+
Parse XP20 action table from short string format.
|
|
92
125
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
for input_index in range(8):
|
|
96
|
-
input_channel = Xp20MsActionTableSerializer._decode_input_channel(
|
|
97
|
-
raw_bytes, input_index
|
|
98
|
-
)
|
|
99
|
-
input_channels.append(input_channel)
|
|
126
|
+
Args:
|
|
127
|
+
action_strings: List of short format strings to parse
|
|
100
128
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
input3=input_channels[2],
|
|
106
|
-
input4=input_channels[3],
|
|
107
|
-
input5=input_channels[4],
|
|
108
|
-
input6=input_channels[5],
|
|
109
|
-
input7=input_channels[6],
|
|
110
|
-
input8=input_channels[7],
|
|
111
|
-
)
|
|
129
|
+
Returns:
|
|
130
|
+
Parsed XP20 action table
|
|
131
|
+
"""
|
|
132
|
+
return Xp20MsActionTable.from_short_format(action_strings)
|
|
112
133
|
|
|
113
134
|
@staticmethod
|
|
114
|
-
def _decode_input_channel(raw_bytes:
|
|
115
|
-
"""
|
|
135
|
+
def _decode_input_channel(raw_bytes: bytes, input_index: int) -> InputChannel:
|
|
136
|
+
"""
|
|
137
|
+
Extract input channel configuration from raw bytes.
|
|
116
138
|
|
|
117
139
|
Args:
|
|
118
140
|
raw_bytes: Raw byte array from telegram
|
|
@@ -146,7 +168,8 @@ class Xp20MsActionTableSerializer:
|
|
|
146
168
|
def _encode_input_channel(
|
|
147
169
|
input_channel: InputChannel, input_index: int, raw_bytes: bytearray
|
|
148
170
|
) -> None:
|
|
149
|
-
"""
|
|
171
|
+
"""
|
|
172
|
+
Encode input channel configuration into raw bytes.
|
|
150
173
|
|
|
151
174
|
Args:
|
|
152
175
|
input_channel: Input channel configuration to encode
|
|
@@ -2,34 +2,77 @@
|
|
|
2
2
|
|
|
3
3
|
from xp.models.actiontable.msactiontable_xp24 import InputAction, Xp24MsActionTable
|
|
4
4
|
from xp.models.telegram.input_action_type import InputActionType
|
|
5
|
+
from xp.models.telegram.system_function import SystemFunction
|
|
5
6
|
from xp.models.telegram.timeparam_type import TimeParam
|
|
7
|
+
from xp.services.actiontable.serializer_protocol import ActionTableSerializerProtocol
|
|
6
8
|
from xp.utils.serialization import de_nibbles, nibbles
|
|
7
9
|
|
|
8
10
|
|
|
9
|
-
class Xp24MsActionTableSerializer:
|
|
11
|
+
class Xp24MsActionTableSerializer(ActionTableSerializerProtocol):
|
|
10
12
|
"""Handles serialization/deserialization of XP24 action tables to/from telegrams."""
|
|
11
13
|
|
|
12
14
|
@staticmethod
|
|
13
|
-
def
|
|
14
|
-
"""
|
|
15
|
+
def download_type() -> SystemFunction:
|
|
16
|
+
"""
|
|
17
|
+
Get the download system function type.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
The download system function: DOWNLOAD_MSACTIONTABLE
|
|
21
|
+
"""
|
|
22
|
+
return SystemFunction.DOWNLOAD_MSACTIONTABLE
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def from_encoded_string(encoded_data: str) -> Xp24MsActionTable:
|
|
26
|
+
"""
|
|
27
|
+
Deserialize action table from raw data parts.
|
|
15
28
|
|
|
16
29
|
Args:
|
|
17
|
-
|
|
30
|
+
encoded_data: Raw action table data string.
|
|
18
31
|
|
|
19
32
|
Returns:
|
|
20
|
-
|
|
33
|
+
Deserialized XP24 MS action table.
|
|
34
|
+
|
|
35
|
+
Raises:
|
|
36
|
+
ValueError: If data length is not 68 bytes.
|
|
21
37
|
"""
|
|
22
|
-
|
|
38
|
+
raw_length = len(encoded_data)
|
|
39
|
+
if raw_length != 64:
|
|
40
|
+
raise ValueError(
|
|
41
|
+
f"Msactiontable is not 64 bytes long ({raw_length}): {encoded_data}"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# Convert hex string to bytes using deNibble (A-P encoding)
|
|
45
|
+
data = de_nibbles(encoded_data)
|
|
46
|
+
|
|
47
|
+
# Decode input actions from positions 0-3 (2 bytes each)
|
|
48
|
+
input_actions = []
|
|
49
|
+
for pos in range(4):
|
|
50
|
+
input_action = Xp24MsActionTableSerializer._decode_input_action(data, pos)
|
|
51
|
+
input_actions.append(input_action)
|
|
52
|
+
|
|
53
|
+
action_table = Xp24MsActionTable(
|
|
54
|
+
input1_action=input_actions[0],
|
|
55
|
+
input2_action=input_actions[1],
|
|
56
|
+
input3_action=input_actions[2],
|
|
57
|
+
input4_action=input_actions[3],
|
|
58
|
+
mutex12=data[8] != 0, # With A-P encoding: AA=0 (False), AB=1 (True)
|
|
59
|
+
mutex34=data[9] != 0,
|
|
60
|
+
mutual_deadtime=data[10],
|
|
61
|
+
curtain12=data[11] != 0,
|
|
62
|
+
curtain34=data[12] != 0,
|
|
63
|
+
)
|
|
64
|
+
return action_table
|
|
23
65
|
|
|
24
66
|
@staticmethod
|
|
25
|
-
def
|
|
26
|
-
"""
|
|
67
|
+
def to_encoded_string(action_table: Xp24MsActionTable) -> str:
|
|
68
|
+
"""
|
|
69
|
+
Serialize action table to telegram format.
|
|
27
70
|
|
|
28
71
|
Args:
|
|
29
72
|
action_table: XP24 MS action table to serialize.
|
|
30
73
|
|
|
31
74
|
Returns:
|
|
32
|
-
Serialized action table data string (
|
|
75
|
+
Serialized action table data string (64 characters).
|
|
33
76
|
"""
|
|
34
77
|
# Build byte array for the action table (32 bytes total)
|
|
35
78
|
raw_bytes = bytearray()
|
|
@@ -56,64 +99,40 @@ class Xp24MsActionTableSerializer:
|
|
|
56
99
|
# Add padding to reach 32 bytes (19 more bytes needed)
|
|
57
100
|
raw_bytes.extend([0x00] * 19)
|
|
58
101
|
|
|
59
|
-
#
|
|
60
|
-
encoded_data = nibbles(bytes(raw_bytes))
|
|
61
|
-
|
|
102
|
+
# Build byte array for the action table (32 bytes total)
|
|
62
103
|
# Prepend action table count "AAAA" (4 chars) -> total 68 chars
|
|
63
|
-
return
|
|
104
|
+
return nibbles(raw_bytes)
|
|
64
105
|
|
|
65
106
|
@staticmethod
|
|
66
|
-
def
|
|
67
|
-
"""
|
|
107
|
+
def to_short_string(action_table: Xp24MsActionTable) -> list[str]:
|
|
108
|
+
"""
|
|
109
|
+
Serialize XP24 action table to humane compact readable format.
|
|
68
110
|
|
|
69
111
|
Args:
|
|
70
|
-
|
|
112
|
+
action_table: XP24 action table to serialize
|
|
71
113
|
|
|
72
114
|
Returns:
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
Raises:
|
|
76
|
-
ValueError: If data length is not 68 bytes.
|
|
115
|
+
Human-readable string describing XP24 action table
|
|
77
116
|
"""
|
|
78
|
-
|
|
79
|
-
if raw_length != 68:
|
|
80
|
-
raise ValueError(
|
|
81
|
-
f"Msactiontable is not 68 bytes long ({raw_length}): {msactiontable_rawdata}"
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
# Remove action table count AAAA, AAAB .
|
|
85
|
-
data = msactiontable_rawdata[4:]
|
|
86
|
-
|
|
87
|
-
# Take first 64 chars (32 bytes) as per pseudocode
|
|
88
|
-
hex_data = data[:64]
|
|
117
|
+
return action_table.to_short_format()
|
|
89
118
|
|
|
90
|
-
|
|
91
|
-
|
|
119
|
+
@staticmethod
|
|
120
|
+
def from_short_string(action_strings: list[str]) -> Xp24MsActionTable:
|
|
121
|
+
"""
|
|
122
|
+
Serialize XP24 action table to humane compact readable format.
|
|
92
123
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
for pos in range(4):
|
|
96
|
-
input_action = Xp24MsActionTableSerializer._decode_input_action(
|
|
97
|
-
raw_bytes, pos
|
|
98
|
-
)
|
|
99
|
-
input_actions.append(input_action)
|
|
124
|
+
Args:
|
|
125
|
+
action_strings: XP24 action table to serialize
|
|
100
126
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
input4_action=input_actions[3],
|
|
106
|
-
mutex12=raw_bytes[8] != 0, # With A-P encoding: AA=0 (False), AB=1 (True)
|
|
107
|
-
mutex34=raw_bytes[9] != 0,
|
|
108
|
-
mutual_deadtime=raw_bytes[10],
|
|
109
|
-
curtain12=raw_bytes[11] != 0,
|
|
110
|
-
curtain34=raw_bytes[12] != 0,
|
|
111
|
-
)
|
|
112
|
-
return action_table
|
|
127
|
+
Returns:
|
|
128
|
+
Human-readable string describing XP24 action table
|
|
129
|
+
"""
|
|
130
|
+
return Xp24MsActionTable.from_short_format(action_strings)
|
|
113
131
|
|
|
114
132
|
@staticmethod
|
|
115
|
-
def _decode_input_action(raw_bytes:
|
|
116
|
-
"""
|
|
133
|
+
def _decode_input_action(raw_bytes: bytes, pos: int) -> InputAction:
|
|
134
|
+
"""
|
|
135
|
+
Decode input action from raw bytes.
|
|
117
136
|
|
|
118
137
|
Args:
|
|
119
138
|
raw_bytes: Raw byte array containing action data.
|
|
@@ -5,16 +5,29 @@ from xp.models.actiontable.msactiontable_xp33 import (
|
|
|
5
5
|
Xp33Output,
|
|
6
6
|
Xp33Scene,
|
|
7
7
|
)
|
|
8
|
+
from xp.models.telegram.system_function import SystemFunction
|
|
8
9
|
from xp.models.telegram.timeparam_type import TimeParam
|
|
10
|
+
from xp.services.actiontable.serializer_protocol import ActionTableSerializerProtocol
|
|
9
11
|
from xp.utils.serialization import bits_to_byte, byte_to_bits, de_nibbles, nibbles
|
|
10
12
|
|
|
11
13
|
|
|
12
|
-
class Xp33MsActionTableSerializer:
|
|
14
|
+
class Xp33MsActionTableSerializer(ActionTableSerializerProtocol):
|
|
13
15
|
"""Handles serialization/deserialization of XP33 action tables to/from telegrams."""
|
|
14
16
|
|
|
15
17
|
@staticmethod
|
|
16
|
-
def
|
|
17
|
-
"""
|
|
18
|
+
def download_type() -> SystemFunction:
|
|
19
|
+
"""
|
|
20
|
+
Get the download system function type.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
The download system function: DOWNLOAD_MSACTIONTABLE
|
|
24
|
+
"""
|
|
25
|
+
return SystemFunction.DOWNLOAD_MSACTIONTABLE
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
28
|
+
def to_short_string(action_table: Xp33MsActionTable) -> list[str]:
|
|
29
|
+
"""
|
|
30
|
+
Serialize XP33 action table to humane compact readable format.
|
|
18
31
|
|
|
19
32
|
Args:
|
|
20
33
|
action_table: XP33 action table to serialize
|
|
@@ -24,6 +37,19 @@ class Xp33MsActionTableSerializer:
|
|
|
24
37
|
"""
|
|
25
38
|
return action_table.to_short_format()
|
|
26
39
|
|
|
40
|
+
@staticmethod
|
|
41
|
+
def from_short_string(action_strings: list[str]) -> Xp33MsActionTable:
|
|
42
|
+
"""
|
|
43
|
+
Serialize XP33 action table to humane compact readable format.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
action_strings: XP33 action table to serialize
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Human-readable string describing XP33 action table
|
|
50
|
+
"""
|
|
51
|
+
return Xp33MsActionTable.from_short_format(action_strings)
|
|
52
|
+
|
|
27
53
|
@staticmethod
|
|
28
54
|
def _percentage_to_byte(percentage: int) -> int:
|
|
29
55
|
"""Convert percentage (0-100) to byte value for telegram encoding."""
|
|
@@ -48,8 +74,9 @@ class Xp33MsActionTableSerializer:
|
|
|
48
74
|
return TimeParam.NONE
|
|
49
75
|
|
|
50
76
|
@staticmethod
|
|
51
|
-
def
|
|
52
|
-
"""
|
|
77
|
+
def to_encoded_string(action_table: Xp33MsActionTable) -> str:
|
|
78
|
+
"""
|
|
79
|
+
Serialize action table to telegram format.
|
|
53
80
|
|
|
54
81
|
Args:
|
|
55
82
|
action_table: XP33 MS action table to serialize.
|
|
@@ -112,11 +139,12 @@ class Xp33MsActionTableSerializer:
|
|
|
112
139
|
encoded_data = nibbles(raw_bytes)
|
|
113
140
|
|
|
114
141
|
# Convert raw bytes to hex string with A-P encoding
|
|
115
|
-
return
|
|
142
|
+
return encoded_data
|
|
116
143
|
|
|
117
144
|
@staticmethod
|
|
118
|
-
def
|
|
119
|
-
"""
|
|
145
|
+
def from_encoded_string(msactiontable_rawdata: str) -> Xp33MsActionTable:
|
|
146
|
+
"""
|
|
147
|
+
Deserialize action table from raw data parts.
|
|
120
148
|
|
|
121
149
|
Args:
|
|
122
150
|
msactiontable_rawdata: Raw action table data string.
|
|
@@ -125,22 +153,16 @@ class Xp33MsActionTableSerializer:
|
|
|
125
153
|
Deserialized XP33 MS action table.
|
|
126
154
|
|
|
127
155
|
Raises:
|
|
128
|
-
ValueError: If data length is less than
|
|
156
|
+
ValueError: If data length is less than 64 characters.
|
|
129
157
|
"""
|
|
130
158
|
raw_length = len(msactiontable_rawdata)
|
|
131
|
-
if raw_length <
|
|
159
|
+
if raw_length < 64: # Minimum: 4 char prefix + 64 chars data
|
|
132
160
|
raise ValueError(
|
|
133
|
-
f"Msactiontable is too short ({raw_length}), minimum
|
|
161
|
+
f"Msactiontable is too short ({raw_length}), minimum 64 characters required"
|
|
134
162
|
)
|
|
135
163
|
|
|
136
|
-
# Remove action table count prefix (first 4 characters: AAAA, AAAB, etc.)
|
|
137
|
-
data = msactiontable_rawdata[4:]
|
|
138
|
-
|
|
139
|
-
# Take first 64 chars (32 bytes) as per pseudocode
|
|
140
|
-
hex_data = data[:64]
|
|
141
|
-
|
|
142
164
|
# Convert hex string to bytes using deNibble (A-P encoding)
|
|
143
|
-
raw_bytes = de_nibbles(
|
|
165
|
+
raw_bytes = de_nibbles(msactiontable_rawdata)
|
|
144
166
|
|
|
145
167
|
# Decode outputs
|
|
146
168
|
output1 = Xp33MsActionTableSerializer._decode_output(raw_bytes, 0)
|
|
@@ -165,7 +187,8 @@ class Xp33MsActionTableSerializer:
|
|
|
165
187
|
|
|
166
188
|
@staticmethod
|
|
167
189
|
def _decode_output(raw_bytes: bytearray, output_index: int) -> Xp33Output:
|
|
168
|
-
"""
|
|
190
|
+
"""
|
|
191
|
+
Extract output configuration from raw bytes.
|
|
169
192
|
|
|
170
193
|
Args:
|
|
171
194
|
raw_bytes: Raw byte array containing output data.
|
|
@@ -219,7 +242,8 @@ class Xp33MsActionTableSerializer:
|
|
|
219
242
|
|
|
220
243
|
@staticmethod
|
|
221
244
|
def _decode_scene(raw_bytes: bytearray, scene_index: int) -> Xp33Scene:
|
|
222
|
-
"""
|
|
245
|
+
"""
|
|
246
|
+
Extract scene configuration from raw bytes.
|
|
223
247
|
|
|
224
248
|
Args:
|
|
225
249
|
raw_bytes: Raw byte array containing scene data.
|