conson-xp 1.18.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 (176) hide show
  1. conson_xp-1.18.0.dist-info/METADATA +412 -0
  2. conson_xp-1.18.0.dist-info/RECORD +176 -0
  3. conson_xp-1.18.0.dist-info/WHEEL +4 -0
  4. conson_xp-1.18.0.dist-info/entry_points.txt +5 -0
  5. conson_xp-1.18.0.dist-info/licenses/LICENSE +29 -0
  6. xp/__init__.py +9 -0
  7. xp/cli/__init__.py +5 -0
  8. xp/cli/__main__.py +6 -0
  9. xp/cli/commands/__init__.py +153 -0
  10. xp/cli/commands/conbus/__init__.py +25 -0
  11. xp/cli/commands/conbus/conbus.py +128 -0
  12. xp/cli/commands/conbus/conbus_actiontable_commands.py +233 -0
  13. xp/cli/commands/conbus/conbus_autoreport_commands.py +108 -0
  14. xp/cli/commands/conbus/conbus_blink_commands.py +163 -0
  15. xp/cli/commands/conbus/conbus_config_commands.py +29 -0
  16. xp/cli/commands/conbus/conbus_custom_commands.py +57 -0
  17. xp/cli/commands/conbus/conbus_datapoint_commands.py +113 -0
  18. xp/cli/commands/conbus/conbus_discover_commands.py +61 -0
  19. xp/cli/commands/conbus/conbus_event_commands.py +81 -0
  20. xp/cli/commands/conbus/conbus_lightlevel_commands.py +207 -0
  21. xp/cli/commands/conbus/conbus_linknumber_commands.py +102 -0
  22. xp/cli/commands/conbus/conbus_modulenumber_commands.py +104 -0
  23. xp/cli/commands/conbus/conbus_msactiontable_commands.py +94 -0
  24. xp/cli/commands/conbus/conbus_output_commands.py +163 -0
  25. xp/cli/commands/conbus/conbus_raw_commands.py +62 -0
  26. xp/cli/commands/conbus/conbus_receive_commands.py +59 -0
  27. xp/cli/commands/conbus/conbus_scan_commands.py +58 -0
  28. xp/cli/commands/file_commands.py +186 -0
  29. xp/cli/commands/homekit/__init__.py +3 -0
  30. xp/cli/commands/homekit/homekit.py +118 -0
  31. xp/cli/commands/homekit/homekit_start_commands.py +43 -0
  32. xp/cli/commands/module_commands.py +187 -0
  33. xp/cli/commands/reverse_proxy_commands.py +178 -0
  34. xp/cli/commands/server/__init__.py +3 -0
  35. xp/cli/commands/server/server_commands.py +135 -0
  36. xp/cli/commands/telegram/__init__.py +5 -0
  37. xp/cli/commands/telegram/telegram.py +41 -0
  38. xp/cli/commands/telegram/telegram_blink_commands.py +79 -0
  39. xp/cli/commands/telegram/telegram_checksum_commands.py +112 -0
  40. xp/cli/commands/telegram/telegram_discover_commands.py +41 -0
  41. xp/cli/commands/telegram/telegram_linknumber_commands.py +86 -0
  42. xp/cli/commands/telegram/telegram_parse_commands.py +75 -0
  43. xp/cli/commands/telegram/telegram_version_commands.py +52 -0
  44. xp/cli/main.py +87 -0
  45. xp/cli/utils/__init__.py +1 -0
  46. xp/cli/utils/click_tree.py +57 -0
  47. xp/cli/utils/datapoint_type_choice.py +57 -0
  48. xp/cli/utils/decorators.py +351 -0
  49. xp/cli/utils/error_handlers.py +201 -0
  50. xp/cli/utils/formatters.py +312 -0
  51. xp/cli/utils/module_type_choice.py +56 -0
  52. xp/cli/utils/serial_number_type.py +52 -0
  53. xp/cli/utils/system_function_choice.py +57 -0
  54. xp/cli/utils/xp_module_type.py +53 -0
  55. xp/connection/__init__.py +13 -0
  56. xp/connection/exceptions.py +22 -0
  57. xp/models/__init__.py +36 -0
  58. xp/models/actiontable/__init__.py +1 -0
  59. xp/models/actiontable/actiontable.py +43 -0
  60. xp/models/actiontable/msactiontable_xp20.py +53 -0
  61. xp/models/actiontable/msactiontable_xp24.py +58 -0
  62. xp/models/actiontable/msactiontable_xp33.py +65 -0
  63. xp/models/conbus/__init__.py +1 -0
  64. xp/models/conbus/conbus.py +87 -0
  65. xp/models/conbus/conbus_autoreport.py +67 -0
  66. xp/models/conbus/conbus_blink.py +80 -0
  67. xp/models/conbus/conbus_client_config.py +55 -0
  68. xp/models/conbus/conbus_connection_status.py +40 -0
  69. xp/models/conbus/conbus_custom.py +58 -0
  70. xp/models/conbus/conbus_datapoint.py +89 -0
  71. xp/models/conbus/conbus_discover.py +64 -0
  72. xp/models/conbus/conbus_event_raw.py +47 -0
  73. xp/models/conbus/conbus_lightlevel.py +52 -0
  74. xp/models/conbus/conbus_linknumber.py +54 -0
  75. xp/models/conbus/conbus_output.py +57 -0
  76. xp/models/conbus/conbus_raw.py +45 -0
  77. xp/models/conbus/conbus_receive.py +42 -0
  78. xp/models/conbus/conbus_writeconfig.py +60 -0
  79. xp/models/homekit/__init__.py +1 -0
  80. xp/models/homekit/homekit_accessory.py +35 -0
  81. xp/models/homekit/homekit_config.py +106 -0
  82. xp/models/homekit/homekit_conson_config.py +86 -0
  83. xp/models/log_entry.py +130 -0
  84. xp/models/protocol/__init__.py +1 -0
  85. xp/models/protocol/conbus_protocol.py +312 -0
  86. xp/models/response.py +42 -0
  87. xp/models/telegram/__init__.py +1 -0
  88. xp/models/telegram/action_type.py +31 -0
  89. xp/models/telegram/datapoint_type.py +82 -0
  90. xp/models/telegram/event_telegram.py +140 -0
  91. xp/models/telegram/event_type.py +15 -0
  92. xp/models/telegram/input_action_type.py +69 -0
  93. xp/models/telegram/input_type.py +17 -0
  94. xp/models/telegram/module_type.py +188 -0
  95. xp/models/telegram/module_type_code.py +205 -0
  96. xp/models/telegram/output_telegram.py +103 -0
  97. xp/models/telegram/reply_telegram.py +297 -0
  98. xp/models/telegram/system_function.py +116 -0
  99. xp/models/telegram/system_telegram.py +94 -0
  100. xp/models/telegram/telegram.py +28 -0
  101. xp/models/telegram/telegram_type.py +19 -0
  102. xp/models/telegram/timeparam_type.py +51 -0
  103. xp/models/write_config_type.py +33 -0
  104. xp/services/__init__.py +26 -0
  105. xp/services/actiontable/__init__.py +1 -0
  106. xp/services/actiontable/actiontable_serializer.py +273 -0
  107. xp/services/actiontable/msactiontable_serializer.py +7 -0
  108. xp/services/actiontable/msactiontable_xp20_serializer.py +169 -0
  109. xp/services/actiontable/msactiontable_xp24_serializer.py +120 -0
  110. xp/services/actiontable/msactiontable_xp33_serializer.py +239 -0
  111. xp/services/conbus/__init__.py +1 -0
  112. xp/services/conbus/actiontable/__init__.py +1 -0
  113. xp/services/conbus/actiontable/actiontable_download_service.py +158 -0
  114. xp/services/conbus/actiontable/actiontable_list_service.py +91 -0
  115. xp/services/conbus/actiontable/actiontable_show_service.py +89 -0
  116. xp/services/conbus/actiontable/actiontable_upload_service.py +211 -0
  117. xp/services/conbus/actiontable/msactiontable_service.py +232 -0
  118. xp/services/conbus/conbus_blink_all_service.py +181 -0
  119. xp/services/conbus/conbus_blink_service.py +158 -0
  120. xp/services/conbus/conbus_custom_service.py +156 -0
  121. xp/services/conbus/conbus_datapoint_queryall_service.py +182 -0
  122. xp/services/conbus/conbus_datapoint_service.py +170 -0
  123. xp/services/conbus/conbus_discover_service.py +312 -0
  124. xp/services/conbus/conbus_event_raw_service.py +181 -0
  125. xp/services/conbus/conbus_output_service.py +194 -0
  126. xp/services/conbus/conbus_raw_service.py +122 -0
  127. xp/services/conbus/conbus_receive_service.py +115 -0
  128. xp/services/conbus/conbus_scan_service.py +150 -0
  129. xp/services/conbus/write_config_service.py +194 -0
  130. xp/services/homekit/__init__.py +1 -0
  131. xp/services/homekit/homekit_cache_service.py +307 -0
  132. xp/services/homekit/homekit_conbus_service.py +93 -0
  133. xp/services/homekit/homekit_config_validator.py +310 -0
  134. xp/services/homekit/homekit_conson_validator.py +121 -0
  135. xp/services/homekit/homekit_dimminglight.py +182 -0
  136. xp/services/homekit/homekit_dimminglight_service.py +148 -0
  137. xp/services/homekit/homekit_hap_service.py +342 -0
  138. xp/services/homekit/homekit_lightbulb.py +120 -0
  139. xp/services/homekit/homekit_lightbulb_service.py +86 -0
  140. xp/services/homekit/homekit_module_service.py +56 -0
  141. xp/services/homekit/homekit_outlet.py +168 -0
  142. xp/services/homekit/homekit_outlet_service.py +121 -0
  143. xp/services/homekit/homekit_service.py +359 -0
  144. xp/services/log_file_service.py +309 -0
  145. xp/services/module_type_service.py +257 -0
  146. xp/services/protocol/__init__.py +21 -0
  147. xp/services/protocol/conbus_event_protocol.py +360 -0
  148. xp/services/protocol/conbus_protocol.py +318 -0
  149. xp/services/protocol/protocol_factory.py +78 -0
  150. xp/services/protocol/telegram_protocol.py +264 -0
  151. xp/services/reverse_proxy_service.py +435 -0
  152. xp/services/server/__init__.py +1 -0
  153. xp/services/server/base_server_service.py +366 -0
  154. xp/services/server/cp20_server_service.py +65 -0
  155. xp/services/server/device_service_factory.py +94 -0
  156. xp/services/server/server_service.py +428 -0
  157. xp/services/server/xp130_server_service.py +67 -0
  158. xp/services/server/xp20_server_service.py +92 -0
  159. xp/services/server/xp230_server_service.py +58 -0
  160. xp/services/server/xp24_server_service.py +245 -0
  161. xp/services/server/xp33_server_service.py +535 -0
  162. xp/services/telegram/__init__.py +1 -0
  163. xp/services/telegram/telegram_blink_service.py +138 -0
  164. xp/services/telegram/telegram_checksum_service.py +149 -0
  165. xp/services/telegram/telegram_datapoint_service.py +82 -0
  166. xp/services/telegram/telegram_discover_service.py +277 -0
  167. xp/services/telegram/telegram_link_number_service.py +216 -0
  168. xp/services/telegram/telegram_output_service.py +322 -0
  169. xp/services/telegram/telegram_service.py +380 -0
  170. xp/services/telegram/telegram_version_service.py +288 -0
  171. xp/utils/__init__.py +12 -0
  172. xp/utils/checksum.py +61 -0
  173. xp/utils/dependencies.py +531 -0
  174. xp/utils/event_helper.py +31 -0
  175. xp/utils/serialization.py +205 -0
  176. xp/utils/time_utils.py +134 -0
