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,1921 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2021-2026
3
+ """
4
+ Model protocol interfaces.
5
+
6
+ This module defines protocol interfaces for Device, Channel, DataPoint,
7
+ Hub, and WeekProfile classes, allowing components to depend on specific
8
+ capabilities without coupling to the full implementation.
9
+
10
+ Protocol Hierarchy
11
+ ------------------
12
+
13
+ Channel protocols (ChannelProtocol composed of):
14
+ - ChannelIdentityProtocol: Basic identification (address, name, no, type_name, unique_id, rega_id)
15
+ - ChannelDataPointAccessProtocol: DataPoint and event access methods
16
+ - ChannelGroupingProtocol: Channel group management (group_master, group_no, link_peer_channels)
17
+ - ChannelMetadataProtocol: Additional metadata (device, function, room, paramset_descriptions)
18
+ - ChannelLinkManagementProtocol: Central link operations
19
+ - ChannelLifecycleProtocol: Lifecycle methods (finalize_init, on_config_changed, remove)
20
+
21
+ Device protocols (DeviceProtocol composed of):
22
+ - DeviceIdentityProtocol: Basic identification (address, name, model, manufacturer, interface)
23
+ - DeviceChannelAccessProtocol: Channel and DataPoint access methods
24
+ - DeviceAvailabilityProtocol: Availability state management
25
+ - DeviceFirmwareProtocol: Firmware information and update operations
26
+ - DeviceLinkManagementProtocol: Central link operations
27
+ - DeviceGroupManagementProtocol: Channel group management
28
+ - DeviceConfigurationProtocol: Device configuration and metadata
29
+ - DeviceWeekProfileProtocol: Week profile support
30
+ - DeviceProvidersProtocol: Protocol interface providers
31
+ - DeviceLifecycleProtocol: Lifecycle methods
32
+ """
33
+
34
+ from __future__ import annotations
35
+
36
+ from abc import abstractmethod
37
+ from collections.abc import Mapping
38
+ from datetime import datetime
39
+ from typing import TYPE_CHECKING, Any, Protocol, TypeAlias, Unpack, runtime_checkable
40
+
41
+ from aiohomematic.const import (
42
+ DP_KEY_VALUE,
43
+ CallSource,
44
+ DataPointCategory,
45
+ DataPointKey,
46
+ DataPointUsage,
47
+ DeviceFirmwareState,
48
+ DeviceTriggerEventType,
49
+ EventData,
50
+ ForcedDeviceAvailability,
51
+ HubValueType,
52
+ Interface,
53
+ ParameterData,
54
+ ParameterStatus,
55
+ ParameterType,
56
+ ParamsetKey,
57
+ ProductGroup,
58
+ ProgramData,
59
+ RxMode,
60
+ )
61
+ from aiohomematic.decorators import inspector
62
+ from aiohomematic.interfaces.operations import (
63
+ DeviceDescriptionProviderProtocol,
64
+ DeviceDetailsProviderProtocol,
65
+ ParameterVisibilityProviderProtocol,
66
+ ParamsetDescriptionProviderProtocol,
67
+ TaskSchedulerProtocol,
68
+ )
69
+ from aiohomematic.property_decorators import state_property
70
+ from aiohomematic.type_aliases import DataPointUpdatedHandler, DeviceRemovedHandler, FirmwareUpdateHandler
71
+
72
+ if TYPE_CHECKING:
73
+ from aiohomematic.interfaces import (
74
+ CentralInfoProtocol,
75
+ ChannelLookupProtocol,
76
+ ClientProtocol,
77
+ ConfigProviderProtocol,
78
+ DataCacheProviderProtocol,
79
+ DataPointProviderProtocol,
80
+ EventBusProviderProtocol,
81
+ EventPublisherProtocol,
82
+ EventSubscriptionManagerProtocol,
83
+ )
84
+ from aiohomematic.interfaces.central import FirmwareDataRefresherProtocol
85
+ from aiohomematic.model.availability import AvailabilityInfo
86
+ from aiohomematic.model.custom import DeviceConfig
87
+ from aiohomematic.model.custom.mixins import StateChangeArgs
88
+ from aiohomematic.model.support import DataPointNameData
89
+ from aiohomematic.type_aliases import UnsubscribeCallback
90
+
91
+ # =============================================================================
92
+ # DataPoint Protocol Interfaces
93
+ # =============================================================================
94
+
95
+
96
+ @runtime_checkable
97
+ class CallbackDataPointProtocol(Protocol):
98
+ """
99
+ Protocol for callback-based data points.
100
+
101
+ Base protocol for all data point types, providing event handling,
102
+ subscription management, and timestamp tracking.
103
+ """
104
+
105
+ __slots__ = ()
106
+
107
+ @property
108
+ @abstractmethod
109
+ def additional_information(self) -> dict[str, Any]:
110
+ """Return additional information."""
111
+
112
+ @property
113
+ @abstractmethod
114
+ def available(self) -> bool:
115
+ """Return the availability of the device."""
116
+
117
+ @property
118
+ @abstractmethod
119
+ def category(self) -> DataPointCategory:
120
+ """Return the category of the data point."""
121
+
122
+ @property
123
+ @abstractmethod
124
+ def custom_id(self) -> str | None:
125
+ """Return the custom id."""
126
+
127
+ @property
128
+ @abstractmethod
129
+ def enabled_default(self) -> bool:
130
+ """Return if data point should be enabled based on usage attribute."""
131
+
132
+ @property
133
+ @abstractmethod
134
+ def full_name(self) -> str:
135
+ """Return the full name of the data point."""
136
+
137
+ @property
138
+ @abstractmethod
139
+ def is_refreshed(self) -> bool:
140
+ """Return if the data_point has been refreshed (received a value)."""
141
+
142
+ @property
143
+ @abstractmethod
144
+ def is_registered(self) -> bool:
145
+ """Return if data point is registered externally."""
146
+
147
+ @property
148
+ @abstractmethod
149
+ def is_status_valid(self) -> bool:
150
+ """Return if the status indicates a valid value."""
151
+
152
+ @property
153
+ @abstractmethod
154
+ def is_valid(self) -> bool:
155
+ """Return if the value is valid (refreshed and status is OK)."""
156
+
157
+ @property
158
+ @abstractmethod
159
+ def modified_at(self) -> datetime:
160
+ """Return the last update datetime value."""
161
+
162
+ @property
163
+ @abstractmethod
164
+ def name(self) -> str:
165
+ """Return the name of the data point."""
166
+
167
+ @property
168
+ @abstractmethod
169
+ def published_event_at(self) -> datetime:
170
+ """Return the data point updated published an event at."""
171
+
172
+ @property
173
+ @abstractmethod
174
+ def published_event_recently(self) -> bool:
175
+ """Return if the data point published an event recently."""
176
+
177
+ @property
178
+ @abstractmethod
179
+ def refreshed_at(self) -> datetime:
180
+ """Return the last refresh datetime value."""
181
+
182
+ @property
183
+ @abstractmethod
184
+ def service_method_names(self) -> tuple[str, ...]:
185
+ """Return all service method names."""
186
+
187
+ @property
188
+ @abstractmethod
189
+ def service_methods(self) -> Mapping[str, Any]:
190
+ """Return all service methods."""
191
+
192
+ @property
193
+ @abstractmethod
194
+ def set_path(self) -> str:
195
+ """Return the base set path of the data point."""
196
+
197
+ @property
198
+ @abstractmethod
199
+ def signature(self) -> str:
200
+ """Return the data point signature."""
201
+
202
+ @property
203
+ @abstractmethod
204
+ def state_path(self) -> str:
205
+ """Return the base state path of the data point."""
206
+
207
+ @property
208
+ @abstractmethod
209
+ def unique_id(self) -> str:
210
+ """Return the unique id."""
211
+
212
+ @property
213
+ @abstractmethod
214
+ def usage(self) -> DataPointUsage:
215
+ """Return the data point usage."""
216
+
217
+ @abstractmethod
218
+ def cleanup_subscriptions(self) -> None:
219
+ """Clean up all EventBus subscriptions for this data point."""
220
+
221
+ @abstractmethod
222
+ def publish_data_point_updated_event(
223
+ self,
224
+ *,
225
+ data_point: CallbackDataPointProtocol | None = None,
226
+ custom_id: str | None = None,
227
+ old_value: Any = None,
228
+ new_value: Any = None,
229
+ ) -> None:
230
+ """Publish a data point updated event."""
231
+
232
+ @abstractmethod
233
+ def publish_device_removed_event(self) -> None:
234
+ """Publish a device removed event."""
235
+
236
+ @abstractmethod
237
+ def subscribe_to_data_point_updated(
238
+ self, *, handler: DataPointUpdatedHandler, custom_id: str
239
+ ) -> UnsubscribeCallback:
240
+ """Subscribe to data point updated event."""
241
+
242
+ @abstractmethod
243
+ def subscribe_to_device_removed(self, *, handler: DeviceRemovedHandler) -> UnsubscribeCallback:
244
+ """Subscribe to the device removed event."""
245
+
246
+
247
+ @runtime_checkable
248
+ class GenericHubDataPointProtocol(CallbackDataPointProtocol, Protocol):
249
+ """
250
+ Protocol for hub-level data points (programs, sysvars).
251
+
252
+ Extends CallbackDataPointProtocol with properties specific to
253
+ hub-level data points that are not bound to device channels.
254
+ """
255
+
256
+ __slots__ = ()
257
+
258
+ @property
259
+ @abstractmethod
260
+ def channel(self) -> ChannelProtocol | None:
261
+ """Return the identified channel."""
262
+
263
+ @property
264
+ @abstractmethod
265
+ def description(self) -> str | None:
266
+ """Return data point description."""
267
+
268
+ @property
269
+ @abstractmethod
270
+ def legacy_name(self) -> str | None:
271
+ """Return the original name."""
272
+
273
+ @property
274
+ @abstractmethod
275
+ def state_uncertain(self) -> bool:
276
+ """Return if the state is uncertain."""
277
+
278
+
279
+ @runtime_checkable
280
+ class GenericSysvarDataPointProtocol(GenericHubDataPointProtocol, Protocol):
281
+ """
282
+ Protocol for system variable data points.
283
+
284
+ Extends GenericHubDataPointProtocol with methods for reading
285
+ and writing system variables.
286
+ """
287
+
288
+ __slots__ = ()
289
+
290
+ @property
291
+ @abstractmethod
292
+ def data_type(self) -> HubValueType | None:
293
+ """Return the data type of the system variable."""
294
+
295
+ @property
296
+ @abstractmethod
297
+ def is_extended(self) -> bool:
298
+ """Return if the data point is an extended type."""
299
+
300
+ @property
301
+ @abstractmethod
302
+ def max(self) -> float | int | None:
303
+ """Return the max value."""
304
+
305
+ @property
306
+ @abstractmethod
307
+ def min(self) -> float | int | None:
308
+ """Return the min value."""
309
+
310
+ @property
311
+ @abstractmethod
312
+ def previous_value(self) -> Any:
313
+ """Return the previous value."""
314
+
315
+ @property
316
+ @abstractmethod
317
+ def unit(self) -> str | None:
318
+ """Return the unit of the data point."""
319
+
320
+ @property
321
+ @abstractmethod
322
+ def value(self) -> Any:
323
+ """Return the value."""
324
+
325
+ @property
326
+ @abstractmethod
327
+ def values(self) -> tuple[str, ...] | None:
328
+ """Return the value list."""
329
+
330
+ @property
331
+ @abstractmethod
332
+ def vid(self) -> str:
333
+ """Return sysvar id."""
334
+
335
+ @abstractmethod
336
+ async def event(self, *, value: Any, received_at: datetime) -> None:
337
+ """Handle event for which this data point has subscribed."""
338
+
339
+ @abstractmethod
340
+ async def send_variable(self, *, value: Any) -> None:
341
+ """Set variable value on the backend."""
342
+
343
+ @abstractmethod
344
+ def write_value(self, *, value: Any, write_at: datetime) -> None:
345
+ """Set variable value on the backend."""
346
+
347
+
348
+ @runtime_checkable
349
+ class GenericProgramDataPointProtocol(GenericHubDataPointProtocol, Protocol):
350
+ """
351
+ Protocol for program data points.
352
+
353
+ Extends GenericHubDataPointProtocol with methods for managing
354
+ CCU programs.
355
+ """
356
+
357
+ __slots__ = ()
358
+
359
+ @property
360
+ @abstractmethod
361
+ def is_active(self) -> bool:
362
+ """Return if the program is active."""
363
+
364
+ @property
365
+ @abstractmethod
366
+ def is_internal(self) -> bool:
367
+ """Return if the program is internal."""
368
+
369
+ @property
370
+ @abstractmethod
371
+ def last_execute_time(self) -> str:
372
+ """Return the last execute time."""
373
+
374
+ @property
375
+ @abstractmethod
376
+ def pid(self) -> str:
377
+ """Return the program id."""
378
+
379
+ @abstractmethod
380
+ def update_data(self, *, data: ProgramData) -> None:
381
+ """Update program data from backend."""
382
+
383
+
384
+ @runtime_checkable
385
+ class HubSensorDataPointProtocol(GenericHubDataPointProtocol, Protocol):
386
+ """
387
+ Protocol for sensors bases on hub data points, that ar no sysvars.
388
+
389
+ Provides properties like data_type.
390
+ """
391
+
392
+ @property
393
+ @abstractmethod
394
+ def data_type(self) -> HubValueType | None:
395
+ """Return the data type of the system variable."""
396
+
397
+ @property
398
+ @abstractmethod
399
+ def unit(self) -> str | None:
400
+ """Return the unit of the data point."""
401
+
402
+ @property
403
+ @abstractmethod
404
+ def value(self) -> Any:
405
+ """Return the value."""
406
+
407
+
408
+ @runtime_checkable
409
+ class HubBinarySensorDataPointProtocol(GenericHubDataPointProtocol, Protocol):
410
+ """
411
+ Protocol for binary sensor hub data points.
412
+
413
+ Provides properties for boolean sensor values like connectivity status.
414
+ """
415
+
416
+ __slots__ = ()
417
+
418
+ @property
419
+ @abstractmethod
420
+ def data_type(self) -> HubValueType | None:
421
+ """Return the data type of the sensor."""
422
+
423
+ @property
424
+ @abstractmethod
425
+ def value(self) -> bool:
426
+ """Return the boolean value."""
427
+
428
+
429
+ @runtime_checkable
430
+ class GenericInstallModeDataPointProtocol(HubSensorDataPointProtocol, Protocol):
431
+ """
432
+ Protocol for install mode sensor data point.
433
+
434
+ Provides properties and methods for monitoring the CCU install mode
435
+ countdown timer (device pairing).
436
+ """
437
+
438
+ __slots__ = ()
439
+
440
+ @property
441
+ @abstractmethod
442
+ def is_active(self) -> bool:
443
+ """Return if install mode is active."""
444
+
445
+ @abstractmethod
446
+ def start_countdown(self, *, seconds: int) -> None:
447
+ """Start local countdown."""
448
+
449
+ @abstractmethod
450
+ def stop_countdown(self) -> None:
451
+ """Stop countdown."""
452
+
453
+ @abstractmethod
454
+ def sync_from_backend(self, *, remaining_seconds: int) -> None:
455
+ """Sync countdown from backend value."""
456
+
457
+
458
+ @runtime_checkable
459
+ class BaseDataPointProtocol(CallbackDataPointProtocol, Protocol):
460
+ """
461
+ Protocol for channel-bound data points.
462
+
463
+ Extends CallbackDataPointProtocol with channel/device associations
464
+ and timer functionality.
465
+ """
466
+
467
+ __slots__ = ()
468
+
469
+ @property
470
+ @abstractmethod
471
+ def channel(self) -> ChannelProtocol:
472
+ """Return the channel of the data point."""
473
+
474
+ @property
475
+ @abstractmethod
476
+ def device(self) -> DeviceProtocol:
477
+ """Return the device of the data point."""
478
+
479
+ @property
480
+ @abstractmethod
481
+ def function(self) -> str | None:
482
+ """Return the function."""
483
+
484
+ @property
485
+ @abstractmethod
486
+ def is_in_multiple_channels(self) -> bool:
487
+ """Return if the parameter is in multiple channels."""
488
+
489
+ @property
490
+ @abstractmethod
491
+ def name_data(self) -> DataPointNameData:
492
+ """Return the data point name data."""
493
+
494
+ @property
495
+ @abstractmethod
496
+ def room(self) -> str | None:
497
+ """Return the room if only one exists."""
498
+
499
+ @property
500
+ @abstractmethod
501
+ def rooms(self) -> set[str]:
502
+ """Return the rooms assigned to the data point."""
503
+
504
+ @property
505
+ @abstractmethod
506
+ def timer_on_time(self) -> float | None:
507
+ """Return the on_time."""
508
+
509
+ @property
510
+ @abstractmethod
511
+ def timer_on_time_running(self) -> bool:
512
+ """Return if on_time is running."""
513
+
514
+ @abstractmethod
515
+ def force_usage(self, *, forced_usage: DataPointUsage) -> None:
516
+ """Set the data point usage."""
517
+
518
+ @abstractmethod
519
+ @inspector(re_raise=False)
520
+ async def load_data_point_value(self, *, call_source: CallSource, direct_call: bool = False) -> None:
521
+ """Initialize the data point data."""
522
+
523
+ @abstractmethod
524
+ def reset_timer_on_time(self) -> None:
525
+ """Reset the on_time."""
526
+
527
+ @abstractmethod
528
+ def set_timer_on_time(self, *, on_time: float) -> None:
529
+ """Set the on_time."""
530
+
531
+
532
+ @runtime_checkable
533
+ class BaseParameterDataPointProtocol[ParameterT](BaseDataPointProtocol, Protocol):
534
+ """
535
+ Protocol for parameter-backed data points with typed values.
536
+
537
+ Extends BaseDataPointProtocol with value handling, unit conversion,
538
+ validation, and RPC communication for data points mapped to
539
+ Homematic device parameters.
540
+
541
+ Type Parameters:
542
+ ParameterT: The type of value this data point holds and returns.
543
+ """
544
+
545
+ __slots__ = ()
546
+
547
+ @property
548
+ @abstractmethod
549
+ def default(self) -> ParameterT:
550
+ """Return default value."""
551
+
552
+ @property
553
+ @abstractmethod
554
+ def dpk(self) -> DataPointKey:
555
+ """Return data point key value."""
556
+
557
+ @property
558
+ @abstractmethod
559
+ def has_events(self) -> bool:
560
+ """Return if data point supports events."""
561
+
562
+ @property
563
+ @abstractmethod
564
+ def hmtype(self) -> ParameterType:
565
+ """Return the Homematic type."""
566
+
567
+ @property
568
+ @abstractmethod
569
+ def ignore_on_initial_load(self) -> bool:
570
+ """Return if parameter should be ignored on initial load."""
571
+
572
+ @property
573
+ @abstractmethod
574
+ def is_forced_sensor(self) -> bool:
575
+ """Return if data point is forced to read only."""
576
+
577
+ @property
578
+ @abstractmethod
579
+ def is_readable(self) -> bool:
580
+ """Return if data point is readable."""
581
+
582
+ @property
583
+ @abstractmethod
584
+ def is_un_ignored(self) -> bool:
585
+ """Return if the parameter is un-ignored."""
586
+
587
+ @property
588
+ @abstractmethod
589
+ def is_unit_fixed(self) -> bool:
590
+ """Return if the unit is fixed."""
591
+
592
+ @property
593
+ @abstractmethod
594
+ def is_writable(self) -> bool:
595
+ """Return if data point is writable."""
596
+
597
+ @property
598
+ @abstractmethod
599
+ def last_non_default_value(self) -> ParameterT | None:
600
+ """Return the last meaningful (non-default) value of the data point."""
601
+
602
+ @property
603
+ @abstractmethod
604
+ def max(self) -> ParameterT:
605
+ """Return max value."""
606
+
607
+ @property
608
+ @abstractmethod
609
+ def min(self) -> ParameterT:
610
+ """Return min value."""
611
+
612
+ @property
613
+ @abstractmethod
614
+ def multiplier(self) -> float:
615
+ """Return multiplier value."""
616
+
617
+ @property
618
+ @abstractmethod
619
+ def parameter(self) -> str:
620
+ """Return parameter name."""
621
+
622
+ @property
623
+ @abstractmethod
624
+ def paramset_key(self) -> ParamsetKey:
625
+ """Return paramset_key name."""
626
+
627
+ @property
628
+ @abstractmethod
629
+ def raw_unit(self) -> str | None:
630
+ """Return raw unit value."""
631
+
632
+ @property
633
+ @abstractmethod
634
+ def requires_polling(self) -> bool:
635
+ """Return whether the data point requires polling."""
636
+
637
+ @property
638
+ @abstractmethod
639
+ def service(self) -> bool:
640
+ """Return if data point is relevant for service messages."""
641
+
642
+ @property
643
+ @abstractmethod
644
+ def state_uncertain(self) -> bool:
645
+ """Return if the state is uncertain."""
646
+
647
+ @property
648
+ @abstractmethod
649
+ def status(self) -> ParameterStatus | None:
650
+ """Return the current status of this parameter value."""
651
+
652
+ @property
653
+ @abstractmethod
654
+ def status_dpk(self) -> DataPointKey | None:
655
+ """Return the DataPointKey for the STATUS parameter."""
656
+
657
+ @property
658
+ @abstractmethod
659
+ def unconfirmed_last_value_send(self) -> ParameterT:
660
+ """Return the unconfirmed value send for the data point."""
661
+
662
+ @property
663
+ @abstractmethod
664
+ def unit(self) -> str | None:
665
+ """Return unit value."""
666
+
667
+ @property
668
+ @abstractmethod
669
+ def values(self) -> tuple[str, ...] | None:
670
+ """Return the values."""
671
+
672
+ @property
673
+ @abstractmethod
674
+ def visible(self) -> bool:
675
+ """Return if data point is visible in backend."""
676
+
677
+ @state_property
678
+ @abstractmethod
679
+ def value(self) -> ParameterT:
680
+ """Return the value."""
681
+
682
+ @abstractmethod
683
+ async def event(self, *, value: Any, received_at: datetime) -> None:
684
+ """Handle event for which this handler has subscribed."""
685
+
686
+ @abstractmethod
687
+ def force_to_sensor(self) -> None:
688
+ """Change the category of the data point to sensor (read-only)."""
689
+
690
+ @abstractmethod
691
+ def get_event_data(self, *, value: Any = None) -> EventData:
692
+ """Get the event data."""
693
+
694
+ @abstractmethod
695
+ def update_parameter_data(self) -> None:
696
+ """Update parameter data."""
697
+
698
+ @abstractmethod
699
+ def update_status(self, *, status_value: int | str) -> None:
700
+ """Update the status from a STATUS parameter event."""
701
+
702
+ @abstractmethod
703
+ def write_temporary_value(self, *, value: Any, write_at: datetime) -> None:
704
+ """Update the temporary value of the data point."""
705
+
706
+ @abstractmethod
707
+ def write_value(self, *, value: Any, write_at: datetime) -> tuple[ParameterT, ParameterT]:
708
+ """Update value of the data point."""
709
+
710
+
711
+ @runtime_checkable
712
+ class GenericDataPointProtocol[ParameterT](BaseParameterDataPointProtocol[ParameterT], Protocol):
713
+ """
714
+ Protocol for generic parameter-backed data points.
715
+
716
+ Extends BaseParameterDataPointProtocol with the usage property
717
+ and send_value method specific to generic data points.
718
+
719
+ Type Parameters:
720
+ ParameterT: The type of value this data point holds and returns.
721
+ """
722
+
723
+ __slots__ = ()
724
+
725
+ @property
726
+ @abstractmethod
727
+ def usage(self) -> DataPointUsage:
728
+ """Return the data point usage."""
729
+
730
+ @abstractmethod
731
+ async def finalize_init(self) -> None:
732
+ """Finalize the data point init action."""
733
+
734
+ @abstractmethod
735
+ def is_state_change(self, *, value: ParameterT) -> bool:
736
+ """Check if the state/value changes."""
737
+
738
+ @abstractmethod
739
+ async def on_config_changed(self) -> None:
740
+ """Handle config changed event."""
741
+
742
+ @abstractmethod
743
+ async def send_value(
744
+ self,
745
+ *,
746
+ value: Any,
747
+ collector: Any | None = None,
748
+ collector_order: int = 50,
749
+ do_validate: bool = True,
750
+ ) -> set[DP_KEY_VALUE]:
751
+ """Send value to CCU or use collector if set."""
752
+
753
+ @abstractmethod
754
+ def subscribe_to_internal_data_point_updated(self, *, handler: DataPointUpdatedHandler) -> UnsubscribeCallback:
755
+ """Subscribe to internal data point updated event."""
756
+
757
+
758
+ @runtime_checkable
759
+ class GenericEventProtocol[ParameterT](BaseParameterDataPointProtocol[ParameterT], Protocol):
760
+ """
761
+ Protocol for event data points.
762
+
763
+ Extends BaseParameterDataPointProtocol with event-specific functionality
764
+ for handling button presses, device errors, and impulse notifications.
765
+
766
+ Type Parameters:
767
+ ParameterT: The type of value this event holds.
768
+ """
769
+
770
+ __slots__ = ()
771
+
772
+ @property
773
+ @abstractmethod
774
+ def event_type(self) -> DeviceTriggerEventType:
775
+ """Return the event type of the event."""
776
+
777
+ @property
778
+ @abstractmethod
779
+ def usage(self) -> DataPointUsage:
780
+ """Return the data point usage."""
781
+
782
+ @abstractmethod
783
+ async def finalize_init(self) -> None:
784
+ """Finalize the event init action."""
785
+
786
+ @abstractmethod
787
+ async def on_config_changed(self) -> None:
788
+ """Handle config changed event."""
789
+
790
+ @abstractmethod
791
+ def publish_event(self, *, value: Any) -> None:
792
+ """Publish an event."""
793
+
794
+
795
+ @runtime_checkable
796
+ class CustomDataPointProtocol(BaseDataPointProtocol, Protocol):
797
+ """
798
+ Protocol for custom device-specific data points.
799
+
800
+ Defines the interface for composite data points that aggregate
801
+ multiple GenericDataPoints to represent complex devices.
802
+ """
803
+
804
+ __slots__ = ()
805
+
806
+ @property
807
+ @abstractmethod
808
+ def allow_undefined_generic_data_points(self) -> bool:
809
+ """Return if undefined generic data points are allowed."""
810
+
811
+ @property
812
+ @abstractmethod
813
+ def data_point_name_postfix(self) -> str:
814
+ """Return the data point name postfix."""
815
+
816
+ @property
817
+ @abstractmethod
818
+ def device_config(self) -> DeviceConfig:
819
+ """Return the custom config."""
820
+
821
+ @property
822
+ @abstractmethod
823
+ def group_no(self) -> int | None:
824
+ """Return the base channel no of the data point."""
825
+
826
+ @property
827
+ @abstractmethod
828
+ def has_data_points(self) -> bool:
829
+ """Return if there are data points."""
830
+
831
+ @property
832
+ @abstractmethod
833
+ def has_schedule(self) -> bool:
834
+ """Return if device supports schedule."""
835
+
836
+ @property
837
+ @abstractmethod
838
+ def schedule(self) -> dict[Any, Any]:
839
+ """Return cached schedule entries from device week profile."""
840
+
841
+ @property
842
+ @abstractmethod
843
+ def state_uncertain(self) -> bool:
844
+ """Return if the state is uncertain."""
845
+
846
+ @property
847
+ @abstractmethod
848
+ def unconfirmed_last_values_send(self) -> Mapping[Any, Any]:
849
+ """Return the unconfirmed values send for the data point."""
850
+
851
+ @abstractmethod
852
+ async def get_schedule(self, *, force_load: bool = False) -> dict[Any, Any]:
853
+ """Get schedule from device week profile."""
854
+
855
+ @abstractmethod
856
+ def has_data_point_key(self, *, data_point_keys: set[DataPointKey]) -> bool:
857
+ """Return if a data point with one of the keys is part of this data point."""
858
+
859
+ @abstractmethod
860
+ def is_state_change(self, **kwargs: Unpack[StateChangeArgs]) -> bool:
861
+ """Check if the state changes due to kwargs."""
862
+
863
+ @abstractmethod
864
+ async def set_schedule(self, *, schedule_data: dict[Any, Any]) -> None:
865
+ """Set schedule on device week profile."""
866
+
867
+ @abstractmethod
868
+ def unsubscribe_from_data_point_updated(self) -> None:
869
+ """Unregister all internal update handlers."""
870
+
871
+
872
+ @runtime_checkable
873
+ class CalculatedDataPointProtocol(BaseDataPointProtocol, Protocol):
874
+ """
875
+ Protocol for calculated data points.
876
+
877
+ Defines the interface for data points that derive their values
878
+ from other data points through calculations.
879
+ """
880
+
881
+ __slots__ = ()
882
+
883
+ @staticmethod
884
+ @abstractmethod
885
+ def is_relevant_for_model(*, channel: ChannelProtocol) -> bool:
886
+ """Return if this calculated data point is relevant for the channel."""
887
+
888
+ @property
889
+ @abstractmethod
890
+ def data_point_name_postfix(self) -> str:
891
+ """Return the data point name postfix."""
892
+
893
+ @property
894
+ @abstractmethod
895
+ def default(self) -> Any:
896
+ """Return default value."""
897
+
898
+ @property
899
+ @abstractmethod
900
+ def dpk(self) -> DataPointKey:
901
+ """Return data point key value."""
902
+
903
+ @property
904
+ @abstractmethod
905
+ def has_data_points(self) -> bool:
906
+ """Return if there are data points."""
907
+
908
+ @property
909
+ @abstractmethod
910
+ def has_events(self) -> bool:
911
+ """Return if data point supports events."""
912
+
913
+ @property
914
+ @abstractmethod
915
+ def hmtype(self) -> ParameterType:
916
+ """Return the Homematic type."""
917
+
918
+ @property
919
+ @abstractmethod
920
+ def is_readable(self) -> bool:
921
+ """Return if data point is readable."""
922
+
923
+ @property
924
+ @abstractmethod
925
+ def is_writable(self) -> bool:
926
+ """Return if data point is writable."""
927
+
928
+ @property
929
+ @abstractmethod
930
+ def max(self) -> Any:
931
+ """Return max value."""
932
+
933
+ @property
934
+ @abstractmethod
935
+ def min(self) -> Any:
936
+ """Return min value."""
937
+
938
+ @property
939
+ @abstractmethod
940
+ def multiplier(self) -> float:
941
+ """Return multiplier value."""
942
+
943
+ @property
944
+ @abstractmethod
945
+ def parameter(self) -> str:
946
+ """Return parameter name."""
947
+
948
+ @property
949
+ @abstractmethod
950
+ def paramset_key(self) -> ParamsetKey:
951
+ """Return paramset_key name."""
952
+
953
+ @property
954
+ @abstractmethod
955
+ def service(self) -> bool:
956
+ """Return if data point is relevant for service messages."""
957
+
958
+ @property
959
+ @abstractmethod
960
+ def state_uncertain(self) -> bool:
961
+ """Return if the state is uncertain."""
962
+
963
+ @property
964
+ @abstractmethod
965
+ def unit(self) -> str | None:
966
+ """Return unit value."""
967
+
968
+ @property
969
+ @abstractmethod
970
+ def value(self) -> Any:
971
+ """Return the calculated value."""
972
+
973
+ @property
974
+ @abstractmethod
975
+ def values(self) -> tuple[str, ...] | None:
976
+ """Return the values."""
977
+
978
+ @property
979
+ @abstractmethod
980
+ def visible(self) -> bool:
981
+ """Return if data point is visible in backend."""
982
+
983
+ @abstractmethod
984
+ async def finalize_init(self) -> None:
985
+ """Finalize the data point init action."""
986
+
987
+ @abstractmethod
988
+ def is_state_change(self, **kwargs: Unpack[StateChangeArgs]) -> bool:
989
+ """Check if the state changes due to kwargs."""
990
+
991
+ @abstractmethod
992
+ async def on_config_changed(self) -> None:
993
+ """Handle config changed event."""
994
+
995
+ @abstractmethod
996
+ def unsubscribe_from_data_point_updated(self) -> None:
997
+ """Unsubscribe from all internal update subscriptions."""
998
+
999
+
1000
+ # =============================================================================
1001
+ # Channel Sub-Protocol Interfaces
1002
+ # =============================================================================
1003
+
1004
+
1005
+ class ChannelIdentityProtocol(Protocol):
1006
+ """
1007
+ Protocol for channel identification.
1008
+
1009
+ Provides basic identity information for a channel.
1010
+ """
1011
+
1012
+ __slots__ = ()
1013
+
1014
+ @property
1015
+ @abstractmethod
1016
+ def address(self) -> str:
1017
+ """Return the address of the channel."""
1018
+
1019
+ @property
1020
+ @abstractmethod
1021
+ def full_name(self) -> str:
1022
+ """Return the full name of the channel."""
1023
+
1024
+ @property
1025
+ @abstractmethod
1026
+ def name(self) -> str:
1027
+ """Return the name of the channel."""
1028
+
1029
+ @property
1030
+ @abstractmethod
1031
+ def no(self) -> int | None:
1032
+ """Return the channel number."""
1033
+
1034
+ @property
1035
+ @abstractmethod
1036
+ def rega_id(self) -> int:
1037
+ """Return the id of the channel."""
1038
+
1039
+ @property
1040
+ @abstractmethod
1041
+ def type_name(self) -> str:
1042
+ """Return the type name of the channel."""
1043
+
1044
+ @property
1045
+ @abstractmethod
1046
+ def unique_id(self) -> str:
1047
+ """Return the unique_id of the channel."""
1048
+
1049
+
1050
+ class ChannelDataPointAccessProtocol(Protocol):
1051
+ """
1052
+ Protocol for channel data point access.
1053
+
1054
+ Provides methods to access and manage data points and events.
1055
+ """
1056
+
1057
+ __slots__ = ()
1058
+
1059
+ @property
1060
+ @abstractmethod
1061
+ def calculated_data_points(self) -> tuple[CalculatedDataPointProtocol, ...]:
1062
+ """Return the calculated data points."""
1063
+
1064
+ @property
1065
+ @abstractmethod
1066
+ def custom_data_point(self) -> CustomDataPointProtocol | None:
1067
+ """Return the custom data point."""
1068
+
1069
+ @property
1070
+ @abstractmethod
1071
+ def data_point_paths(self) -> tuple[str, ...]:
1072
+ """Return the data point paths."""
1073
+
1074
+ @property
1075
+ @abstractmethod
1076
+ def generic_data_points(self) -> tuple[GenericDataPointProtocolAny, ...]:
1077
+ """Return the generic data points."""
1078
+
1079
+ @property
1080
+ @abstractmethod
1081
+ def generic_events(self) -> tuple[GenericEventProtocolAny, ...]:
1082
+ """Return the generic events."""
1083
+
1084
+ @abstractmethod
1085
+ def add_data_point(self, *, data_point: CallbackDataPointProtocol) -> None:
1086
+ """Add a data point to a channel."""
1087
+
1088
+ @abstractmethod
1089
+ def get_calculated_data_point(self, *, parameter: str) -> CalculatedDataPointProtocol | None:
1090
+ """Return a calculated data_point from device."""
1091
+
1092
+ @abstractmethod
1093
+ def get_data_points(
1094
+ self,
1095
+ *,
1096
+ category: DataPointCategory | None = None,
1097
+ exclude_no_create: bool = True,
1098
+ registered: bool | None = None,
1099
+ ) -> tuple[CallbackDataPointProtocol, ...]:
1100
+ """Return all data points of the channel."""
1101
+
1102
+ @abstractmethod
1103
+ def get_events(
1104
+ self, *, event_type: DeviceTriggerEventType, registered: bool | None = None
1105
+ ) -> tuple[GenericEventProtocolAny, ...]:
1106
+ """Return a list of specific events of a channel."""
1107
+
1108
+ @abstractmethod
1109
+ def get_generic_data_point(
1110
+ self, *, parameter: str | None = None, paramset_key: ParamsetKey | None = None, state_path: str | None = None
1111
+ ) -> GenericDataPointProtocolAny | None:
1112
+ """Return a generic data_point from device."""
1113
+
1114
+ @abstractmethod
1115
+ def get_generic_event(
1116
+ self, *, parameter: str | None = None, state_path: str | None = None
1117
+ ) -> GenericEventProtocolAny | None:
1118
+ """Return a generic event from device."""
1119
+
1120
+ @abstractmethod
1121
+ def get_readable_data_points(self, *, paramset_key: ParamsetKey) -> tuple[GenericDataPointProtocolAny, ...]:
1122
+ """Return the list of readable data points."""
1123
+
1124
+
1125
+ class ChannelGroupingProtocol(Protocol):
1126
+ """
1127
+ Protocol for channel group management.
1128
+
1129
+ Provides access to channel grouping and peer relationships.
1130
+ """
1131
+
1132
+ __slots__ = ()
1133
+
1134
+ @property
1135
+ @abstractmethod
1136
+ def group_master(self) -> ChannelProtocol | None:
1137
+ """Return the group master channel."""
1138
+
1139
+ @property
1140
+ @abstractmethod
1141
+ def group_no(self) -> int | None:
1142
+ """Return the no of the channel group."""
1143
+
1144
+ @property
1145
+ @abstractmethod
1146
+ def is_group_master(self) -> bool:
1147
+ """Return if the channel is the group master."""
1148
+
1149
+ @property
1150
+ @abstractmethod
1151
+ def is_in_multi_group(self) -> bool | None:
1152
+ """Return if the channel is in a multi-channel group."""
1153
+
1154
+ @property
1155
+ @abstractmethod
1156
+ def link_peer_channels(self) -> tuple[ChannelProtocol, ...]:
1157
+ """Return the link peer channels."""
1158
+
1159
+
1160
+ class ChannelMetadataProtocol(Protocol):
1161
+ """
1162
+ Protocol for channel metadata access.
1163
+
1164
+ Provides access to additional channel metadata and configuration.
1165
+ """
1166
+
1167
+ __slots__ = ()
1168
+
1169
+ @property
1170
+ @abstractmethod
1171
+ def device(self) -> DeviceProtocol:
1172
+ """Return the device of the channel."""
1173
+
1174
+ @property
1175
+ @abstractmethod
1176
+ def function(self) -> str | None:
1177
+ """Return the function of the channel."""
1178
+
1179
+ @property
1180
+ @abstractmethod
1181
+ def is_schedule_channel(self) -> bool:
1182
+ """Return if channel is a schedule channel."""
1183
+
1184
+ @property
1185
+ @abstractmethod
1186
+ def operation_mode(self) -> str | None:
1187
+ """Return the operation mode of the channel."""
1188
+
1189
+ @property
1190
+ @abstractmethod
1191
+ def paramset_descriptions(self) -> Mapping[ParamsetKey, Mapping[str, ParameterData]]:
1192
+ """Return the paramset descriptions."""
1193
+
1194
+ @property
1195
+ @abstractmethod
1196
+ def paramset_keys(self) -> tuple[ParamsetKey, ...]:
1197
+ """Return the paramset keys of the channel."""
1198
+
1199
+ @property
1200
+ @abstractmethod
1201
+ def room(self) -> str | None:
1202
+ """Return the room of the channel."""
1203
+
1204
+ @property
1205
+ @abstractmethod
1206
+ def rooms(self) -> set[str]:
1207
+ """Return all rooms of the channel."""
1208
+
1209
+
1210
+ class ChannelLinkManagementProtocol(Protocol):
1211
+ """
1212
+ Protocol for channel central link management.
1213
+
1214
+ Provides methods for creating and managing central links.
1215
+ """
1216
+
1217
+ __slots__ = ()
1218
+
1219
+ @abstractmethod
1220
+ async def create_central_link(self) -> None:
1221
+ """Create a central link to support press events."""
1222
+
1223
+ @abstractmethod
1224
+ def has_link_target_category(self, *, category: DataPointCategory) -> bool:
1225
+ """Return if channel has the specified link target category."""
1226
+
1227
+ @abstractmethod
1228
+ async def remove_central_link(self) -> None:
1229
+ """Remove a central link."""
1230
+
1231
+ @abstractmethod
1232
+ def subscribe_to_link_peer_changed(self, *, handler: Any) -> Any:
1233
+ """Subscribe to link peer changed event."""
1234
+
1235
+
1236
+ class ChannelLifecycleProtocol(Protocol):
1237
+ """
1238
+ Protocol for channel lifecycle management.
1239
+
1240
+ Provides methods for initialization, configuration changes, and removal.
1241
+ """
1242
+
1243
+ __slots__ = ()
1244
+
1245
+ @abstractmethod
1246
+ async def finalize_init(self) -> None:
1247
+ """Finalize the channel init action after model setup."""
1248
+
1249
+ @abstractmethod
1250
+ async def init_link_peer(self) -> None:
1251
+ """Initialize the link partners."""
1252
+
1253
+ @abstractmethod
1254
+ async def on_config_changed(self) -> None:
1255
+ """Handle config changed event."""
1256
+
1257
+ @abstractmethod
1258
+ def remove(self) -> None:
1259
+ """Remove data points from collections and central."""
1260
+
1261
+
1262
+ # =============================================================================
1263
+ # Channel Composite Protocol Interface
1264
+ # =============================================================================
1265
+
1266
+
1267
+ @runtime_checkable
1268
+ class ChannelProtocol(
1269
+ ChannelIdentityProtocol,
1270
+ ChannelDataPointAccessProtocol,
1271
+ ChannelGroupingProtocol,
1272
+ ChannelMetadataProtocol,
1273
+ ChannelLinkManagementProtocol,
1274
+ ChannelLifecycleProtocol,
1275
+ Protocol,
1276
+ ):
1277
+ """
1278
+ Composite protocol for complete channel access.
1279
+
1280
+ Combines all channel sub-protocols into a single interface.
1281
+ Implemented by Channel.
1282
+
1283
+ Sub-protocols:
1284
+ - ChannelIdentityProtocol: Basic identification (address, name, no, type_name, unique_id, rega_id)
1285
+ - ChannelDataPointAccessProtocol: DataPoint and event access methods
1286
+ - ChannelGroupingProtocol: Channel group management (group_master, group_no, link_peer_channels)
1287
+ - ChannelMetadataProtocol: Additional metadata (device, function, room, paramset_descriptions)
1288
+ - ChannelLinkManagementProtocol: Central link operations
1289
+ - ChannelLifecycleProtocol: Lifecycle methods (finalize_init, on_config_changed, remove)
1290
+ """
1291
+
1292
+ __slots__ = ()
1293
+
1294
+
1295
+ # =============================================================================
1296
+ # Device Sub-Protocol Interfaces
1297
+ # =============================================================================
1298
+
1299
+
1300
+ class DeviceIdentityProtocol(Protocol):
1301
+ """
1302
+ Protocol for device identification.
1303
+
1304
+ Provides basic identity information for a device.
1305
+ """
1306
+
1307
+ __slots__ = ()
1308
+
1309
+ @property
1310
+ @abstractmethod
1311
+ def address(self) -> str:
1312
+ """Return the address of the device."""
1313
+
1314
+ @property
1315
+ @abstractmethod
1316
+ def identifier(self) -> str:
1317
+ """Return the identifier of the device."""
1318
+
1319
+ @property
1320
+ @abstractmethod
1321
+ def interface(self) -> Interface:
1322
+ """Return the interface of the device."""
1323
+
1324
+ @property
1325
+ @abstractmethod
1326
+ def interface_id(self) -> str:
1327
+ """Return the interface_id of the device."""
1328
+
1329
+ @property
1330
+ @abstractmethod
1331
+ def manufacturer(self) -> str:
1332
+ """Return the manufacturer of the device."""
1333
+
1334
+ @property
1335
+ @abstractmethod
1336
+ def model(self) -> str:
1337
+ """Return the model of the device."""
1338
+
1339
+ @property
1340
+ @abstractmethod
1341
+ def name(self) -> str:
1342
+ """Return the name of the device."""
1343
+
1344
+ @property
1345
+ @abstractmethod
1346
+ def sub_model(self) -> str | None:
1347
+ """Return the sub model of the device."""
1348
+
1349
+
1350
+ class DeviceChannelAccessProtocol(Protocol):
1351
+ """
1352
+ Protocol for device channel and data point access.
1353
+
1354
+ Provides methods to access channels, data points, and events.
1355
+ """
1356
+
1357
+ __slots__ = ()
1358
+
1359
+ @property
1360
+ @abstractmethod
1361
+ def channels(self) -> Mapping[str, ChannelProtocol]:
1362
+ """Return the channels."""
1363
+
1364
+ @property
1365
+ @abstractmethod
1366
+ def data_point_paths(self) -> tuple[str, ...]:
1367
+ """Return the data point paths."""
1368
+
1369
+ @property
1370
+ @abstractmethod
1371
+ def generic_data_points(self) -> tuple[GenericDataPointProtocolAny, ...]:
1372
+ """Return all generic data points."""
1373
+
1374
+ @property
1375
+ @abstractmethod
1376
+ def generic_events(self) -> tuple[GenericEventProtocolAny, ...]:
1377
+ """Return the generic events."""
1378
+
1379
+ @abstractmethod
1380
+ def get_channel(self, *, channel_address: str) -> ChannelProtocol | None:
1381
+ """Return a channel by address."""
1382
+
1383
+ @abstractmethod
1384
+ def get_custom_data_point(self, *, channel_no: int) -> CustomDataPointProtocol | None:
1385
+ """Return a custom data_point from device."""
1386
+
1387
+ @abstractmethod
1388
+ def get_data_points(
1389
+ self,
1390
+ *,
1391
+ category: DataPointCategory | None = None,
1392
+ exclude_no_create: bool = True,
1393
+ registered: bool | None = None,
1394
+ ) -> tuple[CallbackDataPointProtocol, ...]:
1395
+ """Return data points."""
1396
+
1397
+ @abstractmethod
1398
+ def get_events(
1399
+ self, *, event_type: DeviceTriggerEventType, registered: bool | None = None
1400
+ ) -> Mapping[int | None, tuple[GenericEventProtocolAny, ...]]:
1401
+ """Return a list of specific events of a channel."""
1402
+
1403
+ @abstractmethod
1404
+ def get_generic_data_point(
1405
+ self,
1406
+ *,
1407
+ channel_address: str | None = None,
1408
+ parameter: str | None = None,
1409
+ paramset_key: ParamsetKey | None = None,
1410
+ state_path: str | None = None,
1411
+ ) -> GenericDataPointProtocolAny | None:
1412
+ """Return a generic data_point from device."""
1413
+
1414
+ @abstractmethod
1415
+ def get_generic_event(
1416
+ self, *, channel_address: str | None = None, parameter: str | None = None, state_path: str | None = None
1417
+ ) -> GenericEventProtocolAny | None:
1418
+ """Return a generic event from device."""
1419
+
1420
+ @abstractmethod
1421
+ def get_readable_data_points(self, *, paramset_key: ParamsetKey) -> tuple[GenericDataPointProtocolAny, ...]:
1422
+ """Return the list of readable data points."""
1423
+
1424
+ @abstractmethod
1425
+ def identify_channel(self, *, text: str) -> ChannelProtocol | None:
1426
+ """Identify channel within a text."""
1427
+
1428
+
1429
+ class DeviceAvailabilityProtocol(Protocol):
1430
+ """
1431
+ Protocol for device availability state.
1432
+
1433
+ Provides access to device availability and configuration state.
1434
+ """
1435
+
1436
+ __slots__ = ()
1437
+
1438
+ @property
1439
+ @abstractmethod
1440
+ def availability(self) -> AvailabilityInfo:
1441
+ """Return bundled availability information for the device."""
1442
+
1443
+ @property
1444
+ @abstractmethod
1445
+ def available(self) -> bool:
1446
+ """Return the availability of the device."""
1447
+
1448
+ @property
1449
+ @abstractmethod
1450
+ def config_pending(self) -> bool:
1451
+ """Return if a config change of the device is pending."""
1452
+
1453
+ @abstractmethod
1454
+ def set_forced_availability(self, *, forced_availability: ForcedDeviceAvailability) -> None:
1455
+ """Set the availability of the device."""
1456
+
1457
+
1458
+ class DeviceFirmwareProtocol(Protocol):
1459
+ """
1460
+ Protocol for device firmware management.
1461
+
1462
+ Provides access to firmware information and update operations.
1463
+ """
1464
+
1465
+ __slots__ = ()
1466
+
1467
+ @property
1468
+ @abstractmethod
1469
+ def available_firmware(self) -> str | None:
1470
+ """Return the available firmware of the device."""
1471
+
1472
+ @property
1473
+ @abstractmethod
1474
+ def firmware(self) -> str:
1475
+ """Return the firmware of the device."""
1476
+
1477
+ @property
1478
+ @abstractmethod
1479
+ def firmware_updatable(self) -> bool:
1480
+ """Return the firmware update state of the device."""
1481
+
1482
+ @property
1483
+ @abstractmethod
1484
+ def firmware_update_state(self) -> DeviceFirmwareState:
1485
+ """Return the firmware update state of the device."""
1486
+
1487
+ @property
1488
+ @abstractmethod
1489
+ def is_updatable(self) -> bool:
1490
+ """Return if the device is updatable."""
1491
+
1492
+ @abstractmethod
1493
+ def refresh_firmware_data(self) -> None:
1494
+ """Refresh firmware data of the device."""
1495
+
1496
+ @abstractmethod
1497
+ def subscribe_to_firmware_updated(self, *, handler: FirmwareUpdateHandler) -> UnsubscribeCallback:
1498
+ """Subscribe to firmware updated event."""
1499
+
1500
+ @abstractmethod
1501
+ async def update_firmware(self, *, refresh_after_update_intervals: tuple[int, ...]) -> bool:
1502
+ """Update the device firmware."""
1503
+
1504
+
1505
+ class DeviceLinkManagementProtocol(Protocol):
1506
+ """
1507
+ Protocol for device central link management.
1508
+
1509
+ Provides methods for managing central links and peer channels.
1510
+ """
1511
+
1512
+ __slots__ = ()
1513
+
1514
+ @property
1515
+ @abstractmethod
1516
+ def link_peer_channels(self) -> Mapping[ChannelProtocol, tuple[ChannelProtocol, ...]]:
1517
+ """Return the link peer channels."""
1518
+
1519
+ @abstractmethod
1520
+ async def create_central_links(self) -> None:
1521
+ """Create central links to support press events."""
1522
+
1523
+ @abstractmethod
1524
+ async def remove_central_links(self) -> None:
1525
+ """Remove central links."""
1526
+
1527
+
1528
+ class DeviceGroupManagementProtocol(Protocol):
1529
+ """
1530
+ Protocol for device channel group management.
1531
+
1532
+ Provides methods for managing channel groups.
1533
+ """
1534
+
1535
+ __slots__ = ()
1536
+
1537
+ @abstractmethod
1538
+ def add_channel_to_group(self, *, group_no: int, channel_no: int | None) -> None:
1539
+ """Add a channel to a group."""
1540
+
1541
+ @abstractmethod
1542
+ def get_channel_group_no(self, *, channel_no: int | None) -> int | None:
1543
+ """Return the channel group number."""
1544
+
1545
+ @abstractmethod
1546
+ def is_in_multi_channel_group(self, *, channel_no: int | None) -> bool:
1547
+ """Return if multiple channels are in the group."""
1548
+
1549
+
1550
+ class DeviceConfigurationProtocol(Protocol):
1551
+ """
1552
+ Protocol for device configuration and metadata.
1553
+
1554
+ Provides access to device configuration properties.
1555
+ """
1556
+
1557
+ __slots__ = ()
1558
+
1559
+ @property
1560
+ @abstractmethod
1561
+ def allow_undefined_generic_data_points(self) -> bool:
1562
+ """Return if undefined generic data points of this device are allowed."""
1563
+
1564
+ @property
1565
+ @abstractmethod
1566
+ def has_custom_data_point_definition(self) -> bool:
1567
+ """Return if custom data point definition is available for the device."""
1568
+
1569
+ @property
1570
+ @abstractmethod
1571
+ def has_sub_devices(self) -> bool:
1572
+ """Return if the device has sub devices."""
1573
+
1574
+ @property
1575
+ @abstractmethod
1576
+ def ignore_for_custom_data_point(self) -> bool:
1577
+ """Return if the device should be ignored for custom data point creation."""
1578
+
1579
+ @property
1580
+ @abstractmethod
1581
+ def ignore_on_initial_load(self) -> bool:
1582
+ """Return if the device should be ignored on initial load."""
1583
+
1584
+ @property
1585
+ @abstractmethod
1586
+ def product_group(self) -> ProductGroup:
1587
+ """Return the product group of the device."""
1588
+
1589
+ @property
1590
+ @abstractmethod
1591
+ def rega_id(self) -> int:
1592
+ """Return the id of the device."""
1593
+
1594
+ @property
1595
+ @abstractmethod
1596
+ def room(self) -> str | None:
1597
+ """Return the room of the device."""
1598
+
1599
+ @property
1600
+ @abstractmethod
1601
+ def rooms(self) -> set[str]:
1602
+ """Return all rooms of the device."""
1603
+
1604
+ @property
1605
+ @abstractmethod
1606
+ def rx_modes(self) -> tuple[RxMode, ...]:
1607
+ """Return the rx modes."""
1608
+
1609
+
1610
+ class DeviceWeekProfileProtocol(Protocol):
1611
+ """
1612
+ Protocol for device week profile support.
1613
+
1614
+ Provides access to week profile functionality.
1615
+ """
1616
+
1617
+ __slots__ = ()
1618
+
1619
+ @property
1620
+ @abstractmethod
1621
+ def default_schedule_channel(self) -> ChannelProtocol | None:
1622
+ """Return the default schedule channel."""
1623
+
1624
+ @property
1625
+ @abstractmethod
1626
+ def has_week_profile(self) -> bool:
1627
+ """Return if the device supports week profile."""
1628
+
1629
+ @property
1630
+ @abstractmethod
1631
+ def week_profile(self) -> WeekProfileProtocol[dict[Any, Any]] | None:
1632
+ """Return the week profile."""
1633
+
1634
+ @abstractmethod
1635
+ def init_week_profile(self, *, data_point: CustomDataPointProtocol) -> None:
1636
+ """Initialize the week profile."""
1637
+
1638
+
1639
+ class DeviceProvidersProtocol(Protocol):
1640
+ """
1641
+ Protocol for device dependency providers.
1642
+
1643
+ Provides access to protocol interface providers injected into the device.
1644
+ """
1645
+
1646
+ __slots__ = ()
1647
+
1648
+ @property
1649
+ @abstractmethod
1650
+ def central_info(self) -> CentralInfoProtocol:
1651
+ """Return the central info of the device."""
1652
+
1653
+ @property
1654
+ @abstractmethod
1655
+ def channel_lookup(self) -> ChannelLookupProtocol:
1656
+ """Return the channel lookup provider."""
1657
+
1658
+ @property
1659
+ @abstractmethod
1660
+ def client(self) -> ClientProtocol:
1661
+ """Return the client of the device."""
1662
+
1663
+ @property
1664
+ @abstractmethod
1665
+ def config_provider(self) -> ConfigProviderProtocol:
1666
+ """Return the config provider."""
1667
+
1668
+ @property
1669
+ @abstractmethod
1670
+ def data_cache_provider(self) -> DataCacheProviderProtocol:
1671
+ """Return the data cache provider."""
1672
+
1673
+ @property
1674
+ @abstractmethod
1675
+ def data_point_provider(self) -> DataPointProviderProtocol:
1676
+ """Return the data point provider."""
1677
+
1678
+ @property
1679
+ @abstractmethod
1680
+ def device_data_refresher(self) -> FirmwareDataRefresherProtocol:
1681
+ """Return the device data refresher."""
1682
+
1683
+ @property
1684
+ @abstractmethod
1685
+ def device_description_provider(self) -> DeviceDescriptionProviderProtocol:
1686
+ """Return the device description provider."""
1687
+
1688
+ @property
1689
+ @abstractmethod
1690
+ def device_details_provider(self) -> DeviceDetailsProviderProtocol:
1691
+ """Return the device details provider."""
1692
+
1693
+ @property
1694
+ @abstractmethod
1695
+ def event_bus_provider(self) -> EventBusProviderProtocol:
1696
+ """Return the event bus provider."""
1697
+
1698
+ @property
1699
+ @abstractmethod
1700
+ def event_publisher(self) -> EventPublisherProtocol:
1701
+ """Return the event publisher."""
1702
+
1703
+ @property
1704
+ @abstractmethod
1705
+ def event_subscription_manager(self) -> EventSubscriptionManagerProtocol:
1706
+ """Return the event subscription manager."""
1707
+
1708
+ @property
1709
+ @abstractmethod
1710
+ def parameter_visibility_provider(self) -> ParameterVisibilityProviderProtocol:
1711
+ """Return the parameter visibility provider."""
1712
+
1713
+ @property
1714
+ @abstractmethod
1715
+ def paramset_description_provider(self) -> ParamsetDescriptionProviderProtocol:
1716
+ """Return the paramset description provider."""
1717
+
1718
+ @property
1719
+ @abstractmethod
1720
+ def task_scheduler(self) -> TaskSchedulerProtocol:
1721
+ """Return the task scheduler."""
1722
+
1723
+ @property
1724
+ @abstractmethod
1725
+ def value_cache(self) -> Any:
1726
+ """Return the value cache."""
1727
+
1728
+
1729
+ class DeviceLifecycleProtocol(Protocol):
1730
+ """
1731
+ Protocol for device lifecycle management.
1732
+
1733
+ Provides methods for initialization, configuration changes, and removal.
1734
+ """
1735
+
1736
+ __slots__ = ()
1737
+
1738
+ @abstractmethod
1739
+ async def export_device_definition(self) -> None:
1740
+ """Export the device definition for current device."""
1741
+
1742
+ @abstractmethod
1743
+ async def finalize_init(self) -> None:
1744
+ """Finalize the device init action after model setup."""
1745
+
1746
+ @abstractmethod
1747
+ async def on_config_changed(self) -> None:
1748
+ """Handle config changed event."""
1749
+
1750
+ @abstractmethod
1751
+ def publish_device_updated_event(self, *, notify_data_points: bool = False) -> None:
1752
+ """Publish device updated event."""
1753
+
1754
+ @abstractmethod
1755
+ def remove(self) -> None:
1756
+ """Remove data points from collections and central."""
1757
+
1758
+
1759
+ # =============================================================================
1760
+ # Device Combined Sub-Protocol Interfaces
1761
+ # =============================================================================
1762
+
1763
+
1764
+ @runtime_checkable
1765
+ class DeviceRemovalInfoProtocol(DeviceIdentityProtocol, DeviceChannelAccessProtocol, Protocol):
1766
+ """
1767
+ Combined protocol for device removal operations.
1768
+
1769
+ Used by cache and store components that need to remove device data.
1770
+ Provides access to device address, interface_id, and channel addresses.
1771
+ Reduces coupling compared to using full DeviceProtocol.
1772
+
1773
+ Implemented by: Device
1774
+ """
1775
+
1776
+ __slots__ = ()
1777
+
1778
+
1779
+ # =============================================================================
1780
+ # Device Composite Protocol Interface
1781
+ # =============================================================================
1782
+
1783
+
1784
+ @runtime_checkable
1785
+ class DeviceProtocol(
1786
+ DeviceIdentityProtocol,
1787
+ DeviceChannelAccessProtocol,
1788
+ DeviceAvailabilityProtocol,
1789
+ DeviceFirmwareProtocol,
1790
+ DeviceLinkManagementProtocol,
1791
+ DeviceGroupManagementProtocol,
1792
+ DeviceConfigurationProtocol,
1793
+ DeviceWeekProfileProtocol,
1794
+ DeviceProvidersProtocol,
1795
+ DeviceLifecycleProtocol,
1796
+ Protocol,
1797
+ ):
1798
+ """
1799
+ Composite protocol for complete device access.
1800
+
1801
+ Combines all device sub-protocols into a single interface.
1802
+ Implemented by Device.
1803
+
1804
+ Sub-protocols:
1805
+ - DeviceIdentityProtocol: Basic identification (address, name, model, manufacturer, interface)
1806
+ - DeviceChannelAccessProtocol: Channel and DataPoint access methods
1807
+ - DeviceAvailabilityProtocol: Availability state management
1808
+ - DeviceFirmwareProtocol: Firmware information and update operations
1809
+ - DeviceLinkManagementProtocol: Central link operations
1810
+ - DeviceGroupManagementProtocol: Channel group management
1811
+ - DeviceConfigurationProtocol: Device configuration and metadata
1812
+ - DeviceWeekProfileProtocol: Week profile support
1813
+ - DeviceProvidersProtocol: Protocol interface providers
1814
+ - DeviceLifecycleProtocol: Lifecycle methods
1815
+ """
1816
+
1817
+ __slots__ = ()
1818
+
1819
+
1820
+ # =============================================================================
1821
+ # Hub Protocol Interface
1822
+ # =============================================================================
1823
+
1824
+
1825
+ @runtime_checkable
1826
+ class HubProtocol(Protocol):
1827
+ """
1828
+ Protocol for Hub-level operations.
1829
+
1830
+ Provides access to hub data points (inbox, update) and methods
1831
+ for fetching programs, system variables, and other hub data.
1832
+ Inherits fetch operations from HubFetchOperationsProtocol (interfaces.central).
1833
+ Implemented by Hub.
1834
+ """
1835
+
1836
+ __slots__ = ()
1837
+
1838
+ @property
1839
+ @abstractmethod
1840
+ def inbox_dp(self) -> GenericHubDataPointProtocol | None:
1841
+ """Return the inbox data point."""
1842
+
1843
+ @property
1844
+ @abstractmethod
1845
+ def update_dp(self) -> GenericHubDataPointProtocol | None:
1846
+ """Return the system update data point."""
1847
+
1848
+ @abstractmethod
1849
+ async def fetch_inbox_data(self, *, scheduled: bool) -> None:
1850
+ """Fetch inbox data for the hub."""
1851
+
1852
+ @abstractmethod
1853
+ def fetch_metrics_data(self, *, scheduled: bool) -> None:
1854
+ """Refresh metrics hub sensors with current values."""
1855
+
1856
+ @abstractmethod
1857
+ async def fetch_program_data(self, *, scheduled: bool) -> None:
1858
+ """Fetch program data for the hub."""
1859
+
1860
+ @abstractmethod
1861
+ async def fetch_system_update_data(self, *, scheduled: bool) -> None:
1862
+ """Fetch system update data for the hub."""
1863
+
1864
+ @abstractmethod
1865
+ async def fetch_sysvar_data(self, *, scheduled: bool) -> None:
1866
+ """Fetch sysvar data for the hub."""
1867
+
1868
+
1869
+ # =============================================================================
1870
+ # WeekProfile Protocol Interface
1871
+ # =============================================================================
1872
+
1873
+
1874
+ @runtime_checkable
1875
+ class WeekProfileProtocol[SCHEDULE_DICT_T: dict[Any, Any]](Protocol):
1876
+ """
1877
+ Protocol for week profile operations.
1878
+
1879
+ Provides access to device weekly schedules for climate and non-climate devices.
1880
+ Implemented by WeekProfile (base), ClimeateWeekProfile, and DefaultWeekProfile.
1881
+ """
1882
+
1883
+ __slots__ = ()
1884
+
1885
+ @property
1886
+ @abstractmethod
1887
+ def has_schedule(self) -> bool:
1888
+ """Return if climate supports schedule."""
1889
+
1890
+ @property
1891
+ @abstractmethod
1892
+ def schedule(self) -> SCHEDULE_DICT_T:
1893
+ """Return the schedule cache."""
1894
+
1895
+ @property
1896
+ @abstractmethod
1897
+ def schedule_channel_address(self) -> str | None:
1898
+ """Return schedule channel address."""
1899
+
1900
+ @abstractmethod
1901
+ async def get_schedule(self, *, force_load: bool = False) -> SCHEDULE_DICT_T:
1902
+ """Return the schedule dictionary."""
1903
+
1904
+ @abstractmethod
1905
+ async def reload_and_cache_schedule(self, *, force: bool = False) -> None:
1906
+ """Reload schedule entries and update cache."""
1907
+
1908
+ @abstractmethod
1909
+ async def set_schedule(self, *, schedule_data: SCHEDULE_DICT_T) -> None:
1910
+ """Persist the provided schedule dictionary."""
1911
+
1912
+
1913
+ # =============================================================================
1914
+ # Type Aliases for Heterogeneous Collections
1915
+ # =============================================================================
1916
+ # These aliases provide `[Any]`-parameterized versions of the generic protocols
1917
+ # for use in collections where different value types are mixed (e.g., device.generic_data_points).
1918
+
1919
+ BaseParameterDataPointProtocolAny: TypeAlias = BaseParameterDataPointProtocol[Any]
1920
+ GenericDataPointProtocolAny: TypeAlias = GenericDataPointProtocol[Any]
1921
+ GenericEventProtocolAny: TypeAlias = GenericEventProtocol[Any]