conson-xp 1.36.0__py3-none-any.whl → 1.38.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.
Files changed (51) hide show
  1. {conson_xp-1.36.0.dist-info → conson_xp-1.38.0.dist-info}/METADATA +6 -1
  2. {conson_xp-1.36.0.dist-info → conson_xp-1.38.0.dist-info}/RECORD +51 -46
  3. xp/__init__.py +1 -1
  4. xp/cli/commands/__init__.py +2 -0
  5. xp/cli/commands/conbus/__init__.py +2 -0
  6. xp/cli/commands/conbus/conbus.py +26 -0
  7. xp/cli/commands/conbus/conbus_actiontable_commands.py +18 -9
  8. xp/cli/commands/conbus/conbus_autoreport_commands.py +5 -2
  9. xp/cli/commands/conbus/conbus_event_commands.py +1 -7
  10. xp/cli/commands/conbus/conbus_export_commands.py +7 -6
  11. xp/cli/commands/conbus/conbus_lightlevel_commands.py +2 -0
  12. xp/cli/commands/conbus/conbus_linknumber_commands.py +2 -0
  13. xp/cli/commands/conbus/conbus_modulenumber_commands.py +2 -0
  14. xp/cli/commands/conbus/conbus_msactiontable_commands.py +92 -9
  15. xp/cli/commands/conbus/conbus_output_commands.py +4 -0
  16. xp/models/actiontable/msactiontable.py +9 -0
  17. xp/models/actiontable/msactiontable_xp20.py +14 -14
  18. xp/models/actiontable/msactiontable_xp24.py +228 -11
  19. xp/models/actiontable/msactiontable_xp33.py +42 -14
  20. xp/models/conbus/conbus_export.py +6 -2
  21. xp/models/config/__init__.py +1 -0
  22. xp/models/{homekit/homekit_conson_config.py → config/conson_module_config.py} +3 -1
  23. xp/models/protocol/conbus_protocol.py +1 -1
  24. xp/models/telegram/datapoint_type.py +30 -0
  25. xp/services/actiontable/msactiontable_xp20_serializer.py +12 -0
  26. xp/services/actiontable/msactiontable_xp24_serializer.py +13 -1
  27. xp/services/actiontable/msactiontable_xp33_serializer.py +12 -0
  28. xp/services/conbus/actiontable/actiontable_download_service.py +8 -5
  29. xp/services/conbus/actiontable/actiontable_list_service.py +2 -1
  30. xp/services/conbus/actiontable/actiontable_show_service.py +2 -2
  31. xp/services/conbus/actiontable/actiontable_upload_service.py +1 -1
  32. xp/services/conbus/conbus_datapoint_service.py +1 -1
  33. xp/services/conbus/conbus_event_list_service.py +1 -1
  34. xp/services/conbus/conbus_export_service.py +1 -1
  35. xp/services/conbus/msactiontable/__init__.py +1 -0
  36. xp/services/conbus/{actiontable/msactiontable_service.py → msactiontable/msactiontable_download_service.py} +9 -6
  37. xp/services/conbus/msactiontable/msactiontable_list_service.py +92 -0
  38. xp/services/conbus/msactiontable/msactiontable_show_service.py +89 -0
  39. xp/services/homekit/homekit_config_validator.py +1 -1
  40. xp/services/homekit/homekit_conson_validator.py +1 -1
  41. xp/services/homekit/homekit_dimminglight.py +1 -1
  42. xp/services/homekit/homekit_lightbulb.py +1 -1
  43. xp/services/homekit/homekit_module_service.py +1 -1
  44. xp/services/homekit/homekit_outlet.py +1 -1
  45. xp/services/protocol/conbus_event_protocol.py +7 -3
  46. xp/services/server/server_service.py +1 -1
  47. xp/services/term/state_monitor_service.py +1 -1
  48. xp/utils/dependencies.py +36 -14
  49. {conson_xp-1.36.0.dist-info → conson_xp-1.38.0.dist-info}/WHEEL +0 -0
  50. {conson_xp-1.36.0.dist-info → conson_xp-1.38.0.dist-info}/entry_points.txt +0 -0
  51. {conson_xp-1.36.0.dist-info → conson_xp-1.38.0.dist-info}/licenses/LICENSE +0 -0
