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,100 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2021-2026
3
+ """
4
+ Hub (backend) data points for AioHomematic.
5
+
6
+ This package reflects the state and capabilities of the backend at the hub
7
+ level. It exposes backend programs, system variables, install mode, metrics,
8
+ and system updates as data points that can be observed and acted upon by
9
+ higher layers (e.g., Home Assistant integration).
10
+
11
+ Package structure
12
+ -----------------
13
+ - hub.py: Hub orchestrator class and container types (ProgramDpType, MetricsDpType)
14
+ - data_point.py: Base classes (GenericHubDataPoint, GenericProgramDataPoint, GenericSysvarDataPoint)
15
+ - button.py, switch.py: Program data points (ProgramDpButton, ProgramDpSwitch)
16
+ - sensor.py, binary_sensor.py, select.py, number.py, text.py: Sysvar data points
17
+ - install_mode.py: Install mode button and sensor
18
+ - metrics.py: System health, connection latency, event age sensors
19
+ - inbox.py: Inbox device sensor
20
+ - update.py: System update sensor
21
+
22
+ Public API
23
+ ----------
24
+ - Hub: Main orchestrator for hub-level data point lifecycle
25
+ - Program data points: ProgramDpButton, ProgramDpSwitch, ProgramDpType
26
+ - Sysvar data points: SysvarDpSensor, SysvarDpBinarySensor, SysvarDpSelect,
27
+ SysvarDpNumber, SysvarDpSwitch, SysvarDpText
28
+ - Install mode: InstallModeDpButton, InstallModeDpSensor, InstallModeDpType
29
+ - Metrics: HmSystemHealthSensor, HmConnectionLatencySensor, HmLastEventAgeSensor, MetricsDpType
30
+ - Other: HmInboxSensor, HmUpdate
31
+ - Base types: GenericHubDataPoint, GenericProgramDataPoint, GenericSysvarDataPoint
32
+
33
+ Sysvar type mapping
34
+ -------------------
35
+ System variables are mapped to data point types based on SysvarType:
36
+ - ALARM/LOGIC → SysvarDpBinarySensor (or SysvarDpSwitch if extended)
37
+ - LIST (extended) → SysvarDpSelect
38
+ - FLOAT/INTEGER (extended) → SysvarDpNumber
39
+ - STRING (extended) → SysvarDpText
40
+ - Other → SysvarDpSensor
41
+
42
+ Related modules
43
+ ---------------
44
+ - aiohomematic.central: Central unit coordination
45
+ - aiohomematic.const: HUB_CATEGORIES, SystemEventType.HUB_REFRESHED
46
+
47
+ """
48
+
49
+ from __future__ import annotations
50
+
51
+ from aiohomematic.model.hub.binary_sensor import SysvarDpBinarySensor
52
+ from aiohomematic.model.hub.button import ProgramDpButton
53
+ from aiohomematic.model.hub.connectivity import HmInterfaceConnectivitySensor
54
+ from aiohomematic.model.hub.data_point import GenericHubDataPoint, GenericProgramDataPoint, GenericSysvarDataPoint
55
+ from aiohomematic.model.hub.hub import ConnectivityDpType, Hub, MetricsDpType, ProgramDpType
56
+ from aiohomematic.model.hub.inbox import HmInboxSensor
57
+ from aiohomematic.model.hub.install_mode import InstallModeDpButton, InstallModeDpSensor, InstallModeDpType
58
+ from aiohomematic.model.hub.metrics import HmConnectionLatencySensor, HmLastEventAgeSensor, HmSystemHealthSensor
59
+ from aiohomematic.model.hub.number import SysvarDpNumber
60
+ from aiohomematic.model.hub.select import SysvarDpSelect
61
+ from aiohomematic.model.hub.sensor import SysvarDpSensor
62
+ from aiohomematic.model.hub.switch import ProgramDpSwitch, SysvarDpSwitch
63
+ from aiohomematic.model.hub.text import SysvarDpText
64
+ from aiohomematic.model.hub.update import HmUpdate
65
+
66
+ __all__ = [
67
+ # Base
68
+ "GenericHubDataPoint",
69
+ "GenericProgramDataPoint",
70
+ "GenericSysvarDataPoint",
71
+ # Connectivity
72
+ "ConnectivityDpType",
73
+ "HmInterfaceConnectivitySensor",
74
+ # Hub
75
+ "Hub",
76
+ # Inbox
77
+ "HmInboxSensor",
78
+ # Install mode
79
+ "InstallModeDpButton",
80
+ "InstallModeDpSensor",
81
+ "InstallModeDpType",
82
+ # Metrics
83
+ "HmConnectionLatencySensor",
84
+ "HmLastEventAgeSensor",
85
+ "HmSystemHealthSensor",
86
+ "MetricsDpType",
87
+ # Program
88
+ "ProgramDpButton",
89
+ "ProgramDpSwitch",
90
+ "ProgramDpType",
91
+ # Sysvar
92
+ "SysvarDpBinarySensor",
93
+ "SysvarDpNumber",
94
+ "SysvarDpSelect",
95
+ "SysvarDpSensor",
96
+ "SysvarDpSwitch",
97
+ "SysvarDpText",
98
+ # Update
99
+ "HmUpdate",
100
+ ]
@@ -0,0 +1,24 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2021-2026
3
+ """Module for hub data points implemented using the binary_sensor category."""
4
+
5
+ from __future__ import annotations
6
+
7
+ from aiohomematic.const import DataPointCategory
8
+ from aiohomematic.model.hub.data_point import GenericSysvarDataPoint
9
+ from aiohomematic.property_decorators import state_property
10
+
11
+
12
+ class SysvarDpBinarySensor(GenericSysvarDataPoint):
13
+ """Implementation of a sysvar binary_sensor."""
14
+
15
+ __slots__ = ()
16
+
17
+ _category = DataPointCategory.HUB_BINARY_SENSOR
18
+
19
+ @state_property
20
+ def value(self) -> bool | None:
21
+ """Return the value of the data_point."""
22
+ if self._value is not None:
23
+ return bool(self._value)
24
+ return None
@@ -0,0 +1,28 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2021-2026
3
+ """Module for hub data points implemented using the button category."""
4
+
5
+ from __future__ import annotations
6
+
7
+ from aiohomematic.const import DataPointCategory
8
+ from aiohomematic.decorators import inspector
9
+ from aiohomematic.model.hub.data_point import GenericProgramDataPoint
10
+ from aiohomematic.property_decorators import state_property
11
+
12
+
13
+ class ProgramDpButton(GenericProgramDataPoint):
14
+ """Class for a Homematic program button."""
15
+
16
+ __slots__ = ()
17
+
18
+ _category = DataPointCategory.HUB_BUTTON
19
+
20
+ @state_property
21
+ def available(self) -> bool:
22
+ """Return the availability of the device."""
23
+ return self._is_active and self._central_info.available
24
+
25
+ @inspector
26
+ async def press(self) -> None:
27
+ """Handle the button press."""
28
+ await self._hub_data_fetcher.execute_program(pid=self.pid)
@@ -0,0 +1,190 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2021-2026
3
+ """
4
+ Hub sensors for interface connectivity status.
5
+
6
+ This module provides binary sensor data points for monitoring the
7
+ connectivity status of each CCU interface. These sensors show whether
8
+ each interface is connected and operational.
9
+
10
+ Public API
11
+ ----------
12
+ - HmInterfaceConnectivitySensor: Binary sensor showing interface connectivity
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from datetime import datetime
18
+ import logging
19
+ from typing import TYPE_CHECKING, Final
20
+
21
+ from slugify import slugify
22
+
23
+ from aiohomematic.const import CONNECTIVITY_SENSOR_PREFIX, HUB_ADDRESS, DataPointCategory, HubValueType, Interface
24
+ from aiohomematic.interfaces import (
25
+ CentralInfoProtocol,
26
+ ChannelProtocol,
27
+ ConfigProviderProtocol,
28
+ EventBusProviderProtocol,
29
+ EventPublisherProtocol,
30
+ HealthTrackerProtocol,
31
+ HubBinarySensorDataPointProtocol,
32
+ ParameterVisibilityProviderProtocol,
33
+ ParamsetDescriptionProviderProtocol,
34
+ TaskSchedulerProtocol,
35
+ )
36
+ from aiohomematic.model.data_point import CallbackDataPoint
37
+ from aiohomematic.model.support import HubPathData, PathData, generate_unique_id, get_hub_data_point_name_data
38
+ from aiohomematic.property_decorators import DelegatedProperty, Kind, state_property
39
+ from aiohomematic.support import PayloadMixin
40
+
41
+ if TYPE_CHECKING:
42
+ pass
43
+
44
+ _LOGGER: Final = logging.getLogger(__name__)
45
+
46
+
47
+ class HmInterfaceConnectivitySensor(CallbackDataPoint, HubBinarySensorDataPointProtocol, PayloadMixin):
48
+ """
49
+ Binary sensor showing interface connectivity status.
50
+
51
+ This sensor provides a clear indication of whether a specific
52
+ interface (e.g., HmIP-RF, BidCos-RF) is connected and operational.
53
+
54
+ States:
55
+ - True (ON): Interface is connected and circuit breakers are closed
56
+ - False (OFF): Interface is disconnected, failed, or degraded
57
+
58
+ The sensor is always available (never shows unavailable) since its
59
+ purpose is to show the connection state itself.
60
+ """
61
+
62
+ __slots__ = (
63
+ "_cached_value",
64
+ "_health_tracker",
65
+ "_interface",
66
+ "_interface_id",
67
+ "_name_data",
68
+ "_state_uncertain",
69
+ )
70
+
71
+ _category = DataPointCategory.HUB_BINARY_SENSOR
72
+ _enabled_default = True
73
+
74
+ def __init__(
75
+ self,
76
+ *,
77
+ interface_id: str,
78
+ interface: Interface,
79
+ health_tracker: HealthTrackerProtocol,
80
+ config_provider: ConfigProviderProtocol,
81
+ central_info: CentralInfoProtocol,
82
+ event_bus_provider: EventBusProviderProtocol,
83
+ event_publisher: EventPublisherProtocol,
84
+ task_scheduler: TaskSchedulerProtocol,
85
+ paramset_description_provider: ParamsetDescriptionProviderProtocol,
86
+ parameter_visibility_provider: ParameterVisibilityProviderProtocol,
87
+ ) -> None:
88
+ """Initialize the connectivity sensor."""
89
+ PayloadMixin.__init__(self)
90
+ self._interface_id: Final = interface_id
91
+ self._interface: Final = interface
92
+ self._health_tracker: Final = health_tracker
93
+
94
+ # Create unique ID and name
95
+ sensor_name = f"{CONNECTIVITY_SENSOR_PREFIX} {interface.value}"
96
+ unique_id: Final = generate_unique_id(
97
+ config_provider=config_provider,
98
+ address=HUB_ADDRESS,
99
+ parameter=slugify(f"connectivity_{interface_id}"),
100
+ )
101
+ self._name_data: Final = get_hub_data_point_name_data(
102
+ channel=None, legacy_name=sensor_name, central_name=central_info.name
103
+ )
104
+
105
+ super().__init__(
106
+ unique_id=unique_id,
107
+ central_info=central_info,
108
+ event_bus_provider=event_bus_provider,
109
+ event_publisher=event_publisher,
110
+ task_scheduler=task_scheduler,
111
+ paramset_description_provider=paramset_description_provider,
112
+ parameter_visibility_provider=parameter_visibility_provider,
113
+ )
114
+ self._state_uncertain: bool = True
115
+ self._cached_value: bool = False
116
+
117
+ enabled_default: Final = DelegatedProperty[bool](path="_enabled_default")
118
+ full_name: Final = DelegatedProperty[str](path="_name_data.full_name")
119
+ name: Final = DelegatedProperty[str](path="_name_data.name", kind=Kind.CONFIG)
120
+ state_uncertain: Final = DelegatedProperty[bool](path="_state_uncertain")
121
+
122
+ @property
123
+ def available(self) -> bool:
124
+ """
125
+ Return True - connectivity sensor is always available.
126
+
127
+ The sensor itself shows the connection state, so it should
128
+ never be marked unavailable.
129
+ """
130
+ return True
131
+
132
+ @property
133
+ def channel(self) -> ChannelProtocol | None:
134
+ """Return the identified channel."""
135
+ return None
136
+
137
+ @property
138
+ def data_type(self) -> HubValueType | None:
139
+ """Return the data type of the sensor."""
140
+ return HubValueType.LOGIC
141
+
142
+ @property
143
+ def description(self) -> str | None:
144
+ """Return data point description."""
145
+ return f"Connectivity status for {self._interface.value} interface"
146
+
147
+ @property
148
+ def interface(self) -> Interface:
149
+ """Return the interface type."""
150
+ return self._interface
151
+
152
+ @property
153
+ def interface_id(self) -> str:
154
+ """Return the interface ID."""
155
+ return self._interface_id
156
+
157
+ @property
158
+ def legacy_name(self) -> str | None:
159
+ """Return the original name."""
160
+ return None
161
+
162
+ @state_property
163
+ def value(self) -> bool:
164
+ """Return True if interface is connected."""
165
+ return self._get_current_value()
166
+
167
+ def refresh(self, *, write_at: datetime) -> None:
168
+ """Refresh the sensor value from health tracker."""
169
+ current_value = self._get_current_value()
170
+ if self._cached_value != current_value:
171
+ self._cached_value = current_value
172
+ self._set_modified_at(modified_at=write_at)
173
+ else:
174
+ self._set_refreshed_at(refreshed_at=write_at)
175
+ self._state_uncertain = False
176
+ self.publish_data_point_updated_event()
177
+
178
+ def _get_current_value(self) -> bool:
179
+ """Return the current connectivity value."""
180
+ if (health := self._health_tracker.get_client_health(interface_id=self._interface_id)) is not None:
181
+ return health.is_available
182
+ return False
183
+
184
+ def _get_path_data(self) -> PathData:
185
+ """Return the path data of the data_point."""
186
+ return HubPathData(name=slugify(f"connectivity_{self._interface.value}"))
187
+
188
+ def _get_signature(self) -> str:
189
+ """Return the signature of the data_point."""
190
+ return f"{self._category}/{self.name}"
@@ -0,0 +1,342 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2021-2026
3
+ """Module for AioHomematic hub data points."""
4
+
5
+ from __future__ import annotations
6
+
7
+ from datetime import datetime
8
+ from typing import Any, Final
9
+
10
+ from slugify import slugify
11
+
12
+ from aiohomematic.const import (
13
+ PROGRAM_ADDRESS,
14
+ SYSVAR_ADDRESS,
15
+ SYSVAR_TYPE,
16
+ HubData,
17
+ HubValueType,
18
+ ProgramData,
19
+ SystemVariableData,
20
+ )
21
+ from aiohomematic.decorators import inspector
22
+ from aiohomematic.interfaces import (
23
+ CentralInfoProtocol,
24
+ ChannelLookupProtocol,
25
+ ChannelProtocol,
26
+ ConfigProviderProtocol,
27
+ EventBusProviderProtocol,
28
+ EventPublisherProtocol,
29
+ GenericHubDataPointProtocol,
30
+ GenericProgramDataPointProtocol,
31
+ GenericSysvarDataPointProtocol,
32
+ HubDataFetcherProtocol,
33
+ ParameterVisibilityProviderProtocol,
34
+ ParamsetDescriptionProviderProtocol,
35
+ PrimaryClientProviderProtocol,
36
+ TaskSchedulerProtocol,
37
+ )
38
+ from aiohomematic.model.data_point import CallbackDataPoint
39
+ from aiohomematic.model.support import (
40
+ PathData,
41
+ ProgramPathData,
42
+ SysvarPathData,
43
+ generate_unique_id,
44
+ get_hub_data_point_name_data,
45
+ )
46
+ from aiohomematic.property_decorators import DelegatedProperty, Kind, state_property
47
+ from aiohomematic.support import PayloadMixin, parse_sys_var
48
+
49
+
50
+ class GenericHubDataPoint(CallbackDataPoint, GenericHubDataPointProtocol, PayloadMixin):
51
+ """Class for a Homematic system variable."""
52
+
53
+ __slots__ = (
54
+ "_channel",
55
+ "_description",
56
+ "_enabled_default",
57
+ "_legacy_name",
58
+ "_name_data",
59
+ "_primary_client_provider",
60
+ "_state_uncertain",
61
+ )
62
+
63
+ def __init__(
64
+ self,
65
+ *,
66
+ config_provider: ConfigProviderProtocol,
67
+ central_info: CentralInfoProtocol,
68
+ event_bus_provider: EventBusProviderProtocol,
69
+ event_publisher: EventPublisherProtocol,
70
+ task_scheduler: TaskSchedulerProtocol,
71
+ paramset_description_provider: ParamsetDescriptionProviderProtocol,
72
+ parameter_visibility_provider: ParameterVisibilityProviderProtocol,
73
+ channel_lookup: ChannelLookupProtocol,
74
+ primary_client_provider: PrimaryClientProviderProtocol,
75
+ address: str,
76
+ data: HubData,
77
+ ) -> None:
78
+ """Initialize the data_point."""
79
+ PayloadMixin.__init__(self)
80
+ unique_id: Final = generate_unique_id(
81
+ config_provider=config_provider,
82
+ address=address,
83
+ parameter=slugify(data.legacy_name),
84
+ )
85
+ self._legacy_name = data.legacy_name
86
+ self._channel = channel_lookup.identify_channel(text=data.legacy_name)
87
+ self._name_data: Final = get_hub_data_point_name_data(
88
+ channel=self._channel, legacy_name=data.legacy_name, central_name=central_info.name
89
+ )
90
+ self._description = data.description
91
+ super().__init__(
92
+ unique_id=unique_id,
93
+ central_info=central_info,
94
+ event_bus_provider=event_bus_provider,
95
+ event_publisher=event_publisher,
96
+ task_scheduler=task_scheduler,
97
+ paramset_description_provider=paramset_description_provider,
98
+ parameter_visibility_provider=parameter_visibility_provider,
99
+ )
100
+ self._enabled_default: Final = data.enabled_default
101
+ self._state_uncertain: bool = True
102
+ self._primary_client_provider: Final = primary_client_provider
103
+
104
+ channel: Final = DelegatedProperty[ChannelProtocol | None](path="_channel")
105
+ description: Final = DelegatedProperty[str | None](path="_description", kind=Kind.CONFIG)
106
+ enabled_default: Final = DelegatedProperty[bool](path="_enabled_default")
107
+ full_name: Final = DelegatedProperty[str](path="_name_data.full_name")
108
+ legacy_name: Final = DelegatedProperty[str | None](path="_legacy_name")
109
+ name: Final = DelegatedProperty[str](path="_name_data.name", kind=Kind.CONFIG)
110
+ state_uncertain: Final = DelegatedProperty[bool](path="_state_uncertain")
111
+
112
+ @state_property
113
+ def available(self) -> bool:
114
+ """Return the availability of the device."""
115
+ return self._central_info.available
116
+
117
+ def _get_signature(self) -> str:
118
+ """Return the signature of the data_point."""
119
+ return f"{self._category}/{self.name}"
120
+
121
+
122
+ class GenericSysvarDataPoint(GenericHubDataPoint, GenericSysvarDataPointProtocol):
123
+ """Class for a Homematic system variable."""
124
+
125
+ __slots__ = (
126
+ "_current_value",
127
+ "_data_type",
128
+ "_max",
129
+ "_min",
130
+ "_previous_value",
131
+ "_temporary_value",
132
+ "_unit",
133
+ "_values",
134
+ "_vid",
135
+ )
136
+
137
+ _is_extended = False
138
+
139
+ def __init__(
140
+ self,
141
+ *,
142
+ config_provider: ConfigProviderProtocol,
143
+ central_info: CentralInfoProtocol,
144
+ event_bus_provider: EventBusProviderProtocol,
145
+ event_publisher: EventPublisherProtocol,
146
+ task_scheduler: TaskSchedulerProtocol,
147
+ paramset_description_provider: ParamsetDescriptionProviderProtocol,
148
+ parameter_visibility_provider: ParameterVisibilityProviderProtocol,
149
+ channel_lookup: ChannelLookupProtocol,
150
+ primary_client_provider: PrimaryClientProviderProtocol,
151
+ data: SystemVariableData,
152
+ ) -> None:
153
+ """Initialize the data_point."""
154
+ self._vid: Final = data.vid
155
+ super().__init__(
156
+ config_provider=config_provider,
157
+ central_info=central_info,
158
+ event_bus_provider=event_bus_provider,
159
+ event_publisher=event_publisher,
160
+ task_scheduler=task_scheduler,
161
+ paramset_description_provider=paramset_description_provider,
162
+ parameter_visibility_provider=parameter_visibility_provider,
163
+ channel_lookup=channel_lookup,
164
+ primary_client_provider=primary_client_provider,
165
+ address=SYSVAR_ADDRESS,
166
+ data=data,
167
+ )
168
+ self._data_type = data.data_type
169
+ self._values: Final[tuple[str, ...] | None] = tuple(data.values) if data.values else None
170
+ self._max: Final = data.max_value
171
+ self._min: Final = data.min_value
172
+ self._unit: Final = data.unit
173
+ self._current_value: SYSVAR_TYPE = data.value
174
+ self._previous_value: SYSVAR_TYPE = None
175
+ self._temporary_value: SYSVAR_TYPE = None
176
+
177
+ is_extended: Final = DelegatedProperty[bool](path="_is_extended")
178
+ max: Final = DelegatedProperty[float | int | None](path="_max", kind=Kind.CONFIG)
179
+ min: Final = DelegatedProperty[float | int | None](path="_min", kind=Kind.CONFIG)
180
+ previous_value: Final = DelegatedProperty[SYSVAR_TYPE](path="_previous_value")
181
+ unit: Final = DelegatedProperty[str | None](path="_unit", kind=Kind.CONFIG)
182
+ values: Final = DelegatedProperty[tuple[str, ...] | None](path="_values", kind=Kind.STATE)
183
+ vid: Final = DelegatedProperty[str](path="_vid", kind=Kind.CONFIG)
184
+
185
+ @property
186
+ def _value(self) -> Any | None:
187
+ """Return the value."""
188
+ return self._temporary_value if self._temporary_refreshed_at > self._refreshed_at else self._current_value
189
+
190
+ @property
191
+ def data_type(self) -> HubValueType | None:
192
+ """Return the data type."""
193
+ return self._data_type
194
+
195
+ @data_type.setter
196
+ def data_type(self, data_type: HubValueType) -> None:
197
+ """Write data_type."""
198
+ self._data_type = data_type
199
+
200
+ @state_property
201
+ def value(self) -> Any | None:
202
+ """Return the value."""
203
+ return self._value
204
+
205
+ async def event(self, *, value: Any, received_at: datetime) -> None:
206
+ """Handle event for which this data_point has subscribed."""
207
+ self.write_value(value=value, write_at=received_at)
208
+
209
+ @inspector
210
+ async def send_variable(self, *, value: Any) -> None:
211
+ """Set variable value on the backend."""
212
+ if client := self._primary_client_provider.primary_client:
213
+ await client.set_system_variable(
214
+ legacy_name=self._legacy_name, value=parse_sys_var(data_type=self._data_type, raw_value=value)
215
+ )
216
+ self._write_temporary_value(value=value, write_at=datetime.now())
217
+
218
+ def write_value(self, *, value: Any, write_at: datetime) -> None:
219
+ """Set variable value on the backend."""
220
+ self._reset_temporary_value()
221
+
222
+ old_value = self._current_value
223
+ new_value = self._convert_value(old_value=old_value, new_value=value)
224
+ if old_value == new_value:
225
+ self._set_refreshed_at(refreshed_at=write_at)
226
+ else:
227
+ self._set_modified_at(modified_at=write_at)
228
+ self._previous_value = old_value
229
+ self._current_value = new_value
230
+ self._state_uncertain = False
231
+ self.publish_data_point_updated_event()
232
+
233
+ def _convert_value(self, *, old_value: Any, new_value: Any) -> Any:
234
+ """Convert to value to SYSVAR_TYPE."""
235
+ if new_value is None:
236
+ return None
237
+ value = new_value
238
+ if self._data_type:
239
+ value = parse_sys_var(data_type=self._data_type, raw_value=new_value)
240
+ elif isinstance(old_value, bool):
241
+ value = bool(new_value)
242
+ elif isinstance(old_value, int):
243
+ value = int(new_value)
244
+ elif isinstance(old_value, str):
245
+ value = str(new_value)
246
+ elif isinstance(old_value, float):
247
+ value = float(new_value)
248
+ return value
249
+
250
+ def _get_path_data(self) -> PathData:
251
+ """Return the path data of the data_point."""
252
+ return SysvarPathData(vid=self._vid)
253
+
254
+ def _reset_temporary_value(self) -> None:
255
+ """Reset the temp storage."""
256
+ self._temporary_value = None
257
+ self._reset_temporary_timestamps()
258
+
259
+ def _write_temporary_value(self, *, value: Any, write_at: datetime) -> None:
260
+ """Update the temporary value of the data_point."""
261
+ self._reset_temporary_value()
262
+
263
+ temp_value = self._convert_value(old_value=self._current_value, new_value=value)
264
+ if self._value == temp_value:
265
+ self._set_temporary_refreshed_at(refreshed_at=write_at)
266
+ else:
267
+ self._set_temporary_modified_at(modified_at=write_at)
268
+ self._temporary_value = temp_value
269
+ self._state_uncertain = True
270
+ self.publish_data_point_updated_event()
271
+
272
+
273
+ class GenericProgramDataPoint(GenericHubDataPoint, GenericProgramDataPointProtocol):
274
+ """Class for a generic Homematic progran data point."""
275
+
276
+ __slots__ = (
277
+ "_hub_data_fetcher",
278
+ "_is_active",
279
+ "_is_internal",
280
+ "_last_execute_time",
281
+ "_pid",
282
+ )
283
+
284
+ def __init__(
285
+ self,
286
+ *,
287
+ config_provider: ConfigProviderProtocol,
288
+ central_info: CentralInfoProtocol,
289
+ event_bus_provider: EventBusProviderProtocol,
290
+ event_publisher: EventPublisherProtocol,
291
+ task_scheduler: TaskSchedulerProtocol,
292
+ paramset_description_provider: ParamsetDescriptionProviderProtocol,
293
+ parameter_visibility_provider: ParameterVisibilityProviderProtocol,
294
+ channel_lookup: ChannelLookupProtocol,
295
+ primary_client_provider: PrimaryClientProviderProtocol,
296
+ hub_data_fetcher: HubDataFetcherProtocol,
297
+ data: ProgramData,
298
+ ) -> None:
299
+ """Initialize the data_point."""
300
+ self._pid: Final = data.pid
301
+ super().__init__(
302
+ config_provider=config_provider,
303
+ central_info=central_info,
304
+ event_bus_provider=event_bus_provider,
305
+ event_publisher=event_publisher,
306
+ task_scheduler=task_scheduler,
307
+ paramset_description_provider=paramset_description_provider,
308
+ parameter_visibility_provider=parameter_visibility_provider,
309
+ channel_lookup=channel_lookup,
310
+ primary_client_provider=primary_client_provider,
311
+ address=PROGRAM_ADDRESS,
312
+ data=data,
313
+ )
314
+ self._is_active: bool = data.is_active
315
+ self._is_internal: bool = data.is_internal
316
+ self._last_execute_time: str = data.last_execute_time
317
+ self._state_uncertain: bool = True
318
+ self._hub_data_fetcher: Final = hub_data_fetcher
319
+
320
+ is_active: Final = DelegatedProperty[bool](path="_is_active", kind=Kind.STATE)
321
+ is_internal: Final = DelegatedProperty[bool](path="_is_internal", kind=Kind.CONFIG)
322
+ last_execute_time: Final = DelegatedProperty[str](path="_last_execute_time", kind=Kind.STATE)
323
+ pid: Final = DelegatedProperty[str](path="_pid", kind=Kind.CONFIG)
324
+
325
+ def update_data(self, *, data: ProgramData) -> None:
326
+ """Set variable value on the backend."""
327
+ do_update: bool = False
328
+ if self._is_active != data.is_active:
329
+ self._is_active = data.is_active
330
+ do_update = True
331
+ if self._is_internal != data.is_internal:
332
+ self._is_internal = data.is_internal
333
+ do_update = True
334
+ if self._last_execute_time != data.last_execute_time:
335
+ self._last_execute_time = data.last_execute_time
336
+ do_update = True
337
+ if do_update:
338
+ self.publish_data_point_updated_event()
339
+
340
+ def _get_path_data(self) -> PathData:
341
+ """Return the path data of the data_point."""
342
+ return ProgramPathData(pid=self.pid)