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,237 @@
|
|
|
1
|
+
{
|
|
2
|
+
"exception.central.create_devices.no_clients": "CREATE_DEVICES failed: No clients initialized. Not starting central {name}.",
|
|
3
|
+
"exception.central.decorators.backend_system_handler.args_exception": "ARGS-EXCEPTION backend_system_handler [{reason}]",
|
|
4
|
+
"exception.central.decorators.backend_system_handler.identify_central_failed": "EXEC_BACKEND_SYSTEM_CALLBACK failed: Problem with identifying central: {reason}",
|
|
5
|
+
"exception.central.decorators.event_handler.args_exception": "ARGS-EXCEPTION event_handler [{reason}]",
|
|
6
|
+
"exception.central.get_client.interface_missing": "GET_CLIENT failed: Missing client for interface_id {interface_id} on central {name}",
|
|
7
|
+
"exception.central.get_client.interface_type_missing": "GET_CLIENT failed: Missing client for interface {interface} on central {name}",
|
|
8
|
+
"exception.central.get_client.no_parameter": "GET_CLIENT failed: Either interface_id or interface must be provided on central {name}",
|
|
9
|
+
"exception.central.rpc_server.invalid_xml": "Invalid XML: {error}",
|
|
10
|
+
"exception.central.rpc_server.parse_error": "Parse error: {error}",
|
|
11
|
+
"exception.central.start.failed": "Failed to start central unit {name}: {reason}",
|
|
12
|
+
"exception.central.validate_config.no_clients": "VALIDATE_CONFIG: No clients defined.",
|
|
13
|
+
"exception.client.add_link.failed": "ADD_LINK failed for {sender}/{receiver}/{name}/{description}: {reason}",
|
|
14
|
+
"exception.client.client_config.no_connection": "No connection to {interface_id}",
|
|
15
|
+
"exception.client.client_config.unable_to_connect": "Unable to connect {reason}",
|
|
16
|
+
"exception.client.get_all_device_data.failed": "GET_ALL_DEVICE_DATA failed: Unable to fetch device data for interface {interface}",
|
|
17
|
+
"exception.client.get_install_mode.failed": "GET_INSTALL_MODE failed for {interface_id}: {reason}",
|
|
18
|
+
"exception.client.get_link_peers.failed": "GET_LINK_PEERS failed for {address}: {reason}",
|
|
19
|
+
"exception.client.get_links.failed": "GET_LINKS failed for {address}: {reason}",
|
|
20
|
+
"exception.client.get_metadata.failed": "GET_METADATA failed for {address}/{data_id}: {reason}",
|
|
21
|
+
"exception.client.get_paramset.failed": "GET_PARAMSET failed for {address}/{paramset_key}: {reason}",
|
|
22
|
+
"exception.client.get_value.failed": "GET_VALUE failed for {channel_address}/{parameter}/{paramset_key}: {reason}",
|
|
23
|
+
"exception.client.interface_config.port_required": "VALIDATE interface config failed: Port must defined for interface {interface}",
|
|
24
|
+
"exception.client.json_ccu.get_paramset.failed": "GET_PARAMSET failed for {address}/{paramset_key}: {reason}",
|
|
25
|
+
"exception.client.json_ccu.get_value.failed": "GET_VALUE failed for: {channel_address}/{parameter}/{paramset_key}: {reason}",
|
|
26
|
+
"exception.client.json_ccu.set_value.unknown_type": "SET_VALUE failed: Unable to identify parameter type {channel_address}/{paramset_key}/{parameter}",
|
|
27
|
+
"exception.client.json_post.connector_certificate_error": "Connector certificate error: {reason}",
|
|
28
|
+
"exception.client.json_post.connector_error": "Connector error: {reason}",
|
|
29
|
+
"exception.client.json_post.http_status": "HTTP status: {status}",
|
|
30
|
+
"exception.client.json_post.login_failed": "Error while logging in",
|
|
31
|
+
"exception.client.json_post.method_unsupported": "POST: method '{method} not supported by the backend.",
|
|
32
|
+
"exception.client.json_post.no_credentials": "No credentials set",
|
|
33
|
+
"exception.client.json_post.no_response": "POST method failed with no response",
|
|
34
|
+
"exception.client.json_post.no_session": "ClientSession not initialized",
|
|
35
|
+
"exception.client.json_rpc.circuit_open": "Circuit breaker is open for {url} - requests temporarily blocked",
|
|
36
|
+
"exception.client.parameter.not_found": "Parameter {parameter} could not be found: {interface_id}/{channel_address}/{paramset_key}",
|
|
37
|
+
"exception.client.parameter.operation_unsupported": "Parameter {parameter} does not support the requested operation {operation}",
|
|
38
|
+
"exception.client.parameter.value_above_max": "Value {value} for parameter {parameter} exceeds maximum {max_value}",
|
|
39
|
+
"exception.client.parameter.value_below_min": "Value {value} for parameter {parameter} is below minimum {min_value}",
|
|
40
|
+
"exception.client.paramset_key.invalid": "Parameter paramset_key is neither a valid ParamsetKey nor a channel address.",
|
|
41
|
+
"exception.client.put_paramset.failed": "PUT_PARAMSET failed for {channel_address}/{paramset_key}/{values}: {reason}",
|
|
42
|
+
"exception.client.remove_link.failed": "REMOVE_LINK failed for {sender}/{receiver}: {reason}",
|
|
43
|
+
"exception.client.report_value_usage.failed": "REPORT_VALUE_USAGE failed: {address}/{value_id}/{ref_counter}: {reason}",
|
|
44
|
+
"exception.client.rx_mode.unsupported": "Unsupported rx_mode: {rx_mode}",
|
|
45
|
+
"exception.client.script.missing": "Script file for {script} does not exist",
|
|
46
|
+
"exception.client.set_install_mode.failed": "SET_INSTALL_MODE failed for {interface_id}: {reason}",
|
|
47
|
+
"exception.client.set_metadata.failed": "SET_METADATA failed for {address}/{data_id}/{value}: {reason}",
|
|
48
|
+
"exception.client.set_value.failed": "SET_VALUE failed for {channel_address}/{parameter}={value}: {reason}",
|
|
49
|
+
"exception.client.update_device_firmware.failed": "UPDATE_DEVICE_FIRMWARE failed: {reason}",
|
|
50
|
+
"exception.client.xmlrpc.circuit_open": "Circuit breaker is open for {interface_id} - requests temporarily blocked",
|
|
51
|
+
"exception.client.xmlrpc.http_connection_state_error": "HTTP connection state error on {interface_id}: {reason}",
|
|
52
|
+
"exception.client.xmlrpc.method_unsupported": "XmlRPC.__ASYNC_REQUEST: method '{method} not supported by the backend.",
|
|
53
|
+
"exception.client.xmlrpc.no_connection": "No connection to {interface_id}",
|
|
54
|
+
"exception.client.xmlrpc.no_connection_with_reason": "No connection to {context}: {reason}",
|
|
55
|
+
"exception.client.xmlrpc.null_proxy_unsupported": "XML-RPC not supported on {interface_id} - use JSON-RPC methods instead",
|
|
56
|
+
"exception.client.xmlrpc.os_error": "OSError on {interface_id}: {reason}",
|
|
57
|
+
"exception.client.xmlrpc.ssl_error": "SSLError on {interface_id}: {reason}",
|
|
58
|
+
"exception.config.check.callback_host.invalid": "Invalid callback hostname or ipv4 address",
|
|
59
|
+
"exception.config.check.callback_port_xml_rpc.invalid": "Invalid xml rpc callback port",
|
|
60
|
+
"exception.config.check.host.invalid": "Invalid hostname or ipv4 address",
|
|
61
|
+
"exception.config.check.instance_name.separator": "Instance name must not contain {sep}",
|
|
62
|
+
"exception.config.check.json_port.invalid": "Invalid json port",
|
|
63
|
+
"exception.config.check.password.invalid": "Password is not valid",
|
|
64
|
+
"exception.config.check.password.required": "Password is required",
|
|
65
|
+
"exception.config.check.primary_interface.missing": "No primary interface ({interfaces}) defined",
|
|
66
|
+
"exception.config.check.username.empty": "Username must not be empty",
|
|
67
|
+
"exception.config.invalid": "Invalid configuration: {failures}",
|
|
68
|
+
"exception.create_central.failed": "Not able to create a central: {reason}",
|
|
69
|
+
"exception.model.action_select.value_not_in_value_list": "ACTION_SELECT {name} (unique_id={unique_id}): Value not in value list",
|
|
70
|
+
"exception.model.custom.climate.set_temperature.invalid": "SET_TEMPERATURE failed: Invalid temperature: {temperature} (min: {min}, max: {max})",
|
|
71
|
+
"exception.model.custom.definition.create_custom_data_point.failed": "_CREATE_CUSTOM_DATA_POINT: unable to create custom data point: {reason}",
|
|
72
|
+
"exception.model.custom.light.invalid_brightness": "Invalid brightness specified for data_point {full_name}: {value}. Must be 0.0-1.0.",
|
|
73
|
+
"exception.model.custom.light.invalid_color": "Invalid color specified for data_point {full_name}: {value}",
|
|
74
|
+
"exception.model.custom.light.invalid_duration_unit": "Invalid duration unit specified for data_point {full_name}: {value}",
|
|
75
|
+
"exception.model.custom.light.invalid_on_time": "Invalid on time specified for data_point {full_name}: {value}",
|
|
76
|
+
"exception.model.custom.light.invalid_repetitions": "Invalid repetitions specified for data_point {full_name}: {value}",
|
|
77
|
+
"exception.model.custom.siren.invalid_duration_unit": "Invalid duration unit specified for data_point {full_name}: {value}",
|
|
78
|
+
"exception.model.custom.siren.invalid_light": "Invalid light specified for data_point {full_name}: {value}",
|
|
79
|
+
"exception.model.custom.siren.invalid_repetitions": "Invalid repetitions specified for data_point {full_name}: {value}",
|
|
80
|
+
"exception.model.custom.siren.invalid_soundfile": "Invalid soundfile specified for data_point {full_name}: {value}",
|
|
81
|
+
"exception.model.custom.siren.invalid_soundfile_index": "Soundfile index must be 1-189, got {index}",
|
|
82
|
+
"exception.model.custom.siren.invalid_tone": "Invalid tone specified for data_point {full_name}: {value}",
|
|
83
|
+
"exception.model.custom.siren.invalid_volume": "Invalid volume specified for data_point {full_name}: {value}. Must be 0.0-1.0.",
|
|
84
|
+
"exception.model.custom.text_display.invalid_alignment": "Invalid alignment specified for data_point {full_name}: {value}",
|
|
85
|
+
"exception.model.custom.text_display.invalid_background_color": "Invalid background color specified for data_point {full_name}: {value}",
|
|
86
|
+
"exception.model.custom.text_display.invalid_display_id": "Invalid display ID specified for data_point {full_name}: {value}. Must be 1-5.",
|
|
87
|
+
"exception.model.custom.text_display.invalid_icon": "Invalid icon specified for data_point {full_name}: {value}",
|
|
88
|
+
"exception.model.custom.text_display.invalid_interval": "Invalid interval specified for data_point {full_name}: {value}. Must be 1-15.",
|
|
89
|
+
"exception.model.custom.text_display.invalid_repeat": "Invalid repeat count specified for data_point {full_name}: {value}. Must be 0-15.",
|
|
90
|
+
"exception.model.custom.text_display.invalid_sound": "Invalid sound specified for data_point {full_name}: {value}",
|
|
91
|
+
"exception.model.custom.text_display.invalid_text_color": "Invalid text color specified for data_point {full_name}: {value}",
|
|
92
|
+
"exception.model.data_point.subscribe_handler.already_registered": "REGISTER_DATA_POINT_UPDATED_CALLBACK failed: hm_data_point: {full_name} is already registered by {custom_id}",
|
|
93
|
+
"exception.model.device.export_device_definition.failed": "EXPORT_DEVICE_DEFINITION failed: {reason}",
|
|
94
|
+
"exception.model.event.create_event.failed": "CREATE_EVENT_AND_APPEND_TO_CHANNEL: Unable to create event: {reason}",
|
|
95
|
+
"exception.model.generic.create_data_point.failed": "CREATE_DATA_POINT_AND_APPEND_TO_CHANNEL: Unable to create data_point: {reason}",
|
|
96
|
+
"exception.model.hub.number.invalid_value": "SYSVAR.NUMBER failed: Invalid value: {value} (min: {min}, max: {max})",
|
|
97
|
+
"exception.model.number.invalid_value": "NUMBER failed: Invalid value: {value} (min: {min}, max: {max}, special:{special})",
|
|
98
|
+
"exception.model.select.value_not_in_value_list": "Value not in value_list for {name}/{unique_id}",
|
|
99
|
+
"exception.model.week_profile.copy_schedule.profile_count_mismatch": "Copy schedule profile is only: No of schedule profile must be identical",
|
|
100
|
+
"exception.model.week_profile.copy_schedule.same_device_invalid": "Copy schedule profile on same device is only possible with defined and different source/target profiles",
|
|
101
|
+
"exception.model.week_profile.schedule.unsupported": "Schedule is not supported by device {name}",
|
|
102
|
+
"exception.model.week_profile.source_profile.not_loaded": "Source profile {source_profile} could not be loaded.",
|
|
103
|
+
"exception.model.week_profile.validate.base_temperature_out_of_range": "VALIDATE_PROFILE: Base temperature {base_temperature} not in valid range (min: {min}, max: {max})",
|
|
104
|
+
"exception.model.week_profile.validate.endtime_missing": "VALIDATE_PROFILE: ENDTIME is missing.",
|
|
105
|
+
"exception.model.week_profile.validate.overlap": "VALIDATE_PROFILE: Timespans are overlapping with a previous slot for start time: {start} / end time: {end}",
|
|
106
|
+
"exception.model.week_profile.validate.profile_name_invalid": "Not a valid profile name: {profile_name}",
|
|
107
|
+
"exception.model.week_profile.validate.sequence_rising": "VALIDATE_PROFILE: Time sequence must be rising. {time} is lower than the previous value {previous} for profile: {profile} / week day: {weekday} / slot no: {no}",
|
|
108
|
+
"exception.model.week_profile.validate.slot_missing": "VALIDATE_PROFILE: slot no {no} is missing in profile: {profile} / week day: {weekday}",
|
|
109
|
+
"exception.model.week_profile.validate.slot_type_missing": "VALIDATE_PROFILE: slot type {slot_type} is missing in profile: {profile} / week day: {weekday} / slot no: {no}",
|
|
110
|
+
"exception.model.week_profile.validate.start_before_end": "VALIDATE_PROFILE: Start time {start} must be lower than end time {end}",
|
|
111
|
+
"exception.model.week_profile.validate.starttime_missing": "VALIDATE_PROFILE: STARTTIME is missing.",
|
|
112
|
+
"exception.model.week_profile.validate.temperature_missing": "VALIDATE_PROFILE: TEMPERATURE is missing.",
|
|
113
|
+
"exception.model.week_profile.validate.temperature_out_of_range_for_profile_slot": "VALIDATE_PROFILE: Temperature {temperature} not in valid range (min: {min}, max: {max}) for profile: {profile} / week day: {weekday} / slot no: {no}",
|
|
114
|
+
"exception.model.week_profile.validate.temperature_out_of_range_for_times": "VALIDATE_PROFILE: Temperature {temperature} not in valid range (min: {min}, max: {max}) for start time: {start} / end time: {end}",
|
|
115
|
+
"exception.model.week_profile.validate.time_convert_failed": "Failed to convert time {time}. Format must be hh:mm.",
|
|
116
|
+
"exception.model.week_profile.validate.time_invalid_format": "Time {time} is not valid. Format must be hh:mm with min: {min} and max: {max}",
|
|
117
|
+
"exception.model.week_profile.validate.time_out_of_bounds_profile_slot": "VALIDATE_PROFILE: Time {time} must be between {min_time} and {max_time} for profile: {profile} / week day: {weekday} / slot no: {no}",
|
|
118
|
+
"exception.model.week_profile.validate.too_few_slots": "VALIDATE_PROFILE: Too few slots in profile: {profile} / week day: {weekday}",
|
|
119
|
+
"exception.model.week_profile.validate.too_many_slots": "VALIDATE_PROFILE: Too many slots in profile: {profile} / week day: {weekday}",
|
|
120
|
+
"exception.startup.validation_failed": "AioHomematic startup validation failed: {reason}",
|
|
121
|
+
"exception.store.device_description.not_found": "Description not found for {address} on interface {interface_id}",
|
|
122
|
+
"exception.store.session_recorder.ttl_positive": "default_ttl_seconds must be positive",
|
|
123
|
+
"exception.support.boolean.invalid_type": "Invalid literal for boolean. Not a string.",
|
|
124
|
+
"exception.support.check_or_create_directory.failed": "Unable to create directory {directory}: {reason}",
|
|
125
|
+
"exception.support.get_local_ip.resolve_failed": "Can't resolve host for {host}:{port}: {reason}",
|
|
126
|
+
"exception.support.host_empty": "Host cannot be empty",
|
|
127
|
+
"exception.support.host_invalid": "Invalid host format: {host}. Must be a valid hostname or IP address",
|
|
128
|
+
"exception.validator.categories.not_exhaustive": "BLOCKED_CATEGORIES/CATEGORIES/HUB_CATEGORIES are not exhaustive. Missing categories: {missing}",
|
|
129
|
+
"exception.validator.channel_address.invalid": "CHANNEL_ADDRESS is invalid",
|
|
130
|
+
"exception.validator.custom_definition.invalid": "Custom data point definition schema is invalid",
|
|
131
|
+
"exception.validator.device_address.invalid": "DEVICE_ADDRESS is invalid",
|
|
132
|
+
"exception.validator.hostname.invalid": "Hostname is invalid",
|
|
133
|
+
"exception.validator.ipv4_address.invalid": "IPv4_address is invalid",
|
|
134
|
+
"exception.validator.paramset_key.invalid": "PARAMSET_KEY is invalid",
|
|
135
|
+
"exception.validator.password.invalid": "Password is invalid",
|
|
136
|
+
"exception.validator.undefined_in_lists": "DataPointCategory.UNDEFINED must not be present in BLOCKED_CATEGORIES/CATEGORIES/HUB_CATEGORIES",
|
|
137
|
+
"issue.fetch_data_failed": "Failed to fetch device data from interface {interface_id}",
|
|
138
|
+
"issue.ping_pong_mismatch": "Ping-pong mismatch detected on {interface_id} ({mismatch_type}: {mismatch_count} mismatches)",
|
|
139
|
+
"log.backend_detection.detect_backend.backend_type": "DETECT_BACKEND: Detected backend type: {backend}",
|
|
140
|
+
"log.backend_detection.detect_backend.found_interfaces": "DETECT_BACKEND: Found interfaces via JSON-RPC: {interfaces}",
|
|
141
|
+
"log.backend_detection.detect_backend.found_version": "DETECT_BACKEND: Found version '{version}' on port {port}",
|
|
142
|
+
"log.backend_detection.detect_backend.json_rpc_fallback": "DETECT_BACKEND: JSON-RPC query failed, using detected interface",
|
|
143
|
+
"log.backend_detection.detect_backend.no_backend_found": "DETECT_BACKEND: No backend found on host {host}",
|
|
144
|
+
"log.backend_detection.detect_backend.probing": "DETECT_BACKEND: Probing {host}:{port} (TLS={tls}, interface={interface})",
|
|
145
|
+
"log.backend_detection.detect_backend.starting": "DETECT_BACKEND: Starting detection for host {host} (timeout={total_timeout}s)",
|
|
146
|
+
"log.backend_detection.detect_backend.total_timeout": "DETECT_BACKEND: Detection timed out after {total_timeout}s for host {host}",
|
|
147
|
+
"log.backend_detection.json_rpc.auth_failed": "DETECT_BACKEND: Authentication failed at {url}",
|
|
148
|
+
"log.backend_detection.json_rpc.connection_failed": "DETECT_BACKEND: Connection failed at {url}",
|
|
149
|
+
"log.backend_detection.json_rpc.interface_not_present": "DETECT_BACKEND: Interface '{interface}' is installed but not running - skipping",
|
|
150
|
+
"log.backend_detection.json_rpc.interface_present": "DETECT_BACKEND: Interface '{interface}' is present and running",
|
|
151
|
+
"log.backend_detection.json_rpc.is_present_failed": "DETECT_BACKEND: Failed to check if interface '{interface}' is present: {reason}",
|
|
152
|
+
"log.backend_detection.json_rpc.query_failed": "DETECT_BACKEND: JSON-RPC query failed at {url} - {exc_type}: {reason}",
|
|
153
|
+
"log.backend_detection.json_rpc.querying": "DETECT_BACKEND: Querying JSON-RPC at {url}",
|
|
154
|
+
"log.backend_detection.json_rpc.unknown_interface": "DETECT_BACKEND: Unknown interface '{interface}', skipping",
|
|
155
|
+
"log.backend_detection.xml_rpc.probe_error": "DETECT_BACKEND: XML-RPC probe error for {host}:{port} - {exc_type}: {reason}",
|
|
156
|
+
"log.backend_detection.xml_rpc.probe_failed": "DETECT_BACKEND: XML-RPC probe failed for {host}:{port} - {exc_type}: {reason}",
|
|
157
|
+
"log.backend_detection.xml_rpc.probe_timeout": "DETECT_BACKEND: XML-RPC probe timeout for {host}:{port}",
|
|
158
|
+
"log.central.accept_device_in_inbox.no_client": "ACCEPT_DEVICE_IN_INBOX failed: No primary client available for {device_address} on {name}",
|
|
159
|
+
"log.central.create_client.no_connection": "CREATE_CLIENT failed: No connection to interface {interface_id} [{reason}]",
|
|
160
|
+
"log.central.create_clients.already_created": "CREATE_CLIENTS: Clients for {name} are already created",
|
|
161
|
+
"log.central.create_clients.created_count_failed": "CREATE_CLIENTS failed: Created {created} of {total} clients",
|
|
162
|
+
"log.central.create_clients.interface_not_available": "CREATE_CLIENTS failed: Interface: {interface} is not available for the backend {name}",
|
|
163
|
+
"log.central.create_clients.no_interfaces": "CREATE_CLIENTS failed: No interfaces for {name} defined",
|
|
164
|
+
"log.central.create_clients.no_primary_identified": "CREATE_CLIENTS failed: No primary client identified for {name}",
|
|
165
|
+
"log.central.rename_device.not_found": "RENAME_DEVICE failed: Device {device_address} not found on {name}",
|
|
166
|
+
"log.central.restart_clients.restarted": "RESTART_CLIENTS: Central {name} restarted clients",
|
|
167
|
+
"log.central.rpc_server.background_task_failed": "Background task {task_name} failed: {error}",
|
|
168
|
+
"log.central.rpc_server.error": "ERROR failed: interface_id = {interface_id}, error_code = {error_code}, message = {msg}",
|
|
169
|
+
"log.central.rpc_server.method_failed": "XML-RPC method {method_name} failed",
|
|
170
|
+
"log.central.rpc_server.protocol_error": "XML-RPC protocol error: {error}",
|
|
171
|
+
"log.central.rpc_server.unexpected_error": "Unexpected error handling XML-RPC request",
|
|
172
|
+
"log.central.scheduler.check_connection.attempting_reconnect": "CHECK_CONNECTION: RPC stable for {name} - attempting full reconnection",
|
|
173
|
+
"log.central.scheduler.check_connection.connection_loss_detected": "CHECK_CONNECTION: Connection loss detected for {name} - starting staged reconnection",
|
|
174
|
+
"log.central.scheduler.check_connection.failed": "CHECK_CONNECTION failed: {exc_type} [{reason}]",
|
|
175
|
+
"log.central.scheduler.check_connection.no_clients": "CHECK_CONNECTION failed: No clients exist. Trying to create clients for server {name}",
|
|
176
|
+
"log.central.scheduler.check_connection.no_connection": "CHECK_CONNECTION failed: no connection: {reason}",
|
|
177
|
+
"log.central.scheduler.check_connection.reconnect_failed": "CHECK_CONNECTION: Reconnection failed for {name} - will retry",
|
|
178
|
+
"log.central.scheduler.check_connection.reconnect_success": "CHECK_CONNECTION: Reconnection successful for {name} - interfaces: {interfaces}",
|
|
179
|
+
"log.central.scheduler.check_connection.rpc_check_passed": "CHECK_CONNECTION: First RPC check passed for {name} - starting warmup",
|
|
180
|
+
"log.central.scheduler.check_connection.rpc_stable": "CHECK_CONNECTION: Second RPC check passed for {name} - services stable",
|
|
181
|
+
"log.central.scheduler.check_connection.rpc_unstable": "CHECK_CONNECTION: RPC became unavailable during warmup for {name} - retrying",
|
|
182
|
+
"log.central.scheduler.check_connection.tcp_check_timeout": "CHECK_CONNECTION: TCP port check timeout ({timeout}s) for {name} - restarting check",
|
|
183
|
+
"log.central.scheduler.check_connection.tcp_port_available": "CHECK_CONNECTION: TCP port {host}:{port} available for {name}",
|
|
184
|
+
"log.central.set_system_variable.not_found": "SET_SYSTEM_VARIABLE failed: Variable {legacy_name} not found on {name}",
|
|
185
|
+
"log.central.validate_config_and_get_system_information.client_failed": "VALIDATE_CONFIG_AND_GET_SYSTEM_INFORMATION failed for client {interface}: {reason}",
|
|
186
|
+
"log.client.circuit_breaker.state_transition": "CIRCUIT_BREAKER: {old_state} → {new_state} for {interface_id} (failures={failure_count}, successes={success_count})",
|
|
187
|
+
"log.client.create_backup_and_download.completed": "CREATE_BACKUP_AND_DOWNLOAD: Backup completed: {filename} ({size} bytes)",
|
|
188
|
+
"log.client.create_backup_and_download.failed": "CREATE_BACKUP_AND_DOWNLOAD: Backup creation failed",
|
|
189
|
+
"log.client.create_backup_and_download.idle": "CREATE_BACKUP_AND_DOWNLOAD: Unexpected idle status during backup",
|
|
190
|
+
"log.client.create_backup_and_download.running": "CREATE_BACKUP_AND_DOWNLOAD: Backup still running ({elapsed:.1f}s elapsed)",
|
|
191
|
+
"log.client.create_backup_and_download.timeout": "CREATE_BACKUP_AND_DOWNLOAD: Backup timed out after {max_wait_time:.1f}s",
|
|
192
|
+
"log.client.is_callback_alive.no_events": "IS_CALLBACK_ALIVE: Callback for {interface_id} has not received events for {seconds}s",
|
|
193
|
+
"log.client.json_rpc.accept_device_in_inbox.failed": "ACCEPT_DEVICE_IN_INBOX for {device_address} failed: Unable to decode json: {reason}",
|
|
194
|
+
"log.client.json_rpc.create_backup_start.failed": "CREATE_BACKUP_START failed: Unable to decode json: {reason}",
|
|
195
|
+
"log.client.json_rpc.create_backup_status.failed": "CREATE_BACKUP_STATUS failed: Unable to decode json: {reason}",
|
|
196
|
+
"log.client.json_rpc.do_login.max_attempts_reached": "DO_LOGIN failed: Max failed login attempts ({max_attempts}) reached. Consider checking credentials.",
|
|
197
|
+
"log.client.json_rpc.do_login.no_credentials": "DO_LOGIN failed: No credentials set",
|
|
198
|
+
"log.client.json_rpc.do_login.rate_limited": "DO_LOGIN: Rate limited after {attempts} failed attempts. Waiting {wait_time:.1f} seconds.",
|
|
199
|
+
"log.client.json_rpc.download_backup.error": "DOWNLOAD_BACKUP failed: {reason}",
|
|
200
|
+
"log.client.json_rpc.download_backup.failed": "DOWNLOAD_BACKUP failed: HTTP status {status}",
|
|
201
|
+
"log.client.json_rpc.download_backup.no_session": "DOWNLOAD_BACKUP failed: No session ID available",
|
|
202
|
+
"log.client.json_rpc.download_firmware.error": "DOWNLOAD_FIRMWARE failed: {reason}",
|
|
203
|
+
"log.client.json_rpc.download_firmware.failed": "DOWNLOAD_FIRMWARE failed: HTTP status {status}",
|
|
204
|
+
"log.client.json_rpc.download_firmware.no_session": "DOWNLOAD_FIRMWARE failed: No session ID available",
|
|
205
|
+
"log.client.json_rpc.get_all_system_variables.parse_failed": "GET_ALL_SYSTEM_VARIABLES failed: {exc_type} [{reason}] Failed to parse SysVar {legacy_name}",
|
|
206
|
+
"log.client.json_rpc.get_backend_info.failed": "GET_BACKEND_INFO failed: Unable to decode json: {reason}",
|
|
207
|
+
"log.client.json_rpc.get_inbox_devices.decode_failed": "GET_INBOX_DEVICES failed: Unable to decode json: {reason}",
|
|
208
|
+
"log.client.json_rpc.get_program_descriptions.decode_failed": "GET_PROGRAM_DESCRIPTIONS failed: Unable to decode json: {reason}",
|
|
209
|
+
"log.client.json_rpc.get_service_messages.decode_failed": "GET_SERVICE_MESSAGES failed: Unable to decode json: {reason}",
|
|
210
|
+
"log.client.json_rpc.get_system_update_info.decode_failed": "GET_SYSTEM_UPDATE_INFO failed: Unable to decode json: {reason}",
|
|
211
|
+
"log.client.json_rpc.get_system_variable_descriptions.decode_failed": "GET_SYSTEM_VARIABLE_DESCRIPTIONS failed: Unable to decode json: {reason}",
|
|
212
|
+
"log.client.json_rpc.rename_channel.failed": "RENAME_CHANNEL for {channel_address} failed: Unable to decode json: {reason}",
|
|
213
|
+
"log.client.json_rpc.rename_device.failed": "RENAME_DEVICE for {device_address} failed: Unable to decode json: {reason}",
|
|
214
|
+
"log.client.json_rpc.set_system_variable.value_contains_html": "SET_SYSTEM_VARIABLE: Value ({value}) contains html tags. These are filtered out when writing.",
|
|
215
|
+
"log.client.json_rpc.trigger_firmware_update.failed": "TRIGGER_FIRMWARE_UPDATE failed: Unable to decode json: {reason}",
|
|
216
|
+
"log.client.json_rpc.trigger_firmware_update.not_triggered": "TRIGGER_FIRMWARE_UPDATE: {message}",
|
|
217
|
+
"log.client.json_rpc.trigger_firmware_update.success": "TRIGGER_FIRMWARE_UPDATE: {message}",
|
|
218
|
+
"log.client.reconnect.reconnected": "RECONNECT: re-connected client {interface_id}",
|
|
219
|
+
"log.client.update_device_firmware.result": "UPDATE_DEVICE_FIRMWARE: Executed firmware update for {device_address} with result '{result}'",
|
|
220
|
+
"log.client.update_device_firmware.try": "UPDATE_DEVICE_FIRMWARE: Trying firmware update for {device_address}",
|
|
221
|
+
"log.core.signal.shutdown": "Got signal: {sig}. Shutting down central",
|
|
222
|
+
"log.metrics.observer.counter_key_limit": "METRICS OBSERVER: Counter key limit reached, dropping: {metric_key}",
|
|
223
|
+
"log.metrics.observer.gauge_key_limit": "METRICS OBSERVER: Gauge key limit reached, dropping: {metric_key}",
|
|
224
|
+
"log.metrics.observer.latency_key_limit": "METRICS OBSERVER: Latency key limit reached, dropping: {metric_key}",
|
|
225
|
+
"log.model.custom.definition.validate_failed": "The custom data point definition could not be validated. {path}, {msg}",
|
|
226
|
+
"log.model.custom.text_display.send_text.burst_limit_warning": "SEND_TEXT: {full_name}: Burst limit warning active - command may be rate-limited",
|
|
227
|
+
"log.model.device.channel_description_not_found": "INIT_DEVICE: Skipping channel {address} - description could not be retrieved from CCU",
|
|
228
|
+
"log.model.generic_data_point.send_value.not_writable": "SEND_VALUE: writing to non-writable data_point {full_name} is not possible",
|
|
229
|
+
"log.model.hub.update.progress_completed": "Hub firmware update completed: {old_version} -> {new_version}",
|
|
230
|
+
"log.model.hub.update.progress_poll_error": "Error polling update status (CCU may be rebooting): {error}",
|
|
231
|
+
"log.model.hub.update.progress_timeout": "Hub firmware update monitoring timed out after {timeout}s",
|
|
232
|
+
"log.store.dynamic.pending_pong_mismatch": "Pending PONG mismatch: There is a mismatch between send ping events and received pong events for instance {interface_id}. Possible reason 1: You are running multiple instances with the same instance name configured for this integration. Re-add one instance! Otherwise this instance will not receive update events from your CCU. Possible reason 2: Something is stuck on the CCU or hasn't been cleaned up. Therefore, try a CCU restart. Possible reason 3: Your setup is misconfigured and this instance is not able to receive events from the CCU.",
|
|
233
|
+
"log.store.dynamic.unknown_pong_mismatch": "Unknown PONG Mismatch: Your instance {interface_id} receives PONG events, that it hasn't send. Possible reason 1: You are running multiple instances with the same instance name configured for this integration. Re-add one instance! Otherwise the other instance will not receive update events from your CCU. Possible reason 2: Something is stuck on the CCU or hasn't been cleaned up. Therefore, try a CCU restart.",
|
|
234
|
+
"log.store.session_recorder.activate.already_running": "ACTIVATE: Recording session is already running.",
|
|
235
|
+
"log.store.session_recorder.deactivate.already_running": "DEACTIVATE: Recording session is already running.",
|
|
236
|
+
"log.support.check_password.invalid_chars": "CHECK_CONFIG: password contains not allowed characters. Use only allowed characters. See password regex: {pattern}"
|
|
237
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2021-2026
|
|
3
|
+
"""
|
|
4
|
+
Shared typing aliases for handlers and common callable shapes.
|
|
5
|
+
|
|
6
|
+
This module centralizes `Callable[...]` type aliases to avoid repeating
|
|
7
|
+
signatures across the code base and to satisfy mypy strict rules.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from collections.abc import Callable, Coroutine, Mapping
|
|
13
|
+
from typing import Any, TypeAlias
|
|
14
|
+
|
|
15
|
+
ParamType = bool | int | float | str | None
|
|
16
|
+
|
|
17
|
+
# Generic zero-argument handler that returns nothing
|
|
18
|
+
ZeroArgHandler: TypeAlias = Callable[[], None]
|
|
19
|
+
|
|
20
|
+
# Unsubscribe callback - used as return type for subscription methods
|
|
21
|
+
# Single source of truth (formerly duplicated in api.py, central/__init__.py, event_bus.py)
|
|
22
|
+
UnsubscribeCallback: TypeAlias = ZeroArgHandler
|
|
23
|
+
|
|
24
|
+
# Device- and channel-scoped handlers
|
|
25
|
+
DeviceRemovedHandler: TypeAlias = ZeroArgHandler
|
|
26
|
+
DeviceUpdatedHandler: TypeAlias = ZeroArgHandler
|
|
27
|
+
FirmwareUpdateHandler: TypeAlias = ZeroArgHandler
|
|
28
|
+
LinkPeerChangedHandler: TypeAlias = ZeroArgHandler
|
|
29
|
+
|
|
30
|
+
# Data point update handlers may accept various keyword arguments depending on
|
|
31
|
+
# the data point type, hence we keep them variadic.
|
|
32
|
+
DataPointUpdatedHandler: TypeAlias = Callable[..., None]
|
|
33
|
+
|
|
34
|
+
# Common async/sync callable shapes
|
|
35
|
+
# Factory that returns a coroutine that resolves to None
|
|
36
|
+
AsyncTaskFactory: TypeAlias = Callable[[], Coroutine[Any, Any, None]]
|
|
37
|
+
# Factory that returns a coroutine with arbitrary result type
|
|
38
|
+
AsyncTaskFactoryAny: TypeAlias = Callable[[], Coroutine[Any, Any, Any]]
|
|
39
|
+
# Coroutine with any send/throw types and arbitrary result
|
|
40
|
+
CoroutineAny: TypeAlias = Coroutine[Any, Any, Any]
|
|
41
|
+
# Generic sync callable that returns Any
|
|
42
|
+
CallableAny: TypeAlias = Callable[..., Any]
|
|
43
|
+
# Generic sync callable that returns None
|
|
44
|
+
CallableNone: TypeAlias = Callable[..., None]
|
|
45
|
+
|
|
46
|
+
# Service method callable and mapping used by DataPoints and decorators
|
|
47
|
+
ServiceMethod: TypeAlias = Callable[..., Any]
|
|
48
|
+
ServiceMethodMap: TypeAlias = Mapping[str, ServiceMethod]
|
|
49
|
+
|
|
50
|
+
# Factory used by custom data point creation (make_ce_func)
|
|
51
|
+
CustomDataPointFactory: TypeAlias = Callable[..., None]
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2021-2026
|
|
3
|
+
"""
|
|
4
|
+
Validator functions used within aiohomematic.
|
|
5
|
+
|
|
6
|
+
Public API of this module is defined by __all__.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import inspect
|
|
12
|
+
|
|
13
|
+
import voluptuous as vol
|
|
14
|
+
|
|
15
|
+
from aiohomematic import i18n
|
|
16
|
+
from aiohomematic.const import BLOCKED_CATEGORIES, CATEGORIES, HUB_CATEGORIES, MAX_WAIT_FOR_CALLBACK, DataPointCategory
|
|
17
|
+
from aiohomematic.support import (
|
|
18
|
+
check_password,
|
|
19
|
+
is_channel_address,
|
|
20
|
+
is_device_address,
|
|
21
|
+
is_host,
|
|
22
|
+
is_ipv4_address,
|
|
23
|
+
is_paramset_key,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
channel_no: vol.All = vol.All(vol.Coerce(int), vol.Range(min=0, max=999))
|
|
27
|
+
positive_int: vol.All = vol.All(vol.Coerce(int), vol.Range(min=0))
|
|
28
|
+
wait_for: vol.All = vol.All(vol.Coerce(int), vol.Range(min=1, max=MAX_WAIT_FOR_CALLBACK))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def channel_address(value: str, /) -> str:
|
|
32
|
+
"""Validate channel_address."""
|
|
33
|
+
if is_channel_address(address=value):
|
|
34
|
+
return value
|
|
35
|
+
raise vol.Invalid(i18n.tr(key="exception.validator.channel_address.invalid"))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def device_address(value: str, /) -> str:
|
|
39
|
+
"""Validate channel_address."""
|
|
40
|
+
if is_device_address(address=value):
|
|
41
|
+
return value
|
|
42
|
+
raise vol.Invalid(i18n.tr(key="exception.validator.device_address.invalid"))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def hostname(value: str, /) -> str:
|
|
46
|
+
"""Validate hostname."""
|
|
47
|
+
if is_host(host=value):
|
|
48
|
+
return value
|
|
49
|
+
raise vol.Invalid(i18n.tr(key="exception.validator.hostname.invalid"))
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def ipv4_address(value: str, /) -> str:
|
|
53
|
+
"""Validate ipv4_address."""
|
|
54
|
+
if is_ipv4_address(address=value):
|
|
55
|
+
return value
|
|
56
|
+
raise vol.Invalid(i18n.tr(key="exception.validator.ipv4_address.invalid"))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def password(value: str, /) -> str:
|
|
60
|
+
"""Validate password."""
|
|
61
|
+
if check_password(password=value):
|
|
62
|
+
return value
|
|
63
|
+
raise vol.Invalid(i18n.tr(key="exception.validator.password.invalid"))
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def paramset_key(value: str, /) -> str:
|
|
67
|
+
"""Validate paramset_key."""
|
|
68
|
+
if is_paramset_key(paramset_key=value):
|
|
69
|
+
return value
|
|
70
|
+
raise vol.Invalid(i18n.tr(key="exception.validator.paramset_key.invalid"))
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _channel_address_wrapper(value: str, /) -> str:
|
|
74
|
+
"""Wrap channel_address for voluptuous callback."""
|
|
75
|
+
return channel_address(value)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _device_address_wrapper(value: str, /) -> str:
|
|
79
|
+
"""Wrap device_address for voluptuous callback."""
|
|
80
|
+
return device_address(value)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _hostname_wrapper(value: str, /) -> str:
|
|
84
|
+
"""Wrap hostname for voluptuous callback."""
|
|
85
|
+
return hostname(value)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _ipv4_address_wrapper(value: str, /) -> str:
|
|
89
|
+
"""Wrap ipv4_address for voluptuous callback."""
|
|
90
|
+
return ipv4_address(value)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
address = vol.All(vol.Coerce(str), vol.Any(_device_address_wrapper, _channel_address_wrapper))
|
|
94
|
+
host = vol.All(vol.Coerce(str), vol.Any(_hostname_wrapper, _ipv4_address_wrapper))
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def validate_startup() -> None:
|
|
98
|
+
"""
|
|
99
|
+
Validate enum and mapping exhaustiveness at startup.
|
|
100
|
+
|
|
101
|
+
- Ensure DataPointCategory coverage: all categories except UNDEFINED must be present
|
|
102
|
+
in either HUB_CATEGORIES or CATEGORIES. UNDEFINED must not appear in those lists.
|
|
103
|
+
"""
|
|
104
|
+
categories_in_lists = set(BLOCKED_CATEGORIES) | set(CATEGORIES) | set(HUB_CATEGORIES)
|
|
105
|
+
all_categories = set(DataPointCategory)
|
|
106
|
+
if DataPointCategory.UNDEFINED in categories_in_lists:
|
|
107
|
+
raise vol.Invalid(i18n.tr(key="exception.validator.undefined_in_lists"))
|
|
108
|
+
|
|
109
|
+
if missing := all_categories - {DataPointCategory.UNDEFINED} - categories_in_lists:
|
|
110
|
+
missing_str = ", ".join(sorted(c.value for c in missing))
|
|
111
|
+
raise vol.Invalid(
|
|
112
|
+
i18n.tr(
|
|
113
|
+
key="exception.validator.categories.not_exhaustive",
|
|
114
|
+
missing=missing_str,
|
|
115
|
+
)
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# Define public API for this module
|
|
120
|
+
__all__ = tuple(
|
|
121
|
+
sorted(
|
|
122
|
+
name
|
|
123
|
+
for name, obj in globals().items()
|
|
124
|
+
if not name.startswith("_")
|
|
125
|
+
and (inspect.isfunction(obj) or inspect.isclass(obj))
|
|
126
|
+
and getattr(obj, "__module__", __name__) == __name__
|
|
127
|
+
)
|
|
128
|
+
)
|