aiohomematic 2026.1.29__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. aiohomematic/__init__.py +110 -0
  2. aiohomematic/_log_context_protocol.py +29 -0
  3. aiohomematic/api.py +410 -0
  4. aiohomematic/async_support.py +250 -0
  5. aiohomematic/backend_detection.py +462 -0
  6. aiohomematic/central/__init__.py +103 -0
  7. aiohomematic/central/async_rpc_server.py +760 -0
  8. aiohomematic/central/central_unit.py +1152 -0
  9. aiohomematic/central/config.py +463 -0
  10. aiohomematic/central/config_builder.py +772 -0
  11. aiohomematic/central/connection_state.py +160 -0
  12. aiohomematic/central/coordinators/__init__.py +38 -0
  13. aiohomematic/central/coordinators/cache.py +414 -0
  14. aiohomematic/central/coordinators/client.py +480 -0
  15. aiohomematic/central/coordinators/connection_recovery.py +1141 -0
  16. aiohomematic/central/coordinators/device.py +1166 -0
  17. aiohomematic/central/coordinators/event.py +514 -0
  18. aiohomematic/central/coordinators/hub.py +532 -0
  19. aiohomematic/central/decorators.py +184 -0
  20. aiohomematic/central/device_registry.py +229 -0
  21. aiohomematic/central/events/__init__.py +104 -0
  22. aiohomematic/central/events/bus.py +1392 -0
  23. aiohomematic/central/events/integration.py +424 -0
  24. aiohomematic/central/events/types.py +194 -0
  25. aiohomematic/central/health.py +762 -0
  26. aiohomematic/central/rpc_server.py +353 -0
  27. aiohomematic/central/scheduler.py +794 -0
  28. aiohomematic/central/state_machine.py +391 -0
  29. aiohomematic/client/__init__.py +203 -0
  30. aiohomematic/client/_rpc_errors.py +187 -0
  31. aiohomematic/client/backends/__init__.py +48 -0
  32. aiohomematic/client/backends/base.py +335 -0
  33. aiohomematic/client/backends/capabilities.py +138 -0
  34. aiohomematic/client/backends/ccu.py +487 -0
  35. aiohomematic/client/backends/factory.py +116 -0
  36. aiohomematic/client/backends/homegear.py +294 -0
  37. aiohomematic/client/backends/json_ccu.py +252 -0
  38. aiohomematic/client/backends/protocol.py +316 -0
  39. aiohomematic/client/ccu.py +1857 -0
  40. aiohomematic/client/circuit_breaker.py +459 -0
  41. aiohomematic/client/config.py +64 -0
  42. aiohomematic/client/handlers/__init__.py +40 -0
  43. aiohomematic/client/handlers/backup.py +157 -0
  44. aiohomematic/client/handlers/base.py +79 -0
  45. aiohomematic/client/handlers/device_ops.py +1085 -0
  46. aiohomematic/client/handlers/firmware.py +144 -0
  47. aiohomematic/client/handlers/link_mgmt.py +199 -0
  48. aiohomematic/client/handlers/metadata.py +436 -0
  49. aiohomematic/client/handlers/programs.py +144 -0
  50. aiohomematic/client/handlers/sysvars.py +100 -0
  51. aiohomematic/client/interface_client.py +1304 -0
  52. aiohomematic/client/json_rpc.py +2068 -0
  53. aiohomematic/client/request_coalescer.py +282 -0
  54. aiohomematic/client/rpc_proxy.py +629 -0
  55. aiohomematic/client/state_machine.py +324 -0
  56. aiohomematic/const.py +2207 -0
  57. aiohomematic/context.py +275 -0
  58. aiohomematic/converter.py +270 -0
  59. aiohomematic/decorators.py +390 -0
  60. aiohomematic/exceptions.py +185 -0
  61. aiohomematic/hmcli.py +997 -0
  62. aiohomematic/i18n.py +193 -0
  63. aiohomematic/interfaces/__init__.py +407 -0
  64. aiohomematic/interfaces/central.py +1067 -0
  65. aiohomematic/interfaces/client.py +1096 -0
  66. aiohomematic/interfaces/coordinators.py +63 -0
  67. aiohomematic/interfaces/model.py +1921 -0
  68. aiohomematic/interfaces/operations.py +217 -0
  69. aiohomematic/logging_context.py +134 -0
  70. aiohomematic/metrics/__init__.py +125 -0
  71. aiohomematic/metrics/_protocols.py +140 -0
  72. aiohomematic/metrics/aggregator.py +534 -0
  73. aiohomematic/metrics/dataclasses.py +489 -0
  74. aiohomematic/metrics/emitter.py +292 -0
  75. aiohomematic/metrics/events.py +183 -0
  76. aiohomematic/metrics/keys.py +300 -0
  77. aiohomematic/metrics/observer.py +563 -0
  78. aiohomematic/metrics/stats.py +172 -0
  79. aiohomematic/model/__init__.py +189 -0
  80. aiohomematic/model/availability.py +65 -0
  81. aiohomematic/model/calculated/__init__.py +89 -0
  82. aiohomematic/model/calculated/climate.py +276 -0
  83. aiohomematic/model/calculated/data_point.py +315 -0
  84. aiohomematic/model/calculated/field.py +147 -0
  85. aiohomematic/model/calculated/operating_voltage_level.py +286 -0
  86. aiohomematic/model/calculated/support.py +232 -0
  87. aiohomematic/model/custom/__init__.py +214 -0
  88. aiohomematic/model/custom/capabilities/__init__.py +67 -0
  89. aiohomematic/model/custom/capabilities/climate.py +41 -0
  90. aiohomematic/model/custom/capabilities/light.py +87 -0
  91. aiohomematic/model/custom/capabilities/lock.py +44 -0
  92. aiohomematic/model/custom/capabilities/siren.py +63 -0
  93. aiohomematic/model/custom/climate.py +1130 -0
  94. aiohomematic/model/custom/cover.py +722 -0
  95. aiohomematic/model/custom/data_point.py +360 -0
  96. aiohomematic/model/custom/definition.py +300 -0
  97. aiohomematic/model/custom/field.py +89 -0
  98. aiohomematic/model/custom/light.py +1174 -0
  99. aiohomematic/model/custom/lock.py +322 -0
  100. aiohomematic/model/custom/mixins.py +445 -0
  101. aiohomematic/model/custom/profile.py +945 -0
  102. aiohomematic/model/custom/registry.py +251 -0
  103. aiohomematic/model/custom/siren.py +462 -0
  104. aiohomematic/model/custom/switch.py +195 -0
  105. aiohomematic/model/custom/text_display.py +289 -0
  106. aiohomematic/model/custom/valve.py +78 -0
  107. aiohomematic/model/data_point.py +1416 -0
  108. aiohomematic/model/device.py +1840 -0
  109. aiohomematic/model/event.py +216 -0
  110. aiohomematic/model/generic/__init__.py +327 -0
  111. aiohomematic/model/generic/action.py +40 -0
  112. aiohomematic/model/generic/action_select.py +62 -0
  113. aiohomematic/model/generic/binary_sensor.py +30 -0
  114. aiohomematic/model/generic/button.py +31 -0
  115. aiohomematic/model/generic/data_point.py +177 -0
  116. aiohomematic/model/generic/dummy.py +150 -0
  117. aiohomematic/model/generic/number.py +76 -0
  118. aiohomematic/model/generic/select.py +56 -0
  119. aiohomematic/model/generic/sensor.py +76 -0
  120. aiohomematic/model/generic/switch.py +54 -0
  121. aiohomematic/model/generic/text.py +33 -0
  122. aiohomematic/model/hub/__init__.py +100 -0
  123. aiohomematic/model/hub/binary_sensor.py +24 -0
  124. aiohomematic/model/hub/button.py +28 -0
  125. aiohomematic/model/hub/connectivity.py +190 -0
  126. aiohomematic/model/hub/data_point.py +342 -0
  127. aiohomematic/model/hub/hub.py +864 -0
  128. aiohomematic/model/hub/inbox.py +135 -0
  129. aiohomematic/model/hub/install_mode.py +393 -0
  130. aiohomematic/model/hub/metrics.py +208 -0
  131. aiohomematic/model/hub/number.py +42 -0
  132. aiohomematic/model/hub/select.py +52 -0
  133. aiohomematic/model/hub/sensor.py +37 -0
  134. aiohomematic/model/hub/switch.py +43 -0
  135. aiohomematic/model/hub/text.py +30 -0
  136. aiohomematic/model/hub/update.py +221 -0
  137. aiohomematic/model/support.py +592 -0
  138. aiohomematic/model/update.py +140 -0
  139. aiohomematic/model/week_profile.py +1827 -0
  140. aiohomematic/property_decorators.py +719 -0
  141. aiohomematic/py.typed +0 -0
  142. aiohomematic/rega_scripts/accept_device_in_inbox.fn +51 -0
  143. aiohomematic/rega_scripts/create_backup_start.fn +28 -0
  144. aiohomematic/rega_scripts/create_backup_status.fn +89 -0
  145. aiohomematic/rega_scripts/fetch_all_device_data.fn +97 -0
  146. aiohomematic/rega_scripts/get_backend_info.fn +25 -0
  147. aiohomematic/rega_scripts/get_inbox_devices.fn +61 -0
  148. aiohomematic/rega_scripts/get_program_descriptions.fn +31 -0
  149. aiohomematic/rega_scripts/get_serial.fn +44 -0
  150. aiohomematic/rega_scripts/get_service_messages.fn +83 -0
  151. aiohomematic/rega_scripts/get_system_update_info.fn +39 -0
  152. aiohomematic/rega_scripts/get_system_variable_descriptions.fn +31 -0
  153. aiohomematic/rega_scripts/set_program_state.fn +17 -0
  154. aiohomematic/rega_scripts/set_system_variable.fn +19 -0
  155. aiohomematic/rega_scripts/trigger_firmware_update.fn +67 -0
  156. aiohomematic/schemas.py +256 -0
  157. aiohomematic/store/__init__.py +55 -0
  158. aiohomematic/store/dynamic/__init__.py +43 -0
  159. aiohomematic/store/dynamic/command.py +250 -0
  160. aiohomematic/store/dynamic/data.py +175 -0
  161. aiohomematic/store/dynamic/details.py +187 -0
  162. aiohomematic/store/dynamic/ping_pong.py +416 -0
  163. aiohomematic/store/persistent/__init__.py +71 -0
  164. aiohomematic/store/persistent/base.py +285 -0
  165. aiohomematic/store/persistent/device.py +233 -0
  166. aiohomematic/store/persistent/incident.py +380 -0
  167. aiohomematic/store/persistent/paramset.py +241 -0
  168. aiohomematic/store/persistent/session.py +556 -0
  169. aiohomematic/store/serialization.py +150 -0
  170. aiohomematic/store/storage.py +689 -0
  171. aiohomematic/store/types.py +526 -0
  172. aiohomematic/store/visibility/__init__.py +40 -0
  173. aiohomematic/store/visibility/parser.py +141 -0
  174. aiohomematic/store/visibility/registry.py +722 -0
  175. aiohomematic/store/visibility/rules.py +307 -0
  176. aiohomematic/strings.json +237 -0
  177. aiohomematic/support.py +706 -0
  178. aiohomematic/tracing.py +236 -0
  179. aiohomematic/translations/de.json +237 -0
  180. aiohomematic/translations/en.json +237 -0
  181. aiohomematic/type_aliases.py +51 -0
  182. aiohomematic/validator.py +128 -0
  183. aiohomematic-2026.1.29.dist-info/METADATA +296 -0
  184. aiohomematic-2026.1.29.dist-info/RECORD +188 -0
  185. aiohomematic-2026.1.29.dist-info/WHEEL +5 -0
  186. aiohomematic-2026.1.29.dist-info/entry_points.txt +2 -0
  187. aiohomematic-2026.1.29.dist-info/licenses/LICENSE +21 -0
  188. aiohomematic-2026.1.29.dist-info/top_level.txt +1 -0
@@ -0,0 +1,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)