aiohomematic 2025.8.6__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.
Potentially problematic release.
This version of aiohomematic might be problematic. Click here for more details.
- aiohomematic/__init__.py +47 -0
- aiohomematic/async_support.py +146 -0
- aiohomematic/caches/__init__.py +10 -0
- aiohomematic/caches/dynamic.py +554 -0
- aiohomematic/caches/persistent.py +459 -0
- aiohomematic/caches/visibility.py +774 -0
- aiohomematic/central/__init__.py +2034 -0
- aiohomematic/central/decorators.py +110 -0
- aiohomematic/central/xml_rpc_server.py +267 -0
- aiohomematic/client/__init__.py +1746 -0
- aiohomematic/client/json_rpc.py +1193 -0
- aiohomematic/client/xml_rpc.py +222 -0
- aiohomematic/const.py +795 -0
- aiohomematic/context.py +8 -0
- aiohomematic/converter.py +82 -0
- aiohomematic/decorators.py +188 -0
- aiohomematic/exceptions.py +145 -0
- aiohomematic/hmcli.py +159 -0
- aiohomematic/model/__init__.py +137 -0
- aiohomematic/model/calculated/__init__.py +65 -0
- aiohomematic/model/calculated/climate.py +230 -0
- aiohomematic/model/calculated/data_point.py +319 -0
- aiohomematic/model/calculated/operating_voltage_level.py +311 -0
- aiohomematic/model/calculated/support.py +174 -0
- aiohomematic/model/custom/__init__.py +175 -0
- aiohomematic/model/custom/climate.py +1334 -0
- aiohomematic/model/custom/const.py +146 -0
- aiohomematic/model/custom/cover.py +741 -0
- aiohomematic/model/custom/data_point.py +318 -0
- aiohomematic/model/custom/definition.py +861 -0
- aiohomematic/model/custom/light.py +1092 -0
- aiohomematic/model/custom/lock.py +389 -0
- aiohomematic/model/custom/siren.py +268 -0
- aiohomematic/model/custom/support.py +40 -0
- aiohomematic/model/custom/switch.py +172 -0
- aiohomematic/model/custom/valve.py +112 -0
- aiohomematic/model/data_point.py +1109 -0
- aiohomematic/model/decorators.py +173 -0
- aiohomematic/model/device.py +1347 -0
- aiohomematic/model/event.py +210 -0
- aiohomematic/model/generic/__init__.py +211 -0
- aiohomematic/model/generic/action.py +32 -0
- aiohomematic/model/generic/binary_sensor.py +28 -0
- aiohomematic/model/generic/button.py +25 -0
- aiohomematic/model/generic/data_point.py +162 -0
- aiohomematic/model/generic/number.py +73 -0
- aiohomematic/model/generic/select.py +36 -0
- aiohomematic/model/generic/sensor.py +72 -0
- aiohomematic/model/generic/switch.py +52 -0
- aiohomematic/model/generic/text.py +27 -0
- aiohomematic/model/hub/__init__.py +334 -0
- aiohomematic/model/hub/binary_sensor.py +22 -0
- aiohomematic/model/hub/button.py +26 -0
- aiohomematic/model/hub/data_point.py +332 -0
- aiohomematic/model/hub/number.py +37 -0
- aiohomematic/model/hub/select.py +47 -0
- aiohomematic/model/hub/sensor.py +35 -0
- aiohomematic/model/hub/switch.py +42 -0
- aiohomematic/model/hub/text.py +28 -0
- aiohomematic/model/support.py +599 -0
- aiohomematic/model/update.py +136 -0
- aiohomematic/py.typed +0 -0
- aiohomematic/rega_scripts/fetch_all_device_data.fn +75 -0
- aiohomematic/rega_scripts/get_program_descriptions.fn +30 -0
- aiohomematic/rega_scripts/get_serial.fn +44 -0
- aiohomematic/rega_scripts/get_system_variable_descriptions.fn +30 -0
- aiohomematic/rega_scripts/set_program_state.fn +12 -0
- aiohomematic/rega_scripts/set_system_variable.fn +15 -0
- aiohomematic/support.py +482 -0
- aiohomematic/validator.py +65 -0
- aiohomematic-2025.8.6.dist-info/METADATA +69 -0
- aiohomematic-2025.8.6.dist-info/RECORD +77 -0
- aiohomematic-2025.8.6.dist-info/WHEEL +5 -0
- aiohomematic-2025.8.6.dist-info/licenses/LICENSE +21 -0
- aiohomematic-2025.8.6.dist-info/top_level.txt +2 -0
- aiohomematic_support/__init__.py +1 -0
- aiohomematic_support/client_local.py +349 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""Decorators for data points used within aiohomematic."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable, Mapping
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from enum import Enum
|
|
8
|
+
from typing import Any, ParamSpec, TypeVar, cast
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"config_property",
|
|
12
|
+
"get_public_attributes_for_config_property",
|
|
13
|
+
"get_public_attributes_for_info_property",
|
|
14
|
+
"get_public_attributes_for_state_property",
|
|
15
|
+
"info_property",
|
|
16
|
+
"state_property",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
P = ParamSpec("P")
|
|
20
|
+
T = TypeVar("T")
|
|
21
|
+
R = TypeVar("R")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# pylint: disable=invalid-name
|
|
25
|
+
class generic_property[GETTER, SETTER](property):
|
|
26
|
+
"""Generic property implementation."""
|
|
27
|
+
|
|
28
|
+
fget: Callable[[Any], GETTER] | None
|
|
29
|
+
fset: Callable[[Any, SETTER], None] | None
|
|
30
|
+
fdel: Callable[[Any], None] | None
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
fget: Callable[[Any], GETTER] | None = None,
|
|
35
|
+
fset: Callable[[Any, SETTER], None] | None = None,
|
|
36
|
+
fdel: Callable[[Any], None] | None = None,
|
|
37
|
+
doc: str | None = None,
|
|
38
|
+
) -> None:
|
|
39
|
+
"""Init the generic property."""
|
|
40
|
+
super().__init__(fget, fset, fdel, doc)
|
|
41
|
+
if doc is None and fget is not None:
|
|
42
|
+
doc = fget.__doc__
|
|
43
|
+
self.__doc__ = doc
|
|
44
|
+
|
|
45
|
+
def getter(self, fget: Callable[[Any], GETTER], /) -> generic_property:
|
|
46
|
+
"""Return generic getter."""
|
|
47
|
+
return type(self)(fget, self.fset, self.fdel, self.__doc__) # pragma: no cover
|
|
48
|
+
|
|
49
|
+
def setter(self, fset: Callable[[Any, SETTER], None], /) -> generic_property:
|
|
50
|
+
"""Return generic setter."""
|
|
51
|
+
return type(self)(self.fget, fset, self.fdel, self.__doc__)
|
|
52
|
+
|
|
53
|
+
def deleter(self, fdel: Callable[[Any], None], /) -> generic_property:
|
|
54
|
+
"""Return generic deleter."""
|
|
55
|
+
return type(self)(self.fget, self.fset, fdel, self.__doc__)
|
|
56
|
+
|
|
57
|
+
def __get__(self, obj: Any, gtype: type | None = None, /) -> GETTER: # type: ignore[override]
|
|
58
|
+
"""Return the attribute."""
|
|
59
|
+
if obj is None:
|
|
60
|
+
return self # type: ignore[return-value]
|
|
61
|
+
if self.fget is None:
|
|
62
|
+
raise AttributeError("unreadable attribute") # pragma: no cover
|
|
63
|
+
return self.fget(obj)
|
|
64
|
+
|
|
65
|
+
def __set__(self, obj: Any, value: Any, /) -> None:
|
|
66
|
+
"""Set the attribute."""
|
|
67
|
+
if self.fset is None:
|
|
68
|
+
raise AttributeError("can't set attribute") # pragma: no cover
|
|
69
|
+
self.fset(obj, value)
|
|
70
|
+
|
|
71
|
+
def __delete__(self, obj: Any, /) -> None:
|
|
72
|
+
"""Delete the attribute."""
|
|
73
|
+
if self.fdel is None:
|
|
74
|
+
raise AttributeError("can't delete attribute") # pragma: no cover
|
|
75
|
+
self.fdel(obj)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# pylint: disable=invalid-name
|
|
79
|
+
class config_property[GETTER, SETTER](generic_property[GETTER, SETTER]):
|
|
80
|
+
"""Decorate to mark own config properties."""
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# pylint: disable=invalid-name
|
|
84
|
+
class info_property[GETTER, SETTER](generic_property[GETTER, SETTER]):
|
|
85
|
+
"""Decorate to mark own info properties."""
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# pylint: disable=invalid-name
|
|
89
|
+
class state_property[GETTER, SETTER](generic_property[GETTER, SETTER]):
|
|
90
|
+
"""Decorate to mark own value properties."""
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# Cache for per-class attribute names by decorator to avoid repeated dir() scans
|
|
94
|
+
# Keyed by (class, decorator class); value is a tuple of attribute names
|
|
95
|
+
_PUBLIC_ATTR_CACHE: dict[tuple[type, type], tuple[str, ...]] = {}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _get_public_attributes_by_class_decorator(data_object: Any, class_decorator: type) -> Mapping[str, Any]:
|
|
99
|
+
"""
|
|
100
|
+
Return the object attributes by decorator.
|
|
101
|
+
|
|
102
|
+
This caches the attribute names per (class, decorator) to reduce overhead
|
|
103
|
+
from repeated dir()/getattr() scans. Values are not cached as they are
|
|
104
|
+
instance-dependent and may change over time.
|
|
105
|
+
"""
|
|
106
|
+
cls = data_object.__class__
|
|
107
|
+
cache_key = (cls, class_decorator)
|
|
108
|
+
|
|
109
|
+
if (names := _PUBLIC_ATTR_CACHE.get(cache_key)) is None:
|
|
110
|
+
names = tuple(y for y in dir(cls) if not y.startswith("_") and isinstance(getattr(cls, y), class_decorator))
|
|
111
|
+
_PUBLIC_ATTR_CACHE[cache_key] = names
|
|
112
|
+
|
|
113
|
+
return {name: _get_text_value(getattr(data_object, name)) for name in names}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _get_text_value(value: Any) -> Any:
|
|
117
|
+
"""Convert value to text."""
|
|
118
|
+
if isinstance(value, (list, tuple, set)):
|
|
119
|
+
return tuple(_get_text_value(v) for v in value)
|
|
120
|
+
if isinstance(value, Enum):
|
|
121
|
+
return str(value)
|
|
122
|
+
if isinstance(value, datetime):
|
|
123
|
+
return datetime.timestamp(value)
|
|
124
|
+
return value
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def get_public_attributes_for_config_property(data_object: Any) -> Mapping[str, Any]:
|
|
128
|
+
"""Return the object attributes by decorator config_property."""
|
|
129
|
+
return _get_public_attributes_by_class_decorator(data_object=data_object, class_decorator=config_property)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def get_public_attributes_for_info_property(data_object: Any) -> Mapping[str, Any]:
|
|
133
|
+
"""Return the object attributes by decorator info_property."""
|
|
134
|
+
return _get_public_attributes_by_class_decorator(data_object=data_object, class_decorator=info_property)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def get_public_attributes_for_state_property(data_object: Any) -> Mapping[str, Any]:
|
|
138
|
+
"""Return the object attributes by decorator state_property."""
|
|
139
|
+
return _get_public_attributes_by_class_decorator(data_object=data_object, class_decorator=state_property)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# pylint: disable=invalid-name
|
|
143
|
+
class cached_slot_property[T, R]:
|
|
144
|
+
"""A property-like descriptor that caches the computed value in a slot attribute. Designed to work with classes that use __slots__ and do not define __dict__."""
|
|
145
|
+
|
|
146
|
+
def __init__(self, func: Callable[[T], R]) -> None:
|
|
147
|
+
"""Init the cached property."""
|
|
148
|
+
self._func = func # The function to compute the value
|
|
149
|
+
self._cache_attr = f"_cached_{func.__name__}" # Default name of the cache attribute
|
|
150
|
+
self._name = func.__name__
|
|
151
|
+
|
|
152
|
+
def __get__(self, instance: T | None, owner: type | None = None) -> R:
|
|
153
|
+
"""Return the cached value if it exists. Otherwise, compute it using the function and cache it."""
|
|
154
|
+
if instance is None:
|
|
155
|
+
# Accessed from class, return the descriptor itself
|
|
156
|
+
return cast(R, self)
|
|
157
|
+
|
|
158
|
+
# If the cached value is not set yet, compute and store it
|
|
159
|
+
if not hasattr(instance, self._cache_attr):
|
|
160
|
+
value = self._func(instance)
|
|
161
|
+
setattr(instance, self._cache_attr, value)
|
|
162
|
+
|
|
163
|
+
# Return the cached value
|
|
164
|
+
return cast(R, getattr(instance, self._cache_attr))
|
|
165
|
+
|
|
166
|
+
def __set__(self, instance: T, value: Any) -> None:
|
|
167
|
+
"""Raise an error to prevent manual assignment to the property."""
|
|
168
|
+
raise AttributeError(f"Can't set read-only cached property '{self._name}'")
|
|
169
|
+
|
|
170
|
+
def __delete__(self, instance: T) -> None:
|
|
171
|
+
"""Delete the cached value so it can be recomputed on next access."""
|
|
172
|
+
if hasattr(instance, self._cache_attr):
|
|
173
|
+
delattr(instance, self._cache_attr)
|