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.
- aiohomematic/__init__.py +110 -0
- aiohomematic/_log_context_protocol.py +29 -0
- aiohomematic/api.py +410 -0
- aiohomematic/async_support.py +250 -0
- aiohomematic/backend_detection.py +462 -0
- aiohomematic/central/__init__.py +103 -0
- aiohomematic/central/async_rpc_server.py +760 -0
- aiohomematic/central/central_unit.py +1152 -0
- aiohomematic/central/config.py +463 -0
- aiohomematic/central/config_builder.py +772 -0
- aiohomematic/central/connection_state.py +160 -0
- aiohomematic/central/coordinators/__init__.py +38 -0
- aiohomematic/central/coordinators/cache.py +414 -0
- aiohomematic/central/coordinators/client.py +480 -0
- aiohomematic/central/coordinators/connection_recovery.py +1141 -0
- aiohomematic/central/coordinators/device.py +1166 -0
- aiohomematic/central/coordinators/event.py +514 -0
- aiohomematic/central/coordinators/hub.py +532 -0
- aiohomematic/central/decorators.py +184 -0
- aiohomematic/central/device_registry.py +229 -0
- aiohomematic/central/events/__init__.py +104 -0
- aiohomematic/central/events/bus.py +1392 -0
- aiohomematic/central/events/integration.py +424 -0
- aiohomematic/central/events/types.py +194 -0
- aiohomematic/central/health.py +762 -0
- aiohomematic/central/rpc_server.py +353 -0
- aiohomematic/central/scheduler.py +794 -0
- aiohomematic/central/state_machine.py +391 -0
- aiohomematic/client/__init__.py +203 -0
- aiohomematic/client/_rpc_errors.py +187 -0
- aiohomematic/client/backends/__init__.py +48 -0
- aiohomematic/client/backends/base.py +335 -0
- aiohomematic/client/backends/capabilities.py +138 -0
- aiohomematic/client/backends/ccu.py +487 -0
- aiohomematic/client/backends/factory.py +116 -0
- aiohomematic/client/backends/homegear.py +294 -0
- aiohomematic/client/backends/json_ccu.py +252 -0
- aiohomematic/client/backends/protocol.py +316 -0
- aiohomematic/client/ccu.py +1857 -0
- aiohomematic/client/circuit_breaker.py +459 -0
- aiohomematic/client/config.py +64 -0
- aiohomematic/client/handlers/__init__.py +40 -0
- aiohomematic/client/handlers/backup.py +157 -0
- aiohomematic/client/handlers/base.py +79 -0
- aiohomematic/client/handlers/device_ops.py +1085 -0
- aiohomematic/client/handlers/firmware.py +144 -0
- aiohomematic/client/handlers/link_mgmt.py +199 -0
- aiohomematic/client/handlers/metadata.py +436 -0
- aiohomematic/client/handlers/programs.py +144 -0
- aiohomematic/client/handlers/sysvars.py +100 -0
- aiohomematic/client/interface_client.py +1304 -0
- aiohomematic/client/json_rpc.py +2068 -0
- aiohomematic/client/request_coalescer.py +282 -0
- aiohomematic/client/rpc_proxy.py +629 -0
- aiohomematic/client/state_machine.py +324 -0
- aiohomematic/const.py +2207 -0
- aiohomematic/context.py +275 -0
- aiohomematic/converter.py +270 -0
- aiohomematic/decorators.py +390 -0
- aiohomematic/exceptions.py +185 -0
- aiohomematic/hmcli.py +997 -0
- aiohomematic/i18n.py +193 -0
- aiohomematic/interfaces/__init__.py +407 -0
- aiohomematic/interfaces/central.py +1067 -0
- aiohomematic/interfaces/client.py +1096 -0
- aiohomematic/interfaces/coordinators.py +63 -0
- aiohomematic/interfaces/model.py +1921 -0
- aiohomematic/interfaces/operations.py +217 -0
- aiohomematic/logging_context.py +134 -0
- aiohomematic/metrics/__init__.py +125 -0
- aiohomematic/metrics/_protocols.py +140 -0
- aiohomematic/metrics/aggregator.py +534 -0
- aiohomematic/metrics/dataclasses.py +489 -0
- aiohomematic/metrics/emitter.py +292 -0
- aiohomematic/metrics/events.py +183 -0
- aiohomematic/metrics/keys.py +300 -0
- aiohomematic/metrics/observer.py +563 -0
- aiohomematic/metrics/stats.py +172 -0
- aiohomematic/model/__init__.py +189 -0
- aiohomematic/model/availability.py +65 -0
- aiohomematic/model/calculated/__init__.py +89 -0
- aiohomematic/model/calculated/climate.py +276 -0
- aiohomematic/model/calculated/data_point.py +315 -0
- aiohomematic/model/calculated/field.py +147 -0
- aiohomematic/model/calculated/operating_voltage_level.py +286 -0
- aiohomematic/model/calculated/support.py +232 -0
- aiohomematic/model/custom/__init__.py +214 -0
- aiohomematic/model/custom/capabilities/__init__.py +67 -0
- aiohomematic/model/custom/capabilities/climate.py +41 -0
- aiohomematic/model/custom/capabilities/light.py +87 -0
- aiohomematic/model/custom/capabilities/lock.py +44 -0
- aiohomematic/model/custom/capabilities/siren.py +63 -0
- aiohomematic/model/custom/climate.py +1130 -0
- aiohomematic/model/custom/cover.py +722 -0
- aiohomematic/model/custom/data_point.py +360 -0
- aiohomematic/model/custom/definition.py +300 -0
- aiohomematic/model/custom/field.py +89 -0
- aiohomematic/model/custom/light.py +1174 -0
- aiohomematic/model/custom/lock.py +322 -0
- aiohomematic/model/custom/mixins.py +445 -0
- aiohomematic/model/custom/profile.py +945 -0
- aiohomematic/model/custom/registry.py +251 -0
- aiohomematic/model/custom/siren.py +462 -0
- aiohomematic/model/custom/switch.py +195 -0
- aiohomematic/model/custom/text_display.py +289 -0
- aiohomematic/model/custom/valve.py +78 -0
- aiohomematic/model/data_point.py +1416 -0
- aiohomematic/model/device.py +1840 -0
- aiohomematic/model/event.py +216 -0
- aiohomematic/model/generic/__init__.py +327 -0
- aiohomematic/model/generic/action.py +40 -0
- aiohomematic/model/generic/action_select.py +62 -0
- aiohomematic/model/generic/binary_sensor.py +30 -0
- aiohomematic/model/generic/button.py +31 -0
- aiohomematic/model/generic/data_point.py +177 -0
- aiohomematic/model/generic/dummy.py +150 -0
- aiohomematic/model/generic/number.py +76 -0
- aiohomematic/model/generic/select.py +56 -0
- aiohomematic/model/generic/sensor.py +76 -0
- aiohomematic/model/generic/switch.py +54 -0
- aiohomematic/model/generic/text.py +33 -0
- aiohomematic/model/hub/__init__.py +100 -0
- aiohomematic/model/hub/binary_sensor.py +24 -0
- aiohomematic/model/hub/button.py +28 -0
- aiohomematic/model/hub/connectivity.py +190 -0
- aiohomematic/model/hub/data_point.py +342 -0
- aiohomematic/model/hub/hub.py +864 -0
- aiohomematic/model/hub/inbox.py +135 -0
- aiohomematic/model/hub/install_mode.py +393 -0
- aiohomematic/model/hub/metrics.py +208 -0
- aiohomematic/model/hub/number.py +42 -0
- aiohomematic/model/hub/select.py +52 -0
- aiohomematic/model/hub/sensor.py +37 -0
- aiohomematic/model/hub/switch.py +43 -0
- aiohomematic/model/hub/text.py +30 -0
- aiohomematic/model/hub/update.py +221 -0
- aiohomematic/model/support.py +592 -0
- aiohomematic/model/update.py +140 -0
- aiohomematic/model/week_profile.py +1827 -0
- aiohomematic/property_decorators.py +719 -0
- aiohomematic/py.typed +0 -0
- aiohomematic/rega_scripts/accept_device_in_inbox.fn +51 -0
- aiohomematic/rega_scripts/create_backup_start.fn +28 -0
- aiohomematic/rega_scripts/create_backup_status.fn +89 -0
- aiohomematic/rega_scripts/fetch_all_device_data.fn +97 -0
- aiohomematic/rega_scripts/get_backend_info.fn +25 -0
- aiohomematic/rega_scripts/get_inbox_devices.fn +61 -0
- aiohomematic/rega_scripts/get_program_descriptions.fn +31 -0
- aiohomematic/rega_scripts/get_serial.fn +44 -0
- aiohomematic/rega_scripts/get_service_messages.fn +83 -0
- aiohomematic/rega_scripts/get_system_update_info.fn +39 -0
- aiohomematic/rega_scripts/get_system_variable_descriptions.fn +31 -0
- aiohomematic/rega_scripts/set_program_state.fn +17 -0
- aiohomematic/rega_scripts/set_system_variable.fn +19 -0
- aiohomematic/rega_scripts/trigger_firmware_update.fn +67 -0
- aiohomematic/schemas.py +256 -0
- aiohomematic/store/__init__.py +55 -0
- aiohomematic/store/dynamic/__init__.py +43 -0
- aiohomematic/store/dynamic/command.py +250 -0
- aiohomematic/store/dynamic/data.py +175 -0
- aiohomematic/store/dynamic/details.py +187 -0
- aiohomematic/store/dynamic/ping_pong.py +416 -0
- aiohomematic/store/persistent/__init__.py +71 -0
- aiohomematic/store/persistent/base.py +285 -0
- aiohomematic/store/persistent/device.py +233 -0
- aiohomematic/store/persistent/incident.py +380 -0
- aiohomematic/store/persistent/paramset.py +241 -0
- aiohomematic/store/persistent/session.py +556 -0
- aiohomematic/store/serialization.py +150 -0
- aiohomematic/store/storage.py +689 -0
- aiohomematic/store/types.py +526 -0
- aiohomematic/store/visibility/__init__.py +40 -0
- aiohomematic/store/visibility/parser.py +141 -0
- aiohomematic/store/visibility/registry.py +722 -0
- aiohomematic/store/visibility/rules.py +307 -0
- aiohomematic/strings.json +237 -0
- aiohomematic/support.py +706 -0
- aiohomematic/tracing.py +236 -0
- aiohomematic/translations/de.json +237 -0
- aiohomematic/translations/en.json +237 -0
- aiohomematic/type_aliases.py +51 -0
- aiohomematic/validator.py +128 -0
- aiohomematic-2026.1.29.dist-info/METADATA +296 -0
- aiohomematic-2026.1.29.dist-info/RECORD +188 -0
- aiohomematic-2026.1.29.dist-info/WHEEL +5 -0
- aiohomematic-2026.1.29.dist-info/entry_points.txt +2 -0
- aiohomematic-2026.1.29.dist-info/licenses/LICENSE +21 -0
- 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
|