@@ -121,6 +121,7 @@ def xp_output_status(ctx: click.Context, serial_number: str) -> None:
121
121
  response: Datapoint response object.
122
122
  """
123
123
  click.echo(json.dumps(response.to_dict(), indent=2))
124
+ service.stop_reactor()
124
125
 
125
126
  with service:
126
127
  service.on_finish.connect(on_finish)
@@ -128,6 +129,7 @@ def xp_output_status(ctx: click.Context, serial_number: str) -> None:
128
129
  serial_number=serial_number,
129
130
  datapoint_type=DataPointType.MODULE_OUTPUT_STATE,
130
131
  )
132
+ service.start_reactor()
131
133
 
132
134
 
133
135
  @conbus_output.command("state")
@@ -156,6 +158,7 @@ def xp_module_state(ctx: click.Context, serial_number: str) -> None:
156
158
  response: Datapoint response object.
157
159
  """
158
160
  click.echo(json.dumps(response.to_dict(), indent=2))
161
+ service.stop_reactor()
159
162
 
160
163
  with service:
161
164
  service.on_finish.connect(on_finish)
@@ -163,3 +166,4 @@ def xp_module_state(ctx: click.Context, serial_number: str) -> None:
163
166
  serial_number=serial_number,
164
167
  datapoint_type=DataPointType.MODULE_STATE,
165
168
  )
169
+ service.start_reactor()
@@ -0,0 +1,9 @@
1
+ """Base class for MS Action Table models."""
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class MsActionTable(BaseModel):
7
+ """Base class for all MS Action Table types (XP20, XP24, XP33)."""
8
+
9
+ pass
@@ -1,10 +1,11 @@
1
1
  """XP20 Action Table models for input actions and settings."""
2
2
 
3
- from dataclasses import dataclass, field
3
+ from pydantic import BaseModel, Field
4
4
 
5
+ from xp.models.actiontable.msactiontable import MsActionTable
5
6
 
6
- @dataclass
7
- class InputChannel:
7
+
8
+ class InputChannel(BaseModel):
8
9
  """Configuration for a single input channel in XP20 action table.
9
10
 
10
11
  Attributes:
@@ -19,13 +20,12 @@ class InputChannel:
19
20
  invert: bool = False
20
21
  short_long: bool = False
21
22
  group_on_off: bool = False
22
- and_functions: list[bool] = field(default_factory=lambda: [False] * 8)
23
+ and_functions: list[bool] = Field(default_factory=lambda: [False] * 8)
23
24
  sa_function: bool = False
24
25
  ta_function: bool = False
25
26
 
26
27
 
27
- @dataclass
28
- class Xp20MsActionTable:
28
+ class Xp20MsActionTable(MsActionTable):
29
29
  """XP20 Action Table for managing 8 input channels.
30
30
 
31
31
  Contains configuration for 8 input channels (input1 through input8),
@@ -43,11 +43,11 @@ class Xp20MsActionTable:
43
43
  input8: Configuration for input channel 8.
44
44
  """
45
45
 
46
- input1: InputChannel = field(default_factory=InputChannel)
47
- input2: InputChannel = field(default_factory=InputChannel)
48
- input3: InputChannel = field(default_factory=InputChannel)
49
- input4: InputChannel = field(default_factory=InputChannel)
50
- input5: InputChannel = field(default_factory=InputChannel)
51
- input6: InputChannel = field(default_factory=InputChannel)
52
- input7: InputChannel = field(default_factory=InputChannel)
53
- input8: InputChannel = field(default_factory=InputChannel)
46
+ input1: InputChannel = Field(default_factory=InputChannel)
47
+ input2: InputChannel = Field(default_factory=InputChannel)
48
+ input3: InputChannel = Field(default_factory=InputChannel)
49
+ input4: InputChannel = Field(default_factory=InputChannel)
50
+ input5: InputChannel = Field(default_factory=InputChannel)
51
+ input6: InputChannel = Field(default_factory=InputChannel)
52
+ input7: InputChannel = Field(default_factory=InputChannel)
53
+ input8: InputChannel = Field(default_factory=InputChannel)
@@ -1,26 +1,88 @@
1
1
  """XP24 Action Table models for input actions and settings."""
2
2
 
3
- from dataclasses import dataclass, field
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
- @dataclass
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
- @dataclass
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 = field(default_factory=InputAction)
49
- input2_action: InputAction = field(default_factory=InputAction)
50
- input3_action: InputAction = field(default_factory=InputAction)
51
- input4_action: InputAction = field(default_factory=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,118 @@ 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, include_settings: bool = False) -> str:
163
+ """Convert action table to short format string.
164
+
165
+ Args:
166
+ include_settings: Include settings after pipe separator.
167
+
168
+ Returns:
169
+ Short format string (e.g., "XP24 T:1 T:2 T:0 T:0").
170
+ """
171
+ # Format input actions
172
+ actions = [
173
+ self.input1_action,
174
+ self.input2_action,
175
+ self.input3_action,
176
+ self.input4_action,
177
+ ]
178
+
179
+ action_parts = []
180
+ for action in actions:
181
+ short_code = self.ACTION_SHORT_CODES.get(action.type, "??")
182
+ param_value = action.param.value
183
+ action_parts.append(f"{short_code}:{param_value}")
184
+
185
+ result = f"XP24 {' '.join(action_parts)}"
186
+
187
+ # Add settings if requested
188
+ if include_settings:
189
+ settings = (
190
+ f"M12:{1 if self.mutex12 else 0} "
191
+ f"M34:{1 if self.mutex34 else 0} "
192
+ f"C12:{1 if self.curtain12 else 0} "
193
+ f"C34:{1 if self.curtain34 else 0} "
194
+ f"DT:{self.mutual_deadtime}"
195
+ )
196
+ result = f"{result} | {settings}"
197
+
198
+ return result
199
+
200
+ @classmethod
201
+ def from_short_format(cls, short_str: str) -> "Xp24MsActionTable":
202
+ """Parse short format string into action table.
203
+
204
+ Args:
205
+ short_str: Short format string.
206
+
207
+ Returns:
208
+ Xp24MsActionTable instance.
209
+
210
+ Raises:
211
+ ValueError: If format is invalid.
212
+ """
213
+ # Split by pipe to separate actions from settings
214
+ parts = short_str.split("|")
215
+ action_part = parts[0].strip()
216
+ settings_part = parts[1].strip() if len(parts) > 1 else None
217
+
218
+ # Parse action part
219
+ tokens = action_part.split()
220
+ if len(tokens) != 5 or tokens[0] != "XP24":
221
+ raise ValueError(
222
+ f"Invalid short format: expected 'XP24 <a1> <a2> <a3> <a4>', got '{action_part}'"
223
+ )
224
+
225
+ # Parse input actions
226
+ input_actions = []
227
+ for i, token in enumerate(tokens[1:5], 1):
228
+ if ":" not in token:
229
+ raise ValueError(f"Invalid action format at position {i}: '{token}'")
230
+
231
+ code, param_str = token.split(":", 1)
232
+
233
+ # Look up action type
234
+ if code not in cls.SHORT_CODE_TO_ACTION:
235
+ raise ValueError(f"Unknown action code: '{code}'")
236
+
237
+ action_type = cls.SHORT_CODE_TO_ACTION[code]
238
+
239
+ # Parse param
240
+ try:
241
+ param_value = int(param_str)
242
+ param_type = TimeParam(param_value)
243
+ except (ValueError, KeyError):
244
+ raise ValueError(f"Invalid time param: '{param_str}'")
245
+
246
+ input_actions.append(InputAction(type=action_type, param=param_type))
247
+
248
+ # Parse settings if present
249
+ kwargs: dict[str, Any] = {
250
+ "input1_action": input_actions[0],
251
+ "input2_action": input_actions[1],
252
+ "input3_action": input_actions[2],
253
+ "input4_action": input_actions[3],
254
+ }
255
+
256
+ if settings_part:
257
+ # Parse settings: M12:0 M34:1 C12:0 C34:1 DT:12
258
+ for setting in settings_part.split():
259
+ if ":" not in setting:
260
+ continue
261
+
262
+ key, value = setting.split(":", 1)
263
+
264
+ if key == "M12":
265
+ kwargs["mutex12"] = value == "1"
266
+ elif key == "M34":
267
+ kwargs["mutex34"] = value == "1"
268
+ elif key == "C12":
269
+ kwargs["curtain12"] = value == "1"
270
+ elif key == "C34":
271
+ kwargs["curtain34"] = value == "1"
272
+ elif key == "DT":
273
+ kwargs["mutual_deadtime"] = int(value)
274
+
275
+ return cls(**kwargs)
@@ -1,12 +1,14 @@
1
1
  """XP33 Action Table models for output and scene configuration."""
2
2
 
3
- from dataclasses import dataclass, field
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
- @dataclass
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
- @dataclass
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
- @dataclass
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 = field(default_factory=Xp33Output)
59
- output2: Xp33Output = field(default_factory=Xp33Output)
60
- output3: Xp33Output = field(default_factory=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 = field(default_factory=Xp33Scene)
63
- scene2: Xp33Scene = field(default_factory=Xp33Scene)
64
- scene3: Xp33Scene = field(default_factory=Xp33Scene)
65
- scene4: Xp33Scene = field(default_factory=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.homekit.homekit_conson_config import ConsonModuleListConfig
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, FAILED_TIMEOUT, FAILED_NO_DEVICES, etc.).
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:
@@ -30,6 +30,36 @@ class DataPointType(str, Enum):
30
30
  SW_TOP_VERSION: Software top version.
31
31
  VOLTAGE: Voltage data point.
32
32
  AUTO_REPORT_STATUS: Auto report status.
33
+
34
+ Sample telegram datapoint request:
35
+ <S0020044966F02D30FL>
36
+
37
+ Sample telegram datapoint reply:
38
+ <R0020044966F02D00XP24FH>
39
+ <R0020044966F02D01XP24FG>
40
+ <R0020044966F02D02XP24_V0.34.03GI>
41
+ <R0020044966F02D030020044966FB>
42
+ <R0020044966F02D0406FL>
43
+ <R0020044966F02D050007FL>
44
+ <R0020044966F02D0601FO>
45
+ <R0020044966F02D0707FJ>
46
+ <R0020044966F02D08MIFF>
47
+ <R0020044966F02D09OFFBP>
48
+ <R0020044966F02D1000FI>
49
+ <R0020044966F02D11xxxx0000FJ>
50
+ <R0020044966F02D12xxxx0000FK>
51
+ <R0020044966F02D1500:000[%],01:000[%],02:000[%],03:000[%]HB>
52
+ <R0020044966F02D1600:000[H],01:000[H],02:000[H],03:000[H]HC>
53
+ <R0020044966F02D1700:00000[NA],01:00000[NA],02:00000[NA],03:00000[NA]HD>
54
+ <R0020044966F02D18+28,5§CIM>
55
+ <R0020044966F02D19??FB>
56
+ <R0020044966F02D20??FL>
57
+ <R0020044966F02D21PPFK>
58
+ <R0020044966F02D22PLLKMDINEM>
59
+ <R0020044966F02D23AAFI>
60
+ <R0020044966F02D24AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFP>
61
+ <R0020044966F02D25AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFO>
62
+ <R0020044966F02D26??FN>
33
63
  """
34
64
 
35
65
  MODULE_TYPE = "00" # Module type (XP24, XP33, ..)
@@ -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 ActionTableService:
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(object) # (ActionTable, Dict[str, Any], list[str])
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((actiontable, actiontable_dict, actiontable_short))
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) -> "ActionTableService":
178
+ def __enter__(self) -> "ActionTableDownloadService":
176
179
  """Enter context manager - reset state for singleton reuse.
177
180
 
178
181
  Returns: