conson-xp 1.37.0__py3-none-any.whl → 1.39.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.37.0.dist-info → conson_xp-1.39.0.dist-info}/METADATA +6 -1
- {conson_xp-1.37.0.dist-info → conson_xp-1.39.0.dist-info}/RECORD +43 -40
- xp/__init__.py +1 -1
- xp/cli/commands/__init__.py +2 -0
- xp/cli/commands/conbus/__init__.py +2 -0
- xp/cli/commands/conbus/conbus.py +26 -0
- xp/cli/commands/conbus/conbus_actiontable_commands.py +17 -8
- xp/cli/commands/conbus/conbus_event_commands.py +1 -7
- xp/cli/commands/conbus/conbus_export_commands.py +7 -6
- xp/cli/commands/conbus/conbus_msactiontable_commands.py +92 -9
- xp/models/actiontable/msactiontable.py +9 -0
- xp/models/actiontable/msactiontable_xp20.py +14 -14
- xp/models/actiontable/msactiontable_xp24.py +223 -11
- xp/models/actiontable/msactiontable_xp33.py +42 -14
- xp/models/conbus/conbus_export.py +6 -2
- xp/models/config/__init__.py +1 -0
- xp/models/{homekit/homekit_conson_config.py → config/conson_module_config.py} +3 -1
- xp/models/protocol/conbus_protocol.py +1 -1
- xp/services/actiontable/msactiontable_xp20_serializer.py +12 -0
- xp/services/actiontable/msactiontable_xp24_serializer.py +13 -1
- xp/services/actiontable/msactiontable_xp33_serializer.py +12 -0
- xp/services/conbus/actiontable/actiontable_download_service.py +8 -5
- xp/services/conbus/actiontable/actiontable_list_service.py +2 -1
- xp/services/conbus/actiontable/actiontable_show_service.py +2 -2
- xp/services/conbus/actiontable/actiontable_upload_service.py +1 -1
- xp/services/conbus/conbus_event_list_service.py +1 -1
- xp/services/conbus/conbus_export_service.py +1 -1
- xp/services/conbus/msactiontable/__init__.py +1 -0
- xp/services/conbus/{actiontable/msactiontable_service.py → msactiontable/msactiontable_download_service.py} +9 -5
- xp/services/conbus/msactiontable/msactiontable_list_service.py +92 -0
- xp/services/conbus/msactiontable/msactiontable_show_service.py +89 -0
- xp/services/homekit/homekit_config_validator.py +1 -1
- xp/services/homekit/homekit_conson_validator.py +1 -1
- xp/services/homekit/homekit_dimminglight.py +1 -1
- xp/services/homekit/homekit_lightbulb.py +1 -1
- xp/services/homekit/homekit_module_service.py +1 -1
- xp/services/homekit/homekit_outlet.py +1 -1
- xp/services/server/server_service.py +1 -1
- xp/services/term/state_monitor_service.py +1 -1
- xp/utils/dependencies.py +36 -14
- xp/connection/__init__.py +0 -13
- xp/connection/exceptions.py +0 -22
- {conson_xp-1.37.0.dist-info → conson_xp-1.39.0.dist-info}/WHEEL +0 -0
- {conson_xp-1.37.0.dist-info → conson_xp-1.39.0.dist-info}/entry_points.txt +0 -0
- {conson_xp-1.37.0.dist-info → conson_xp-1.39.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,26 +1,88 @@
|
|
|
1
1
|
"""XP24 Action Table models for input actions and settings."""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from typing import Any, ClassVar, Union
|
|
4
4
|
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
6
|
+
|
|
7
|
+
from xp.models.actiontable.msactiontable import MsActionTable
|
|
5
8
|
from xp.models.telegram.input_action_type import InputActionType
|
|
6
9
|
from xp.models.telegram.timeparam_type import TimeParam
|
|
7
10
|
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
class InputAction:
|
|
12
|
+
class InputAction(BaseModel):
|
|
11
13
|
"""Represents an input action with type and parameter.
|
|
12
14
|
|
|
13
15
|
Attributes:
|
|
16
|
+
model_config: Pydantic configuration to preserve enum objects.
|
|
14
17
|
type: The input action type.
|
|
15
18
|
param: Time parameter for the action.
|
|
16
19
|
"""
|
|
17
20
|
|
|
21
|
+
model_config = ConfigDict(use_enum_values=False)
|
|
22
|
+
|
|
18
23
|
type: InputActionType = InputActionType.TOGGLE
|
|
19
24
|
param: TimeParam = TimeParam.NONE
|
|
20
25
|
|
|
26
|
+
@field_validator("type", mode="before")
|
|
27
|
+
@classmethod
|
|
28
|
+
def validate_action_type(
|
|
29
|
+
cls, v: Union[str, int, InputActionType]
|
|
30
|
+
) -> InputActionType:
|
|
31
|
+
"""Convert string or int to InputActionType enum.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
v: Input value (can be string name, int value, or enum).
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
InputActionType enum value.
|
|
38
|
+
|
|
39
|
+
Raises:
|
|
40
|
+
ValueError: If the value cannot be converted to InputActionType.
|
|
41
|
+
"""
|
|
42
|
+
if isinstance(v, InputActionType):
|
|
43
|
+
return v
|
|
44
|
+
if isinstance(v, str):
|
|
45
|
+
try:
|
|
46
|
+
return InputActionType[v]
|
|
47
|
+
except KeyError:
|
|
48
|
+
raise ValueError(f"Invalid InputActionType: {v}")
|
|
49
|
+
if isinstance(v, int):
|
|
50
|
+
try:
|
|
51
|
+
return InputActionType(v)
|
|
52
|
+
except ValueError:
|
|
53
|
+
raise ValueError(f"Invalid InputActionType value: {v}")
|
|
54
|
+
raise ValueError(f"Invalid type for InputActionType: {type(v)}")
|
|
55
|
+
|
|
56
|
+
@field_validator("param", mode="before")
|
|
57
|
+
@classmethod
|
|
58
|
+
def validate_time_param(cls, v: Union[str, int, TimeParam]) -> TimeParam:
|
|
59
|
+
"""Convert string or int to TimeParam enum.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
v: Input value (can be string name, int value, or enum).
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
TimeParam enum value.
|
|
66
|
+
|
|
67
|
+
Raises:
|
|
68
|
+
ValueError: If the value cannot be converted to TimeParam.
|
|
69
|
+
"""
|
|
70
|
+
if isinstance(v, TimeParam):
|
|
71
|
+
return v
|
|
72
|
+
if isinstance(v, str):
|
|
73
|
+
try:
|
|
74
|
+
return TimeParam[v]
|
|
75
|
+
except KeyError:
|
|
76
|
+
raise ValueError(f"Invalid TimeParam: {v}")
|
|
77
|
+
if isinstance(v, int):
|
|
78
|
+
try:
|
|
79
|
+
return TimeParam(v)
|
|
80
|
+
except ValueError:
|
|
81
|
+
raise ValueError(f"Invalid TimeParam value: {v}")
|
|
82
|
+
raise ValueError(f"Invalid type for TimeParam: {type(v)}")
|
|
21
83
|
|
|
22
|
-
|
|
23
|
-
class Xp24MsActionTable:
|
|
84
|
+
|
|
85
|
+
class Xp24MsActionTable(MsActionTable):
|
|
24
86
|
"""XP24 Action Table for managing input actions and settings.
|
|
25
87
|
|
|
26
88
|
Each input has an action type (TOGGLE, ON, LEVELSET, etc.)
|
|
@@ -29,6 +91,8 @@ class Xp24MsActionTable:
|
|
|
29
91
|
Attributes:
|
|
30
92
|
MS300: Timing constant for 300ms.
|
|
31
93
|
MS500: Timing constant for 500ms.
|
|
94
|
+
ACTION_SHORT_CODES: Mapping from InputActionType to short code strings.
|
|
95
|
+
SHORT_CODE_TO_ACTION: Reverse mapping from short codes to InputActionType.
|
|
32
96
|
input1_action: Action configuration for input 1.
|
|
33
97
|
input2_action: Action configuration for input 2.
|
|
34
98
|
input3_action: Action configuration for input 3.
|
|
@@ -41,14 +105,52 @@ class Xp24MsActionTable:
|
|
|
41
105
|
"""
|
|
42
106
|
|
|
43
107
|
# MS timing constants
|
|
44
|
-
MS300 = 12
|
|
45
|
-
MS500 = 20
|
|
108
|
+
MS300: ClassVar[int] = 12
|
|
109
|
+
MS500: ClassVar[int] = 20
|
|
110
|
+
|
|
111
|
+
# Short format mapping for InputActionType
|
|
112
|
+
ACTION_SHORT_CODES: ClassVar[dict[InputActionType, str]] = {
|
|
113
|
+
InputActionType.VOID: "V",
|
|
114
|
+
InputActionType.ON: "ON",
|
|
115
|
+
InputActionType.OFF: "OF",
|
|
116
|
+
InputActionType.TOGGLE: "T",
|
|
117
|
+
InputActionType.BLOCK: "BL",
|
|
118
|
+
InputActionType.AUXRELAY: "AX",
|
|
119
|
+
InputActionType.MUTUALEX: "MX",
|
|
120
|
+
InputActionType.LEVELUP: "LU",
|
|
121
|
+
InputActionType.LEVELDOWN: "LD",
|
|
122
|
+
InputActionType.LEVELINC: "LI",
|
|
123
|
+
InputActionType.LEVELDEC: "LC",
|
|
124
|
+
InputActionType.LEVELSET: "LS",
|
|
125
|
+
InputActionType.FADETIME: "FT",
|
|
126
|
+
InputActionType.SCENESET: "SS",
|
|
127
|
+
InputActionType.SCENENEXT: "SN",
|
|
128
|
+
InputActionType.SCENEPREV: "SP",
|
|
129
|
+
InputActionType.CTRLMETHOD: "CM",
|
|
130
|
+
InputActionType.RETURNDATA: "RD",
|
|
131
|
+
InputActionType.DELAYEDON: "DO",
|
|
132
|
+
InputActionType.EVENTTIMER1: "E1",
|
|
133
|
+
InputActionType.EVENTTIMER2: "E2",
|
|
134
|
+
InputActionType.EVENTTIMER3: "E3",
|
|
135
|
+
InputActionType.EVENTTIMER4: "E4",
|
|
136
|
+
InputActionType.STEPCTRL: "SC",
|
|
137
|
+
InputActionType.STEPCTRLUP: "SU",
|
|
138
|
+
InputActionType.STEPCTRLDOWN: "SD",
|
|
139
|
+
InputActionType.LEVELSETINTERN: "LN",
|
|
140
|
+
InputActionType.FADE: "FD",
|
|
141
|
+
InputActionType.LEARN: "LR",
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# Reverse mapping for parsing
|
|
145
|
+
SHORT_CODE_TO_ACTION: ClassVar[dict[str, InputActionType]] = {
|
|
146
|
+
v: k for k, v in ACTION_SHORT_CODES.items()
|
|
147
|
+
}
|
|
46
148
|
|
|
47
149
|
# Input actions for each input (default to TOGGLE with None parameter)
|
|
48
|
-
input1_action: InputAction =
|
|
49
|
-
input2_action: InputAction =
|
|
50
|
-
input3_action: InputAction =
|
|
51
|
-
input4_action: InputAction =
|
|
150
|
+
input1_action: InputAction = Field(default_factory=InputAction)
|
|
151
|
+
input2_action: InputAction = Field(default_factory=InputAction)
|
|
152
|
+
input3_action: InputAction = Field(default_factory=InputAction)
|
|
153
|
+
input4_action: InputAction = Field(default_factory=InputAction)
|
|
52
154
|
|
|
53
155
|
# Boolean settings
|
|
54
156
|
mutex12: bool = False # Mutual exclusion between inputs 1-2
|
|
@@ -56,3 +158,113 @@ class Xp24MsActionTable:
|
|
|
56
158
|
curtain12: bool = False # Curtain setting for inputs 1-2
|
|
57
159
|
curtain34: bool = False # Curtain setting for inputs 3-4
|
|
58
160
|
mutual_deadtime: int = MS300 # Master timing (MS300=12 or MS500=20)
|
|
161
|
+
|
|
162
|
+
def to_short_format(self) -> str:
|
|
163
|
+
"""Convert action table to short format string.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
Short format string with settings (e.g., "XP24 T:1 T:2 T:0 T:0 | M12:0 M34:0 C12:0 C34:0 DT:12").
|
|
167
|
+
"""
|
|
168
|
+
# Format input actions
|
|
169
|
+
actions = [
|
|
170
|
+
self.input1_action,
|
|
171
|
+
self.input2_action,
|
|
172
|
+
self.input3_action,
|
|
173
|
+
self.input4_action,
|
|
174
|
+
]
|
|
175
|
+
|
|
176
|
+
action_parts = []
|
|
177
|
+
for action in actions:
|
|
178
|
+
short_code = self.ACTION_SHORT_CODES.get(action.type, "??")
|
|
179
|
+
param_value = action.param.value
|
|
180
|
+
action_parts.append(f"{short_code}:{param_value}")
|
|
181
|
+
|
|
182
|
+
result = f"XP24 {' '.join(action_parts)}"
|
|
183
|
+
|
|
184
|
+
# Add settings
|
|
185
|
+
settings = (
|
|
186
|
+
f"M12:{1 if self.mutex12 else 0} "
|
|
187
|
+
f"M34:{1 if self.mutex34 else 0} "
|
|
188
|
+
f"C12:{1 if self.curtain12 else 0} "
|
|
189
|
+
f"C34:{1 if self.curtain34 else 0} "
|
|
190
|
+
f"DT:{self.mutual_deadtime}"
|
|
191
|
+
)
|
|
192
|
+
result = f"{result} | {settings}"
|
|
193
|
+
|
|
194
|
+
return result
|
|
195
|
+
|
|
196
|
+
@classmethod
|
|
197
|
+
def from_short_format(cls, short_str: str) -> "Xp24MsActionTable":
|
|
198
|
+
"""Parse short format string into action table.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
short_str: Short format string.
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
Xp24MsActionTable instance.
|
|
205
|
+
|
|
206
|
+
Raises:
|
|
207
|
+
ValueError: If format is invalid.
|
|
208
|
+
"""
|
|
209
|
+
# Split by pipe to separate actions from settings
|
|
210
|
+
parts = short_str.split("|")
|
|
211
|
+
action_part = parts[0].strip()
|
|
212
|
+
settings_part = parts[1].strip()
|
|
213
|
+
|
|
214
|
+
# Parse action part
|
|
215
|
+
tokens = action_part.split()
|
|
216
|
+
if len(tokens) != 5 or tokens[0] != "XP24":
|
|
217
|
+
raise ValueError(
|
|
218
|
+
f"Invalid short format: expected 'XP24 <a1> <a2> <a3> <a4>', got '{action_part}'"
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
# Parse input actions
|
|
222
|
+
input_actions = []
|
|
223
|
+
for i, token in enumerate(tokens[1:5], 1):
|
|
224
|
+
if ":" not in token:
|
|
225
|
+
raise ValueError(f"Invalid action format at position {i}: '{token}'")
|
|
226
|
+
|
|
227
|
+
code, param_str = token.split(":", 1)
|
|
228
|
+
|
|
229
|
+
# Look up action type
|
|
230
|
+
if code not in cls.SHORT_CODE_TO_ACTION:
|
|
231
|
+
raise ValueError(f"Unknown action code: '{code}'")
|
|
232
|
+
|
|
233
|
+
action_type = cls.SHORT_CODE_TO_ACTION[code]
|
|
234
|
+
|
|
235
|
+
# Parse param
|
|
236
|
+
try:
|
|
237
|
+
param_value = int(param_str)
|
|
238
|
+
param_type = TimeParam(param_value)
|
|
239
|
+
except (ValueError, KeyError):
|
|
240
|
+
raise ValueError(f"Invalid time param: '{param_str}'")
|
|
241
|
+
|
|
242
|
+
input_actions.append(InputAction(type=action_type, param=param_type))
|
|
243
|
+
|
|
244
|
+
# Parse settings if present
|
|
245
|
+
kwargs: dict[str, Any] = {
|
|
246
|
+
"input1_action": input_actions[0],
|
|
247
|
+
"input2_action": input_actions[1],
|
|
248
|
+
"input3_action": input_actions[2],
|
|
249
|
+
"input4_action": input_actions[3],
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
# Parse settings: M12:0 M34:1 C12:0 C34:1 DT:12
|
|
253
|
+
for setting in settings_part.split():
|
|
254
|
+
if ":" not in setting:
|
|
255
|
+
continue
|
|
256
|
+
|
|
257
|
+
key, value = setting.split(":", 1)
|
|
258
|
+
|
|
259
|
+
if key == "M12":
|
|
260
|
+
kwargs["mutex12"] = value == "1"
|
|
261
|
+
elif key == "M34":
|
|
262
|
+
kwargs["mutex34"] = value == "1"
|
|
263
|
+
elif key == "C12":
|
|
264
|
+
kwargs["curtain12"] = value == "1"
|
|
265
|
+
elif key == "C34":
|
|
266
|
+
kwargs["curtain34"] = value == "1"
|
|
267
|
+
elif key == "DT":
|
|
268
|
+
kwargs["mutual_deadtime"] = int(value)
|
|
269
|
+
|
|
270
|
+
return cls(**kwargs)
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
"""XP33 Action Table models for output and scene configuration."""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from typing import Union
|
|
4
4
|
|
|
5
|
+
from pydantic import BaseModel, Field, field_validator
|
|
6
|
+
|
|
7
|
+
from xp.models.actiontable.msactiontable import MsActionTable
|
|
5
8
|
from xp.models.telegram.timeparam_type import TimeParam
|
|
6
9
|
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
class Xp33Output:
|
|
11
|
+
class Xp33Output(BaseModel):
|
|
10
12
|
"""Represents an XP33 output configuration.
|
|
11
13
|
|
|
12
14
|
Attributes:
|
|
@@ -24,8 +26,7 @@ class Xp33Output:
|
|
|
24
26
|
leading_edge: bool = False
|
|
25
27
|
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
class Xp33Scene:
|
|
29
|
+
class Xp33Scene(BaseModel):
|
|
29
30
|
"""Represents a scene configuration.
|
|
30
31
|
|
|
31
32
|
Attributes:
|
|
@@ -40,9 +41,36 @@ class Xp33Scene:
|
|
|
40
41
|
output3_level: int = 0
|
|
41
42
|
time: TimeParam = TimeParam.NONE
|
|
42
43
|
|
|
44
|
+
@field_validator("time", mode="before")
|
|
45
|
+
@classmethod
|
|
46
|
+
def validate_time_param(cls, v: Union[str, int, TimeParam]) -> TimeParam:
|
|
47
|
+
"""Convert string or int to TimeParam enum.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
v: Input value (can be string name, int value, or enum).
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
TimeParam enum value.
|
|
54
|
+
|
|
55
|
+
Raises:
|
|
56
|
+
ValueError: If the value cannot be converted to TimeParam.
|
|
57
|
+
"""
|
|
58
|
+
if isinstance(v, TimeParam):
|
|
59
|
+
return v
|
|
60
|
+
if isinstance(v, str):
|
|
61
|
+
try:
|
|
62
|
+
return TimeParam[v]
|
|
63
|
+
except KeyError:
|
|
64
|
+
raise ValueError(f"Invalid TimeParam: {v}")
|
|
65
|
+
if isinstance(v, int):
|
|
66
|
+
try:
|
|
67
|
+
return TimeParam(v)
|
|
68
|
+
except ValueError:
|
|
69
|
+
raise ValueError(f"Invalid TimeParam value: {v}")
|
|
70
|
+
raise ValueError(f"Invalid type for TimeParam: {type(v)}")
|
|
71
|
+
|
|
43
72
|
|
|
44
|
-
|
|
45
|
-
class Xp33MsActionTable:
|
|
73
|
+
class Xp33MsActionTable(MsActionTable):
|
|
46
74
|
"""XP33 Action Table for managing outputs and scenes.
|
|
47
75
|
|
|
48
76
|
Attributes:
|
|
@@ -55,11 +83,11 @@ class Xp33MsActionTable:
|
|
|
55
83
|
scene4: Configuration for scene 4.
|
|
56
84
|
"""
|
|
57
85
|
|
|
58
|
-
output1: Xp33Output =
|
|
59
|
-
output2: Xp33Output =
|
|
60
|
-
output3: Xp33Output =
|
|
86
|
+
output1: Xp33Output = Field(default_factory=Xp33Output)
|
|
87
|
+
output2: Xp33Output = Field(default_factory=Xp33Output)
|
|
88
|
+
output3: Xp33Output = Field(default_factory=Xp33Output)
|
|
61
89
|
|
|
62
|
-
scene1: Xp33Scene =
|
|
63
|
-
scene2: Xp33Scene =
|
|
64
|
-
scene3: Xp33Scene =
|
|
65
|
-
scene4: Xp33Scene =
|
|
90
|
+
scene1: Xp33Scene = Field(default_factory=Xp33Scene)
|
|
91
|
+
scene2: Xp33Scene = Field(default_factory=Xp33Scene)
|
|
92
|
+
scene3: Xp33Scene = Field(default_factory=Xp33Scene)
|
|
93
|
+
scene4: Xp33Scene = Field(default_factory=Xp33Scene)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
4
|
from typing import Optional
|
|
5
5
|
|
|
6
|
-
from xp.models.
|
|
6
|
+
from xp.models.config.conson_module_config import ConsonModuleListConfig
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
@dataclass
|
|
@@ -14,8 +14,10 @@ class ConbusExportResponse:
|
|
|
14
14
|
success: Whether the operation was successful.
|
|
15
15
|
config: Exported module configuration list.
|
|
16
16
|
device_count: Number of devices exported.
|
|
17
|
+
actiontable_count: Number of action tables downloaded.
|
|
18
|
+
actiontable_failed: Number of action table downloads that failed.
|
|
17
19
|
output_file: Path to output file.
|
|
18
|
-
export_status: Export status (OK,
|
|
20
|
+
export_status: Export status (OK, PARTIAL_ACTIONTABLE, FAILED_TIMEOUT, etc.).
|
|
19
21
|
error: Error message if operation failed.
|
|
20
22
|
sent_telegrams: List of telegrams sent during export.
|
|
21
23
|
received_telegrams: List of telegrams received during export.
|
|
@@ -24,6 +26,8 @@ class ConbusExportResponse:
|
|
|
24
26
|
success: bool
|
|
25
27
|
config: Optional[ConsonModuleListConfig] = None
|
|
26
28
|
device_count: int = 0
|
|
29
|
+
actiontable_count: int = 0
|
|
30
|
+
actiontable_failed: int = 0
|
|
27
31
|
output_file: str = "export.yml"
|
|
28
32
|
export_status: str = "OK"
|
|
29
33
|
error: Optional[str] = None
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Configuration models package."""
|
|
@@ -23,6 +23,7 @@ class ConsonModuleConfig(BaseModel):
|
|
|
23
23
|
sw_version: Optional software version.
|
|
24
24
|
hw_version: Optional hardware version.
|
|
25
25
|
action_table: Optional action table configuration.
|
|
26
|
+
msaction_table: Optional ms action table configuration.
|
|
26
27
|
auto_report_status: Optional auto report status.
|
|
27
28
|
"""
|
|
28
29
|
|
|
@@ -37,8 +38,9 @@ class ConsonModuleConfig(BaseModel):
|
|
|
37
38
|
conbus_port: Optional[int] = None
|
|
38
39
|
sw_version: Optional[str] = None
|
|
39
40
|
hw_version: Optional[str] = None
|
|
40
|
-
action_table: Optional[List[str]] = None
|
|
41
41
|
auto_report_status: Optional[str] = None
|
|
42
|
+
action_table: Optional[List[str]] = None
|
|
43
|
+
msaction_table: Optional[str] = None
|
|
42
44
|
|
|
43
45
|
|
|
44
46
|
class ConsonModuleListConfig(BaseModel):
|
|
@@ -7,8 +7,8 @@ from typing import TYPE_CHECKING, Union
|
|
|
7
7
|
from bubus import BaseEvent
|
|
8
8
|
from pydantic import Field
|
|
9
9
|
|
|
10
|
+
from xp.models.config.conson_module_config import ConsonModuleConfig
|
|
10
11
|
from xp.models.homekit.homekit_config import HomekitAccessoryConfig
|
|
11
|
-
from xp.models.homekit.homekit_conson_config import ConsonModuleConfig
|
|
12
12
|
from xp.models.telegram.datapoint_type import DataPointType
|
|
13
13
|
|
|
14
14
|
if TYPE_CHECKING:
|
|
@@ -15,6 +15,18 @@ TA_FUNCTION_INDEX: int = 12
|
|
|
15
15
|
class Xp20MsActionTableSerializer:
|
|
16
16
|
"""Handles serialization/deserialization of XP20 action tables to/from telegrams."""
|
|
17
17
|
|
|
18
|
+
@staticmethod
|
|
19
|
+
def format_decoded_output(action_table: Xp20MsActionTable) -> str:
|
|
20
|
+
"""Serialize XP20 action table to humane compact readable format.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
action_table: XP20 action table to serialize
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Human-readable string describing XP20 action table
|
|
27
|
+
"""
|
|
28
|
+
return ""
|
|
29
|
+
|
|
18
30
|
@staticmethod
|
|
19
31
|
def to_data(action_table: Xp20MsActionTable) -> str:
|
|
20
32
|
"""Serialize XP20 action table to telegram hex string format.
|
|
@@ -9,6 +9,18 @@ from xp.utils.serialization import de_nibbles, nibbles
|
|
|
9
9
|
class Xp24MsActionTableSerializer:
|
|
10
10
|
"""Handles serialization/deserialization of XP24 action tables to/from telegrams."""
|
|
11
11
|
|
|
12
|
+
@staticmethod
|
|
13
|
+
def format_decoded_output(action_table: Xp24MsActionTable) -> str:
|
|
14
|
+
"""Serialize XP24 action table to humane compact readable format.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
action_table: XP24 action table to serialize
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Human-readable string describing XP24 action table
|
|
21
|
+
"""
|
|
22
|
+
return action_table.to_short_format()
|
|
23
|
+
|
|
12
24
|
@staticmethod
|
|
13
25
|
def to_data(action_table: Xp24MsActionTable) -> str:
|
|
14
26
|
"""Serialize action table to telegram format.
|
|
@@ -117,4 +129,4 @@ class Xp24MsActionTableSerializer:
|
|
|
117
129
|
action_type = InputActionType(function_id)
|
|
118
130
|
param_type = TimeParam(param_id)
|
|
119
131
|
|
|
120
|
-
return InputAction(action_type, param_type)
|
|
132
|
+
return InputAction(type=action_type, param=param_type)
|
|
@@ -12,6 +12,18 @@ from xp.utils.serialization import bits_to_byte, byte_to_bits, de_nibbles, nibbl
|
|
|
12
12
|
class Xp33MsActionTableSerializer:
|
|
13
13
|
"""Handles serialization/deserialization of XP33 action tables to/from telegrams."""
|
|
14
14
|
|
|
15
|
+
@staticmethod
|
|
16
|
+
def format_decoded_output(action_table: Xp33MsActionTable) -> str:
|
|
17
|
+
"""Serialize XP33 action table to humane compact readable format.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
action_table: XP33 action table to serialize
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Human-readable string describing XP33 action table
|
|
24
|
+
"""
|
|
25
|
+
return ""
|
|
26
|
+
|
|
15
27
|
@staticmethod
|
|
16
28
|
def _percentage_to_byte(percentage: int) -> int:
|
|
17
29
|
"""Convert percentage (0-100) to byte value for telegram encoding."""
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
from dataclasses import asdict
|
|
5
|
-
from typing import Any, Optional
|
|
5
|
+
from typing import Any, Dict, Optional
|
|
6
6
|
|
|
7
7
|
from psygnal import Signal
|
|
8
8
|
|
|
9
|
+
from xp.models.actiontable.actiontable import ActionTable
|
|
9
10
|
from xp.models.protocol.conbus_protocol import TelegramReceivedEvent
|
|
10
11
|
from xp.models.telegram.system_function import SystemFunction
|
|
11
12
|
from xp.models.telegram.telegram_type import TelegramType
|
|
@@ -14,7 +15,7 @@ from xp.services.protocol.conbus_event_protocol import ConbusEventProtocol
|
|
|
14
15
|
from xp.services.telegram.telegram_service import TelegramService
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
class
|
|
18
|
+
class ActionTableDownloadService:
|
|
18
19
|
"""TCP client service for downloading action tables from Conbus modules.
|
|
19
20
|
|
|
20
21
|
Manages TCP socket connections, handles telegram generation and transmission,
|
|
@@ -28,7 +29,9 @@ class ActionTableService:
|
|
|
28
29
|
|
|
29
30
|
on_progress: Signal = Signal(str)
|
|
30
31
|
on_error: Signal = Signal(str)
|
|
31
|
-
on_finish: Signal = Signal(
|
|
32
|
+
on_finish: Signal = Signal(
|
|
33
|
+
ActionTable, Dict[str, Any], list[str]
|
|
34
|
+
) # (ActionTable, Dict[str, Any], list[str])
|
|
32
35
|
|
|
33
36
|
def __init__(
|
|
34
37
|
self,
|
|
@@ -123,7 +126,7 @@ class ActionTableService:
|
|
|
123
126
|
actiontable = self.serializer.from_encoded_string(all_data)
|
|
124
127
|
actiontable_dict = asdict(actiontable)
|
|
125
128
|
actiontable_short = self.serializer.format_decoded_output(actiontable)
|
|
126
|
-
self.on_finish.emit(
|
|
129
|
+
self.on_finish.emit(actiontable, actiontable_dict, actiontable_short)
|
|
127
130
|
|
|
128
131
|
def timeout(self) -> None:
|
|
129
132
|
"""Handle timeout event."""
|
|
@@ -172,7 +175,7 @@ class ActionTableService:
|
|
|
172
175
|
"""Stop the reactor."""
|
|
173
176
|
self.conbus_protocol.stop_reactor()
|
|
174
177
|
|
|
175
|
-
def __enter__(self) -> "
|
|
178
|
+
def __enter__(self) -> "ActionTableDownloadService":
|
|
176
179
|
"""Enter context manager - reset state for singleton reuse.
|
|
177
180
|
|
|
178
181
|
Returns:
|
|
@@ -59,7 +59,7 @@ class ActionTableListService:
|
|
|
59
59
|
|
|
60
60
|
# Load configuration
|
|
61
61
|
try:
|
|
62
|
-
from xp.models.
|
|
62
|
+
from xp.models.config.conson_module_config import ConsonModuleListConfig
|
|
63
63
|
|
|
64
64
|
config = ConsonModuleListConfig.from_yaml(str(config_path))
|
|
65
65
|
except Exception as e:
|
|
@@ -72,6 +72,7 @@ class ActionTableListService:
|
|
|
72
72
|
{
|
|
73
73
|
"serial_number": module.serial_number,
|
|
74
74
|
"module_type": module.module_type,
|
|
75
|
+
"action_table": len(module.action_table) if module.action_table else 0,
|
|
75
76
|
}
|
|
76
77
|
for module in config.root
|
|
77
78
|
]
|
|
@@ -4,7 +4,7 @@ import logging
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from typing import Any, Callable, Optional
|
|
6
6
|
|
|
7
|
-
from xp.models.
|
|
7
|
+
from xp.models.config.conson_module_config import ConsonModuleConfig
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class ActionTableShowService:
|
|
@@ -61,7 +61,7 @@ class ActionTableShowService:
|
|
|
61
61
|
|
|
62
62
|
# Load configuration
|
|
63
63
|
try:
|
|
64
|
-
from xp.models.
|
|
64
|
+
from xp.models.config.conson_module_config import ConsonModuleListConfig
|
|
65
65
|
|
|
66
66
|
config = ConsonModuleListConfig.from_yaml(str(config_path))
|
|
67
67
|
except Exception as e:
|
|
@@ -5,7 +5,7 @@ from typing import Any, Optional
|
|
|
5
5
|
|
|
6
6
|
from psygnal import Signal
|
|
7
7
|
|
|
8
|
-
from xp.models.
|
|
8
|
+
from xp.models.config.conson_module_config import ConsonModuleListConfig
|
|
9
9
|
from xp.models.protocol.conbus_protocol import TelegramReceivedEvent
|
|
10
10
|
from xp.models.telegram.system_function import SystemFunction
|
|
11
11
|
from xp.models.telegram.telegram_type import TelegramType
|
|
@@ -9,7 +9,7 @@ from collections import defaultdict
|
|
|
9
9
|
from typing import Dict, List
|
|
10
10
|
|
|
11
11
|
from xp.models import ConbusEventListResponse
|
|
12
|
-
from xp.models.
|
|
12
|
+
from xp.models.config.conson_module_config import ConsonModuleListConfig
|
|
13
13
|
from xp.services.actiontable.actiontable_serializer import ActionTableSerializer
|
|
14
14
|
|
|
15
15
|
|
|
@@ -9,7 +9,7 @@ import yaml
|
|
|
9
9
|
from psygnal import Signal
|
|
10
10
|
|
|
11
11
|
from xp.models.conbus.conbus_export import ConbusExportResponse
|
|
12
|
-
from xp.models.
|
|
12
|
+
from xp.models.config.conson_module_config import (
|
|
13
13
|
ConsonModuleConfig,
|
|
14
14
|
ConsonModuleListConfig,
|
|
15
15
|
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""MsAction table services for Conbus."""
|
|
@@ -5,6 +5,7 @@ from typing import Any, Optional, Union
|
|
|
5
5
|
|
|
6
6
|
from psygnal import Signal
|
|
7
7
|
|
|
8
|
+
from xp.models.actiontable.msactiontable import MsActionTable
|
|
8
9
|
from xp.models.actiontable.msactiontable_xp20 import Xp20MsActionTable
|
|
9
10
|
from xp.models.actiontable.msactiontable_xp24 import Xp24MsActionTable
|
|
10
11
|
from xp.models.actiontable.msactiontable_xp33 import Xp33MsActionTable
|
|
@@ -30,7 +31,7 @@ class MsActionTableError(Exception):
|
|
|
30
31
|
pass
|
|
31
32
|
|
|
32
33
|
|
|
33
|
-
class
|
|
34
|
+
class MsActionTableDownloadService:
|
|
34
35
|
"""
|
|
35
36
|
Service for downloading MS action tables via Conbus protocol.
|
|
36
37
|
|
|
@@ -46,7 +47,7 @@ class MsActionTableService:
|
|
|
46
47
|
|
|
47
48
|
on_progress: Signal = Signal(str)
|
|
48
49
|
on_error: Signal = Signal(str)
|
|
49
|
-
on_finish: Signal = Signal(
|
|
50
|
+
on_finish: Signal = Signal(MsActionTable, str) # Union type for Xp20/24/33 or None
|
|
50
51
|
|
|
51
52
|
def __init__(
|
|
52
53
|
self,
|
|
@@ -164,7 +165,8 @@ class MsActionTableService:
|
|
|
164
165
|
all_data = "".join(self.msactiontable_data)
|
|
165
166
|
# Deserialize from received data
|
|
166
167
|
msactiontable = self.serializer.from_data(all_data)
|
|
167
|
-
self.
|
|
168
|
+
msactiontable_short = self.serializer.format_decoded_output(msactiontable) # type: ignore[arg-type]
|
|
169
|
+
self.succeed(msactiontable, msactiontable_short)
|
|
168
170
|
return
|
|
169
171
|
|
|
170
172
|
self.logger.debug("Invalid msactiontable response")
|
|
@@ -186,13 +188,15 @@ class MsActionTableService:
|
|
|
186
188
|
def succeed(
|
|
187
189
|
self,
|
|
188
190
|
msactiontable: Union[Xp20MsActionTable, Xp24MsActionTable, Xp33MsActionTable],
|
|
191
|
+
msactiontable_short: str,
|
|
189
192
|
) -> None:
|
|
190
193
|
"""Handle succeed connection event.
|
|
191
194
|
|
|
192
195
|
Args:
|
|
193
196
|
msactiontable: result.
|
|
197
|
+
msactiontable_short: result in short form.
|
|
194
198
|
"""
|
|
195
|
-
self.on_finish.emit(msactiontable)
|
|
199
|
+
self.on_finish.emit(msactiontable, msactiontable_short)
|
|
196
200
|
|
|
197
201
|
def start(
|
|
198
202
|
self,
|
|
@@ -241,7 +245,7 @@ class MsActionTableService:
|
|
|
241
245
|
"""Stop the reactor."""
|
|
242
246
|
self.conbus_protocol.stop_reactor()
|
|
243
247
|
|
|
244
|
-
def __enter__(self) -> "
|
|
248
|
+
def __enter__(self) -> "MsActionTableDownloadService":
|
|
245
249
|
"""Enter context manager.
|
|
246
250
|
|
|
247
251
|
Returns:
|