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,144 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2021-2026
3
+ """
4
+ Firmware handler.
5
+
6
+ Handles device and system firmware update operations.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import logging
12
+ from typing import TYPE_CHECKING, Final
13
+
14
+ from aiohomematic import i18n
15
+ from aiohomematic.client.handlers.base import BaseHandler
16
+ from aiohomematic.const import ProductGroup
17
+ from aiohomematic.decorators import inspector
18
+ from aiohomematic.exceptions import BaseHomematicException, ClientException
19
+ from aiohomematic.interfaces import FirmwareOperationsProtocol
20
+ from aiohomematic.property_decorators import DelegatedProperty
21
+ from aiohomematic.support import extract_exc_args
22
+
23
+ if TYPE_CHECKING:
24
+ from aiohomematic.client import AioJsonRpcAioHttpClient, BaseRpcProxy
25
+ from aiohomematic.const import Interface
26
+ from aiohomematic.interfaces import ClientDependenciesProtocol
27
+
28
+ _LOGGER: Final = logging.getLogger(__name__)
29
+
30
+
31
+ class FirmwareHandler(BaseHandler, FirmwareOperationsProtocol):
32
+ """
33
+ Handler for firmware update operations.
34
+
35
+ Implements FirmwareOperationsProtocol protocol for ISP-compliant client operations.
36
+
37
+ Handles:
38
+ - Updating device firmware
39
+ - Triggering system firmware updates
40
+ """
41
+
42
+ __slots__ = (
43
+ "_has_device_firmware_update",
44
+ "_has_firmware_update_trigger",
45
+ )
46
+
47
+ def __init__(
48
+ self,
49
+ *,
50
+ client_deps: ClientDependenciesProtocol,
51
+ interface: Interface,
52
+ interface_id: str,
53
+ json_rpc_client: AioJsonRpcAioHttpClient,
54
+ proxy: BaseRpcProxy,
55
+ proxy_read: BaseRpcProxy,
56
+ has_device_firmware_update: bool,
57
+ has_firmware_update_trigger: bool,
58
+ ) -> None:
59
+ """Initialize the firmware handler."""
60
+ super().__init__(
61
+ client_deps=client_deps,
62
+ interface=interface,
63
+ interface_id=interface_id,
64
+ json_rpc_client=json_rpc_client,
65
+ proxy=proxy,
66
+ proxy_read=proxy_read,
67
+ )
68
+ self._has_device_firmware_update: Final = has_device_firmware_update
69
+ self._has_firmware_update_trigger: Final = has_firmware_update_trigger
70
+
71
+ has_device_firmware_update: Final = DelegatedProperty[bool](path="_has_device_firmware_update")
72
+ has_firmware_update_trigger: Final = DelegatedProperty[bool](path="_has_firmware_update_trigger")
73
+
74
+ @inspector(re_raise=False)
75
+ async def trigger_firmware_update(self) -> bool:
76
+ """
77
+ Trigger the CCU system firmware update process.
78
+
79
+ Initiates the CCU's own firmware update (not device firmware). The CCU
80
+ will download and install available system updates.
81
+
82
+ Returns:
83
+ True if the update was triggered successfully, False if unsupported.
84
+
85
+ """
86
+ if not self._has_firmware_update_trigger:
87
+ _LOGGER.debug("TRIGGER_FIRMWARE_UPDATE: Not supported by client for %s", self._interface_id)
88
+ return False
89
+
90
+ return await self._json_rpc_client.trigger_firmware_update()
91
+
92
+ @inspector
93
+ async def update_device_firmware(self, *, device_address: str) -> bool:
94
+ """
95
+ Update firmware on a single Homematic device.
96
+
97
+ Uses installFirmware() for HmIP/HmIPW devices and updateFirmware() for
98
+ BidCos devices. The device must have a firmware update available.
99
+
100
+ Args:
101
+ device_address: Device address (e.g., "VCU0000001").
102
+
103
+ Returns:
104
+ True if the update was initiated successfully, False if device not
105
+ found or update unsupported.
106
+
107
+ Raises:
108
+ ClientException: If the RPC call fails.
109
+
110
+ """
111
+ if not self._has_device_firmware_update:
112
+ _LOGGER.debug("UPDATE_DEVICE_FIRMWARE: Not supported by client for %s", self._interface_id)
113
+ return False
114
+
115
+ if device := self._client_deps.device_coordinator.get_device(address=device_address):
116
+ _LOGGER.info(
117
+ i18n.tr(
118
+ key="log.client.update_device_firmware.try",
119
+ device_address=device_address,
120
+ )
121
+ )
122
+ try:
123
+ update_result = (
124
+ await self._proxy.installFirmware(device_address)
125
+ if device.product_group in (ProductGroup.HMIPW, ProductGroup.HMIP)
126
+ else await self._proxy.updateFirmware(device_address)
127
+ )
128
+ result = bool(update_result) if isinstance(update_result, bool) else bool(update_result[0])
129
+ _LOGGER.info(
130
+ i18n.tr(
131
+ key="log.client.update_device_firmware.result",
132
+ device_address=device_address,
133
+ result=("success" if result else "failed"),
134
+ )
135
+ )
136
+ except BaseHomematicException as bhexc:
137
+ raise ClientException(
138
+ i18n.tr(
139
+ key="exception.client.update_device_firmware.failed",
140
+ reason=extract_exc_args(exc=bhexc),
141
+ )
142
+ ) from bhexc
143
+ return result
144
+ return False
@@ -0,0 +1,199 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2021-2026
3
+ """
4
+ Link management handler.
5
+
6
+ Handles device linking operations.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import logging
12
+ from typing import TYPE_CHECKING, Any, Final, cast
13
+
14
+ from aiohomematic import i18n
15
+ from aiohomematic.client.handlers.base import BaseHandler
16
+ from aiohomematic.decorators import inspector
17
+ from aiohomematic.exceptions import BaseHomematicException, ClientException
18
+ from aiohomematic.interfaces import LinkOperationsProtocol
19
+ from aiohomematic.property_decorators import DelegatedProperty
20
+ from aiohomematic.support import extract_exc_args
21
+
22
+ if TYPE_CHECKING:
23
+ from aiohomematic.client import AioJsonRpcAioHttpClient, BaseRpcProxy
24
+ from aiohomematic.const import Interface
25
+ from aiohomematic.interfaces import ClientDependenciesProtocol
26
+
27
+ _LOGGER: Final = logging.getLogger(__name__)
28
+
29
+
30
+ class LinkHandler(BaseHandler, LinkOperationsProtocol):
31
+ """
32
+ Handler for device linking operations.
33
+
34
+ Implements LinkOperationsProtocol protocol for ISP-compliant client operations.
35
+
36
+ Handles:
37
+ - Adding links between devices
38
+ - Removing links between devices
39
+ - Querying link information and peers
40
+ """
41
+
42
+ __slots__ = ("_has_linking",)
43
+
44
+ def __init__(
45
+ self,
46
+ *,
47
+ client_deps: ClientDependenciesProtocol,
48
+ interface: Interface,
49
+ interface_id: str,
50
+ json_rpc_client: AioJsonRpcAioHttpClient,
51
+ proxy: BaseRpcProxy,
52
+ proxy_read: BaseRpcProxy,
53
+ has_linking: bool,
54
+ ) -> None:
55
+ """Initialize the link management handler."""
56
+ super().__init__(
57
+ client_deps=client_deps,
58
+ interface=interface,
59
+ interface_id=interface_id,
60
+ json_rpc_client=json_rpc_client,
61
+ proxy=proxy,
62
+ proxy_read=proxy_read,
63
+ )
64
+ self._has_linking: Final = has_linking
65
+
66
+ has_linking: Final = DelegatedProperty[bool](path="_has_linking")
67
+
68
+ @inspector
69
+ async def add_link(
70
+ self,
71
+ *,
72
+ sender_address: str,
73
+ receiver_address: str,
74
+ name: str,
75
+ description: str,
76
+ ) -> None:
77
+ """
78
+ Create a direct link between two device channels.
79
+
80
+ Direct links allow devices to communicate without the CCU. The sender
81
+ triggers actions on the receiver (e.g., a button press turning on a light).
82
+
83
+ Args:
84
+ sender_address: Source channel address (e.g., "VCU0000001:1").
85
+ receiver_address: Target channel address (e.g., "VCU0000002:1").
86
+ name: User-defined link name.
87
+ description: User-defined link description.
88
+
89
+ Raises:
90
+ ClientException: If the RPC call fails.
91
+
92
+ """
93
+ if not self._has_linking:
94
+ _LOGGER.debug("ADD_LINK: Not supported by client for %s", self._interface_id)
95
+ return
96
+
97
+ try:
98
+ await self._proxy.addLink(sender_address, receiver_address, name, description)
99
+ except BaseHomematicException as bhexc:
100
+ raise ClientException(
101
+ i18n.tr(
102
+ key="exception.client.add_link.failed",
103
+ sender=sender_address,
104
+ receiver=receiver_address,
105
+ name=name,
106
+ description=description,
107
+ reason=extract_exc_args(exc=bhexc),
108
+ )
109
+ ) from bhexc
110
+
111
+ @inspector
112
+ async def get_link_peers(self, *, address: str) -> tuple[str, ...]:
113
+ """
114
+ Return addresses of all channels linked to the given address.
115
+
116
+ Args:
117
+ address: Channel address to query (e.g., "VCU0000001:1").
118
+
119
+ Returns:
120
+ Tuple of peer channel addresses that are linked to this channel.
121
+
122
+ Raises:
123
+ ClientException: If the RPC call fails.
124
+
125
+ """
126
+ if not self._has_linking:
127
+ _LOGGER.debug("GET_LINK_PEERS: Not supported by client for %s", self._interface_id)
128
+ return ()
129
+
130
+ try:
131
+ return tuple(links) if (links := await self._proxy.getLinkPeers(address)) else ()
132
+ except BaseHomematicException as bhexc:
133
+ raise ClientException(
134
+ i18n.tr(
135
+ key="exception.client.get_link_peers.failed",
136
+ address=address,
137
+ reason=extract_exc_args(exc=bhexc),
138
+ )
139
+ ) from bhexc
140
+
141
+ @inspector
142
+ async def get_links(self, *, address: str, flags: int) -> dict[str, Any]:
143
+ """
144
+ Return detailed link information for a channel.
145
+
146
+ Args:
147
+ address: Channel address to query (e.g., "VCU0000001:1").
148
+ flags: Bitmask controlling returned information (backend-specific).
149
+
150
+ Returns:
151
+ Dict containing link details including sender, receiver, and paramsets.
152
+
153
+ Raises:
154
+ ClientException: If the RPC call fails.
155
+
156
+ """
157
+ if not self._has_linking:
158
+ _LOGGER.debug("GET_LINKS: Not supported by client for %s", self._interface_id)
159
+ return {}
160
+
161
+ try:
162
+ return cast(dict[str, Any], await self._proxy.getLinks(address, flags))
163
+ except BaseHomematicException as bhexc:
164
+ raise ClientException(
165
+ i18n.tr(
166
+ key="exception.client.get_links.failed",
167
+ address=address,
168
+ reason=extract_exc_args(exc=bhexc),
169
+ )
170
+ ) from bhexc
171
+
172
+ @inspector
173
+ async def remove_link(self, *, sender_address: str, receiver_address: str) -> None:
174
+ """
175
+ Remove a direct link between two device channels.
176
+
177
+ Args:
178
+ sender_address: Source channel address (e.g., "VCU0000001:1").
179
+ receiver_address: Target channel address (e.g., "VCU0000002:1").
180
+
181
+ Raises:
182
+ ClientException: If the RPC call fails.
183
+
184
+ """
185
+ if not self._has_linking:
186
+ _LOGGER.debug("REMOVE_LINK: Not supported by client for %s", self._interface_id)
187
+ return
188
+
189
+ try:
190
+ await self._proxy.removeLink(sender_address, receiver_address)
191
+ except BaseHomematicException as bhexc:
192
+ raise ClientException(
193
+ i18n.tr(
194
+ key="exception.client.remove_link.failed",
195
+ sender=sender_address,
196
+ receiver=receiver_address,
197
+ reason=extract_exc_args(exc=bhexc),
198
+ )
199
+ ) from bhexc