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,772 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2021-2026
|
|
3
|
+
"""
|
|
4
|
+
Builder pattern for CentralConfig with fluent interface.
|
|
5
|
+
|
|
6
|
+
This module provides a builder class that offers step-by-step configuration
|
|
7
|
+
with method chaining, early validation, and preset configurations.
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
# Simple CCU setup
|
|
11
|
+
config = (
|
|
12
|
+
CentralConfigBuilder()
|
|
13
|
+
.with_name(name="my-ccu")
|
|
14
|
+
.with_host(host="192.168.1.100")
|
|
15
|
+
.with_credentials(username="Admin", password="secret")
|
|
16
|
+
.add_hmip_interface()
|
|
17
|
+
.add_bidcos_rf_interface()
|
|
18
|
+
.build()
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
# Using preset
|
|
22
|
+
config = (
|
|
23
|
+
CentralConfigBuilder.for_ccu(host="192.168.1.100")
|
|
24
|
+
.with_credentials(username="Admin", password="secret")
|
|
25
|
+
.with_tls(enabled=True)
|
|
26
|
+
.build()
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
Public API of this module is defined by __all__.
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from __future__ import annotations
|
|
34
|
+
|
|
35
|
+
from dataclasses import dataclass
|
|
36
|
+
from typing import TYPE_CHECKING, Any, Self
|
|
37
|
+
|
|
38
|
+
from aiohomematic.central import CentralConfig
|
|
39
|
+
from aiohomematic.client import InterfaceConfig
|
|
40
|
+
from aiohomematic.const import (
|
|
41
|
+
DEFAULT_DELAY_NEW_DEVICE_CREATION,
|
|
42
|
+
DEFAULT_ENABLE_DEVICE_FIRMWARE_CHECK,
|
|
43
|
+
DEFAULT_ENABLE_PROGRAM_SCAN,
|
|
44
|
+
DEFAULT_ENABLE_SYSVAR_SCAN,
|
|
45
|
+
DEFAULT_IGNORE_CUSTOM_DEVICE_DEFINITION_MODELS,
|
|
46
|
+
DEFAULT_INTERFACES_REQUIRING_PERIODIC_REFRESH,
|
|
47
|
+
DEFAULT_LOCALE,
|
|
48
|
+
DEFAULT_MAX_READ_WORKERS,
|
|
49
|
+
DEFAULT_OPTIONAL_SETTINGS,
|
|
50
|
+
DEFAULT_PROGRAM_MARKERS,
|
|
51
|
+
DEFAULT_SCHEDULE_TIMER_CONFIG,
|
|
52
|
+
DEFAULT_STORAGE_DIRECTORY,
|
|
53
|
+
DEFAULT_SYSVAR_MARKERS,
|
|
54
|
+
DEFAULT_TIMEOUT_CONFIG,
|
|
55
|
+
DEFAULT_TLS,
|
|
56
|
+
DEFAULT_UN_IGNORES,
|
|
57
|
+
DEFAULT_USE_GROUP_CHANNEL_FOR_COVER_STATE,
|
|
58
|
+
DEFAULT_VERIFY_TLS,
|
|
59
|
+
PORT_ANY,
|
|
60
|
+
DescriptionMarker,
|
|
61
|
+
Interface,
|
|
62
|
+
OptionalSettings,
|
|
63
|
+
ScheduleTimerConfig,
|
|
64
|
+
TimeoutConfig,
|
|
65
|
+
get_interface_default_port,
|
|
66
|
+
get_json_rpc_default_port,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
if TYPE_CHECKING:
|
|
70
|
+
from aiohttp import ClientSession
|
|
71
|
+
|
|
72
|
+
__all__ = ["CentralConfigBuilder", "ValidationError"]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclass(frozen=True, slots=True)
|
|
76
|
+
class ValidationError:
|
|
77
|
+
"""Validation error with field name and message."""
|
|
78
|
+
|
|
79
|
+
field: str
|
|
80
|
+
message: str
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class CentralConfigBuilder:
|
|
84
|
+
"""
|
|
85
|
+
Builder for CentralConfig with fluent interface.
|
|
86
|
+
|
|
87
|
+
Provides step-by-step configuration with method chaining, early validation,
|
|
88
|
+
and preset configurations for common backends.
|
|
89
|
+
|
|
90
|
+
The builder enforces that required fields (name, host, username, password)
|
|
91
|
+
are set before building, and provides sensible defaults for all optional fields.
|
|
92
|
+
|
|
93
|
+
Example:
|
|
94
|
+
# Full configuration
|
|
95
|
+
config = (
|
|
96
|
+
CentralConfigBuilder()
|
|
97
|
+
.with_name(name="production-ccu")
|
|
98
|
+
.with_host(host="ccu.local")
|
|
99
|
+
.with_credentials(username="admin", password="secret")
|
|
100
|
+
.with_tls(enabled=True, verify=True)
|
|
101
|
+
.add_hmip_interface()
|
|
102
|
+
.add_bidcos_rf_interface()
|
|
103
|
+
.with_storage(directory="/var/lib/aiohomematic")
|
|
104
|
+
.with_programs(enabled=True)
|
|
105
|
+
.with_sysvars(enabled=True)
|
|
106
|
+
.build()
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Validate before building
|
|
110
|
+
builder = CentralConfigBuilder().with_name(name="test")
|
|
111
|
+
errors = builder.validate()
|
|
112
|
+
if errors:
|
|
113
|
+
for error in errors:
|
|
114
|
+
print(f"{error.field}: {error.message}")
|
|
115
|
+
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
__slots__ = (
|
|
119
|
+
# Required
|
|
120
|
+
"_name",
|
|
121
|
+
"_host",
|
|
122
|
+
"_username",
|
|
123
|
+
"_password",
|
|
124
|
+
# Interfaces
|
|
125
|
+
"_interfaces",
|
|
126
|
+
# Connection
|
|
127
|
+
"_tls",
|
|
128
|
+
"_verify_tls",
|
|
129
|
+
"_json_port",
|
|
130
|
+
"_client_session",
|
|
131
|
+
# Callback server
|
|
132
|
+
"_callback_host",
|
|
133
|
+
"_callback_port_xml_rpc",
|
|
134
|
+
"_default_callback_port_xml_rpc",
|
|
135
|
+
"_listen_ip_addr",
|
|
136
|
+
"_listen_port_xml_rpc",
|
|
137
|
+
# Features
|
|
138
|
+
"_enable_program_scan",
|
|
139
|
+
"_enable_sysvar_scan",
|
|
140
|
+
"_enable_device_firmware_check",
|
|
141
|
+
"_program_markers",
|
|
142
|
+
"_sysvar_markers",
|
|
143
|
+
# Storage
|
|
144
|
+
"_storage_directory",
|
|
145
|
+
# Advanced
|
|
146
|
+
"_central_id",
|
|
147
|
+
"_delay_new_device_creation",
|
|
148
|
+
"_ignore_custom_device_definition_models",
|
|
149
|
+
"_interfaces_requiring_periodic_refresh",
|
|
150
|
+
"_max_read_workers",
|
|
151
|
+
"_optional_settings",
|
|
152
|
+
"_schedule_timer_config",
|
|
153
|
+
"_start_direct",
|
|
154
|
+
"_timeout_config",
|
|
155
|
+
"_un_ignore_list",
|
|
156
|
+
"_use_group_channel_for_cover_state",
|
|
157
|
+
"_locale",
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
def __init__(self) -> None:
|
|
161
|
+
"""Initialize builder with default values."""
|
|
162
|
+
# Required (no default)
|
|
163
|
+
self._name: str | None = None
|
|
164
|
+
self._host: str | None = None
|
|
165
|
+
self._username: str | None = None
|
|
166
|
+
self._password: str | None = None
|
|
167
|
+
|
|
168
|
+
# Interfaces
|
|
169
|
+
self._interfaces: list[tuple[Interface, int | None, str | None]] = []
|
|
170
|
+
|
|
171
|
+
# Connection
|
|
172
|
+
self._tls: bool = DEFAULT_TLS
|
|
173
|
+
self._verify_tls: bool = DEFAULT_VERIFY_TLS
|
|
174
|
+
self._json_port: int | None = None
|
|
175
|
+
self._client_session: ClientSession | None = None
|
|
176
|
+
|
|
177
|
+
# Callback server
|
|
178
|
+
self._callback_host: str | None = None
|
|
179
|
+
self._callback_port_xml_rpc: int | None = None
|
|
180
|
+
self._default_callback_port_xml_rpc: int = PORT_ANY
|
|
181
|
+
self._listen_ip_addr: str | None = None
|
|
182
|
+
self._listen_port_xml_rpc: int | None = None
|
|
183
|
+
|
|
184
|
+
# Features
|
|
185
|
+
self._enable_program_scan: bool = DEFAULT_ENABLE_PROGRAM_SCAN
|
|
186
|
+
self._enable_sysvar_scan: bool = DEFAULT_ENABLE_SYSVAR_SCAN
|
|
187
|
+
self._enable_device_firmware_check: bool = DEFAULT_ENABLE_DEVICE_FIRMWARE_CHECK
|
|
188
|
+
self._program_markers: tuple[DescriptionMarker | str, ...] = DEFAULT_PROGRAM_MARKERS
|
|
189
|
+
self._sysvar_markers: tuple[DescriptionMarker | str, ...] = DEFAULT_SYSVAR_MARKERS
|
|
190
|
+
|
|
191
|
+
# Storage
|
|
192
|
+
self._storage_directory: str = DEFAULT_STORAGE_DIRECTORY
|
|
193
|
+
|
|
194
|
+
# Advanced
|
|
195
|
+
self._central_id: str | None = None
|
|
196
|
+
self._delay_new_device_creation: bool = DEFAULT_DELAY_NEW_DEVICE_CREATION
|
|
197
|
+
self._ignore_custom_device_definition_models: frozenset[str] = DEFAULT_IGNORE_CUSTOM_DEVICE_DEFINITION_MODELS
|
|
198
|
+
self._interfaces_requiring_periodic_refresh: frozenset[Interface] = (
|
|
199
|
+
DEFAULT_INTERFACES_REQUIRING_PERIODIC_REFRESH
|
|
200
|
+
)
|
|
201
|
+
self._max_read_workers: int = DEFAULT_MAX_READ_WORKERS
|
|
202
|
+
self._optional_settings: tuple[OptionalSettings | str, ...] = DEFAULT_OPTIONAL_SETTINGS
|
|
203
|
+
self._schedule_timer_config: ScheduleTimerConfig = DEFAULT_SCHEDULE_TIMER_CONFIG
|
|
204
|
+
self._start_direct: bool = False
|
|
205
|
+
self._timeout_config: TimeoutConfig = DEFAULT_TIMEOUT_CONFIG
|
|
206
|
+
self._un_ignore_list: frozenset[str] = DEFAULT_UN_IGNORES
|
|
207
|
+
self._use_group_channel_for_cover_state: bool = DEFAULT_USE_GROUP_CHANNEL_FOR_COVER_STATE
|
|
208
|
+
self._locale: str = DEFAULT_LOCALE
|
|
209
|
+
|
|
210
|
+
@classmethod
|
|
211
|
+
def for_ccu(cls, *, host: str, name: str = "ccu") -> Self:
|
|
212
|
+
"""
|
|
213
|
+
Create builder preset for CCU3/CCU2 backends.
|
|
214
|
+
|
|
215
|
+
Pre-configures standard CCU interfaces (HMIP_RF, BIDCOS_RF).
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
host: CCU hostname or IP address.
|
|
219
|
+
name: Central unit name (default: "ccu").
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
Pre-configured builder. Add credentials and call build().
|
|
223
|
+
|
|
224
|
+
Example:
|
|
225
|
+
config = (
|
|
226
|
+
CentralConfigBuilder.for_ccu(host="192.168.1.100")
|
|
227
|
+
.with_credentials(username="Admin", password="secret")
|
|
228
|
+
.build()
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
"""
|
|
232
|
+
return cls().with_name(name=name).with_host(host=host).add_all_standard_interfaces()
|
|
233
|
+
|
|
234
|
+
@classmethod
|
|
235
|
+
def for_homegear(cls, *, host: str, name: str = "homegear", port: int | None = None) -> Self:
|
|
236
|
+
"""
|
|
237
|
+
Create builder preset for Homegear backends.
|
|
238
|
+
|
|
239
|
+
Pre-configures BidCos-RF interface.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
host: Homegear hostname or IP address.
|
|
243
|
+
name: Central unit name (default: "homegear").
|
|
244
|
+
port: Custom BidCos-RF port (default: 2001).
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
Pre-configured builder. Add credentials and call build().
|
|
248
|
+
|
|
249
|
+
Example:
|
|
250
|
+
config = (
|
|
251
|
+
CentralConfigBuilder.for_homegear(host="192.168.1.50")
|
|
252
|
+
.with_credentials(username="homegear", password="secret")
|
|
253
|
+
.build()
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
"""
|
|
257
|
+
return cls().with_name(name=name).with_host(host=host).add_bidcos_rf_interface(port=port)
|
|
258
|
+
|
|
259
|
+
def add_all_standard_interfaces(self) -> Self:
|
|
260
|
+
"""
|
|
261
|
+
Add all standard CCU interfaces (HMIP_RF, BIDCOS_RF).
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Self for method chaining.
|
|
265
|
+
|
|
266
|
+
"""
|
|
267
|
+
return self.add_hmip_interface().add_bidcos_rf_interface()
|
|
268
|
+
|
|
269
|
+
def add_bidcos_rf_interface(self, *, port: int | None = None) -> Self:
|
|
270
|
+
"""
|
|
271
|
+
Add BidCos RF wireless interface.
|
|
272
|
+
|
|
273
|
+
Default ports: 2001 (plain), 42001 (TLS).
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
port: Custom port. Uses default if not specified.
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
Self for method chaining.
|
|
280
|
+
|
|
281
|
+
"""
|
|
282
|
+
return self.add_interface(interface=Interface.BIDCOS_RF, port=port)
|
|
283
|
+
|
|
284
|
+
def add_bidcos_wired_interface(self, *, port: int | None = None) -> Self:
|
|
285
|
+
"""
|
|
286
|
+
Add BidCos wired interface.
|
|
287
|
+
|
|
288
|
+
Default ports: 2000 (plain), 42000 (TLS).
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
port: Custom port. Uses default if not specified.
|
|
292
|
+
|
|
293
|
+
Returns:
|
|
294
|
+
Self for method chaining.
|
|
295
|
+
|
|
296
|
+
"""
|
|
297
|
+
return self.add_interface(interface=Interface.BIDCOS_WIRED, port=port)
|
|
298
|
+
|
|
299
|
+
def add_cuxd_interface(self) -> Self:
|
|
300
|
+
"""
|
|
301
|
+
Add CUxD interface.
|
|
302
|
+
|
|
303
|
+
CUxD uses JSON-RPC only and does not require an XML-RPC port.
|
|
304
|
+
|
|
305
|
+
Returns:
|
|
306
|
+
Self for method chaining.
|
|
307
|
+
|
|
308
|
+
"""
|
|
309
|
+
return self.add_interface(interface=Interface.CUXD, port=0)
|
|
310
|
+
|
|
311
|
+
def add_hmip_interface(self, *, port: int | None = None) -> Self:
|
|
312
|
+
"""
|
|
313
|
+
Add HomematicIP wireless interface (HMIP_RF).
|
|
314
|
+
|
|
315
|
+
Default ports: 2010 (plain), 42010 (TLS).
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
port: Custom port. Uses default if not specified.
|
|
319
|
+
|
|
320
|
+
Returns:
|
|
321
|
+
Self for method chaining.
|
|
322
|
+
|
|
323
|
+
"""
|
|
324
|
+
return self.add_interface(interface=Interface.HMIP_RF, port=port)
|
|
325
|
+
|
|
326
|
+
def add_interface(
|
|
327
|
+
self,
|
|
328
|
+
*,
|
|
329
|
+
interface: Interface,
|
|
330
|
+
port: int | None = None,
|
|
331
|
+
remote_path: str | None = None,
|
|
332
|
+
) -> Self:
|
|
333
|
+
"""
|
|
334
|
+
Add a Homematic interface.
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
interface: Interface type (HMIP_RF, BIDCOS_RF, etc.).
|
|
338
|
+
port: Custom port. If not specified, uses default port
|
|
339
|
+
(adjusted for TLS if enabled).
|
|
340
|
+
remote_path: Optional remote path for the interface.
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
Self for method chaining.
|
|
344
|
+
|
|
345
|
+
"""
|
|
346
|
+
self._interfaces.append((interface, port, remote_path))
|
|
347
|
+
return self
|
|
348
|
+
|
|
349
|
+
def add_virtual_devices_interface(self, *, port: int | None = None) -> Self:
|
|
350
|
+
"""
|
|
351
|
+
Add virtual devices interface.
|
|
352
|
+
|
|
353
|
+
Default ports: 9292 (plain), 49292 (TLS).
|
|
354
|
+
|
|
355
|
+
Args:
|
|
356
|
+
port: Custom port. Uses default if not specified.
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
Self for method chaining.
|
|
360
|
+
|
|
361
|
+
"""
|
|
362
|
+
return self.add_interface(interface=Interface.VIRTUAL_DEVICES, port=port, remote_path="/groups")
|
|
363
|
+
|
|
364
|
+
def build(self) -> CentralConfig:
|
|
365
|
+
"""
|
|
366
|
+
Build the CentralConfig instance.
|
|
367
|
+
|
|
368
|
+
Returns:
|
|
369
|
+
Configured CentralConfig ready for create_central().
|
|
370
|
+
|
|
371
|
+
Raises:
|
|
372
|
+
ValueError: If required configuration is missing or invalid.
|
|
373
|
+
|
|
374
|
+
"""
|
|
375
|
+
if errors := self.validate():
|
|
376
|
+
error_msgs = [f"{e.field}: {e.message}" for e in errors]
|
|
377
|
+
raise ValueError(f"Invalid configuration: {', '.join(error_msgs)}") # i18n-exc: ignore
|
|
378
|
+
|
|
379
|
+
# Build interface configs with resolved ports
|
|
380
|
+
interface_configs: set[InterfaceConfig] = set()
|
|
381
|
+
for interface, port, remote_path in self._interfaces:
|
|
382
|
+
# Use explicit port if provided, otherwise get default
|
|
383
|
+
# port=0 is valid for JSON-RPC-only interfaces (CUxD, CCU-Jack)
|
|
384
|
+
resolved_port = port if port is not None else get_interface_default_port(interface=interface, tls=self._tls)
|
|
385
|
+
if resolved_port is not None:
|
|
386
|
+
config_kwargs: dict[str, Any] = {
|
|
387
|
+
"central_name": self._name,
|
|
388
|
+
"interface": interface,
|
|
389
|
+
"port": resolved_port,
|
|
390
|
+
}
|
|
391
|
+
if remote_path:
|
|
392
|
+
config_kwargs["remote_path"] = remote_path
|
|
393
|
+
interface_configs.add(InterfaceConfig(**config_kwargs))
|
|
394
|
+
|
|
395
|
+
# Determine central_id
|
|
396
|
+
central_id = self._central_id or f"{self._name}-{self._host}"
|
|
397
|
+
|
|
398
|
+
# Determine json_port
|
|
399
|
+
json_port = self._json_port or get_json_rpc_default_port(tls=self._tls)
|
|
400
|
+
|
|
401
|
+
return CentralConfig(
|
|
402
|
+
# Required
|
|
403
|
+
central_id=central_id,
|
|
404
|
+
host=self._host, # type: ignore[arg-type]
|
|
405
|
+
interface_configs=interface_configs,
|
|
406
|
+
name=self._name, # type: ignore[arg-type]
|
|
407
|
+
password=self._password, # type: ignore[arg-type]
|
|
408
|
+
username=self._username, # type: ignore[arg-type]
|
|
409
|
+
# Connection
|
|
410
|
+
client_session=self._client_session,
|
|
411
|
+
tls=self._tls,
|
|
412
|
+
verify_tls=self._verify_tls,
|
|
413
|
+
json_port=json_port,
|
|
414
|
+
# Callback
|
|
415
|
+
callback_host=self._callback_host,
|
|
416
|
+
callback_port_xml_rpc=self._callback_port_xml_rpc,
|
|
417
|
+
default_callback_port_xml_rpc=self._default_callback_port_xml_rpc,
|
|
418
|
+
listen_ip_addr=self._listen_ip_addr,
|
|
419
|
+
listen_port_xml_rpc=self._listen_port_xml_rpc,
|
|
420
|
+
# Features
|
|
421
|
+
enable_program_scan=self._enable_program_scan,
|
|
422
|
+
enable_sysvar_scan=self._enable_sysvar_scan,
|
|
423
|
+
enable_device_firmware_check=self._enable_device_firmware_check,
|
|
424
|
+
program_markers=self._program_markers,
|
|
425
|
+
sysvar_markers=self._sysvar_markers,
|
|
426
|
+
# Storage
|
|
427
|
+
storage_directory=self._storage_directory,
|
|
428
|
+
# Advanced
|
|
429
|
+
delay_new_device_creation=self._delay_new_device_creation,
|
|
430
|
+
ignore_custom_device_definition_models=self._ignore_custom_device_definition_models,
|
|
431
|
+
interfaces_requiring_periodic_refresh=self._interfaces_requiring_periodic_refresh,
|
|
432
|
+
max_read_workers=self._max_read_workers,
|
|
433
|
+
optional_settings=self._optional_settings,
|
|
434
|
+
schedule_timer_config=self._schedule_timer_config,
|
|
435
|
+
start_direct=self._start_direct,
|
|
436
|
+
timeout_config=self._timeout_config,
|
|
437
|
+
un_ignore_list=self._un_ignore_list,
|
|
438
|
+
use_group_channel_for_cover_state=self._use_group_channel_for_cover_state,
|
|
439
|
+
locale=self._locale,
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
def validate(self) -> list[ValidationError]:
|
|
443
|
+
"""
|
|
444
|
+
Validate the current configuration.
|
|
445
|
+
|
|
446
|
+
Returns:
|
|
447
|
+
List of validation errors. Empty if configuration is valid.
|
|
448
|
+
|
|
449
|
+
"""
|
|
450
|
+
errors: list[ValidationError] = []
|
|
451
|
+
|
|
452
|
+
if not self._name:
|
|
453
|
+
errors.append(ValidationError(field="name", message="Name is required"))
|
|
454
|
+
|
|
455
|
+
if not self._host:
|
|
456
|
+
errors.append(ValidationError(field="host", message="Host is required"))
|
|
457
|
+
|
|
458
|
+
if self._username is None:
|
|
459
|
+
errors.append(ValidationError(field="username", message="Username is required"))
|
|
460
|
+
|
|
461
|
+
if self._password is None:
|
|
462
|
+
errors.append(ValidationError(field="password", message="Password is required"))
|
|
463
|
+
|
|
464
|
+
if not self._interfaces:
|
|
465
|
+
errors.append(ValidationError(field="interfaces", message="At least one interface is required"))
|
|
466
|
+
|
|
467
|
+
return errors
|
|
468
|
+
|
|
469
|
+
def with_callback(
|
|
470
|
+
self,
|
|
471
|
+
*,
|
|
472
|
+
host: str | None = None,
|
|
473
|
+
port: int | None = None,
|
|
474
|
+
listen_ip: str | None = None,
|
|
475
|
+
listen_port: int | None = None,
|
|
476
|
+
) -> Self:
|
|
477
|
+
"""
|
|
478
|
+
Configure XML-RPC callback server settings.
|
|
479
|
+
|
|
480
|
+
The callback server receives events from the CCU. If not configured,
|
|
481
|
+
the system auto-detects appropriate values.
|
|
482
|
+
|
|
483
|
+
Args:
|
|
484
|
+
host: Callback host address reported to CCU. Auto-detected if None.
|
|
485
|
+
port: Callback port reported to CCU. Auto-assigned if None.
|
|
486
|
+
listen_ip: IP address to bind the server to. Uses host if None.
|
|
487
|
+
listen_port: Port to listen on. Uses port if None.
|
|
488
|
+
|
|
489
|
+
Returns:
|
|
490
|
+
Self for method chaining.
|
|
491
|
+
|
|
492
|
+
"""
|
|
493
|
+
self._callback_host = host
|
|
494
|
+
self._callback_port_xml_rpc = port
|
|
495
|
+
self._listen_ip_addr = listen_ip
|
|
496
|
+
self._listen_port_xml_rpc = listen_port
|
|
497
|
+
return self
|
|
498
|
+
|
|
499
|
+
def with_central_id(self, *, central_id: str) -> Self:
|
|
500
|
+
"""
|
|
501
|
+
Set custom central ID.
|
|
502
|
+
|
|
503
|
+
Args:
|
|
504
|
+
central_id: Unique ID for this central. Auto-generated as
|
|
505
|
+
"{name}-{host}" if not specified.
|
|
506
|
+
|
|
507
|
+
Returns:
|
|
508
|
+
Self for method chaining.
|
|
509
|
+
|
|
510
|
+
"""
|
|
511
|
+
self._central_id = central_id
|
|
512
|
+
return self
|
|
513
|
+
|
|
514
|
+
def with_client_session(self, *, session: ClientSession) -> Self:
|
|
515
|
+
"""
|
|
516
|
+
Provide a custom aiohttp ClientSession.
|
|
517
|
+
|
|
518
|
+
Args:
|
|
519
|
+
session: Existing aiohttp session to use for HTTP requests.
|
|
520
|
+
If not provided, a new session will be created.
|
|
521
|
+
|
|
522
|
+
Returns:
|
|
523
|
+
Self for method chaining.
|
|
524
|
+
|
|
525
|
+
"""
|
|
526
|
+
self._client_session = session
|
|
527
|
+
return self
|
|
528
|
+
|
|
529
|
+
def with_credentials(self, *, username: str, password: str) -> Self:
|
|
530
|
+
"""
|
|
531
|
+
Set authentication credentials (required).
|
|
532
|
+
|
|
533
|
+
Args:
|
|
534
|
+
username: CCU/Homegear username.
|
|
535
|
+
password: CCU/Homegear password.
|
|
536
|
+
|
|
537
|
+
Returns:
|
|
538
|
+
Self for method chaining.
|
|
539
|
+
|
|
540
|
+
"""
|
|
541
|
+
self._username = username
|
|
542
|
+
self._password = password
|
|
543
|
+
return self
|
|
544
|
+
|
|
545
|
+
def with_firmware_check(self, *, enabled: bool = True) -> Self:
|
|
546
|
+
"""
|
|
547
|
+
Enable device firmware update checking.
|
|
548
|
+
|
|
549
|
+
Args:
|
|
550
|
+
enabled: Enable periodic firmware update availability checks.
|
|
551
|
+
|
|
552
|
+
Returns:
|
|
553
|
+
Self for method chaining.
|
|
554
|
+
|
|
555
|
+
"""
|
|
556
|
+
self._enable_device_firmware_check = enabled
|
|
557
|
+
return self
|
|
558
|
+
|
|
559
|
+
def with_host(self, *, host: str) -> Self:
|
|
560
|
+
"""
|
|
561
|
+
Set the CCU/Homegear host address (required).
|
|
562
|
+
|
|
563
|
+
Args:
|
|
564
|
+
host: IP address or hostname of the CCU/Homegear backend.
|
|
565
|
+
|
|
566
|
+
Returns:
|
|
567
|
+
Self for method chaining.
|
|
568
|
+
|
|
569
|
+
Raises:
|
|
570
|
+
ValueError: If host is empty or whitespace-only.
|
|
571
|
+
|
|
572
|
+
"""
|
|
573
|
+
if not host or not host.strip():
|
|
574
|
+
raise ValueError("Host cannot be empty") # i18n-exc: ignore
|
|
575
|
+
self._host = host.strip()
|
|
576
|
+
return self
|
|
577
|
+
|
|
578
|
+
def with_json_port(self, *, port: int) -> Self:
|
|
579
|
+
"""
|
|
580
|
+
Set JSON-RPC port for ReGaHss communication.
|
|
581
|
+
|
|
582
|
+
Args:
|
|
583
|
+
port: JSON-RPC port (default: 80 plain, 443 TLS).
|
|
584
|
+
|
|
585
|
+
Returns:
|
|
586
|
+
Self for method chaining.
|
|
587
|
+
|
|
588
|
+
"""
|
|
589
|
+
self._json_port = port
|
|
590
|
+
return self
|
|
591
|
+
|
|
592
|
+
def with_locale(self, *, locale: str) -> Self:
|
|
593
|
+
"""
|
|
594
|
+
Set locale for translations.
|
|
595
|
+
|
|
596
|
+
Args:
|
|
597
|
+
locale: Locale code (e.g., "en", "de").
|
|
598
|
+
|
|
599
|
+
Returns:
|
|
600
|
+
Self for method chaining.
|
|
601
|
+
|
|
602
|
+
"""
|
|
603
|
+
self._locale = locale
|
|
604
|
+
return self
|
|
605
|
+
|
|
606
|
+
def with_name(self, *, name: str) -> Self:
|
|
607
|
+
"""
|
|
608
|
+
Set the central unit name (required).
|
|
609
|
+
|
|
610
|
+
Args:
|
|
611
|
+
name: Unique identifier for this central unit.
|
|
612
|
+
Used in logging, entity IDs, and storage paths.
|
|
613
|
+
|
|
614
|
+
Returns:
|
|
615
|
+
Self for method chaining.
|
|
616
|
+
|
|
617
|
+
Raises:
|
|
618
|
+
ValueError: If name is empty or whitespace-only.
|
|
619
|
+
|
|
620
|
+
"""
|
|
621
|
+
if not name or not name.strip():
|
|
622
|
+
raise ValueError("Name cannot be empty") # i18n-exc: ignore
|
|
623
|
+
self._name = name.strip()
|
|
624
|
+
return self
|
|
625
|
+
|
|
626
|
+
def with_optional_settings(self, *, settings: tuple[OptionalSettings | str, ...]) -> Self:
|
|
627
|
+
"""
|
|
628
|
+
Set optional feature settings.
|
|
629
|
+
|
|
630
|
+
Args:
|
|
631
|
+
settings: Tuple of OptionalSettings flags.
|
|
632
|
+
|
|
633
|
+
Returns:
|
|
634
|
+
Self for method chaining.
|
|
635
|
+
|
|
636
|
+
"""
|
|
637
|
+
self._optional_settings = settings
|
|
638
|
+
return self
|
|
639
|
+
|
|
640
|
+
def with_programs(
|
|
641
|
+
self,
|
|
642
|
+
*,
|
|
643
|
+
enabled: bool = True,
|
|
644
|
+
markers: tuple[DescriptionMarker | str, ...] | None = None,
|
|
645
|
+
) -> Self:
|
|
646
|
+
"""
|
|
647
|
+
Configure CCU program scanning.
|
|
648
|
+
|
|
649
|
+
Args:
|
|
650
|
+
enabled: Enable program discovery and synchronization.
|
|
651
|
+
markers: Optional markers to filter programs by description content.
|
|
652
|
+
|
|
653
|
+
Returns:
|
|
654
|
+
Self for method chaining.
|
|
655
|
+
|
|
656
|
+
"""
|
|
657
|
+
self._enable_program_scan = enabled
|
|
658
|
+
if markers is not None:
|
|
659
|
+
self._program_markers = markers
|
|
660
|
+
return self
|
|
661
|
+
|
|
662
|
+
def with_schedule_timer_config(self, *, config: ScheduleTimerConfig) -> Self:
|
|
663
|
+
"""
|
|
664
|
+
Set custom scheduler timer configuration.
|
|
665
|
+
|
|
666
|
+
Args:
|
|
667
|
+
config: ScheduleTimerConfig with custom interval values.
|
|
668
|
+
|
|
669
|
+
Returns:
|
|
670
|
+
Self for method chaining.
|
|
671
|
+
|
|
672
|
+
"""
|
|
673
|
+
self._schedule_timer_config = config
|
|
674
|
+
return self
|
|
675
|
+
|
|
676
|
+
def with_start_direct(self, *, enabled: bool = True) -> Self:
|
|
677
|
+
"""
|
|
678
|
+
Enable direct start mode (skip some initialization).
|
|
679
|
+
|
|
680
|
+
Args:
|
|
681
|
+
enabled: Enable direct start mode.
|
|
682
|
+
|
|
683
|
+
Returns:
|
|
684
|
+
Self for method chaining.
|
|
685
|
+
|
|
686
|
+
"""
|
|
687
|
+
self._start_direct = enabled
|
|
688
|
+
return self
|
|
689
|
+
|
|
690
|
+
def with_storage(self, *, directory: str) -> Self:
|
|
691
|
+
"""
|
|
692
|
+
Configure persistent storage location.
|
|
693
|
+
|
|
694
|
+
Args:
|
|
695
|
+
directory: Path to storage directory for caches and descriptions.
|
|
696
|
+
|
|
697
|
+
Returns:
|
|
698
|
+
Self for method chaining.
|
|
699
|
+
|
|
700
|
+
"""
|
|
701
|
+
self._storage_directory = directory
|
|
702
|
+
return self
|
|
703
|
+
|
|
704
|
+
def with_sysvars(
|
|
705
|
+
self,
|
|
706
|
+
*,
|
|
707
|
+
enabled: bool = True,
|
|
708
|
+
markers: tuple[DescriptionMarker | str, ...] | None = None,
|
|
709
|
+
) -> Self:
|
|
710
|
+
"""
|
|
711
|
+
Configure CCU system variable scanning.
|
|
712
|
+
|
|
713
|
+
Args:
|
|
714
|
+
enabled: Enable sysvar discovery and synchronization.
|
|
715
|
+
markers: Optional markers to filter sysvars by description content.
|
|
716
|
+
|
|
717
|
+
Returns:
|
|
718
|
+
Self for method chaining.
|
|
719
|
+
|
|
720
|
+
"""
|
|
721
|
+
self._enable_sysvar_scan = enabled
|
|
722
|
+
if markers is not None:
|
|
723
|
+
self._sysvar_markers = markers
|
|
724
|
+
return self
|
|
725
|
+
|
|
726
|
+
def with_timeout_config(self, *, config: TimeoutConfig) -> Self:
|
|
727
|
+
"""
|
|
728
|
+
Set custom timeout configuration.
|
|
729
|
+
|
|
730
|
+
Args:
|
|
731
|
+
config: TimeoutConfig with custom timeout values.
|
|
732
|
+
|
|
733
|
+
Returns:
|
|
734
|
+
Self for method chaining.
|
|
735
|
+
|
|
736
|
+
"""
|
|
737
|
+
self._timeout_config = config
|
|
738
|
+
return self
|
|
739
|
+
|
|
740
|
+
def with_tls(self, *, enabled: bool = True, verify: bool = True) -> Self:
|
|
741
|
+
"""
|
|
742
|
+
Configure TLS settings.
|
|
743
|
+
|
|
744
|
+
When TLS is enabled, interface ports automatically use TLS variants
|
|
745
|
+
(e.g., 2010 → 42010 for HMIP_RF).
|
|
746
|
+
|
|
747
|
+
Args:
|
|
748
|
+
enabled: Enable TLS encryption for all connections.
|
|
749
|
+
verify: Verify TLS certificates. Set to False for self-signed certs.
|
|
750
|
+
Note: Disabling verification reduces security.
|
|
751
|
+
|
|
752
|
+
Returns:
|
|
753
|
+
Self for method chaining.
|
|
754
|
+
|
|
755
|
+
"""
|
|
756
|
+
self._tls = enabled
|
|
757
|
+
self._verify_tls = verify
|
|
758
|
+
return self
|
|
759
|
+
|
|
760
|
+
def with_un_ignore_list(self, *, parameters: frozenset[str]) -> Self:
|
|
761
|
+
"""
|
|
762
|
+
Set parameters to un-ignore (make visible).
|
|
763
|
+
|
|
764
|
+
Args:
|
|
765
|
+
parameters: Frozenset of parameter names to un-ignore.
|
|
766
|
+
|
|
767
|
+
Returns:
|
|
768
|
+
Self for method chaining.
|
|
769
|
+
|
|
770
|
+
"""
|
|
771
|
+
self._un_ignore_list = parameters
|
|
772
|
+
return self
|