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,463 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2021-2026
3
+ """
4
+ Configuration classes for CentralUnit initialization.
5
+
6
+ This module provides CentralConfig for configuring and creating CentralUnit instances.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import asyncio
12
+ from collections.abc import Set as AbstractSet
13
+ from typing import TYPE_CHECKING, Any, Final
14
+
15
+ from aiohttp import ClientSession
16
+
17
+ from aiohomematic import client as hmcl, i18n
18
+ from aiohomematic.central.central_unit import CentralUnit
19
+ from aiohomematic.const import (
20
+ DEFAULT_DELAY_NEW_DEVICE_CREATION,
21
+ DEFAULT_ENABLE_DEVICE_FIRMWARE_CHECK,
22
+ DEFAULT_ENABLE_PROGRAM_SCAN,
23
+ DEFAULT_ENABLE_SYSVAR_SCAN,
24
+ DEFAULT_IGNORE_CUSTOM_DEVICE_DEFINITION_MODELS,
25
+ DEFAULT_INTERFACES_REQUIRING_PERIODIC_REFRESH,
26
+ DEFAULT_LOCALE,
27
+ DEFAULT_MAX_READ_WORKERS,
28
+ DEFAULT_OPTIONAL_SETTINGS,
29
+ DEFAULT_PROGRAM_MARKERS,
30
+ DEFAULT_SCHEDULE_TIMER_CONFIG,
31
+ DEFAULT_SESSION_RECORDER_START_FOR_SECONDS,
32
+ DEFAULT_STORAGE_DIRECTORY,
33
+ DEFAULT_SYSVAR_MARKERS,
34
+ DEFAULT_TIMEOUT_CONFIG,
35
+ DEFAULT_TLS,
36
+ DEFAULT_UN_IGNORES,
37
+ DEFAULT_USE_GROUP_CHANNEL_FOR_COVER_STATE,
38
+ DEFAULT_VERIFY_TLS,
39
+ IDENTIFIER_SEPARATOR,
40
+ PORT_ANY,
41
+ PRIMARY_CLIENT_CANDIDATE_INTERFACES,
42
+ DescriptionMarker,
43
+ Interface,
44
+ OptionalSettings,
45
+ RpcServerType,
46
+ ScheduleTimerConfig,
47
+ TimeoutConfig,
48
+ get_interface_default_port,
49
+ get_json_rpc_default_port,
50
+ )
51
+ from aiohomematic.exceptions import AioHomematicConfigException, AioHomematicException, BaseHomematicException
52
+ from aiohomematic.property_decorators import DelegatedProperty
53
+ from aiohomematic.support import (
54
+ _check_or_create_directory_sync,
55
+ check_password,
56
+ extract_exc_args,
57
+ is_host,
58
+ is_ipv4_address,
59
+ is_port,
60
+ )
61
+
62
+ if TYPE_CHECKING:
63
+ from aiohomematic.store import StorageFactoryProtocol
64
+
65
+
66
+ class CentralConfig:
67
+ """Configuration for CentralUnit initialization and behavior."""
68
+
69
+ def __init__(
70
+ self,
71
+ *,
72
+ central_id: str,
73
+ host: str,
74
+ interface_configs: AbstractSet[hmcl.InterfaceConfig],
75
+ name: str,
76
+ password: str,
77
+ username: str,
78
+ client_session: ClientSession | None = None,
79
+ callback_host: str | None = None,
80
+ callback_port_xml_rpc: int | None = None,
81
+ default_callback_port_xml_rpc: int = PORT_ANY,
82
+ delay_new_device_creation: bool = DEFAULT_DELAY_NEW_DEVICE_CREATION,
83
+ enable_device_firmware_check: bool = DEFAULT_ENABLE_DEVICE_FIRMWARE_CHECK,
84
+ enable_program_scan: bool = DEFAULT_ENABLE_PROGRAM_SCAN,
85
+ enable_sysvar_scan: bool = DEFAULT_ENABLE_SYSVAR_SCAN,
86
+ ignore_custom_device_definition_models: frozenset[str] = DEFAULT_IGNORE_CUSTOM_DEVICE_DEFINITION_MODELS,
87
+ interfaces_requiring_periodic_refresh: frozenset[Interface] = DEFAULT_INTERFACES_REQUIRING_PERIODIC_REFRESH,
88
+ json_port: int | None = None,
89
+ listen_ip_addr: str | None = None,
90
+ listen_port_xml_rpc: int | None = None,
91
+ max_read_workers: int = DEFAULT_MAX_READ_WORKERS,
92
+ optional_settings: tuple[OptionalSettings | str, ...] = DEFAULT_OPTIONAL_SETTINGS,
93
+ program_markers: tuple[DescriptionMarker | str, ...] = DEFAULT_PROGRAM_MARKERS,
94
+ schedule_timer_config: ScheduleTimerConfig = DEFAULT_SCHEDULE_TIMER_CONFIG,
95
+ start_direct: bool = False,
96
+ storage_directory: str = DEFAULT_STORAGE_DIRECTORY,
97
+ storage_factory: StorageFactoryProtocol | None = None,
98
+ sysvar_markers: tuple[DescriptionMarker | str, ...] = DEFAULT_SYSVAR_MARKERS,
99
+ timeout_config: TimeoutConfig = DEFAULT_TIMEOUT_CONFIG,
100
+ tls: bool = DEFAULT_TLS,
101
+ un_ignore_list: frozenset[str] = DEFAULT_UN_IGNORES,
102
+ use_group_channel_for_cover_state: bool = DEFAULT_USE_GROUP_CHANNEL_FOR_COVER_STATE,
103
+ verify_tls: bool = DEFAULT_VERIFY_TLS,
104
+ locale: str = DEFAULT_LOCALE,
105
+ ) -> None:
106
+ """Initialize the central configuration."""
107
+ self._interface_configs: Final = interface_configs
108
+ self._optional_settings: Final = frozenset(optional_settings or ())
109
+ self.requires_xml_rpc_server: Final = any(
110
+ ic for ic in interface_configs if ic.rpc_server == RpcServerType.XML_RPC
111
+ )
112
+ self.callback_host: Final = callback_host
113
+ self.callback_port_xml_rpc: Final = callback_port_xml_rpc
114
+ self.central_id: Final = central_id
115
+ self.client_session: Final = client_session
116
+ self.default_callback_port_xml_rpc: Final = default_callback_port_xml_rpc
117
+ self.delay_new_device_creation: Final = delay_new_device_creation
118
+ self.enable_device_firmware_check: Final = enable_device_firmware_check
119
+ self.enable_program_scan: Final = enable_program_scan
120
+ self.enable_sysvar_scan: Final = enable_sysvar_scan
121
+ self.host: Final = host
122
+ self.ignore_custom_device_definition_models: Final = frozenset(ignore_custom_device_definition_models or ())
123
+ self.interfaces_requiring_periodic_refresh: Final = frozenset(interfaces_requiring_periodic_refresh or ())
124
+ self.json_port: Final = json_port
125
+ self.listen_ip_addr: Final = listen_ip_addr
126
+ self.listen_port_xml_rpc: Final = listen_port_xml_rpc
127
+ self.max_read_workers = max_read_workers
128
+ self.name: Final = name
129
+ self.password: Final = password
130
+ self.program_markers: Final = program_markers
131
+ self.start_direct: Final = start_direct
132
+ self.session_recorder_randomize_output = (
133
+ OptionalSettings.SR_DISABLE_RANDOMIZE_OUTPUT not in self._optional_settings
134
+ )
135
+ self.session_recorder_start_for_seconds: Final = (
136
+ DEFAULT_SESSION_RECORDER_START_FOR_SECONDS
137
+ if OptionalSettings.SR_RECORD_SYSTEM_INIT in self._optional_settings
138
+ else 0
139
+ )
140
+ self.session_recorder_start = self.session_recorder_start_for_seconds > 0
141
+ self.schedule_timer_config: Final = schedule_timer_config
142
+ self.storage_directory: Final = storage_directory
143
+ self.storage_factory: Final = storage_factory
144
+ self.sysvar_markers: Final = sysvar_markers
145
+ self.timeout_config: Final = timeout_config
146
+ self.tls: Final = tls
147
+ self.un_ignore_list: Final = un_ignore_list
148
+ self.use_group_channel_for_cover_state: Final = use_group_channel_for_cover_state
149
+ self.username: Final = username
150
+ self.verify_tls: Final = verify_tls
151
+ self.locale: Final = locale
152
+
153
+ @classmethod
154
+ def for_ccu(
155
+ cls,
156
+ *,
157
+ host: str,
158
+ username: str,
159
+ password: str,
160
+ name: str = "ccu",
161
+ central_id: str | None = None,
162
+ tls: bool = False,
163
+ enable_hmip: bool = True,
164
+ enable_bidcos_rf: bool = True,
165
+ enable_bidcos_wired: bool = False,
166
+ enable_virtual_devices: bool = False,
167
+ **kwargs: Any,
168
+ ) -> CentralConfig:
169
+ """
170
+ Create a CentralConfig preset for CCU3/CCU2 backends.
171
+
172
+ This factory method simplifies configuration for CCU backends by
173
+ automatically setting up common interfaces with their default ports.
174
+
175
+ Args:
176
+ host: Hostname or IP address of the CCU.
177
+ username: CCU username for authentication.
178
+ password: CCU password for authentication.
179
+ name: Name identifier for the central unit.
180
+ central_id: Unique identifier for the central. Auto-generated if not provided.
181
+ tls: Enable TLS encryption for connections.
182
+ enable_hmip: Enable HomematicIP wireless interface (port 2010/42010).
183
+ enable_bidcos_rf: Enable BidCos RF interface (port 2001/42001).
184
+ enable_bidcos_wired: Enable BidCos wired interface (port 2000/42000).
185
+ enable_virtual_devices: Enable virtual devices interface (port 9292/49292).
186
+ **kwargs: Additional arguments passed to CentralConfig constructor.
187
+
188
+ Returns:
189
+ Configured CentralConfig instance ready for create_central().
190
+
191
+ Example:
192
+ config = CentralConfig.for_ccu(
193
+ host="192.168.1.100",
194
+ username="Admin",
195
+ password="secret",
196
+ )
197
+ central = await config.create_central()
198
+
199
+ """
200
+ interface_configs: set[hmcl.InterfaceConfig] = set()
201
+
202
+ if enable_hmip and (port := get_interface_default_port(interface=Interface.HMIP_RF, tls=tls)):
203
+ interface_configs.add(
204
+ hmcl.InterfaceConfig(
205
+ central_name=name,
206
+ interface=Interface.HMIP_RF,
207
+ port=port,
208
+ )
209
+ )
210
+
211
+ if enable_bidcos_rf and (port := get_interface_default_port(interface=Interface.BIDCOS_RF, tls=tls)):
212
+ interface_configs.add(
213
+ hmcl.InterfaceConfig(
214
+ central_name=name,
215
+ interface=Interface.BIDCOS_RF,
216
+ port=port,
217
+ )
218
+ )
219
+
220
+ if enable_bidcos_wired and (port := get_interface_default_port(interface=Interface.BIDCOS_WIRED, tls=tls)):
221
+ interface_configs.add(
222
+ hmcl.InterfaceConfig(
223
+ central_name=name,
224
+ interface=Interface.BIDCOS_WIRED,
225
+ port=port,
226
+ )
227
+ )
228
+
229
+ if enable_virtual_devices and (
230
+ port := get_interface_default_port(interface=Interface.VIRTUAL_DEVICES, tls=tls)
231
+ ):
232
+ interface_configs.add(
233
+ hmcl.InterfaceConfig(
234
+ central_name=name,
235
+ interface=Interface.VIRTUAL_DEVICES,
236
+ port=port,
237
+ remote_path="/groups",
238
+ )
239
+ )
240
+
241
+ return cls(
242
+ central_id=central_id or f"{name}-{host}",
243
+ host=host,
244
+ username=username,
245
+ password=password,
246
+ name=name,
247
+ interface_configs=interface_configs,
248
+ json_port=get_json_rpc_default_port(tls=tls),
249
+ tls=tls,
250
+ **kwargs,
251
+ )
252
+
253
+ @classmethod
254
+ def for_homegear(
255
+ cls,
256
+ *,
257
+ host: str,
258
+ username: str,
259
+ password: str,
260
+ name: str = "homegear",
261
+ central_id: str | None = None,
262
+ tls: bool = False,
263
+ port: int | None = None,
264
+ **kwargs: Any,
265
+ ) -> CentralConfig:
266
+ """
267
+ Create a CentralConfig preset for Homegear backends.
268
+
269
+ This factory method simplifies configuration for Homegear backends
270
+ with the BidCos-RF interface.
271
+
272
+ Args:
273
+ host: Hostname or IP address of the Homegear server.
274
+ username: Homegear username for authentication.
275
+ password: Homegear password for authentication.
276
+ name: Name identifier for the central unit.
277
+ central_id: Unique identifier for the central. Auto-generated if not provided.
278
+ tls: Enable TLS encryption for connections.
279
+ port: Custom port for BidCos-RF interface. Uses default (2001/42001) if not set.
280
+ **kwargs: Additional arguments passed to CentralConfig constructor.
281
+
282
+ Returns:
283
+ Configured CentralConfig instance ready for create_central().
284
+
285
+ Example:
286
+ config = CentralConfig.for_homegear(
287
+ host="192.168.1.50",
288
+ username="homegear",
289
+ password="secret",
290
+ )
291
+ central = await config.create_central()
292
+
293
+ """
294
+ interface_port = port or get_interface_default_port(interface=Interface.BIDCOS_RF, tls=tls) or 2001
295
+
296
+ interface_configs: set[hmcl.InterfaceConfig] = {
297
+ hmcl.InterfaceConfig(
298
+ central_name=name,
299
+ interface=Interface.BIDCOS_RF,
300
+ port=interface_port,
301
+ )
302
+ }
303
+
304
+ return cls(
305
+ central_id=central_id or f"{name}-{host}",
306
+ host=host,
307
+ username=username,
308
+ password=password,
309
+ name=name,
310
+ interface_configs=interface_configs,
311
+ tls=tls,
312
+ **kwargs,
313
+ )
314
+
315
+ optional_settings: Final = DelegatedProperty[frozenset[OptionalSettings | str]](path="_optional_settings")
316
+
317
+ @property
318
+ def connection_check_port(self) -> int:
319
+ """Return the connection check port."""
320
+ if used_ports := tuple(ic.port for ic in self._interface_configs if ic.port is not None):
321
+ return used_ports[0]
322
+ if self.json_port:
323
+ return self.json_port
324
+ return 443 if self.tls else 80
325
+
326
+ @property
327
+ def enable_xml_rpc_server(self) -> bool:
328
+ """Return if server and connection checker should be started."""
329
+ return self.requires_xml_rpc_server and self.start_direct is False
330
+
331
+ @property
332
+ def enabled_interface_configs(self) -> frozenset[hmcl.InterfaceConfig]:
333
+ """Return the interface configs."""
334
+ return frozenset(ic for ic in self._interface_configs if ic.enabled is True)
335
+
336
+ @property
337
+ def load_un_ignore(self) -> bool:
338
+ """Return if un_ignore should be loaded."""
339
+ return self.start_direct is False
340
+
341
+ @property
342
+ def use_caches(self) -> bool:
343
+ """Return if store should be used."""
344
+ return self.start_direct is False
345
+
346
+ async def check_config(self) -> None:
347
+ """Check central config asynchronously."""
348
+ if config_failures := await check_config(
349
+ central_name=self.name,
350
+ host=self.host,
351
+ username=self.username,
352
+ password=self.password,
353
+ storage_directory=self.storage_directory,
354
+ callback_host=self.callback_host,
355
+ callback_port_xml_rpc=self.callback_port_xml_rpc,
356
+ json_port=self.json_port,
357
+ interface_configs=self._interface_configs,
358
+ ):
359
+ failures = ", ".join(config_failures)
360
+ msg = i18n.tr(key="exception.config.invalid", failures=failures)
361
+ raise AioHomematicConfigException(msg)
362
+
363
+ async def create_central(self) -> CentralUnit:
364
+ """Create the central asynchronously."""
365
+ try:
366
+ await self.check_config()
367
+ return CentralUnit(central_config=self)
368
+ except BaseHomematicException as bhexc: # pragma: no cover
369
+ raise AioHomematicException(
370
+ i18n.tr(
371
+ key="exception.create_central.failed",
372
+ reason=extract_exc_args(exc=bhexc),
373
+ )
374
+ ) from bhexc
375
+
376
+ def create_central_url(self) -> str:
377
+ """Return the required url."""
378
+ url = "https://" if self.tls else "http://"
379
+ url = f"{url}{self.host}"
380
+ if self.json_port:
381
+ url = f"{url}:{self.json_port}"
382
+ return f"{url}"
383
+
384
+
385
+ def _check_config_sync(
386
+ *,
387
+ central_name: str,
388
+ host: str,
389
+ username: str,
390
+ password: str,
391
+ storage_directory: str,
392
+ callback_host: str | None,
393
+ callback_port_xml_rpc: int | None,
394
+ json_port: int | None,
395
+ interface_configs: AbstractSet[hmcl.InterfaceConfig] | None = None,
396
+ ) -> list[str]:
397
+ """Check config (internal sync implementation)."""
398
+ config_failures: list[str] = []
399
+ if central_name and IDENTIFIER_SEPARATOR in central_name:
400
+ config_failures.append(i18n.tr(key="exception.config.check.instance_name.separator", sep=IDENTIFIER_SEPARATOR))
401
+
402
+ if not (is_host(host=host) or is_ipv4_address(address=host)):
403
+ config_failures.append(i18n.tr(key="exception.config.check.host.invalid"))
404
+ if not username:
405
+ config_failures.append(i18n.tr(key="exception.config.check.username.empty"))
406
+ if not password:
407
+ config_failures.append(i18n.tr(key="exception.config.check.password.required"))
408
+ if not check_password(password=password):
409
+ config_failures.append(i18n.tr(key="exception.config.check.password.invalid"))
410
+ try:
411
+ _check_or_create_directory_sync(directory=storage_directory)
412
+ except BaseHomematicException as bhexc:
413
+ config_failures.append(extract_exc_args(exc=bhexc)[0])
414
+ if callback_host and not (is_host(host=callback_host) or is_ipv4_address(address=callback_host)):
415
+ config_failures.append(i18n.tr(key="exception.config.check.callback_host.invalid"))
416
+ if callback_port_xml_rpc and not is_port(port=callback_port_xml_rpc):
417
+ config_failures.append(i18n.tr(key="exception.config.check.callback_port_xml_rpc.invalid"))
418
+ if json_port and not is_port(port=json_port):
419
+ config_failures.append(i18n.tr(key="exception.config.check.json_port.invalid"))
420
+ if interface_configs and not _has_primary_client(interface_configs=interface_configs):
421
+ config_failures.append(
422
+ i18n.tr(
423
+ key="exception.config.check.primary_interface.missing",
424
+ interfaces=", ".join(PRIMARY_CLIENT_CANDIDATE_INTERFACES),
425
+ )
426
+ )
427
+
428
+ return config_failures
429
+
430
+
431
+ async def check_config(
432
+ *,
433
+ central_name: str,
434
+ host: str,
435
+ username: str,
436
+ password: str,
437
+ storage_directory: str,
438
+ callback_host: str | None,
439
+ callback_port_xml_rpc: int | None,
440
+ json_port: int | None,
441
+ interface_configs: AbstractSet[hmcl.InterfaceConfig] | None = None,
442
+ ) -> list[str]:
443
+ """Check config asynchronously."""
444
+ return await asyncio.to_thread(
445
+ _check_config_sync,
446
+ central_name=central_name,
447
+ host=host,
448
+ username=username,
449
+ password=password,
450
+ storage_directory=storage_directory,
451
+ callback_host=callback_host,
452
+ callback_port_xml_rpc=callback_port_xml_rpc,
453
+ json_port=json_port,
454
+ interface_configs=interface_configs,
455
+ )
456
+
457
+
458
+ def _has_primary_client(*, interface_configs: AbstractSet[hmcl.InterfaceConfig]) -> bool:
459
+ """Check if all configured clients exists in central."""
460
+ for interface_config in interface_configs:
461
+ if interface_config.interface in PRIMARY_CLIENT_CANDIDATE_INTERFACES:
462
+ return True
463
+ return False