aiohomematic 2026.1.29__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 (188) hide show
  1. aiohomematic/__init__.py +110 -0
  2. aiohomematic/_log_context_protocol.py +29 -0
  3. aiohomematic/api.py +410 -0
  4. aiohomematic/async_support.py +250 -0
  5. aiohomematic/backend_detection.py +462 -0
  6. aiohomematic/central/__init__.py +103 -0
  7. aiohomematic/central/async_rpc_server.py +760 -0
  8. aiohomematic/central/central_unit.py +1152 -0
  9. aiohomematic/central/config.py +463 -0
  10. aiohomematic/central/config_builder.py +772 -0
  11. aiohomematic/central/connection_state.py +160 -0
  12. aiohomematic/central/coordinators/__init__.py +38 -0
  13. aiohomematic/central/coordinators/cache.py +414 -0
  14. aiohomematic/central/coordinators/client.py +480 -0
  15. aiohomematic/central/coordinators/connection_recovery.py +1141 -0
  16. aiohomematic/central/coordinators/device.py +1166 -0
  17. aiohomematic/central/coordinators/event.py +514 -0
  18. aiohomematic/central/coordinators/hub.py +532 -0
  19. aiohomematic/central/decorators.py +184 -0
  20. aiohomematic/central/device_registry.py +229 -0
  21. aiohomematic/central/events/__init__.py +104 -0
  22. aiohomematic/central/events/bus.py +1392 -0
  23. aiohomematic/central/events/integration.py +424 -0
  24. aiohomematic/central/events/types.py +194 -0
  25. aiohomematic/central/health.py +762 -0
  26. aiohomematic/central/rpc_server.py +353 -0
  27. aiohomematic/central/scheduler.py +794 -0
  28. aiohomematic/central/state_machine.py +391 -0
  29. aiohomematic/client/__init__.py +203 -0
  30. aiohomematic/client/_rpc_errors.py +187 -0
  31. aiohomematic/client/backends/__init__.py +48 -0
  32. aiohomematic/client/backends/base.py +335 -0
  33. aiohomematic/client/backends/capabilities.py +138 -0
  34. aiohomematic/client/backends/ccu.py +487 -0
  35. aiohomematic/client/backends/factory.py +116 -0
  36. aiohomematic/client/backends/homegear.py +294 -0
  37. aiohomematic/client/backends/json_ccu.py +252 -0
  38. aiohomematic/client/backends/protocol.py +316 -0
  39. aiohomematic/client/ccu.py +1857 -0
  40. aiohomematic/client/circuit_breaker.py +459 -0
  41. aiohomematic/client/config.py +64 -0
  42. aiohomematic/client/handlers/__init__.py +40 -0
  43. aiohomematic/client/handlers/backup.py +157 -0
  44. aiohomematic/client/handlers/base.py +79 -0
  45. aiohomematic/client/handlers/device_ops.py +1085 -0
  46. aiohomematic/client/handlers/firmware.py +144 -0
  47. aiohomematic/client/handlers/link_mgmt.py +199 -0
  48. aiohomematic/client/handlers/metadata.py +436 -0
  49. aiohomematic/client/handlers/programs.py +144 -0
  50. aiohomematic/client/handlers/sysvars.py +100 -0
  51. aiohomematic/client/interface_client.py +1304 -0
  52. aiohomematic/client/json_rpc.py +2068 -0
  53. aiohomematic/client/request_coalescer.py +282 -0
  54. aiohomematic/client/rpc_proxy.py +629 -0
  55. aiohomematic/client/state_machine.py +324 -0
  56. aiohomematic/const.py +2207 -0
  57. aiohomematic/context.py +275 -0
  58. aiohomematic/converter.py +270 -0
  59. aiohomematic/decorators.py +390 -0
  60. aiohomematic/exceptions.py +185 -0
  61. aiohomematic/hmcli.py +997 -0
  62. aiohomematic/i18n.py +193 -0
  63. aiohomematic/interfaces/__init__.py +407 -0
  64. aiohomematic/interfaces/central.py +1067 -0
  65. aiohomematic/interfaces/client.py +1096 -0
  66. aiohomematic/interfaces/coordinators.py +63 -0
  67. aiohomematic/interfaces/model.py +1921 -0
  68. aiohomematic/interfaces/operations.py +217 -0
  69. aiohomematic/logging_context.py +134 -0
  70. aiohomematic/metrics/__init__.py +125 -0
  71. aiohomematic/metrics/_protocols.py +140 -0
  72. aiohomematic/metrics/aggregator.py +534 -0
  73. aiohomematic/metrics/dataclasses.py +489 -0
  74. aiohomematic/metrics/emitter.py +292 -0
  75. aiohomematic/metrics/events.py +183 -0
  76. aiohomematic/metrics/keys.py +300 -0
  77. aiohomematic/metrics/observer.py +563 -0
  78. aiohomematic/metrics/stats.py +172 -0
  79. aiohomematic/model/__init__.py +189 -0
  80. aiohomematic/model/availability.py +65 -0
  81. aiohomematic/model/calculated/__init__.py +89 -0
  82. aiohomematic/model/calculated/climate.py +276 -0
  83. aiohomematic/model/calculated/data_point.py +315 -0
  84. aiohomematic/model/calculated/field.py +147 -0
  85. aiohomematic/model/calculated/operating_voltage_level.py +286 -0
  86. aiohomematic/model/calculated/support.py +232 -0
  87. aiohomematic/model/custom/__init__.py +214 -0
  88. aiohomematic/model/custom/capabilities/__init__.py +67 -0
  89. aiohomematic/model/custom/capabilities/climate.py +41 -0
  90. aiohomematic/model/custom/capabilities/light.py +87 -0
  91. aiohomematic/model/custom/capabilities/lock.py +44 -0
  92. aiohomematic/model/custom/capabilities/siren.py +63 -0
  93. aiohomematic/model/custom/climate.py +1130 -0
  94. aiohomematic/model/custom/cover.py +722 -0
  95. aiohomematic/model/custom/data_point.py +360 -0
  96. aiohomematic/model/custom/definition.py +300 -0
  97. aiohomematic/model/custom/field.py +89 -0
  98. aiohomematic/model/custom/light.py +1174 -0
  99. aiohomematic/model/custom/lock.py +322 -0
  100. aiohomematic/model/custom/mixins.py +445 -0
  101. aiohomematic/model/custom/profile.py +945 -0
  102. aiohomematic/model/custom/registry.py +251 -0
  103. aiohomematic/model/custom/siren.py +462 -0
  104. aiohomematic/model/custom/switch.py +195 -0
  105. aiohomematic/model/custom/text_display.py +289 -0
  106. aiohomematic/model/custom/valve.py +78 -0
  107. aiohomematic/model/data_point.py +1416 -0
  108. aiohomematic/model/device.py +1840 -0
  109. aiohomematic/model/event.py +216 -0
  110. aiohomematic/model/generic/__init__.py +327 -0
  111. aiohomematic/model/generic/action.py +40 -0
  112. aiohomematic/model/generic/action_select.py +62 -0
  113. aiohomematic/model/generic/binary_sensor.py +30 -0
  114. aiohomematic/model/generic/button.py +31 -0
  115. aiohomematic/model/generic/data_point.py +177 -0
  116. aiohomematic/model/generic/dummy.py +150 -0
  117. aiohomematic/model/generic/number.py +76 -0
  118. aiohomematic/model/generic/select.py +56 -0
  119. aiohomematic/model/generic/sensor.py +76 -0
  120. aiohomematic/model/generic/switch.py +54 -0
  121. aiohomematic/model/generic/text.py +33 -0
  122. aiohomematic/model/hub/__init__.py +100 -0
  123. aiohomematic/model/hub/binary_sensor.py +24 -0
  124. aiohomematic/model/hub/button.py +28 -0
  125. aiohomematic/model/hub/connectivity.py +190 -0
  126. aiohomematic/model/hub/data_point.py +342 -0
  127. aiohomematic/model/hub/hub.py +864 -0
  128. aiohomematic/model/hub/inbox.py +135 -0
  129. aiohomematic/model/hub/install_mode.py +393 -0
  130. aiohomematic/model/hub/metrics.py +208 -0
  131. aiohomematic/model/hub/number.py +42 -0
  132. aiohomematic/model/hub/select.py +52 -0
  133. aiohomematic/model/hub/sensor.py +37 -0
  134. aiohomematic/model/hub/switch.py +43 -0
  135. aiohomematic/model/hub/text.py +30 -0
  136. aiohomematic/model/hub/update.py +221 -0
  137. aiohomematic/model/support.py +592 -0
  138. aiohomematic/model/update.py +140 -0
  139. aiohomematic/model/week_profile.py +1827 -0
  140. aiohomematic/property_decorators.py +719 -0
  141. aiohomematic/py.typed +0 -0
  142. aiohomematic/rega_scripts/accept_device_in_inbox.fn +51 -0
  143. aiohomematic/rega_scripts/create_backup_start.fn +28 -0
  144. aiohomematic/rega_scripts/create_backup_status.fn +89 -0
  145. aiohomematic/rega_scripts/fetch_all_device_data.fn +97 -0
  146. aiohomematic/rega_scripts/get_backend_info.fn +25 -0
  147. aiohomematic/rega_scripts/get_inbox_devices.fn +61 -0
  148. aiohomematic/rega_scripts/get_program_descriptions.fn +31 -0
  149. aiohomematic/rega_scripts/get_serial.fn +44 -0
  150. aiohomematic/rega_scripts/get_service_messages.fn +83 -0
  151. aiohomematic/rega_scripts/get_system_update_info.fn +39 -0
  152. aiohomematic/rega_scripts/get_system_variable_descriptions.fn +31 -0
  153. aiohomematic/rega_scripts/set_program_state.fn +17 -0
  154. aiohomematic/rega_scripts/set_system_variable.fn +19 -0
  155. aiohomematic/rega_scripts/trigger_firmware_update.fn +67 -0
  156. aiohomematic/schemas.py +256 -0
  157. aiohomematic/store/__init__.py +55 -0
  158. aiohomematic/store/dynamic/__init__.py +43 -0
  159. aiohomematic/store/dynamic/command.py +250 -0
  160. aiohomematic/store/dynamic/data.py +175 -0
  161. aiohomematic/store/dynamic/details.py +187 -0
  162. aiohomematic/store/dynamic/ping_pong.py +416 -0
  163. aiohomematic/store/persistent/__init__.py +71 -0
  164. aiohomematic/store/persistent/base.py +285 -0
  165. aiohomematic/store/persistent/device.py +233 -0
  166. aiohomematic/store/persistent/incident.py +380 -0
  167. aiohomematic/store/persistent/paramset.py +241 -0
  168. aiohomematic/store/persistent/session.py +556 -0
  169. aiohomematic/store/serialization.py +150 -0
  170. aiohomematic/store/storage.py +689 -0
  171. aiohomematic/store/types.py +526 -0
  172. aiohomematic/store/visibility/__init__.py +40 -0
  173. aiohomematic/store/visibility/parser.py +141 -0
  174. aiohomematic/store/visibility/registry.py +722 -0
  175. aiohomematic/store/visibility/rules.py +307 -0
  176. aiohomematic/strings.json +237 -0
  177. aiohomematic/support.py +706 -0
  178. aiohomematic/tracing.py +236 -0
  179. aiohomematic/translations/de.json +237 -0
  180. aiohomematic/translations/en.json +237 -0
  181. aiohomematic/type_aliases.py +51 -0
  182. aiohomematic/validator.py +128 -0
  183. aiohomematic-2026.1.29.dist-info/METADATA +296 -0
  184. aiohomematic-2026.1.29.dist-info/RECORD +188 -0
  185. aiohomematic-2026.1.29.dist-info/WHEEL +5 -0
  186. aiohomematic-2026.1.29.dist-info/entry_points.txt +2 -0
  187. aiohomematic-2026.1.29.dist-info/licenses/LICENSE +21 -0
  188. aiohomematic-2026.1.29.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1096 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2021-2026
