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,292 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2021-2026
3
+ """
4
+ Metric emission utilities for event-driven metrics.
5
+
6
+ This module provides utilities for emitting metric events without
7
+ coupling productive code to the metrics system.
8
+
9
+ Public API
10
+ ----------
11
+ - emit_latency: Emit a latency metric event
12
+ - emit_counter: Emit a counter metric event
13
+ - emit_gauge: Emit a gauge metric event
14
+ - emit_health: Emit a health metric event
15
+ - LatencyContext: Context manager for automatic latency tracking
16
+ - MetricEmitterMixin: Mixin class for components that emit metrics
17
+
18
+ Usage
19
+ -----
20
+ from aiohomematic.metrics import MetricKeys, emit_latency
21
+
22
+ # Emit with type-safe key
23
+ emit_latency(
24
+ event_bus=bus,
25
+ key=MetricKeys.ping_pong_rtt(interface_id="hmip_rf"),
26
+ duration_ms=42.5,
27
+ )
28
+
29
+ # Emit with string key (for dynamic keys)
30
+ emit_counter(
31
+ event_bus=bus,
32
+ key="custom.metric.key",
33
+ delta=1,
34
+ )
35
+ """
36
+
37
+ from __future__ import annotations
38
+
39
+ from datetime import datetime
40
+ import time
41
+ from typing import TYPE_CHECKING, Protocol, Self, runtime_checkable
42
+
43
+ from aiohomematic.metrics.events import CounterMetricEvent, GaugeMetricEvent, HealthMetricEvent, LatencyMetricEvent
44
+ from aiohomematic.metrics.keys import MetricKey
45
+
46
+ if TYPE_CHECKING:
47
+ from aiohomematic.central.events import EventBus
48
+
49
+
50
+ @runtime_checkable
51
+ class EventBusProviderProtocol(Protocol):
52
+ """Protocol for objects that provide an EventBus."""
53
+
54
+ @property
55
+ def event_bus(self) -> EventBus:
56
+ """Return the event bus."""
57
+
58
+
59
+ # =============================================================================
60
+ # Standalone Emission Functions
61
+ # =============================================================================
62
+
63
+
64
+ def emit_latency(
65
+ *,
66
+ event_bus: EventBus,
67
+ key: MetricKey | str,
68
+ duration_ms: float,
69
+ ) -> None:
70
+ """
71
+ Emit a latency metric event.
72
+
73
+ Args:
74
+ event_bus: EventBus to publish to.
75
+ key: Metric key (MetricKey instance or string).
76
+ duration_ms: Duration in milliseconds.
77
+
78
+ """
79
+ event_bus.publish_sync(
80
+ event=LatencyMetricEvent(
81
+ timestamp=datetime.now(),
82
+ metric_key=str(key),
83
+ duration_ms=duration_ms,
84
+ )
85
+ )
86
+
87
+
88
+ def emit_counter(
89
+ *,
90
+ event_bus: EventBus,
91
+ key: MetricKey | str,
92
+ delta: int = 1,
93
+ ) -> None:
94
+ """
95
+ Emit a counter metric event.
96
+
97
+ Args:
98
+ event_bus: EventBus to publish to.
99
+ key: Metric key (MetricKey instance or string).
100
+ delta: Amount to change the counter by (default: 1).
101
+
102
+ """
103
+ event_bus.publish_sync(
104
+ event=CounterMetricEvent(
105
+ timestamp=datetime.now(),
106
+ metric_key=str(key),
107
+ delta=delta,
108
+ )
109
+ )
110
+
111
+
112
+ def emit_gauge(
113
+ *,
114
+ event_bus: EventBus,
115
+ key: MetricKey | str,
116
+ value: float,
117
+ ) -> None:
118
+ """
119
+ Emit a gauge metric event.
120
+
121
+ Args:
122
+ event_bus: EventBus to publish to.
123
+ key: Metric key (MetricKey instance or string).
124
+ value: Current gauge value.
125
+
126
+ """
127
+ event_bus.publish_sync(
128
+ event=GaugeMetricEvent(
129
+ timestamp=datetime.now(),
130
+ metric_key=str(key),
131
+ value=value,
132
+ )
133
+ )
134
+
135
+
136
+ def emit_health(
137
+ *,
138
+ event_bus: EventBus,
139
+ key: MetricKey | str,
140
+ healthy: bool,
141
+ reason: str | None = None,
142
+ ) -> None:
143
+ """
144
+ Emit a health metric event.
145
+
146
+ Args:
147
+ event_bus: EventBus to publish to.
148
+ key: Metric key (MetricKey instance or string).
149
+ healthy: Whether the component is healthy.
150
+ reason: Optional reason for the state.
151
+
152
+ """
153
+ event_bus.publish_sync(
154
+ event=HealthMetricEvent(
155
+ timestamp=datetime.now(),
156
+ metric_key=str(key),
157
+ healthy=healthy,
158
+ reason=reason,
159
+ )
160
+ )
161
+
162
+
163
+ # =============================================================================
164
+ # Context Manager for Latency Tracking
165
+ # =============================================================================
166
+
167
+
168
+ class LatencyContext:
169
+ """
170
+ Context manager for automatic latency tracking.
171
+
172
+ Usage:
173
+ with LatencyContext(
174
+ event_bus=bus,
175
+ key=MetricKeys.handler_execution(event_type="MyEvent"),
176
+ ):
177
+ # ... do work ...
178
+ # Latency event emitted automatically on exit
179
+ """
180
+
181
+ __slots__ = ("_event_bus", "_key", "_start_time")
182
+
183
+ def __init__(
184
+ self,
185
+ *,
186
+ event_bus: EventBus | None,
187
+ key: MetricKey | str,
188
+ ) -> None:
189
+ """Initialize the context."""
190
+ self._event_bus = event_bus
191
+ self._key = key
192
+ self._start_time: float = 0.0
193
+
194
+ def __enter__(self) -> Self:
195
+ """Start timing."""
196
+ self._start_time = time.monotonic()
197
+ return self
198
+
199
+ def __exit__( # kwonly: disable
200
+ self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: object
201
+ ) -> None:
202
+ """Emit latency event."""
203
+ if self._event_bus is None:
204
+ return
205
+ duration_ms = (time.monotonic() - self._start_time) * 1000
206
+ emit_latency(
207
+ event_bus=self._event_bus,
208
+ key=self._key,
209
+ duration_ms=duration_ms,
210
+ )
211
+
212
+
213
+ # =============================================================================
214
+ # Mixin Class
215
+ # =============================================================================
216
+
217
+
218
+ class MetricEmitterMixin:
219
+ """
220
+ Mixin class for components that emit metrics.
221
+
222
+ Components using this mixin must have:
223
+ - _event_bus_provider: EventBusProviderProtocol (or _event_bus: EventBus)
224
+
225
+ The mixin provides protected methods for emitting each metric type.
226
+ These methods are no-ops if no EventBus is available, making metrics
227
+ collection truly optional.
228
+ """
229
+
230
+ # This will be provided by the implementing class
231
+ _event_bus_provider: EventBusProviderProtocol | None
232
+
233
+ def _emit_counter(self, *, key: MetricKey | str, delta: int = 1) -> None:
234
+ """
235
+ Emit a counter metric event.
236
+
237
+ Args:
238
+ key: Metric key (MetricKey instance or string).
239
+ delta: Amount to change the counter by.
240
+
241
+ """
242
+ if (bus := self._get_event_bus()) is None:
243
+ return
244
+ emit_counter(event_bus=bus, key=key, delta=delta)
245
+
246
+ def _emit_gauge(self, *, key: MetricKey | str, value: float) -> None:
247
+ """
248
+ Emit a gauge metric event.
249
+
250
+ Args:
251
+ key: Metric key (MetricKey instance or string).
252
+ value: Current gauge value.
253
+
254
+ """
255
+ if (bus := self._get_event_bus()) is None:
256
+ return
257
+ emit_gauge(event_bus=bus, key=key, value=value)
258
+
259
+ def _emit_health(self, *, key: MetricKey | str, healthy: bool, reason: str | None = None) -> None:
260
+ """
261
+ Emit a health metric event.
262
+
263
+ Args:
264
+ key: Metric key (MetricKey instance or string).
265
+ healthy: Whether the component is healthy.
266
+ reason: Optional reason for the state.
267
+
268
+ """
269
+ if (bus := self._get_event_bus()) is None:
270
+ return
271
+ emit_health(event_bus=bus, key=key, healthy=healthy, reason=reason)
272
+
273
+ def _emit_latency(self, *, key: MetricKey | str, duration_ms: float) -> None:
274
+ """
275
+ Emit a latency metric event.
276
+
277
+ Args:
278
+ key: Metric key (MetricKey instance or string).
279
+ duration_ms: Duration in milliseconds.
280
+
281
+ """
282
+ if (bus := self._get_event_bus()) is None:
283
+ return
284
+ emit_latency(event_bus=bus, key=key, duration_ms=duration_ms)
285
+
286
+ def _get_event_bus(self) -> EventBus | None:
287
+ """Get the EventBus if available."""
288
+ if hasattr(self, "_event_bus_provider") and self._event_bus_provider is not None:
289
+ return self._event_bus_provider.event_bus
290
+ if hasattr(self, "_event_bus"):
291
+ return getattr(self, "_event_bus", None)
292
+ return None
@@ -0,0 +1,183 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2021-2026
3
+ """
4
+ Metric event types for event-driven metrics collection.
5
+
6
+ This module defines the event hierarchy for metrics emission. Components emit
7
+ these events to the EventBus, where MetricsObserver aggregates them.
8
+
9
+ Event Types
10
+ -----------
11
+ - LatencyMetricEvent: For timing measurements (RPC calls, ping/pong, handlers)
12
+ - CounterMetricEvent: For countable events (cache hits, failures, successes)
13
+ - GaugeMetricEvent: For current-value metrics (queue depth, connection count)
14
+ - HealthMetricEvent: For health state changes (client health, circuit breaker)
15
+
16
+ Usage
17
+ -----
18
+ from aiohomematic.metrics import MetricKeys, emit_latency
19
+
20
+ # Emit latency metric with type-safe key
21
+ emit_latency(
22
+ event_bus=event_bus,
23
+ key=MetricKeys.ping_pong_rtt(interface_id="hmip_rf"),
24
+ duration_ms=45.2,
25
+ )
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ from dataclasses import dataclass
31
+ from enum import Enum
32
+ from typing import Final
33
+
34
+ from aiohomematic.central.events.types import Event
35
+
36
+
37
+ class MetricType(Enum):
38
+ """Type of metric for categorization."""
39
+
40
+ LATENCY = "latency"
41
+ COUNTER = "counter"
42
+ GAUGE = "gauge"
43
+ HEALTH = "health"
44
+
45
+
46
+ @dataclass(frozen=True, slots=True)
47
+ class MetricEvent(Event):
48
+ """
49
+ Base class for all metric events.
50
+
51
+ All metric events share a common metric_key field that identifies
52
+ the metric. The key follows the pattern: {component}.{metric}.{identifier}
53
+ """
54
+
55
+ metric_key: str
56
+ """
57
+ Full metric key identifying this metric.
58
+
59
+ Pattern: {component}.{metric}.{identifier}
60
+ Examples: "ping_pong.rtt.hmip_rf", "cache.data.hit", "handler.execution.DataPointValueReceivedEvent"
61
+ """
62
+
63
+ @property
64
+ def key(self) -> str:
65
+ """Return the event key for EventBus routing."""
66
+ return self.metric_key
67
+
68
+
69
+ @dataclass(frozen=True, slots=True)
70
+ class LatencyMetricEvent(MetricEvent):
71
+ """
72
+ Event for timing measurements.
73
+
74
+ Emitted when an operation completes to record its duration.
75
+ Used for RPC calls, ping/pong round-trips, handler execution, etc.
76
+ """
77
+
78
+ duration_ms: float = 0.0
79
+ """Duration of the operation in milliseconds."""
80
+
81
+
82
+ @dataclass(frozen=True, slots=True)
83
+ class CounterMetricEvent(MetricEvent):
84
+ """
85
+ Event for countable occurrences.
86
+
87
+ Emitted when a countable event occurs (cache hit, RPC success, failure, etc.).
88
+ """
89
+
90
+ delta: int = 1
91
+ """Amount to change the counter by (default: 1)."""
92
+
93
+
94
+ @dataclass(frozen=True, slots=True)
95
+ class GaugeMetricEvent(MetricEvent):
96
+ """
97
+ Event for current-value metrics.
98
+
99
+ Emitted when a gauge value changes (queue depth, connection count, etc.).
100
+ Unlike counters, gauges represent the current state, not a delta.
101
+ """
102
+
103
+ value: float = 0.0
104
+ """Current value of the gauge."""
105
+
106
+
107
+ @dataclass(frozen=True, slots=True)
108
+ class HealthMetricEvent(MetricEvent):
109
+ """
110
+ Event for health state changes.
111
+
112
+ Emitted when a component's health state changes.
113
+ """
114
+
115
+ healthy: bool = True
116
+ """Whether the component is healthy."""
117
+
118
+ reason: str | None = None
119
+ """Optional reason for the health state (especially for unhealthy)."""
120
+
121
+
122
+ # =============================================================================
123
+ # Self-Healing Events
124
+ # =============================================================================
125
+
126
+
127
+ @dataclass(frozen=True, slots=True)
128
+ class SelfHealingTriggeredEvent(Event):
129
+ """
130
+ Emitted when self-healing coordinator reacts to a circuit breaker event.
131
+
132
+ Key is interface_id.
133
+
134
+ This event allows external observers (like MetricsAggregator) to track
135
+ self-healing activity without the coordinator maintaining internal stats.
136
+ """
137
+
138
+ interface_id: str
139
+ action: str # "trip_logged", "recovery_initiated", "data_refresh_scheduled"
140
+ details: str | None
141
+
142
+ @property
143
+ def key(self) -> str:
144
+ """Return interface_id as key."""
145
+ return self.interface_id
146
+
147
+
148
+ @dataclass(frozen=True, slots=True)
149
+ class SelfHealingDataRefreshEvent(Event):
150
+ """
151
+ Emitted after self-healing data refresh completes.
152
+
153
+ Key is interface_id.
154
+
155
+ Allows subscribers to track data refresh success/failure.
156
+ """
157
+
158
+ interface_id: str
159
+ success: bool
160
+ error_message: str | None
161
+
162
+ @property
163
+ def key(self) -> str:
164
+ """Return interface_id as key."""
165
+ return self.interface_id
166
+
167
+
168
+ # Type alias for any metric event
169
+ AnyMetricEvent = LatencyMetricEvent | CounterMetricEvent | GaugeMetricEvent | HealthMetricEvent
170
+
171
+ # All metric event types for subscription
172
+ METRIC_EVENT_TYPES: Final = (
173
+ LatencyMetricEvent,
174
+ CounterMetricEvent,
175
+ GaugeMetricEvent,
176
+ HealthMetricEvent,
177
+ )
178
+
179
+ # Self-healing event types for subscription
180
+ SELF_HEALING_EVENT_TYPES: Final = (
181
+ SelfHealingTriggeredEvent,
182
+ SelfHealingDataRefreshEvent,
183
+ )