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,307 @@
1
+ """Bubus cache service for caching datapoint responses."""
2
+
3
+ import json
4
+ import logging
5
+ from datetime import datetime
6
+ from pathlib import Path
7
+ from typing import Any, TypedDict, Union
8
+
9
+ from bubus import EventBus
10
+
11
+ from xp.models.protocol.conbus_protocol import (
12
+ LightLevelReceivedEvent,
13
+ OutputStateReceivedEvent,
14
+ ReadDatapointEvent,
15
+ ReadDatapointFromProtocolEvent,
16
+ )
17
+ from xp.models.telegram.datapoint_type import DataPointType
18
+
19
+ # Cache file configuration
20
+ CACHE_DIR = Path(".cache")
21
+ CACHE_FILE = CACHE_DIR / "homekit_cache.json"
22
+
23
+
24
+ class CacheEntry(TypedDict):
25
+ """Cache entry type definition.
26
+
27
+ Attributes:
28
+ event: The cached event (OutputStateReceivedEvent or LightLevelReceivedEvent).
29
+ timestamp: When the event was cached.
30
+ """
31
+
32
+ event: Union[OutputStateReceivedEvent, LightLevelReceivedEvent]
33
+ timestamp: datetime
34
+
35
+
36
+ class HomeKitCacheService:
37
+ """
38
+ Cache service that intercepts bubus protocol messages to reduce redundant queries.
39
+
40
+ Caches OutputStateReceivedEvent and LightLevelReceivedEvent responses.
41
+ When a ReadDatapointEvent is received, checks cache and either:
42
+ - Returns cached response if available (cache hit)
43
+ - Forwards to protocol via ReadDatapointFromProtocolEvent (cache miss)
44
+ """
45
+
46
+ def __init__(self, event_bus: EventBus, enable_persistence: bool = True):
47
+ """Initialize the HomeKit cache service.
48
+
49
+ Args:
50
+ event_bus: Event bus for inter-service communication.
51
+ enable_persistence: Whether to persist cache to disk.
52
+ """
53
+ self.logger = logging.getLogger(__name__)
54
+ self.event_bus = event_bus
55
+ self.cache: dict[tuple[str, DataPointType], CacheEntry] = {}
56
+ self.enable_persistence = enable_persistence
57
+
58
+ # Load cache from disk
59
+ if self.enable_persistence:
60
+ self._load_cache()
61
+
62
+ # Register event handlers
63
+ # Note: These must be registered BEFORE HomeKitConbusService registers its handlers
64
+ self.event_bus.on(ReadDatapointEvent, self.handle_read_datapoint_event)
65
+ self.event_bus.on(
66
+ OutputStateReceivedEvent, self.handle_output_state_received_event
67
+ )
68
+ self.event_bus.on(
69
+ LightLevelReceivedEvent, self.handle_light_level_received_event
70
+ )
71
+
72
+ self.logger.info(
73
+ f"HomeKitCacheService initialized with {len(self.cache)} cached entries"
74
+ )
75
+
76
+ def _serialize_cache_key(self, key: tuple[str, DataPointType]) -> str:
77
+ """Serialize cache key to JSON-compatible string."""
78
+ serial_number, datapoint_type = key
79
+ return f"{serial_number}.{datapoint_type.value}"
80
+
81
+ def _deserialize_cache_key(self, key_str: str) -> tuple[str, DataPointType]:
82
+ """Deserialize cache key from JSON string."""
83
+ serial_number, datapoint_type_str = key_str.rsplit(".", 1)
84
+ return (serial_number, DataPointType(datapoint_type_str))
85
+
86
+ def _serialize_cache(self) -> dict[str, Any]:
87
+ """Serialize cache to JSON-compatible dict."""
88
+ serialized = {}
89
+ for key, entry in self.cache.items():
90
+ key_str = self._serialize_cache_key(key)
91
+ serialized[key_str] = {
92
+ "event": entry["event"].model_dump(mode="json"),
93
+ "timestamp": entry["timestamp"].isoformat(),
94
+ }
95
+ return serialized
96
+
97
+ def _deserialize_cache(
98
+ self, data: dict[str, Any]
99
+ ) -> dict[tuple[str, DataPointType], CacheEntry]:
100
+ """Deserialize cache from JSON dict."""
101
+ cache: dict[tuple[str, DataPointType], CacheEntry] = {}
102
+ for key_str, entry_data in data.items():
103
+ try:
104
+ key = self._deserialize_cache_key(key_str)
105
+ event_data = entry_data["event"]
106
+
107
+ # Reconstruct event based on datapoint_type
108
+ if key[1] == DataPointType.MODULE_OUTPUT_STATE:
109
+ event = OutputStateReceivedEvent(**event_data)
110
+ elif key[1] == DataPointType.MODULE_LIGHT_LEVEL:
111
+ event = LightLevelReceivedEvent(**event_data)
112
+ else:
113
+ self.logger.warning(f"Unknown datapoint type in cache: {key[1]}")
114
+ continue
115
+
116
+ cache[key] = {
117
+ "event": event,
118
+ "timestamp": datetime.fromisoformat(entry_data["timestamp"]),
119
+ }
120
+ except Exception as e:
121
+ self.logger.warning(f"Failed to deserialize cache entry {key_str}: {e}")
122
+ continue
123
+
124
+ return cache
125
+
126
+ def _load_cache(self) -> None:
127
+ """Load cache from disk."""
128
+ if not CACHE_FILE.exists():
129
+ self.logger.debug("No cache file found, starting with empty cache")
130
+ return
131
+
132
+ try:
133
+ with CACHE_FILE.open("r") as f:
134
+ data = json.load(f)
135
+
136
+ self.cache = self._deserialize_cache(data)
137
+ self.logger.info(f"Loaded {len(self.cache)} entries from cache file")
138
+ except Exception as e:
139
+ self.logger.error(f"Failed to load cache from disk: {e}")
140
+ self.cache = {}
141
+
142
+ def _save_cache(self) -> None:
143
+ """Save cache to disk atomically."""
144
+ if not self.enable_persistence:
145
+ return
146
+
147
+ try:
148
+ # Ensure cache directory exists
149
+ CACHE_DIR.mkdir(parents=True, exist_ok=True)
150
+
151
+ # Atomic write: write to temp file, then rename
152
+ temp_file = CACHE_FILE.with_suffix(".tmp")
153
+ with temp_file.open("w") as f:
154
+ json.dump(self._serialize_cache(), f, indent=2)
155
+
156
+ # Atomic rename
157
+ temp_file.replace(CACHE_FILE)
158
+
159
+ self.logger.debug(f"Saved {len(self.cache)} entries to cache file")
160
+ except Exception as e:
161
+ self.logger.error(f"Failed to save cache to disk: {e}")
162
+
163
+ def _get_cache_key(
164
+ self, serial_number: str, datapoint_type: DataPointType
165
+ ) -> tuple[str, DataPointType]:
166
+ """Generate cache key from serial number and datapoint type."""
167
+ return (serial_number, datapoint_type)
168
+
169
+ def _cache_event(
170
+ self, event: Union[OutputStateReceivedEvent, LightLevelReceivedEvent]
171
+ ) -> None:
172
+ """Store an event in the cache."""
173
+ cache_key = self._get_cache_key(event.serial_number, event.datapoint_type)
174
+ cache_entry: CacheEntry = {
175
+ "event": event,
176
+ "timestamp": datetime.now(),
177
+ }
178
+ self.cache[cache_key] = cache_entry
179
+ self.logger.debug(
180
+ f"Cached event: "
181
+ f"serial={event.serial_number}, "
182
+ f"type={event.datapoint_type}, "
183
+ f"value={event.data_value}"
184
+ )
185
+
186
+ # Persist to disk
187
+ self._save_cache()
188
+
189
+ def _get_cached_event(
190
+ self, serial_number: str, datapoint_type: DataPointType
191
+ ) -> Union[OutputStateReceivedEvent, LightLevelReceivedEvent, None]:
192
+ """Retrieve an event from the cache if it exists."""
193
+ cache_key = self._get_cache_key(serial_number, datapoint_type)
194
+ cache_entry = self.cache.get(cache_key)
195
+
196
+ if cache_entry:
197
+ self.logger.debug(
198
+ f"Cache hit: " f"serial={serial_number}, " f"type={datapoint_type}"
199
+ )
200
+ return cache_entry["event"]
201
+
202
+ self.logger.debug(f"Cache miss: serial={serial_number}, type={datapoint_type}")
203
+ return None
204
+
205
+ def handle_read_datapoint_event(self, event: ReadDatapointEvent) -> None:
206
+ """Handle ReadDatapointEvent by checking cache or refresh flag.
207
+
208
+ On refresh_cache=True: invalidate cache and force protocol query
209
+ On cache hit: dispatch cached response event
210
+ On cache miss: forward to protocol via ReadDatapointFromProtocolEvent
211
+
212
+ Args:
213
+ event: Read datapoint event with serial number, datapoint type, and refresh flag.
214
+ """
215
+ self.logger.debug(
216
+ f"Handling ReadDatapointEvent: "
217
+ f"serial={event.serial_number}, "
218
+ f"type={event.datapoint_type}, "
219
+ f"refresh_cache={event.refresh_cache}"
220
+ )
221
+
222
+ # Check if cache refresh requested
223
+ if event.refresh_cache:
224
+ self.logger.info(
225
+ f"Cache refresh requested: "
226
+ f"serial={event.serial_number}, "
227
+ f"type={event.datapoint_type}"
228
+ )
229
+ # Invalidate cache entry
230
+ cache_key = self._get_cache_key(event.serial_number, event.datapoint_type)
231
+ if cache_key in self.cache:
232
+ del self.cache[cache_key]
233
+ self.logger.debug(f"Invalidated cache entry: {cache_key}")
234
+ # Persist invalidation
235
+ self._save_cache()
236
+
237
+ # Normal cache lookup flow
238
+ cached_event = self._get_cached_event(event.serial_number, event.datapoint_type)
239
+
240
+ if cached_event:
241
+ # Cache hit - dispatch the cached event
242
+ self.logger.debug(
243
+ f"Returning cached response: "
244
+ f"serial={event.serial_number}, "
245
+ f"type={event.datapoint_type}"
246
+ )
247
+ self.event_bus.dispatch(cached_event)
248
+ return
249
+
250
+ # Cache miss - forward to protocol
251
+ self.logger.debug(
252
+ f"Forwarding to protocol: "
253
+ f"serial={event.serial_number}, "
254
+ f"type={event.datapoint_type}"
255
+ )
256
+ self.event_bus.dispatch(
257
+ ReadDatapointFromProtocolEvent(
258
+ serial_number=event.serial_number,
259
+ datapoint_type=event.datapoint_type,
260
+ )
261
+ )
262
+
263
+ def handle_output_state_received_event(
264
+ self, event: OutputStateReceivedEvent
265
+ ) -> None:
266
+ """Cache OutputStateReceivedEvent for future queries.
267
+
268
+ Args:
269
+ event: Output state received event to cache.
270
+ """
271
+ self.logger.debug(
272
+ f"Caching OutputStateReceivedEvent: "
273
+ f"serial={event.serial_number}, "
274
+ f"type={event.datapoint_type}, "
275
+ f"value={event.data_value}"
276
+ )
277
+ self._cache_event(event)
278
+
279
+ def handle_light_level_received_event(self, event: LightLevelReceivedEvent) -> None:
280
+ """Cache LightLevelReceivedEvent for future queries.
281
+
282
+ Args:
283
+ event: Light level received event to cache.
284
+ """
285
+ self.logger.debug(
286
+ f"Caching LightLevelReceivedEvent: "
287
+ f"serial={event.serial_number}, "
288
+ f"type={event.datapoint_type}, "
289
+ f"value={event.data_value}"
290
+ )
291
+ self._cache_event(event)
292
+
293
+ def clear_cache(self) -> None:
294
+ """Clear all cached entries."""
295
+ self.logger.info("Clearing cache")
296
+ self.cache.clear()
297
+ self._save_cache()
298
+
299
+ def get_cache_stats(self) -> dict[str, int]:
300
+ """Get cache statistics.
301
+
302
+ Returns:
303
+ Dictionary with cache statistics including total_entries.
304
+ """
305
+ return {
306
+ "total_entries": len(self.cache),
307
+ }
@@ -0,0 +1,93 @@
1
+ """HomeKit Conbus Service for protocol communication.
2
+
3
+ This module bridges HomeKit events with the Conbus protocol for device control.
4
+ """
5
+
6
+ import logging
7
+
8
+ from bubus import EventBus
9
+
10
+ from xp.models.protocol.conbus_protocol import (
11
+ ReadDatapointFromProtocolEvent,
12
+ SendActionEvent,
13
+ SendWriteConfigEvent,
14
+ )
15
+ from xp.models.telegram.datapoint_type import DataPointType
16
+ from xp.models.telegram.system_function import SystemFunction
17
+ from xp.services.protocol.telegram_protocol import TelegramProtocol
18
+
19
+
20
+ class HomeKitConbusService:
21
+ """Service for bridging HomeKit events with Conbus protocol.
22
+
23
+ Attributes:
24
+ event_bus: Event bus for inter-service communication.
25
+ telegram_protocol: Protocol for sending telegrams.
26
+ logger: Logger instance.
27
+ """
28
+
29
+ event_bus: EventBus
30
+
31
+ def __init__(self, event_bus: EventBus, telegram_protocol: TelegramProtocol):
32
+ """Initialize the HomeKit Conbus service.
33
+
34
+ Args:
35
+ event_bus: Event bus instance.
36
+ telegram_protocol: Telegram protocol instance.
37
+ """
38
+ self.logger = logging.getLogger(__name__)
39
+ self.event_bus = event_bus
40
+ self.telegram_protocol = telegram_protocol
41
+
42
+ # Register event handlers
43
+ self.event_bus.on(
44
+ ReadDatapointFromProtocolEvent, self.handle_read_datapoint_request
45
+ )
46
+ self.event_bus.on(SendActionEvent, self.handle_send_action_event)
47
+ self.event_bus.on(SendWriteConfigEvent, self.handle_send_write_config_event)
48
+
49
+ def handle_read_datapoint_request(
50
+ self, event: ReadDatapointFromProtocolEvent
51
+ ) -> None:
52
+ """Handle request to read datapoint from protocol.
53
+
54
+ Args:
55
+ event: Read datapoint event with serial number and datapoint type.
56
+ """
57
+ self.logger.debug(f"read_datapoint_request {event}")
58
+
59
+ system_function = SystemFunction.READ_DATAPOINT.value
60
+ datapoint_value = event.datapoint_type.value
61
+ telegram = f"S{event.serial_number}F{system_function}D{datapoint_value}"
62
+ self.telegram_protocol.sendFrame(telegram.encode())
63
+
64
+ def handle_send_write_config_event(self, event: SendWriteConfigEvent) -> None:
65
+ """Handle send write config event.
66
+
67
+ Args:
68
+ event: Write config event with configuration data.
69
+ """
70
+ self.logger.debug(f"send_write_config_event {event}")
71
+
72
+ # Format data as output_number:level (e.g., "02:050")
73
+ system_function = SystemFunction.WRITE_CONFIG.value
74
+ datapoint_type = DataPointType.MODULE_LIGHT_LEVEL.value
75
+ config_data = f"{event.output_number:02d}:{event.value:03d}"
76
+ telegram = (
77
+ f"S{event.serial_number}F{system_function}D{datapoint_type}{config_data}"
78
+ )
79
+ self.telegram_protocol.sendFrame(telegram.encode())
80
+
81
+ def handle_send_action_event(self, event: SendActionEvent) -> None:
82
+ """Handle send action event.
83
+
84
+ Args:
85
+ event: Send action event with action data.
86
+ """
87
+ self.logger.debug(f"send_action_event {event}")
88
+
89
+ telegram = event.on_action if event.value else event.off_action
90
+ telegram_make = f"{telegram}M"
91
+ telegram_break = f"{telegram}B"
92
+ self.telegram_protocol.sendFrame(telegram_make.encode())
93
+ self.telegram_protocol.sendFrame(telegram_break.encode())