3
+ """
4
+ Client protocol interfaces.
5
+
6
+ This module defines protocol interfaces for client operations,
7
+ allowing components to depend on client functionality without coupling
8
+ to specific implementations (ClientCCU, ClientJsonCCU, ClientHomegear).
9
+
10
+ Protocol Hierarchy
11
+ ------------------
12
+
13
+ Client protocols are organized following the Interface Segregation Principle:
14
+
15
+ **Core Protocols (4):**
16
+ - `ClientIdentityProtocol`: Basic identification (interface, interface_id, model, version)
17
+ - `ClientConnectionProtocol`: Connection state management (available, is_connected, reconnect)
18
+ - `ClientLifecycleProtocol`: Lifecycle operations (init_client, stop, proxy management)
19
+
20
+ **Handler-Based Protocols (9):**
21
+ - `DeviceDiscoveryOperationsProtocol`: Device discovery (list_devices, get_device_description)
22
+ - `ParamsetOperationsProtocol`: Paramset operations (get_paramset, put_paramset, fetch)
23
+ - `ValueOperationsProtocol`: Value operations (get_value, set_value, report_value_usage)
24
+ - `LinkOperationsProtocol`: Device linking (add_link, remove_link, get_link_peers)
25
+ - `FirmwareOperationsProtocol`: Firmware updates (update_device_firmware, trigger_firmware_update)
26
+ - `SystemVariableOperationsProtocol`: System variables (get/set/delete_system_variable)
27
+ - `ProgramOperationsProtocol`: Program execution (get_all_programs, execute_program)
28
+ - `BackupOperationsProtocol`: Backup creation (create_backup_and_download)
29
+ - `MetadataOperationsProtocol`: Metadata, rooms, functions, install mode, inbox, service messages
30
+
31
+ **Composite Protocol:**
32
+ - `ClientProtocol`: Combines all sub-protocols for complete client access
33
+ """
34
+
35
+ from __future__ import annotations
36
+
37
+ from abc import abstractmethod
38
+ from collections.abc import Mapping
39
+ from datetime import datetime
40
+ from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
41
+
42
+ from aiohomematic.const import (
43
+ BackupData,
44
+ CallSource,
45
+ CentralState,
46
+ ClientState,
47
+ DataPointKey,
48
+ DeviceDescription,
49
+ FailureReason,
50
+ InboxDeviceData,
51
+ Interface,
52
+ ParameterData,
53
+ ParamsetKey,
54
+ ProductGroup,
55
+ ProgramData,
56
+ ProxyInitState,
57
+ SystemInformation,
58
+ )
59
+ from aiohomematic.interfaces.operations import TaskSchedulerProtocol
60
+ from aiohomematic.metrics._protocols import ClientProviderForMetricsProtocol
61
+
62
+ if TYPE_CHECKING:
63
+ from aiohomematic.central import CentralConnectionState, DeviceRegistry
64
+ from aiohomematic.central.coordinators import CacheCoordinator, DeviceCoordinator, EventCoordinator
65
+ from aiohomematic.central.events import EventBus
66
+ from aiohomematic.client import AioJsonRpcAioHttpClient, InterfaceConfig
67
+ from aiohomematic.client.backends.capabilities import BackendCapabilities
68
+ from aiohomematic.interfaces.central import CentralConfigProtocol
69
+ from aiohomematic.interfaces.model import DeviceProtocol
70
+ from aiohomematic.store.persistent import SessionRecorder
71
+
72
+
73
+ # =============================================================================
74
+ # Client Sub-Protocol Interfaces
75
+ # =============================================================================
76
+
77
+
78
+ class ClientStateMachineProtocol(Protocol):
79
+ """
80
+ Protocol for client state machine operations.
81
+
82
+ Provides access to state machine properties for failure tracking.
83
+ """
84
+
85
+ __slots__ = ()
86
+
87
+ @property
88
+ def failure_message(self) -> str:
89
+ """Return human-readable failure message."""
90
+
91
+ @property
92
+ def failure_reason(self) -> FailureReason:
93
+ """Return the reason for the failed state."""
94
+
95
+ @property
96
+ def is_failed(self) -> bool:
97
+ """Return True if client is in failed state."""
98
+
99
+ @property
100
+ def state(self) -> ClientState:
101
+ """Return the current state."""
102
+
103
+
104
+ class ClientIdentityProtocol(Protocol):
105
+ """
106
+ Protocol for client identification.
107
+
108
+ Provides basic identity information for a client.
109
+ """
110
+
111
+ __slots__ = ()
112
+
113
+ @property
114
+ def central(self) -> ClientDependenciesProtocol:
115
+ """Return the central of the client."""
116
+
117
+ @property
118
+ def interface(self) -> Interface:
119
+ """Return the interface of the client."""
120
+
121
+ @property
122
+ def interface_id(self) -> str:
123
+ """Return the interface id of the client."""
124
+
125
+ @property
126
+ def is_initialized(self) -> bool:
127
+ """Return if interface is initialized."""
128
+
129
+ @property
130
+ def model(self) -> str:
131
+ """Return the model of the backend."""
132
+
133
+ @property
134
+ def system_information(self) -> SystemInformation:
135
+ """Return the system_information of the client."""
136
+
137
+ @property
138
+ def version(self) -> str:
139
+ """Return the version id of the client."""
140
+
141
+
142
+ class ClientConnectionProtocol(Protocol):
143
+ """
144
+ Protocol for client connection state management.
145
+
146
+ Provides connection state and health check operations.
147
+ """
148
+
149
+ __slots__ = ()
150
+
151
+ @property
152
+ def all_circuit_breakers_closed(self) -> bool:
153
+ """Return True if all circuit breakers are in closed state."""
154
+
155
+ @property
156
+ def available(self) -> bool:
157
+ """Return the availability of the client."""
158
+
159
+ @property
160
+ def modified_at(self) -> datetime:
161
+ """Return the last update datetime value."""
162
+
163
+ @modified_at.setter
164
+ def modified_at(self, value: datetime) -> None:
165
+ """Write the last update datetime value."""
166
+
167
+ @property
168
+ def state(self) -> ClientState:
169
+ """Return the current client state."""
170
+
171
+ @property
172
+ def state_machine(self) -> ClientStateMachineProtocol:
173
+ """Return the client state machine."""
174
+
175
+ async def check_connection_availability(self, *, handle_ping_pong: bool) -> bool:
176
+ """Check if proxy is still initialized."""
177
+
178
+ def clear_json_rpc_session(self) -> None:
179
+ """Clear the JSON-RPC session to force re-authentication on next request."""
180
+
181
+ def is_callback_alive(self) -> bool:
182
+ """Return if XmlRPC-Server is alive based on received events for this client."""
183
+
184
+ async def is_connected(self) -> bool:
185
+ """Perform actions required for connectivity check."""
186
+
187
+ async def reconnect(self) -> bool:
188
+ """Re-init all RPC clients."""
189
+
190
+ def reset_circuit_breakers(self) -> None:
191
+ """Reset all circuit breakers to closed state."""
192
+
193
+
194
+ class ClientLifecycleProtocol(Protocol):
195
+ """
196
+ Protocol for client lifecycle operations.
197
+
198
+ Provides initialization and shutdown operations.
199
+ """
200
+
201
+ __slots__ = ()
202
+
203
+ async def deinitialize_proxy(self) -> ProxyInitState:
204
+ """De-init to stop the backend from sending events for this remote."""
205
+
206
+ async def init_client(self) -> None:
207
+ """Initialize the client."""
208
+
209
+ async def initialize_proxy(self) -> ProxyInitState:
210
+ """Initialize the proxy has to tell the backend where to send the events."""
211
+
212
+ async def reinitialize_proxy(self) -> ProxyInitState:
213
+ """Reinit Proxy."""
214
+
215
+ async def stop(self) -> None:
216
+ """Stop depending services."""
217
+
218
+
219
+ class DeviceDiscoveryOperationsProtocol(Protocol):
220
+ """
221
+ Protocol for device discovery operations.
222
+
223
+ Provides methods for listing and discovering devices from the backend.
224
+ Implemented by DeviceHandler.
225
+ """
226
+
227
+ __slots__ = ()
228
+
229
+ async def fetch_all_device_data(self) -> None:
230
+ """Fetch all device data from the backend."""
231
+
232
+ async def fetch_device_details(self) -> None:
233
+ """Get all names via JSON-RPC and store in data."""
234
+
235
+ async def get_all_device_descriptions(self, *, device_address: str) -> tuple[DeviceDescription, ...]:
236
+ """Get all device descriptions from the backend."""
237
+
238
+ async def get_device_description(self, *, address: str) -> DeviceDescription | None:
239
+ """Get device descriptions from the backend."""
240
+
241
+ async def list_devices(self) -> tuple[DeviceDescription, ...] | None:
242
+ """List devices of the backend."""
243
+
244
+
245
+ class ParamsetOperationsProtocol(Protocol):
246
+ """
247
+ Protocol for paramset operations.
248
+
249
+ Provides methods for reading and writing paramsets and paramset descriptions.
250
+ Implemented by DeviceHandler.
251
+ """
252
+
253
+ __slots__ = ()
254
+
255
+ async def fetch_paramset_description(self, *, channel_address: str, paramset_key: ParamsetKey) -> None:
256
+ """Fetch a specific paramset and add it to the known ones."""
257
+
258
+ async def fetch_paramset_descriptions(self, *, device_description: DeviceDescription) -> None:
259
+ """Fetch paramsets for provided device description."""
260
+
261
+ async def get_all_paramset_descriptions(
262
+ self, *, device_descriptions: tuple[DeviceDescription, ...]
263
+ ) -> dict[str, dict[ParamsetKey, dict[str, ParameterData]]]:
264
+ """Get all paramset descriptions for provided device descriptions."""
265
+
266
+ async def get_paramset(
267
+ self,
268
+ *,
269
+ address: str,
270
+ paramset_key: ParamsetKey | str,
271
+ call_source: CallSource = CallSource.MANUAL_OR_SCHEDULED,
272
+ ) -> dict[str, Any]:
273
+ """Return a paramset from the backend."""
274
+
275
+ async def get_paramset_descriptions(
276
+ self, *, device_description: DeviceDescription
277
+ ) -> dict[str, dict[ParamsetKey, dict[str, ParameterData]]]:
278
+ """Get paramsets for provided device description."""
279
+
280
+ async def put_paramset(
281
+ self,
282
+ *,
283
+ channel_address: str,
284
+ paramset_key_or_link_address: ParamsetKey | str,
285
+ values: dict[str, Any],
286
+ wait_for_callback: int | None = None,
287
+ rx_mode: Any | None = None,
288
+ check_against_pd: bool = False,
289
+ ) -> set[Any]:
290
+ """Set paramsets manually."""
291
+
292
+ async def update_paramset_descriptions(self, *, device_address: str) -> None:
293
+ """Update paramsets descriptions for provided device_address."""
294
+
295
+
296
+ class ValueOperationsProtocol(Protocol):
297
+ """
298
+ Protocol for value read/write operations.
299
+
300
+ Provides methods for reading and writing single parameter values.
301
+ Implemented by DeviceHandler.
302
+ """
303
+
304
+ __slots__ = ()
305
+
306
+ async def get_value(
307
+ self,
308
+ *,
309
+ channel_address: str,
310
+ paramset_key: ParamsetKey,
311
+ parameter: str,
312
+ call_source: CallSource = CallSource.MANUAL_OR_SCHEDULED,
313
+ ) -> Any:
314
+ """Return a value from the backend."""
315
+
316
+ async def report_value_usage(self, *, address: str, value_id: str, ref_counter: int) -> bool:
317
+ """Report value usage."""
318
+
319
+ async def set_value(
320
+ self,
321
+ *,
322
+ channel_address: str,
323
+ paramset_key: ParamsetKey,
324
+ parameter: str,
325
+ value: Any,
326
+ wait_for_callback: int | None = None,
327
+ rx_mode: Any | None = None,
328
+ check_against_pd: bool = False,
329
+ ) -> set[Any]:
330
+ """Set single value on paramset VALUES."""
331
+
332
+
333
+ class LinkOperationsProtocol(Protocol):
334
+ """
335
+ Protocol for device linking operations.
336
+
337
+ Provides methods for creating and managing direct links between devices.
338
+ Implemented by LinkHandler.
339
+ """
340
+
341
+ __slots__ = ()
342
+
343
+ async def add_link(self, *, sender_address: str, receiver_address: str, name: str, description: str) -> None:
344
+ """Add a link between two devices."""
345
+
346
+ async def get_link_peers(self, *, address: str) -> tuple[str, ...]:
347
+ """Return a list of link peers."""
348
+
349
+ async def get_links(self, *, address: str, flags: int) -> dict[str, Any]:
350
+ """Return a list of links."""
351
+
352
+ async def remove_link(self, *, sender_address: str, receiver_address: str) -> None:
353
+ """Remove a link between two devices."""
354
+
355
+
356
+ class FirmwareOperationsProtocol(Protocol):
357
+ """
358
+ Protocol for firmware update operations.
359
+
360
+ Provides methods for updating device and system firmware.
361
+ Implemented by FirmwareHandler.
362
+ """
363
+
364
+ __slots__ = ()
365
+
366
+ async def trigger_firmware_update(self) -> bool:
367
+ """Trigger the CCU firmware update process."""
368
+
369
+ async def update_device_firmware(self, *, device_address: str) -> bool:
370
+ """Update the firmware of a Homematic device."""
371
+
372
+
373
+ class SystemVariableOperationsProtocol(Protocol):
374
+ """
375
+ Protocol for system variable operations.
376
+
377
+ Provides methods for managing CCU system variables.
378
+ Implemented by SystemVariableHandler.
379
+ """
380
+
381
+ __slots__ = ()
382
+
383
+ async def delete_system_variable(self, *, name: str) -> bool:
384
+ """Delete a system variable from the backend."""
385
+
386
+ async def get_all_system_variables(self, *, markers: tuple[Any, ...]) -> tuple[Any, ...] | None:
387
+ """Get all system variables from the backend."""
388
+
389
+ async def get_system_variable(self, *, name: str) -> Any:
390
+ """Get single system variable from the backend."""
391
+
392
+ async def set_system_variable(self, *, legacy_name: str, value: Any) -> bool:
393
+ """Set a system variable on the backend."""
394
+
395
+
396
+ class ProgramOperationsProtocol(Protocol):
397
+ """
398
+ Protocol for program operations.
399
+
400
+ Provides methods for managing CCU programs.
401
+ Implemented by ProgramHandler.
402
+ """
403
+
404
+ __slots__ = ()
405
+
406
+ async def execute_program(self, *, pid: str) -> bool:
407
+ """Execute a program on the backend."""
408
+
409
+ async def get_all_programs(self, *, markers: tuple[Any, ...]) -> tuple[ProgramData, ...]:
410
+ """Get all programs, if available."""
411
+
412
+ async def has_program_ids(self, *, rega_id: int) -> bool:
413
+ """Return if a channel has program ids."""
414
+
415
+ async def set_program_state(self, *, pid: str, state: bool) -> bool:
416
+ """Set the program state on the backend."""
417
+
418
+
419
+ class BackupOperationsProtocol(Protocol):
420
+ """
421
+ Protocol for backup operations.
422
+
423
+ Provides methods for creating and downloading CCU backups.
424
+ Implemented by ClientCCU.
425
+ """
426
+
427
+ __slots__ = ()
428
+
429
+ async def create_backup_and_download(
430
+ self,
431
+ *,
432
+ max_wait_time: float = ...,
433
+ poll_interval: float = ...,
434
+ ) -> BackupData | None:
435
+ """Create a backup on the CCU and download it."""
436
+
437
+
438
+ class MetadataOperationsProtocol(Protocol):
439
+ """
440
+ Protocol for metadata and system operations.
441
+
442
+ Provides methods for metadata, rooms, functions, install mode, inbox devices,
443
+ service messages, and other system-level operations.
444
+ Implemented by MetadataHandler.
445
+ """
446
+
447
+ __slots__ = ()
448
+
449
+ async def accept_device_in_inbox(self, *, device_address: str) -> bool:
450
+ """Accept a device from the CCU inbox."""
451
+
452
+ async def get_all_functions(self) -> dict[str, set[str]]:
453
+ """Get all functions from the backend."""
454
+
455
+ async def get_all_rooms(self) -> dict[str, set[str]]:
456
+ """Get all rooms from the backend."""
457
+
458
+ async def get_inbox_devices(self) -> tuple[InboxDeviceData, ...]:
459
+ """Get all devices in the inbox (not yet configured)."""
460
+
461
+ async def get_install_mode(self) -> int:
462
+ """Return the remaining time in install mode."""
463
+
464
+ async def get_metadata(self, *, address: str, data_id: str) -> dict[str, Any]:
465
+ """Return the metadata for an object."""
466
+
467
+ async def get_rega_id_by_address(self, *, address: str) -> int | None:
468
+ """Get the ReGa ID for a device or channel address."""
469
+
470
+ async def get_service_messages(self, *, message_type: Any | None = None) -> tuple[Any, ...]:
471
+ """Get all active service messages from the backend."""
472
+
473
+ async def get_system_update_info(self) -> Any | None:
474
+ """Get system update information from the backend."""
475
+
476
+ async def rename_channel(self, *, rega_id: int, new_name: str) -> bool:
477
+ """Rename a channel on the CCU."""
478
+
479
+ async def rename_device(self, *, rega_id: int, new_name: str) -> bool:
480
+ """Rename a device on the CCU."""
481
+
482
+ async def set_install_mode(
483
+ self,
484
+ *,
485
+ on: bool = True,
486
+ time: int = 60,
487
+ mode: int = 1,
488
+ device_address: str | None = None,
489
+ ) -> bool:
490
+ """Set the install mode on the backend."""
491
+
492
+ async def set_metadata(self, *, address: str, data_id: str, value: dict[str, Any]) -> dict[str, Any]:
493
+ """Write the metadata for an object."""
494
+
495
+
496
+ class ClientSupportProtocol(Protocol):
497
+ """
498
+ Protocol for client support operations.
499
+
500
+ Provides utility methods and caches that are implemented directly by ClientCCU
501
+ rather than by handlers.
502
+ """
503
+
504
+ __slots__ = ()
505
+
506
+ @property
507
+ def last_value_send_tracker(self) -> CommandTrackerProtocol:
508
+ """Return the last value send tracker."""
509
+
510
+ @property
511
+ def ping_pong_tracker(self) -> PingPongTrackerProtocol:
512
+ """Return the ping pong cache."""
513
+
514
+ def get_product_group(self, *, model: str) -> ProductGroup:
515
+ """Return the product group."""
516
+
517
+ def get_virtual_remote(self) -> DeviceProtocol | None:
518
+ """Get the virtual remote for the Client."""
519
+
520
+
521
+ # =============================================================================
522
+ # Client Combined Sub-Protocol Interfaces
523
+ # =============================================================================
524
+
525
+
526
+ @runtime_checkable
527
+ class ValueAndParamsetOperationsProtocol(ValueOperationsProtocol, ParamsetOperationsProtocol, Protocol):
528
+ """
529
+ Combined protocol for value and paramset operations.
530
+
531
+ Used by components that need to send values to the backend,
532
+ either individually (set_value) or in batches (put_paramset).
533
+ Reduces coupling compared to using full ClientProtocol.
534
+
535
+ Implemented by: ClientCCU, ClientJsonCCU, ClientHomegear
536
+ """
537
+
538
+ __slots__ = ()
539
+
540
+
541
+ @runtime_checkable
542
+ class DeviceDiscoveryWithIdentityProtocol(DeviceDiscoveryOperationsProtocol, ClientIdentityProtocol, Protocol):
543
+ """
544
+ Combined protocol for device discovery with client identity.
545
+
546
+ Used by components that need to discover devices and access
547
+ basic client identification (interface_id, interface).
548
+ Reduces coupling compared to using full ClientProtocol.
549
+
550
+ Implemented by: ClientCCU, ClientJsonCCU, ClientHomegear
551
+ """
552
+
553
+ __slots__ = ()
554
+
555
+
556
+ @runtime_checkable
557
+ class DeviceDiscoveryAndMetadataProtocol(DeviceDiscoveryOperationsProtocol, MetadataOperationsProtocol, Protocol):
558
+ """
559
+ Combined protocol for device discovery and metadata operations.
560
+
561
+ Used by components that need to discover devices and perform
562
+ metadata operations like renaming devices/channels.
563
+ Reduces coupling compared to using full ClientProtocol.
564
+
565
+ Implemented by: ClientCCU, ClientJsonCCU, ClientHomegear
566
+ """
567
+
568
+ __slots__ = ()
569
+
570
+
571
+ # =============================================================================
572
+ # Client Composite Protocol Interface
573
+ # =============================================================================
574
+
575
+
576
+ @runtime_checkable
577
+ class ClientProtocol(
578
+ ClientIdentityProtocol,
579
+ ClientConnectionProtocol,
580
+ ClientLifecycleProtocol,
581
+ DeviceDiscoveryOperationsProtocol,
582
+ ParamsetOperationsProtocol,
583
+ ValueOperationsProtocol,
584
+ LinkOperationsProtocol,
585
+ FirmwareOperationsProtocol,
586
+ SystemVariableOperationsProtocol,
587
+ ProgramOperationsProtocol,
588
+ BackupOperationsProtocol,
589
+ MetadataOperationsProtocol,
590
+ ClientSupportProtocol,
591
+ Protocol,
592
+ ):
593
+ """
594
+ Composite protocol for complete Homematic client operations.
595
+
596
+ Combines all client sub-protocols into a single interface providing full
597
+ access to backend communication, device management, and system operations.
598
+ Implemented by ClientCCU, ClientJsonCCU, and ClientHomegear.
599
+
600
+ Sub-protocols:
601
+ - ClientIdentityProtocol: Basic identification
602
+ - ClientConnectionProtocol: Connection state management
603
+ - ClientLifecycleProtocol: Lifecycle operations
604
+ - DeviceDiscoveryOperationsProtocol: Device discovery
605
+ - ParamsetOperationsProtocol: Paramset operations
606
+ - ValueOperationsProtocol: Value read/write
607
+ - LinkOperationsProtocol: Device linking
608
+ - FirmwareOperationsProtocol: Firmware updates
609
+ - SystemVariableOperationsProtocol: System variables
610
+ - ProgramOperationsProtocol: Program execution
611
+ - BackupOperationsProtocol: Backup creation
612
+ - MetadataOperationsProtocol: Metadata and system operations
613
+ - ClientSupportProtocol: Utility methods and caches
614
+ """
615
+
616
+ __slots__ = ()
617
+
618
+ @property
619
+ def capabilities(self) -> BackendCapabilities:
620
+ """Return the capability flags for this backend."""
621
+
622
+
623
+ # =============================================================================
624
+ # Client-Related Protocols
625
+ # =============================================================================
626
+
627
+
628
+ @runtime_checkable
629
+ class ClientProviderProtocol(ClientProviderForMetricsProtocol, Protocol):
630
+ """
631
+ Protocol for accessing client instances.
632
+
633
+ Extends ClientProviderForMetricsProtocol with additional properties and methods.
634
+ Implemented by CentralUnit.
635
+ """
636
+
637
+ @property
638
+ @abstractmethod
639
+ def has_clients(self) -> bool:
640
+ """Check if any clients exist."""
641
+
642
+ @property
643
+ @abstractmethod
644
+ def interface_ids(self) -> frozenset[str]:
645
+ """Get all interface IDs."""
646
+
647
+ @abstractmethod
648
+ def get_client(self, *, interface_id: str | None = None, interface: Interface | None = None) -> ClientProtocol:
649
+ """Get client by interface_id or interface type."""
650
+
651
+ @abstractmethod
652
+ def has_client(self, *, interface_id: str) -> bool:
653
+ """Check if a client exists for the given interface."""
654
+
655
+
656
+ @runtime_checkable
657
+ class ClientFactoryProtocol(Protocol):
658
+ """
659
+ Protocol for creating client instances.
660
+
661
+ Implemented by CentralUnit.
662
+ """
663
+
664
+ @abstractmethod
665
+ async def create_client_instance(
666
+ self,
667
+ *,
668
+ interface_config: InterfaceConfig,
669
+ ) -> ClientProtocol:
670
+ """
671
+ Create a client for the given interface configuration.
672
+
673
+ Args:
674
+ interface_config: Configuration for the interface.
675
+
676
+ Returns:
677
+ Client instance for the interface.
678
+
679
+ """
680
+
681
+
682
+ @runtime_checkable
683
+ class ClientCoordinationProtocol(Protocol):
684
+ """
685
+ Protocol for client coordination operations.
686
+
687
+ Implemented by CentralUnit.
688
+ """
689
+
690
+ @property
691
+ @abstractmethod
692
+ def all_clients_active(self) -> bool:
693
+ """Check if all clients are active."""
694
+
695
+ @property
696
+ @abstractmethod
697
+ def clients(self) -> tuple[ClientProtocol, ...]:
698
+ """Get all clients as a tuple (snapshot for safe iteration)."""
699
+
700
+ @property
701
+ @abstractmethod
702
+ def interface_ids(self) -> frozenset[str]:
703
+ """Get all interface IDs."""
704
+
705
+ @property
706
+ @abstractmethod
707
+ def poll_clients(self) -> tuple[ClientProtocol, ...] | None:
708
+ """Get clients that require polling."""
709
+
710
+ @abstractmethod
711
+ def get_client(self, *, interface_id: str | None = None, interface: Interface | None = None) -> ClientProtocol:
712
+ """Get client by interface_id or interface type."""
713
+
714
+ @abstractmethod
715
+ async def restart_clients(self) -> None:
716
+ """Restart all clients."""
717
+
718
+
719
+ @runtime_checkable
720
+ class PrimaryClientProviderProtocol(Protocol):
721
+ """
722
+ Protocol for accessing primary client.
723
+
724
+ Implemented by CentralUnit.
725
+ """
726
+
727
+ @property
728
+ @abstractmethod
729
+ def primary_client(self) -> ClientProtocol | None:
730
+ """Get primary client."""
731
+
732
+
733
+ @runtime_checkable
734
+ class LastEventTrackerProtocol(Protocol):
735
+ """
736
+ Protocol for tracking last event times per interface.
737
+
738
+ Implemented by CentralUnit.
739
+ """
740
+
741
+ @abstractmethod
742
+ def get_last_event_seen_for_interface(self, *, interface_id: str) -> datetime | None:
743
+ """Get the last event timestamp for an interface."""
744
+
745
+
746
+ @runtime_checkable
747
+ class DeviceLookupProtocol(Protocol):
748
+ """
749
+ Protocol for looking up devices and data points.
750
+
751
+ Implemented by CentralUnit.
752
+ """
753
+
754
+ @abstractmethod
755
+ def get_device(self, *, address: str) -> DeviceProtocol | None:
756
+ """Get device by address."""
757
+
758
+ @abstractmethod
759
+ def get_generic_data_point(
760
+ self,
761
+ *,
762
+ channel_address: str,
763
+ parameter: str,
764
+ paramset_key: ParamsetKey,
765
+ ) -> Any | None:
766
+ """Get generic data point."""
767
+
768
+
769
+ @runtime_checkable
770
+ class NewDeviceHandlerProtocol(Protocol):
771
+ """
772
+ Protocol for handling new device registration.
773
+
774
+ Implemented by CentralUnit.
775
+ """
776
+
777
+ @abstractmethod
778
+ async def add_new_devices(
779
+ self,
780
+ *,
781
+ interface_id: str,
782
+ device_descriptions: tuple[DeviceDescription, ...],
783
+ ) -> None:
784
+ """Add new devices from the backend."""
785
+
786
+
787
+ @runtime_checkable
788
+ class DataCacheWriterProtocol(Protocol):
789
+ """
790
+ Protocol for writing data to the central data cache.
791
+
792
+ Implemented by CentralDataCache.
793
+ """
794
+
795
+ @abstractmethod
796
+ def add_data(self, *, interface: Interface, all_device_data: Mapping[str, Any]) -> None:
797
+ """Add all device data to the cache."""
798
+
799
+
800
+ @runtime_checkable
801
+ class ParamsetDescriptionWriterProtocol(Protocol):
802
+ """
803
+ Protocol for writing paramset descriptions.
804
+
805
+ Implemented by ParamsetDescriptionRegistry.
806
+ """
807
+
808
+ @abstractmethod
809
+ def add(
810
+ self,
811
+ *,
812
+ interface_id: str,
813
+ channel_address: str,
814
+ paramset_key: ParamsetKey,
815
+ paramset_description: dict[str, Any],
816
+ ) -> None:
817
+ """Add a paramset description."""
818
+
819
+
820
+ @runtime_checkable
821
+ class DeviceDetailsWriterProtocol(Protocol):
822
+ """
823
+ Protocol for writing device details.
824
+
825
+ Implemented by DeviceDetailsCache.
826
+ """
827
+
828
+ @property
829
+ @abstractmethod
830
+ def device_channel_rega_ids(self) -> Mapping[str, int]:
831
+ """Return the device channel ReGa IDs."""
832
+
833
+ @abstractmethod
834
+ def add_address_rega_id(self, *, address: str, rega_id: int) -> None:
835
+ """Add a ReGa ID for an address."""
836
+
837
+ @abstractmethod
838
+ def add_interface(self, *, address: str, interface: Interface) -> None:
839
+ """Add an interface for an address."""
840
+
841
+ @abstractmethod
842
+ def add_name(self, *, address: str, name: str) -> None:
843
+ """Add a name for an address."""
844
+
845
+
846
+ @runtime_checkable
847
+ class DeviceDescriptionsAccessProtocol(Protocol):
848
+ """
849
+ Protocol for accessing device descriptions from cache.
850
+
851
+ Implemented by DeviceDescriptionRegistry.
852
+ """
853
+
854
+ @abstractmethod
855
+ def find_device_description(
856
+ self,
857
+ *,
858
+ interface_id: str,
859
+ device_address: str,
860
+ ) -> DeviceDescription | None:
861
+ """Find a device description."""
862
+
863
+ @abstractmethod
864
+ def get_device_descriptions(self, *, interface_id: str) -> Mapping[str, DeviceDescription]:
865
+ """Get all device descriptions for an interface."""
866
+
867
+
868
+ @runtime_checkable
869
+ class ConnectionStateProviderProtocol(Protocol):
870
+ """
871
+ Protocol for accessing connection state.
872
+
873
+ Implemented by CentralUnit.
874
+ """
875
+
876
+ @property
877
+ @abstractmethod
878
+ def connection_state(self) -> CentralConnectionState:
879
+ """Get connection state."""
880
+
881
+
882
+ @runtime_checkable
883
+ class SessionRecorderProviderProtocol(Protocol):
884
+ """
885
+ Protocol for accessing session recorder.
886
+
887
+ Implemented by CentralUnit.
888
+ """
889
+
890
+ @property
891
+ @abstractmethod
892
+ def recorder(self) -> SessionRecorder:
893
+ """Get session recorder."""
894
+
895
+
896
+ @runtime_checkable
897
+ class JsonRpcClientProviderProtocol(Protocol):
898
+ """
899
+ Protocol for accessing JSON-RPC client.
900
+
901
+ Implemented by CentralUnit.
902
+ """
903
+
904
+ @property
905
+ @abstractmethod
906
+ def json_rpc_client(self) -> AioJsonRpcAioHttpClient:
907
+ """Get JSON-RPC client."""
908
+
909
+
910
+ @runtime_checkable
911
+ class CallbackAddressProviderProtocol(Protocol):
912
+ """
913
+ Protocol for accessing callback address information.
914
+
915
+ Implemented by CentralUnit.
916
+ """
917
+
918
+ @property
919
+ @abstractmethod
920
+ def callback_ip_addr(self) -> str:
921
+ """Get callback IP address."""
922
+
923
+ @property
924
+ @abstractmethod
925
+ def listen_port_xml_rpc(self) -> int:
926
+ """Get XML-RPC listen port."""
927
+
928
+
929
+ @runtime_checkable
930
+ class CommandTrackerProtocol(Protocol):
931
+ """Protocol for command tracker operations."""
932
+
933
+ @abstractmethod
934
+ def add_put_paramset(
935
+ self, *, channel_address: str, paramset_key: ParamsetKey, values: dict[str, Any]
936
+ ) -> set[tuple[DataPointKey, Any]]:
937
+ """Add data from put paramset command."""
938
+
939
+ @abstractmethod
940
+ def add_set_value(
941
+ self,
942
+ *,
943
+ channel_address: str,
944
+ parameter: str,
945
+ value: Any,
946
+ ) -> set[tuple[DataPointKey, Any]]:
947
+ """Add data from set value command."""
948
+
949
+ @abstractmethod
950
+ def clear(self) -> None:
951
+ """Clear all tracked command entries."""
952
+
953
+ @abstractmethod
954
+ def get_last_value_send(self, *, dpk: DataPointKey, max_age: int = ...) -> Any:
955
+ """Return the last send value."""
956
+
957
+ @abstractmethod
958
+ def remove_last_value_send(
959
+ self,
960
+ *,
961
+ dpk: DataPointKey,
962
+ value: Any = None,
963
+ max_age: int = ...,
964
+ ) -> None:
965
+ """Remove the last send value."""
966
+
967
+
968
+ @runtime_checkable
969
+ class PingPongTrackerProtocol(Protocol):
970
+ """Protocol for ping/pong cache operations."""
971
+
972
+ @abstractmethod
973
+ def clear(self) -> None:
974
+ """Clear the cache."""
975
+
976
+ @abstractmethod
977
+ def handle_received_pong(self, *, pong_token: str) -> None:
978
+ """Handle received pong token."""
979
+
980
+ @abstractmethod
981
+ def handle_send_ping(self, *, ping_token: str) -> None:
982
+ """Handle send ping token."""
983
+
984
+
985
+ @runtime_checkable
986
+ class ClientDependenciesProtocol(Protocol):
987
+ """
988
+ Composite protocol for all dependencies required by Client classes.
989
+
990
+ This protocol combines all the individual protocols needed by ClientCCU,
991
+ ClientConfig, and related classes. CentralUnit implements this protocol.
992
+
993
+ Using a composite protocol allows clients to depend on a single interface
994
+ instead of many individual protocols, while still maintaining decoupling
995
+ from the full CentralUnit implementation.
996
+ """
997
+
998
+ # CentralInfoProtocol
999
+ @property
1000
+ @abstractmethod
1001
+ def available(self) -> bool:
1002
+ """Check if central is available."""
1003
+
1004
+ @property
1005
+ @abstractmethod
1006
+ def cache_coordinator(self) -> CacheCoordinator:
1007
+ """Get cache coordinator."""
1008
+
1009
+ @property
1010
+ @abstractmethod
1011
+ def callback_ip_addr(self) -> str:
1012
+ """Return callback IP address."""
1013
+
1014
+ @property
1015
+ @abstractmethod
1016
+ def config(self) -> CentralConfigProtocol:
1017
+ """Return central configuration."""
1018
+
1019
+ @property
1020
+ @abstractmethod
1021
+ def connection_state(self) -> CentralConnectionState:
1022
+ """Return connection state."""
1023
+
1024
+ @property
1025
+ @abstractmethod
1026
+ def device_coordinator(self) -> DeviceCoordinator:
1027
+ """Return the device coordinator."""
1028
+
1029
+ @property
1030
+ @abstractmethod
1031
+ def device_registry(self) -> DeviceRegistry:
1032
+ """Return the device registry."""
1033
+
1034
+ @property
1035
+ @abstractmethod
1036
+ def event_bus(self) -> EventBus:
1037
+ """Return the event bus."""
1038
+
1039
+ @property
1040
+ @abstractmethod
1041
+ def event_coordinator(self) -> EventCoordinator:
1042
+ """Return the event coordinator for publishing events."""
1043
+
1044
+ @property
1045
+ @abstractmethod
1046
+ def info_payload(self) -> Mapping[str, Any]:
1047
+ """Return the info payload."""
1048
+
1049
+ @property
1050
+ @abstractmethod
1051
+ def json_rpc_client(self) -> AioJsonRpcAioHttpClient:
1052
+ """Return JSON-RPC client."""
1053
+
1054
+ @property
1055
+ @abstractmethod
1056
+ def listen_port_xml_rpc(self) -> int:
1057
+ """Return XML-RPC listen port."""
1058
+
1059
+ @property
1060
+ @abstractmethod
1061
+ def looper(self) -> TaskSchedulerProtocol:
1062
+ """Return task scheduler/looper."""
1063
+
1064
+ @property
1065
+ @abstractmethod
1066
+ def model(self) -> str | None:
1067
+ """Return backend model."""
1068
+
1069
+ @property
1070
+ @abstractmethod
1071
+ def name(self) -> str:
1072
+ """Return central name."""
1073
+
1074
+ @property
1075
+ @abstractmethod
1076
+ def state(self) -> CentralState:
1077
+ """Return the current central state from the state machine."""
1078
+
1079
+ @abstractmethod
1080
+ def get_generic_data_point(
1081
+ self,
1082
+ *,
1083
+ channel_address: str,
1084
+ parameter: str,
1085
+ paramset_key: ParamsetKey,
1086
+ ) -> Any | None:
1087
+ """Return generic data point."""
1088
+
1089
+ @abstractmethod
1090
+ async def save_files(
1091
+ self,
1092
+ *,
1093
+ save_device_descriptions: bool = False,
1094
+ save_paramset_descriptions: bool = False,
1095
+ ) -> None:
1096
+ """Save persistent files to disk."""