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,217 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2021-2026
3
+ """
4
+ Operation protocol interfaces.
5
+
6
+ This module defines protocol interfaces for operational tasks like
7
+ scheduling, caching, and parameter visibility management.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from abc import abstractmethod
13
+ import asyncio
14
+ from collections.abc import Callable, Mapping
15
+ from concurrent.futures import ThreadPoolExecutor
16
+ from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
17
+
18
+ from aiohomematic.const import DeviceDescription, Interface, ParameterData, ParamsetKey
19
+ from aiohomematic.type_aliases import AsyncTaskFactoryAny, CoroutineAny
20
+
21
+ if TYPE_CHECKING:
22
+ from aiohomematic.interfaces import ChannelProtocol
23
+ from aiohomematic.store import CacheName, CacheStatistics
24
+ from aiohomematic.store.types import IncidentSeverity, IncidentSnapshot, IncidentType, PingPongJournal
25
+
26
+
27
+ @runtime_checkable
28
+ class TaskSchedulerProtocol(Protocol):
29
+ """
30
+ Protocol for scheduling async tasks.
31
+
32
+ Implemented by Looper.
33
+ """
34
+
35
+ @abstractmethod
36
+ def async_add_executor_job[T](
37
+ self, target: Callable[..., T], *args: Any, name: str, executor: ThreadPoolExecutor | None = None
38
+ ) -> asyncio.Future[T]:
39
+ """Add an executor job from within the event_loop."""
40
+
41
+ @abstractmethod
42
+ async def block_till_done(self, *, wait_time: float | None = None) -> None:
43
+ """Block until all pending work is done."""
44
+
45
+ @abstractmethod
46
+ def cancel_tasks(self) -> None:
47
+ """Cancel running tasks."""
48
+
49
+ @abstractmethod
50
+ def create_task(self, *, target: CoroutineAny | AsyncTaskFactoryAny, name: str) -> None:
51
+ """Create and schedule an async task."""
52
+
53
+
54
+ @runtime_checkable
55
+ class ParameterVisibilityProviderProtocol(Protocol):
56
+ """
57
+ Protocol for accessing parameter visibility information.
58
+
59
+ Implemented by ParameterVisibilityRegistry.
60
+ """
61
+
62
+ @abstractmethod
63
+ def is_relevant_paramset(self, *, channel: ChannelProtocol, paramset_key: ParamsetKey) -> bool:
64
+ """
65
+ Return if a paramset is relevant.
66
+
67
+ Required to load MASTER paramsets, which are not initialized by default.
68
+ """
69
+
70
+ @abstractmethod
71
+ def model_is_ignored(self, *, model: str) -> bool:
72
+ """Check if a model should be ignored for custom data points."""
73
+
74
+ @abstractmethod
75
+ def parameter_is_hidden(self, *, channel: ChannelProtocol, paramset_key: ParamsetKey, parameter: str) -> bool:
76
+ """Check if a parameter is hidden."""
77
+
78
+ @abstractmethod
79
+ def parameter_is_un_ignored(
80
+ self, *, channel: ChannelProtocol, paramset_key: ParamsetKey, parameter: str, custom_only: bool = False
81
+ ) -> bool:
82
+ """Check if a parameter is un-ignored (visible)."""
83
+
84
+ @abstractmethod
85
+ def should_skip_parameter(
86
+ self, *, channel: ChannelProtocol, paramset_key: ParamsetKey, parameter: str, parameter_is_un_ignored: bool
87
+ ) -> bool:
88
+ """Determine if a parameter should be skipped."""
89
+
90
+
91
+ @runtime_checkable
92
+ class DeviceDetailsProviderProtocol(Protocol):
93
+ """
94
+ Protocol for accessing device details.
95
+
96
+ Implemented by DeviceDescriptionRegistry.
97
+ """
98
+
99
+ @abstractmethod
100
+ def get_address_id(self, *, address: str) -> int:
101
+ """Get an ID for an address."""
102
+
103
+ @abstractmethod
104
+ def get_channel_rooms(self, *, channel_address: str) -> set[str]:
105
+ """Get rooms for a channel."""
106
+
107
+ @abstractmethod
108
+ def get_device_rooms(self, *, device_address: str) -> set[str]:
109
+ """Get rooms for a device."""
110
+
111
+ @abstractmethod
112
+ def get_function_text(self, *, address: str) -> str | None:
113
+ """Get function text for an address."""
114
+
115
+ @abstractmethod
116
+ def get_interface(self, *, address: str) -> Interface:
117
+ """Get interface for an address."""
118
+
119
+ @abstractmethod
120
+ def get_name(self, *, address: str) -> str | None:
121
+ """Get name for an address."""
122
+
123
+
124
+ @runtime_checkable
125
+ class DeviceDescriptionProviderProtocol(Protocol):
126
+ """
127
+ Protocol for accessing device descriptions.
128
+
129
+ Implemented by DeviceDescriptionRegistry.
130
+ """
131
+
132
+ @abstractmethod
133
+ def get_device_description(self, *, interface_id: str, address: str) -> DeviceDescription:
134
+ """Get device description."""
135
+
136
+ @abstractmethod
137
+ def get_device_with_channels(self, *, interface_id: str, device_address: str) -> Mapping[str, DeviceDescription]:
138
+ """Get device with all channel descriptions."""
139
+
140
+
141
+ @runtime_checkable
142
+ class ParamsetDescriptionProviderProtocol(Protocol):
143
+ """
144
+ Protocol for accessing paramset descriptions.
145
+
146
+ Implemented by ParamsetDescriptionRegistry.
147
+ """
148
+
149
+ @abstractmethod
150
+ def get_channel_paramset_descriptions(
151
+ self, *, interface_id: str, channel_address: str
152
+ ) -> Mapping[ParamsetKey, Mapping[str, ParameterData]]:
153
+ """Get all paramset descriptions for a channel."""
154
+
155
+ @abstractmethod
156
+ def get_parameter_data(
157
+ self, *, interface_id: str, channel_address: str, paramset_key: ParamsetKey, parameter: str
158
+ ) -> ParameterData | None:
159
+ """Get parameter data from paramset description."""
160
+
161
+ @abstractmethod
162
+ def has_parameter(
163
+ self, *, interface_id: str, channel_address: str, paramset_key: ParamsetKey, parameter: str
164
+ ) -> bool:
165
+ """Check if a parameter exists in the paramset description."""
166
+
167
+ @abstractmethod
168
+ def is_in_multiple_channels(self, *, channel_address: str, parameter: str) -> bool:
169
+ """Check if parameter is in multiple channels per device."""
170
+
171
+
172
+ @runtime_checkable
173
+ class CacheWithStatisticsProtocol(Protocol):
174
+ """
175
+ Protocol for caches that provide statistics.
176
+
177
+ Caches implementing this protocol provide local counters for hits, misses,
178
+ and evictions that can be read directly by MetricsAggregator without
179
+ requiring EventBus events.
180
+ """
181
+
182
+ @property
183
+ @abstractmethod
184
+ def name(self) -> CacheName:
185
+ """Return the cache name for identification."""
186
+
187
+ @property
188
+ @abstractmethod
189
+ def size(self) -> int:
190
+ """Return current number of entries in the cache."""
191
+
192
+ @property
193
+ @abstractmethod
194
+ def statistics(self) -> CacheStatistics:
195
+ """Return the cache statistics container."""
196
+
197
+
198
+ @runtime_checkable
199
+ class IncidentRecorderProtocol(Protocol):
200
+ """
201
+ Protocol for recording diagnostic incidents.
202
+
203
+ Implemented by IncidentStore.
204
+ """
205
+
206
+ @abstractmethod
207
+ async def record_incident(
208
+ self,
209
+ *,
210
+ incident_type: IncidentType,
211
+ severity: IncidentSeverity,
212
+ message: str,
213
+ interface_id: str | None = None,
214
+ context: dict[str, Any] | None = None,
215
+ journal: PingPongJournal | None = None,
216
+ ) -> IncidentSnapshot:
217
+ """Record a new incident and persist it."""
@@ -0,0 +1,134 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2021-2026
3
+ """
4
+ Logging integration with request context.
5
+
6
+ This module provides logging utilities that automatically include request context
7
+ information in log messages, enabling correlation of logs across async call chains.
8
+
9
+ Key features:
10
+ - ContextualLoggerAdapter: Logger adapter that prefixes messages with request ID
11
+ - RequestContextFilter: Logging filter that adds context fields to log records
12
+ - get_contextual_logger: Factory function for contextual loggers
13
+
14
+ Example:
15
+ _LOGGER = get_contextual_logger(name=__name__)
16
+ _LOGGER.info("Processing device") # → "[abc12345:set_value] Processing device"
17
+
18
+ Public API of this module is defined by __all__.
19
+
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ from collections.abc import MutableMapping
25
+ import logging
26
+ from typing import Any
27
+
28
+ from aiohomematic.context import get_request_context
29
+
30
+
31
+ class ContextualLoggerAdapter(logging.LoggerAdapter[logging.Logger]):
32
+ """
33
+ Logger adapter that includes request context in log messages.
34
+
35
+ Automatically prefixes log messages with request_id and operation
36
+ from the current RequestContext.
37
+
38
+ Example:
39
+ _LOGGER = ContextualLoggerAdapter(logging.getLogger(__name__), {})
40
+ # With request context set:
41
+ _LOGGER.info("Processing") # → "[abc12345:set_value] Processing"
42
+ # Without request context:
43
+ _LOGGER.info("Processing") # → "Processing"
44
+
45
+ """
46
+
47
+ def process( # kwonly: disable
48
+ self,
49
+ msg: str,
50
+ kwargs: MutableMapping[str, Any],
51
+ ) -> tuple[str, MutableMapping[str, Any]]:
52
+ """
53
+ Process log message to include request context.
54
+
55
+ Args:
56
+ msg: The log message.
57
+ kwargs: Additional logging keyword arguments.
58
+
59
+ Returns:
60
+ Tuple of (processed message, kwargs).
61
+
62
+ """
63
+ if ctx := get_request_context():
64
+ prefix = f"[{ctx.request_id}]"
65
+ if ctx.operation:
66
+ prefix = f"[{ctx.request_id}:{ctx.operation}]"
67
+ msg = f"{prefix} {msg}"
68
+
69
+ return msg, kwargs
70
+
71
+
72
+ class RequestContextFilter(logging.Filter):
73
+ """
74
+ Logging filter that adds context fields to log records.
75
+
76
+ Use with JSON formatters or structured logging to include request context
77
+ as separate fields in log records.
78
+
79
+ Example:
80
+ handler = logging.StreamHandler()
81
+ handler.addFilter(RequestContextFilter())
82
+ # Log records will have: request_id, operation, device_address, elapsed_ms
83
+
84
+ """
85
+
86
+ def filter(self, record: logging.LogRecord) -> bool: # kwonly: disable
87
+ """
88
+ Add request context fields to log record.
89
+
90
+ Args:
91
+ record: The log record to augment.
92
+
93
+ Returns:
94
+ Always True (never filters out records).
95
+
96
+ """
97
+ ctx = get_request_context()
98
+
99
+ record.request_id = ctx.request_id if ctx else "none"
100
+ record.operation = ctx.operation if ctx else "none"
101
+ record.device_address = ctx.device_address if ctx else "none"
102
+ record.elapsed_ms = ctx.elapsed_ms if ctx else 0.0
103
+
104
+ return True
105
+
106
+
107
+ def get_contextual_logger(*, name: str) -> ContextualLoggerAdapter:
108
+ """
109
+ Get a logger that includes request context in messages.
110
+
111
+ Factory function that creates a ContextualLoggerAdapter wrapping a
112
+ standard logger. Use this instead of logging.getLogger() to get
113
+ automatic request context prefixing.
114
+
115
+ Args:
116
+ name: Logger name (typically __name__).
117
+
118
+ Returns:
119
+ ContextualLoggerAdapter that prefixes messages with request context.
120
+
121
+ Example:
122
+ _LOGGER = get_contextual_logger(name=__name__)
123
+ _LOGGER.info("Processing device") # → "[abc12345:set_value] Processing device"
124
+
125
+ """
126
+ return ContextualLoggerAdapter(logging.getLogger(name), {})
127
+
128
+
129
+ # Define public API for this module
130
+ __all__ = [
131
+ "ContextualLoggerAdapter",
132
+ "RequestContextFilter",
133
+ "get_contextual_logger",
134
+ ]
@@ -0,0 +1,125 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2021-2026
3
+ """
4
+ Metrics collection, aggregation, and observability.
5
+
6
+ This module provides both event-driven and polling-based metrics collection.
7
+
8
+ Event-Driven Metrics (MetricsObserver)
9
+ --------------------------------------
10
+ Components emit metric events to the EventBus, and MetricsObserver aggregates them.
11
+ Use emit_* functions to emit metrics and MetricsObserver to query them.
12
+
13
+ Polling-Based Metrics (MetricsAggregator)
14
+ -----------------------------------------
15
+ MetricsAggregator queries system components directly to collect metrics.
16
+ Use for comprehensive system snapshots.
17
+
18
+ Public API
19
+ ----------
20
+ Event-driven:
21
+ - MetricEvent, LatencyMetricEvent, CounterMetricEvent, GaugeMetricEvent, HealthMetricEvent
22
+ - MetricsObserver, ObserverSnapshot, LatencyTracker, HealthState
23
+ - emit_latency, emit_counter, emit_gauge, emit_health
24
+ - MetricEmitterMixin, LatencyContext
25
+
26
+ Polling-based:
27
+ - MetricsAggregator, MetricsSnapshot
28
+ - RpcMetrics, RpcServerMetrics, EventMetrics, CacheMetrics, HealthMetrics
29
+ - RecoveryMetrics, ModelMetrics, ServiceMetrics
30
+
31
+ Note: Protocol dependencies for MetricsAggregator are in aiohomematic.interfaces:
32
+ - ClientProviderForMetricsProtocol
33
+ - DeviceProviderForMetricsProtocol
34
+ - HubDataPointManagerForMetricsProtocol
35
+
36
+ """
37
+
38
+ from __future__ import annotations
39
+
40
+ from aiohomematic.metrics.aggregator import MetricsAggregator
41
+ from aiohomematic.metrics.dataclasses import (
42
+ CacheMetrics,
43
+ EventMetrics,
44
+ HealthMetrics,
45
+ MetricsSnapshot,
46
+ ModelMetrics,
47
+ RecoveryMetrics,
48
+ RpcMetrics,
49
+ RpcServerMetrics,
50
+ ServiceMetrics,
51
+ )
52
+ from aiohomematic.metrics.emitter import (
53
+ EventBusProviderProtocol,
54
+ LatencyContext,
55
+ MetricEmitterMixin,
56
+ emit_counter,
57
+ emit_gauge,
58
+ emit_health,
59
+ emit_latency,
60
+ )
61
+ from aiohomematic.metrics.events import (
62
+ METRIC_EVENT_TYPES,
63
+ AnyMetricEvent,
64
+ CounterMetricEvent,
65
+ GaugeMetricEvent,
66
+ HealthMetricEvent,
67
+ LatencyMetricEvent,
68
+ MetricEvent,
69
+ MetricType,
70
+ )
71
+ from aiohomematic.metrics.keys import MetricKey, MetricKeys
72
+ from aiohomematic.metrics.observer import (
73
+ MAX_METRIC_KEYS,
74
+ HealthState,
75
+ LatencyTracker,
76
+ MetricsObserver,
77
+ ObserverSnapshot,
78
+ )
79
+ from aiohomematic.metrics.stats import CacheStats, LatencyStats, ServiceStats, SizeOnlyStats
80
+
81
+ __all__ = [
82
+ # Aggregator
83
+ "MetricsAggregator",
84
+ # Dataclasses
85
+ "CacheMetrics",
86
+ "EventMetrics",
87
+ "HealthMetrics",
88
+ "MetricsSnapshot",
89
+ "ModelMetrics",
90
+ "RecoveryMetrics",
91
+ "RpcMetrics",
92
+ "RpcServerMetrics",
93
+ "ServiceMetrics",
94
+ # Emitter
95
+ "EventBusProviderProtocol",
96
+ "LatencyContext",
97
+ "MetricEmitterMixin",
98
+ "emit_counter",
99
+ "emit_gauge",
100
+ "emit_health",
101
+ "emit_latency",
102
+ # Events
103
+ "AnyMetricEvent",
104
+ "CounterMetricEvent",
105
+ "GaugeMetricEvent",
106
+ "HealthMetricEvent",
107
+ "LatencyMetricEvent",
108
+ "METRIC_EVENT_TYPES",
109
+ "MetricEvent",
110
+ "MetricType",
111
+ # Keys
112
+ "MetricKey",
113
+ "MetricKeys",
114
+ # Observer
115
+ "HealthState",
116
+ "LatencyTracker",
117
+ "MAX_METRIC_KEYS",
118
+ "MetricsObserver",
119
+ "ObserverSnapshot",
120
+ # Stats
121
+ "CacheStats",
122
+ "LatencyStats",
123
+ "ServiceStats",
124
+ "SizeOnlyStats",
125
+ ]
@@ -0,0 +1,140 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2021-2026
3
+ """
4
+ Minimal protocol interfaces for metrics aggregation.
5
+
6
+ This private module defines minimal protocol interfaces used by MetricsAggregator.
7
+ Located in metrics/ (not interfaces/) to avoid circular imports during initialization.
8
+
9
+ Import path: aiohomematic.interfaces (public) or aiohomematic.metrics._protocols (internal)
10
+
11
+ These protocols are:
12
+ - Re-exported via interfaces/__init__.py for public use
13
+ - Extended by full protocols (DeviceProviderProtocol, ClientProviderProtocol,
14
+ HubDataPointManagerProtocol) in interfaces/central.py and interfaces/client.py
15
+
16
+ IMPORTANT: This module must NOT import from interfaces/ to avoid circular imports.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from abc import abstractmethod
22
+ from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
23
+
24
+ if TYPE_CHECKING:
25
+ from aiohomematic.store.types import CacheStatistics
26
+
27
+
28
+ @runtime_checkable
29
+ class DeviceProviderForMetricsProtocol(Protocol):
30
+ """
31
+ Minimal protocol for device access in metrics context.
32
+
33
+ Provides only the `devices` property needed by MetricsAggregator
34
+ to collect model statistics without requiring the full DeviceProviderProtocol.
35
+
36
+ Implemented by CentralUnit.
37
+ """
38
+
39
+ @property
40
+ @abstractmethod
41
+ def devices(self) -> tuple[Any, ...]:
42
+ """Return all registered devices."""
43
+
44
+
45
+ @runtime_checkable
46
+ class ClientProviderForMetricsProtocol(Protocol):
47
+ """
48
+ Minimal protocol for client access in metrics context.
49
+
50
+ Provides only the `clients` property needed by MetricsAggregator
51
+ to collect RPC metrics without requiring the full ClientProviderProtocol.
52
+
53
+ Implemented by CentralUnit.
54
+ """
55
+
56
+ @property
57
+ @abstractmethod
58
+ def clients(self) -> tuple[Any, ...]:
59
+ """Return all connected clients."""
60
+
61
+
62
+ @runtime_checkable
63
+ class HubDataPointManagerForMetricsProtocol(Protocol):
64
+ """
65
+ Minimal protocol for hub data point access in metrics context.
66
+
67
+ Provides only the properties needed by MetricsAggregator to collect
68
+ program/sysvar counts without requiring the full HubDataPointManagerProtocol.
69
+
70
+ Implemented by HubCoordinator.
71
+ """
72
+
73
+ @property
74
+ @abstractmethod
75
+ def program_data_points(self) -> tuple[Any, ...]:
76
+ """Return all program data points."""
77
+
78
+ @property
79
+ @abstractmethod
80
+ def sysvar_data_points(self) -> tuple[Any, ...]:
81
+ """Return all system variable data points."""
82
+
83
+
84
+ @runtime_checkable
85
+ class CacheProviderForMetricsProtocol(Protocol):
86
+ """
87
+ Minimal protocol for cache access in metrics context.
88
+
89
+ Provides cache sizes and statistics needed by MetricsAggregator to collect
90
+ cache metrics without requiring the full CacheCoordinator.
91
+
92
+ Implemented by CacheCoordinator.
93
+ """
94
+
95
+ @property
96
+ @abstractmethod
97
+ def data_cache_size(self) -> int:
98
+ """Return data cache size."""
99
+
100
+ @property
101
+ @abstractmethod
102
+ def data_cache_statistics(self) -> CacheStatistics:
103
+ """Return data cache statistics."""
104
+
105
+ @property
106
+ @abstractmethod
107
+ def device_descriptions_size(self) -> int:
108
+ """Return device descriptions cache size."""
109
+
110
+ @property
111
+ @abstractmethod
112
+ def paramset_descriptions_size(self) -> int:
113
+ """Return paramset descriptions cache size."""
114
+
115
+ @property
116
+ @abstractmethod
117
+ def visibility_cache_size(self) -> int:
118
+ """Return visibility cache size."""
119
+
120
+
121
+ @runtime_checkable
122
+ class RecoveryProviderForMetricsProtocol(Protocol):
123
+ """
124
+ Minimal protocol for recovery status access in metrics context.
125
+
126
+ Provides recovery statistics needed by MetricsAggregator to collect
127
+ recovery metrics without requiring the full ConnectionRecoveryCoordinator.
128
+
129
+ Implemented by ConnectionRecoveryCoordinator.
130
+ """
131
+
132
+ @property
133
+ @abstractmethod
134
+ def in_recovery(self) -> bool:
135
+ """Return True if any recovery is in progress."""
136
+
137
+ @property
138
+ @abstractmethod
139
+ def recovery_states(self) -> dict[str, Any]:
140
+ """Return recovery states for all tracked interfaces."""