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,150 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2021-2026
|
|
3
|
+
"""
|
|
4
|
+
Serialization utilities for session recording.
|
|
5
|
+
|
|
6
|
+
This module provides functions to freeze and unfreeze Python objects for use
|
|
7
|
+
as dictionary keys in session caching. These utilities handle complex nested
|
|
8
|
+
structures including dicts, lists, sets, and datetime objects.
|
|
9
|
+
|
|
10
|
+
Public API
|
|
11
|
+
----------
|
|
12
|
+
- freeze_params: Convert any structure to a hashable string key
|
|
13
|
+
- unfreeze_params: Reconstruct original structure from frozen string
|
|
14
|
+
- cleanup_params_for_session: Clean script content in params for lookup
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import ast
|
|
20
|
+
from datetime import datetime
|
|
21
|
+
from typing import Any, Final
|
|
22
|
+
|
|
23
|
+
from aiohomematic.support import cleanup_script_for_session_recorder
|
|
24
|
+
|
|
25
|
+
_SCRIPT_KEY: Final = "script"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def cleanup_params_for_session(*, params: Any) -> Any:
|
|
29
|
+
"""Clean script in params for session lookup, keeping only !# name: and !# param: lines."""
|
|
30
|
+
if isinstance(params, dict) and _SCRIPT_KEY in params:
|
|
31
|
+
cleaned_params = dict(params)
|
|
32
|
+
cleaned_params[_SCRIPT_KEY] = cleanup_script_for_session_recorder(script=params[_SCRIPT_KEY])
|
|
33
|
+
return cleaned_params
|
|
34
|
+
return params
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def freeze_params(*, params: Any) -> str:
|
|
38
|
+
"""
|
|
39
|
+
Recursively freeze any structure so it can be used as a dictionary key.
|
|
40
|
+
|
|
41
|
+
Purpose:
|
|
42
|
+
Session recording needs to cache RPC responses keyed by their parameters.
|
|
43
|
+
Python dicts require hashable keys, but RPC params can contain unhashable
|
|
44
|
+
types (dict, list, set, datetime). This function converts any structure
|
|
45
|
+
into a string representation suitable for use as a dict key.
|
|
46
|
+
|
|
47
|
+
Transformation rules:
|
|
48
|
+
- dict → dict with recursively frozen values, keys sorted for determinism
|
|
49
|
+
- list/tuple → tuple of frozen elements (tuples are hashable)
|
|
50
|
+
- set/frozenset → tagged tuple ("__set__", sorted frozen elements)
|
|
51
|
+
- datetime → tagged tuple ("__datetime__", ISO 8601 string)
|
|
52
|
+
- other types → unchanged (assumed hashable: str, int, bool, None)
|
|
53
|
+
|
|
54
|
+
Tagged tuples:
|
|
55
|
+
Sets and datetimes use a tag prefix ("__set__", "__datetime__") so that
|
|
56
|
+
unfreeze_params can reconstruct the original type during deserialization.
|
|
57
|
+
|
|
58
|
+
Why sort?
|
|
59
|
+
Sorting dict keys and set elements ensures deterministic output.
|
|
60
|
+
Without sorting, {"a":1, "b":2} and {"b":2, "a":1} would produce
|
|
61
|
+
different frozen strings, breaking cache lookups.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
String representation of the frozen structure, suitable for dict keys.
|
|
65
|
+
|
|
66
|
+
"""
|
|
67
|
+
res: Any = ""
|
|
68
|
+
match params:
|
|
69
|
+
case datetime():
|
|
70
|
+
# orjson cannot serialize datetime objects as dict keys even with OPT_NON_STR_KEYS.
|
|
71
|
+
# Use a tagged ISO string to preserve value and guarantee a stable, hashable key.
|
|
72
|
+
res = ("__datetime__", params.isoformat())
|
|
73
|
+
case dict():
|
|
74
|
+
# Sort by key for deterministic output regardless of insertion order
|
|
75
|
+
res = {k: freeze_params(params=v) for k, v in sorted(params.items())}
|
|
76
|
+
case list() | tuple():
|
|
77
|
+
# Convert to tuple (hashable) while preserving element order
|
|
78
|
+
res = tuple(freeze_params(params=x) for x in params)
|
|
79
|
+
case set() | frozenset():
|
|
80
|
+
# Sets are unordered, so sort by repr for determinism.
|
|
81
|
+
# Tag with "__set__" so unfreeze_params can reconstruct.
|
|
82
|
+
frozen_elems = tuple(sorted((freeze_params(params=x) for x in params), key=repr))
|
|
83
|
+
res = ("__set__", frozen_elems)
|
|
84
|
+
case _:
|
|
85
|
+
# Primitives (str, int, bool, None) pass through unchanged
|
|
86
|
+
res = params
|
|
87
|
+
|
|
88
|
+
return str(res)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def unfreeze_params(*, frozen_params: str) -> Any:
|
|
92
|
+
"""
|
|
93
|
+
Reverse the freeze_params transformation.
|
|
94
|
+
|
|
95
|
+
Purpose:
|
|
96
|
+
Reconstruct the original parameter structure from a frozen string.
|
|
97
|
+
Used when loading cached session data to get back the original params.
|
|
98
|
+
|
|
99
|
+
Algorithm:
|
|
100
|
+
1. Parse the frozen string using ast.literal_eval (safe eval for literals)
|
|
101
|
+
2. Recursively walk the parsed structure
|
|
102
|
+
3. Detect tagged tuples and reconstruct original types:
|
|
103
|
+
- ("__set__", items) → set(items)
|
|
104
|
+
- ("__datetime__", iso_string) → datetime object
|
|
105
|
+
4. Recursively process nested dicts, lists, and tuples
|
|
106
|
+
|
|
107
|
+
Error handling:
|
|
108
|
+
If ast.literal_eval fails (malformed string), return the original string.
|
|
109
|
+
This provides graceful degradation for corrupted cache entries.
|
|
110
|
+
|
|
111
|
+
The _walk helper:
|
|
112
|
+
Performs depth-first traversal, checking each node for tagged tuples
|
|
113
|
+
before recursively processing children. Order matters: check tags first,
|
|
114
|
+
then handle generic containers.
|
|
115
|
+
"""
|
|
116
|
+
try:
|
|
117
|
+
obj = ast.literal_eval(frozen_params)
|
|
118
|
+
except Exception:
|
|
119
|
+
# Malformed frozen string - return as-is for graceful degradation
|
|
120
|
+
return frozen_params
|
|
121
|
+
|
|
122
|
+
def _walk(o: Any) -> Any:
|
|
123
|
+
"""Recursively reconstruct original types from frozen representation."""
|
|
124
|
+
if o and isinstance(o, tuple):
|
|
125
|
+
tag = o[0]
|
|
126
|
+
# Check for tagged set: ("__set__", (item1, item2, ...))
|
|
127
|
+
if tag == "__set__" and len(o) == 2 and isinstance(o[1], tuple):
|
|
128
|
+
return {_walk(x) for x in o[1]}
|
|
129
|
+
# Check for tagged datetime: ("__datetime__", "2024-01-01T00:00:00")
|
|
130
|
+
if tag == "__datetime__" and len(o) == 2 and isinstance(o[1], str):
|
|
131
|
+
try:
|
|
132
|
+
return datetime.fromisoformat(o[1])
|
|
133
|
+
except Exception:
|
|
134
|
+
# Invalid ISO format - return the string value
|
|
135
|
+
return o[1]
|
|
136
|
+
# Generic tuple - recursively process elements
|
|
137
|
+
return tuple(_walk(x) for x in o)
|
|
138
|
+
if isinstance(o, dict):
|
|
139
|
+
# Recursively process dict values (keys are always strings)
|
|
140
|
+
return {k: _walk(v) for k, v in o.items()}
|
|
141
|
+
if isinstance(o, list):
|
|
142
|
+
return [_walk(x) for x in o]
|
|
143
|
+
if isinstance(o, tuple):
|
|
144
|
+
return tuple(_walk(x) for x in o)
|
|
145
|
+
# Handle string that looks like a dict literal (edge case from old format)
|
|
146
|
+
if isinstance(o, str) and o.startswith("{") and o.endswith("}"):
|
|
147
|
+
return ast.literal_eval(o)
|
|
148
|
+
return o
|
|
149
|
+
|
|
150
|
+
return _walk(obj)
|