@@ -0,0 +1,205 @@
1
+ """Binary serialization utility functions.
2
+
3
+ This module provides common binary manipulation functions used across
4
+ the XP protocol serializers for consistent data encoding/decoding.
5
+ """
6
+
7
+ from typing import List
8
+
9
+ # BCD and bit manipulation constants
10
+ UPPER4 = 240 # 0xF0
11
+ LOWER4 = 15 # 0x0F
12
+ LOWER3 = 7 # 0x07
13
+ UPPER5 = 248 # 0xF8
14
+
15
+
16
+ def de_bcd(byte_val: int) -> int:
17
+ """Convert BCD byte to decimal.
18
+
19
+ Args:
20
+ byte_val: BCD encoded byte
21
+
22
+ Returns:
23
+ Decimal value
24
+ """
25
+ return ((UPPER4 & byte_val) >> 4) * 10 + (LOWER4 & byte_val)
26
+
27
+
28
+ def to_bcd(decimal_val: int) -> int:
29
+ """Convert decimal to BCD byte.
30
+
31
+ Args:
32
+ decimal_val: Decimal value to convert
33
+
34
+ Returns:
35
+ BCD encoded byte
36
+ """
37
+ tens = (decimal_val // 10) % 10
38
+ ones = decimal_val % 10
39
+ return (tens << 4) | ones
40
+
41
+
42
+ def lower3(byte_val: int) -> int:
43
+ """Extract lower 3 bits from byte.
44
+
45
+ Args:
46
+ byte_val: Input byte
47
+
48
+ Returns:
49
+ Lower 3 bits as integer
50
+ """
51
+ return byte_val & LOWER3
52
+
53
+
54
+ def upper5(byte_val: int) -> int:
55
+ """Extract upper 5 bits from byte.
56
+
57
+ Args:
58
+ byte_val: Input byte
59
+
60
+ Returns:
61
+ Upper 5 bits as integer
62
+ """
63
+ return (byte_val & UPPER5) >> 3
64
+
65
+
66
+ def byte_to_bits(byte_value: int) -> List[bool]:
67
+ """Convert a byte value to 8-bit boolean array.
68
+
69
+ Args:
70
+ byte_value: Byte value to convert
71
+
72
+ Returns:
73
+ List of 8 boolean values representing the bits
74
+ """
75
+ return [(byte_value & (1 << n)) != 0 for n in range(8)]
76
+
77
+
78
+ def bits_to_byte(bits: List[bool]) -> int:
79
+ """Convert boolean array to byte value.
80
+
81
+ Args:
82
+ bits: List of boolean values representing bits
83
+
84
+ Returns:
85
+ Byte value
86
+ """
87
+ byte_val = 0
88
+ for i, bit in enumerate(bits[:8]): # Limit to 8 bits
89
+ if bit:
90
+ byte_val |= 1 << i
91
+ return byte_val
92
+
93
+
94
+ def highest_bit_set(value: int) -> int:
95
+ """Remove the high bit (0x80) from a byte value.
96
+
97
+ Args:
98
+ value: Byte value to process
99
+
100
+ Returns:
101
+ Value with high bit cleared (XOR with 0x80 if high bit was set)
102
+ """
103
+ return (value & 0x80) == 128
104
+
105
+
106
+ def remove_highest_bit(value: int) -> int:
107
+ """Remove the high bit (0x80) from a byte value.
108
+
109
+ Args:
110
+ value: Byte value to process
111
+
112
+ Returns:
113
+ Value with high bit cleared (XOR with 0x80 if high bit was set)
114
+ """
115
+ return value ^ 0x80 if (value & 0x80) == 128 else value
116
+
117
+
118
+ def byte_to_unsigned(byte_val: int) -> int:
119
+ """Convert signed byte to unsigned integer.
120
+
121
+ Args:
122
+ byte_val: Byte value (can be negative)
123
+
124
+ Returns:
125
+ Unsigned integer (0-255)
126
+ """
127
+ if byte_val < 0:
128
+ return byte_val + 256
129
+ return byte_val
130
+
131
+
132
+ def nibble(byte_val: int) -> str:
133
+ """Convert byte value to two-character nibble representation.
134
+
135
+ Args:
136
+ byte_val: Byte value (0-255)
137
+
138
+ Returns:
139
+ Two-character string representing the nibble
140
+ """
141
+ low_cc = ((byte_val & 0xF0) >> 4) + 65
142
+ high_cc = (byte_val & 0xF) + 65
143
+ return chr(low_cc) + chr(high_cc)
144
+
145
+
146
+ def de_nibble(nibble_str: str) -> int:
147
+ """Convert two-character nibble string to byte value.
148
+
149
+ Based on pseudocode: A=0, B=1, C=2, ..., P=15
150
+
151
+ Args:
152
+ nibble_str: Two-character string with A-P encoding
153
+
154
+ Returns:
155
+ Byte value (0-255)
156
+
157
+ Raises:
158
+ ValueError: If nibble string is not exactly 2 characters
159
+ """
160
+ if len(nibble_str) != 2:
161
+ raise ValueError("Nibble string must be exactly 2 characters")
162
+
163
+ high_char = nibble_str[0]
164
+ low_char = nibble_str[1]
165
+
166
+ # Convert A-P to 0-15 (A=65 in ASCII, so A-65=0)
167
+ high_nibble = (ord(high_char) - 65) << 4
168
+ low_nibble = ord(low_char) - 65
169
+
170
+ return high_nibble + low_nibble
171
+
172
+
173
+ def de_nibbles(str_val: str) -> bytearray:
174
+ """Convert hex string with A-P encoding to list of integers.
175
+
176
+ Based on pseudocode: A=0, B=1, C=2, ..., P=15
177
+
178
+ Args:
179
+ str_val: Hex string with A-P encoding
180
+
181
+ Returns:
182
+ List of integers representing the decoded bytes
183
+
184
+ Raises:
185
+ ValueError: If string length is not even for nibble conversion
186
+ """
187
+ if len(str_val) % 2 != 0:
188
+ raise ValueError("String length must be even for nibble conversion")
189
+
190
+ result = bytearray()
191
+ for i in range(0, len(str_val), 2):
192
+ result.append(de_nibble(str_val[i : i + 2]))
193
+ return result
194
+
195
+
196
+ def nibbles(data: bytes) -> str:
197
+ """Convert bytes data to nibble string representation.
198
+
199
+ Args:
200
+ data: Bytes data to convert
201
+
202
+ Returns:
203
+ String representation using A-P encoding
204
+ """
205
+ return "".join(nibble(byte) for byte in data)
xp/utils/time_utils.py ADDED
@@ -0,0 +1,134 @@
1
+ """Time parsing utilities for console bus logs."""
2
+
3
+ import re
4
+ from datetime import datetime, time
5
+ from typing import Optional
6
+
7
+
8
+ class TimeParsingError(Exception):
9
+ """Raised when time parsing fails."""
10
+
11
+ pass
12
+
13
+
14
+ def parse_log_timestamp(
15
+ timestamp_str: str, base_date: Optional[datetime] = None
16
+ ) -> datetime:
17
+ """Parse timestamp from console bus log format: HH:MM:SS,mmm.
18
+
19
+ Args:
20
+ timestamp_str: Timestamp string (e.g., "22:44:20,352")
21
+ base_date: Base date to use (defaults to today)
22
+
23
+ Returns:
24
+ datetime object with parsed time
25
+
26
+ Raises:
27
+ TimeParsingError: If timestamp format is invalid
28
+ """
29
+ # Pattern: HH:MM:SS,mmm
30
+ pattern = r"^(\d{2}):(\d{2}):(\d{2}),(\d{3})$"
31
+ match = re.match(pattern, timestamp_str.strip())
32
+
33
+ if not match:
34
+ raise TimeParsingError(f"Invalid timestamp format: {timestamp_str}")
35
+
36
+ try:
37
+ hour = int(match.group(1))
38
+ minute = int(match.group(2))
39
+ second = int(match.group(3))
40
+ millisecond = int(match.group(4))
41
+
42
+ # Validate ranges
43
+ if not (0 <= hour <= 23):
44
+ raise TimeParsingError(f"Invalid hour: {hour}")
45
+ if not (0 <= minute <= 59):
46
+ raise TimeParsingError(f"Invalid minute: {minute}")
47
+ if not (0 <= second <= 59):
48
+ raise TimeParsingError(f"Invalid second: {second}")
49
+ if not (0 <= millisecond <= 999):
50
+ raise TimeParsingError(f"Invalid millisecond: {millisecond}")
51
+
52
+ # Create time object
53
+ time_obj = time(hour, minute, second, millisecond * 1000) # microseconds
54
+
55
+ # Use base date or today
56
+ if base_date is None:
57
+ date_part = datetime.now().date()
58
+ else:
59
+ date_part = base_date.date()
60
+
61
+ return datetime.combine(date_part, time_obj)
62
+
63
+ except ValueError as e:
64
+ raise TimeParsingError(f"Error parsing timestamp {timestamp_str}: {e}")
65
+
66
+
67
+ def format_log_timestamp(dt: datetime) -> str:
68
+ """Format datetime to console bus log timestamp format: HH:MM:SS,mmm.
69
+
70
+ Args:
71
+ dt: datetime object to format
72
+
73
+ Returns:
74
+ Formatted timestamp string
75
+ """
76
+ return dt.strftime("%H:%M:%S,%f")[:-3] # Remove last 3 digits of microseconds
77
+
78
+
79
+ def parse_time_range(
80
+ time_range_str: str, base_date: Optional[datetime] = None
81
+ ) -> tuple[datetime, datetime]:
82
+ """Parse time range string like "22:44:20,352-22:44:25,500".
83
+
84
+ Args:
85
+ time_range_str: Time range string
86
+ base_date: Base date to use
87
+
88
+ Returns:
89
+ Tuple of (start_time, end_time)
90
+
91
+ Raises:
92
+ TimeParsingError: If format is invalid
93
+ """
94
+ parts = time_range_str.split("-")
95
+ if len(parts) != 2:
96
+ raise TimeParsingError(f"Invalid time range format: {time_range_str}")
97
+
98
+ start_time = parse_log_timestamp(parts[0].strip(), base_date)
99
+ end_time = parse_log_timestamp(parts[1].strip(), base_date)
100
+
101
+ if start_time > end_time:
102
+ raise TimeParsingError(f"Start time {parts[0]} is after end time {parts[1]}")
103
+
104
+ return start_time, end_time
105
+
106
+
107
+ def calculate_duration_ms(start_time: datetime, end_time: datetime) -> int:
108
+ """Calculate duration between two timestamps in milliseconds.
109
+
110
+ Args:
111
+ start_time: Start timestamp
112
+ end_time: End timestamp
113
+
114
+ Returns:
115
+ Duration in milliseconds
116
+ """
117
+ duration = end_time - start_time
118
+ return int(duration.total_seconds() * 1000)
119
+
120
+
121
+ def is_valid_log_timestamp(timestamp_str: str) -> bool:
122
+ """Check if timestamp string is valid console bus log format.
123
+
124
+ Args:
125
+ timestamp_str: Timestamp string to validate
126
+
127
+ Returns:
128
+ True if valid format, False otherwise
129
+ """
130
+ try:
131
+ parse_log_timestamp(timestamp_str)
132
+ return True
133
+ except TimeParsingError:
134
+ return False