conson-xp 1.37.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 (43) hide show
  1. {conson_xp-1.37.0.dist-info → conson_xp-1.38.0.dist-info}/METADATA +6 -1
  2. {conson_xp-1.37.0.dist-info → conson_xp-1.38.0.dist-info}/RECORD +43 -38
  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 +17 -8
  8. xp/cli/commands/conbus/conbus_event_commands.py +1 -7
  9. xp/cli/commands/conbus/conbus_export_commands.py +7 -6
  10. xp/cli/commands/conbus/conbus_msactiontable_commands.py +92 -9
  11. xp/models/actiontable/msactiontable.py +9 -0
  12. xp/models/actiontable/msactiontable_xp20.py +14 -14
  13. xp/models/actiontable/msactiontable_xp24.py +228 -11
  14. xp/models/actiontable/msactiontable_xp33.py +42 -14
  15. xp/models/conbus/conbus_export.py +6 -2
  16. xp/models/config/__init__.py +1 -0
  17. xp/models/{homekit/homekit_conson_config.py → config/conson_module_config.py} +3 -1
  18. xp/models/protocol/conbus_protocol.py +1 -1
  19. xp/services/actiontable/msactiontable_xp20_serializer.py +12 -0
  20. xp/services/actiontable/msactiontable_xp24_serializer.py +13 -1
  21. xp/services/actiontable/msactiontable_xp33_serializer.py +12 -0
  22. xp/services/conbus/actiontable/actiontable_download_service.py +8 -5
  23. xp/services/conbus/actiontable/actiontable_list_service.py +2 -1
  24. xp/services/conbus/actiontable/actiontable_show_service.py +2 -2
  25. xp/services/conbus/actiontable/actiontable_upload_service.py +1 -1
  26. xp/services/conbus/conbus_event_list_service.py +1 -1
  27. xp/services/conbus/conbus_export_service.py +1 -1
  28. xp/services/conbus/msactiontable/__init__.py +1 -0
  29. xp/services/conbus/{actiontable/msactiontable_service.py → msactiontable/msactiontable_download_service.py} +9 -5
  30. xp/services/conbus/msactiontable/msactiontable_list_service.py +92 -0
  31. xp/services/conbus/msactiontable/msactiontable_show_service.py +89 -0
  32. xp/services/homekit/homekit_config_validator.py +1 -1
  33. xp/services/homekit/homekit_conson_validator.py +1 -1
  34. xp/services/homekit/homekit_dimminglight.py +1 -1
  35. xp/services/homekit/homekit_lightbulb.py +1 -1
  36. xp/services/homekit/homekit_module_service.py +1 -1
  37. xp/services/homekit/homekit_outlet.py +1 -1
  38. xp/services/server/server_service.py +1 -1
  39. xp/services/term/state_monitor_service.py +1 -1
  40. xp/utils/dependencies.py +36 -14
  41. {conson_xp-1.37.0.dist-info → conson_xp-1.38.0.dist-info}/WHEEL +0 -0
  42. {conson_xp-1.37.0.dist-info → conson_xp-1.38.0.dist-info}/entry_points.txt +0 -0
  43. {conson_xp-1.37.0.dist-info → conson_xp-1.38.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 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:
@@ -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:
@@ -59,7 +59,7 @@ class ActionTableListService:
59
59
 
60
60
  # Load configuration
61
61
  try:
62
- from xp.models.homekit.homekit_conson_config import ConsonModuleListConfig
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.homekit.homekit_conson_config import ConsonModuleConfig
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.homekit.homekit_conson_config import ConsonModuleListConfig
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.homekit.homekit_conson_config import ConsonModuleListConfig
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.homekit.homekit_conson_config import ConsonModuleListConfig
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.homekit.homekit_conson_config import (
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 MsActionTableService:
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(object) # Union type for Xp20/24/33 or None
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.succeed(msactiontable)
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) -> "MsActionTableService":
248
+ def __enter__(self) -> "MsActionTableDownloadService":
245
249
  """Enter context manager.
246
250
 
247
251
  Returns: