aiohomematic 2025.10.7__tar.gz → 2025.10.8__tar.gz
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-2025.10.7 → aiohomematic-2025.10.8}/PKG-INFO +1 -1
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/central/__init__.py +1 -1
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/client/__init__.py +1 -1
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/const.py +3 -1
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/calculated/__init__.py +20 -3
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/calculated/climate.py +58 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/calculated/support.py +40 -2
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/device.py +1 -4
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/support.py +4 -5
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic.egg-info/PKG-INFO +1 -1
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_central.py +7 -7
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_central_pydevccu.py +4 -4
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/LICENSE +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/README.md +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/__init__.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/async_support.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/caches/__init__.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/caches/dynamic.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/caches/persistent.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/caches/visibility.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/central/decorators.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/central/rpc_server.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/client/_rpc_errors.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/client/json_rpc.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/client/rpc_proxy.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/context.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/converter.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/decorators.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/exceptions.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/hmcli.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/__init__.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/calculated/data_point.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/calculated/operating_voltage_level.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/custom/__init__.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/custom/climate.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/custom/const.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/custom/cover.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/custom/data_point.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/custom/definition.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/custom/light.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/custom/lock.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/custom/siren.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/custom/support.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/custom/switch.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/custom/valve.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/data_point.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/event.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/generic/__init__.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/generic/action.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/generic/binary_sensor.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/generic/button.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/generic/data_point.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/generic/number.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/generic/select.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/generic/sensor.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/generic/switch.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/generic/text.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/hub/__init__.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/hub/binary_sensor.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/hub/button.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/hub/data_point.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/hub/number.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/hub/select.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/hub/sensor.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/hub/switch.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/hub/text.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/support.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/update.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/property_decorators.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/py.typed +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/rega_scripts/fetch_all_device_data.fn +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/rega_scripts/get_program_descriptions.fn +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/rega_scripts/get_serial.fn +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/rega_scripts/get_system_variable_descriptions.fn +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/rega_scripts/set_program_state.fn +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/rega_scripts/set_system_variable.fn +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/validator.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic.egg-info/SOURCES.txt +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic.egg-info/dependency_links.txt +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic.egg-info/requires.txt +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic.egg-info/top_level.txt +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic_support/__init__.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic_support/client_local.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/pyproject.toml +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/setup.cfg +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_action.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_async_support.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_binary_sensor.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_button.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_calculated_support.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_climate.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_cover.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_decorator.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_device.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_dynamic_caches.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_entity.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_event.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_json_rpc.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_json_rpc_client_integration.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_kwonly_lint.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_light.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_lock.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_logging_support.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_number.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_select.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_sensor.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_siren.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_support.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_support_extra.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_switch.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_text.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_valve.py +0 -0
- {aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/tests/test_xml_rpc_proxy_integration.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aiohomematic
|
|
3
|
-
Version: 2025.10.
|
|
3
|
+
Version: 2025.10.8
|
|
4
4
|
Summary: Homematic interface for Home Assistant running on Python 3.
|
|
5
5
|
Home-page: https://github.com/sukramj/aiohomematic
|
|
6
6
|
Author-email: SukramJ <sukramj@icloud.com>, Daniel Perna <danielperna84@gmail.com>
|
|
@@ -1068,7 +1068,7 @@ class CentralUnit(LogContextMixin, PayloadMixin):
|
|
|
1068
1068
|
)
|
|
1069
1069
|
return
|
|
1070
1070
|
client = self._clients[interface_id]
|
|
1071
|
-
if (device_descriptions := await client.
|
|
1071
|
+
if (device_descriptions := await client.get_all_device_descriptions(device_address=address)) is None:
|
|
1072
1072
|
_LOGGER.warning(
|
|
1073
1073
|
"ADD_NEW_DEVICES_MANUALLY failed: No device description found for address %s on interface_id %s",
|
|
1074
1074
|
address,
|
|
@@ -507,7 +507,7 @@ class Client(ABC, LogContextMixin):
|
|
|
507
507
|
return None
|
|
508
508
|
|
|
509
509
|
@inspector(re_raise=False)
|
|
510
|
-
async def
|
|
510
|
+
async def get_all_device_descriptions(self, *, device_address: str) -> tuple[DeviceDescription, ...] | None:
|
|
511
511
|
"""Get all device descriptions from the backend."""
|
|
512
512
|
all_device_description: list[DeviceDescription] = []
|
|
513
513
|
if main_dd := await self.get_device_description(device_address=device_address):
|
|
@@ -19,7 +19,7 @@ import sys
|
|
|
19
19
|
from types import MappingProxyType
|
|
20
20
|
from typing import Any, Final, NamedTuple, Required, TypeAlias, TypedDict
|
|
21
21
|
|
|
22
|
-
VERSION: Final = "2025.10.
|
|
22
|
+
VERSION: Final = "2025.10.8"
|
|
23
23
|
|
|
24
24
|
# Detect test speedup mode via environment
|
|
25
25
|
_TEST_SPEEDUP: Final = (
|
|
@@ -179,6 +179,8 @@ class CalulatedParameter(StrEnum):
|
|
|
179
179
|
|
|
180
180
|
APPARENT_TEMPERATURE = "APPARENT_TEMPERATURE"
|
|
181
181
|
DEW_POINT = "DEW_POINT"
|
|
182
|
+
DEW_POINT_SPREAD = "DEW_POINT_SPREAD"
|
|
183
|
+
ENTHALPY = "ENTHALPY"
|
|
182
184
|
FROST_POINT = "FROST_POINT"
|
|
183
185
|
OPERATING_VOLTAGE_LEVEL = "OPERATING_VOLTAGE_LEVEL"
|
|
184
186
|
VAPOR_CONCENTRATION = "VAPOR_CONCENTRATION"
|
|
@@ -24,7 +24,7 @@ Factory:
|
|
|
24
24
|
normal read-only data points.
|
|
25
25
|
|
|
26
26
|
Modules/classes:
|
|
27
|
-
- ApparentTemperature, DewPoint, FrostPoint, VaporConcentration: Climate-related
|
|
27
|
+
- ApparentTemperature, DewPoint, DewPointSpread, Enthalphy, FrostPoint, VaporConcentration: Climate-related
|
|
28
28
|
sensors implemented in climate.py using well-known formulas (see
|
|
29
29
|
aiohomematic.model.calculated.support for details and references).
|
|
30
30
|
- OperatingVoltageLevel: Interprets battery/voltage values and exposes a human
|
|
@@ -41,7 +41,14 @@ from typing import Final
|
|
|
41
41
|
|
|
42
42
|
from aiohomematic.decorators import inspector
|
|
43
43
|
from aiohomematic.model import device as hmd
|
|
44
|
-
from aiohomematic.model.calculated.climate import
|
|
44
|
+
from aiohomematic.model.calculated.climate import (
|
|
45
|
+
ApparentTemperature,
|
|
46
|
+
DewPoint,
|
|
47
|
+
DewPointSpread,
|
|
48
|
+
Enthalpy,
|
|
49
|
+
FrostPoint,
|
|
50
|
+
VaporConcentration,
|
|
51
|
+
)
|
|
45
52
|
from aiohomematic.model.calculated.data_point import CalculatedDataPoint
|
|
46
53
|
from aiohomematic.model.calculated.operating_voltage_level import OperatingVoltageLevel
|
|
47
54
|
|
|
@@ -49,13 +56,23 @@ __all__ = [
|
|
|
49
56
|
"ApparentTemperature",
|
|
50
57
|
"CalculatedDataPoint",
|
|
51
58
|
"DewPoint",
|
|
59
|
+
"DewPointSpread",
|
|
60
|
+
"Enthalpy",
|
|
52
61
|
"FrostPoint",
|
|
53
62
|
"OperatingVoltageLevel",
|
|
54
63
|
"VaporConcentration",
|
|
55
64
|
"create_calculated_data_points",
|
|
56
65
|
]
|
|
57
66
|
|
|
58
|
-
_CALCULATED_DATA_POINTS: Final = (
|
|
67
|
+
_CALCULATED_DATA_POINTS: Final = (
|
|
68
|
+
ApparentTemperature,
|
|
69
|
+
DewPoint,
|
|
70
|
+
DewPointSpread,
|
|
71
|
+
Enthalpy,
|
|
72
|
+
FrostPoint,
|
|
73
|
+
OperatingVoltageLevel,
|
|
74
|
+
VaporConcentration,
|
|
75
|
+
)
|
|
59
76
|
_LOGGER: Final = logging.getLogger(__name__)
|
|
60
77
|
|
|
61
78
|
|
|
@@ -13,6 +13,8 @@ from aiohomematic.model.calculated.data_point import CalculatedDataPoint
|
|
|
13
13
|
from aiohomematic.model.calculated.support import (
|
|
14
14
|
calculate_apparent_temperature,
|
|
15
15
|
calculate_dew_point,
|
|
16
|
+
calculate_dew_point_spread,
|
|
17
|
+
calculate_enthalpy,
|
|
16
18
|
calculate_frost_point,
|
|
17
19
|
calculate_vapor_concentration,
|
|
18
20
|
)
|
|
@@ -141,6 +143,62 @@ class DewPoint(BaseClimateSensor):
|
|
|
141
143
|
return None
|
|
142
144
|
|
|
143
145
|
|
|
146
|
+
class DewPointSpread(BaseClimateSensor):
|
|
147
|
+
"""Implementation of a calculated sensor for dew point spread."""
|
|
148
|
+
|
|
149
|
+
__slots__ = ()
|
|
150
|
+
|
|
151
|
+
_calculated_parameter = CalulatedParameter.DEW_POINT_SPREAD
|
|
152
|
+
|
|
153
|
+
def __init__(self, *, channel: hmd.Channel) -> None:
|
|
154
|
+
"""Initialize the data point."""
|
|
155
|
+
super().__init__(channel=channel)
|
|
156
|
+
self._unit = "K"
|
|
157
|
+
|
|
158
|
+
@staticmethod
|
|
159
|
+
def is_relevant_for_model(*, channel: hmd.Channel) -> bool:
|
|
160
|
+
"""Return if this calculated data point is relevant for the model."""
|
|
161
|
+
return _is_relevant_for_model_temperature_and_humidity(channel=channel)
|
|
162
|
+
|
|
163
|
+
@state_property
|
|
164
|
+
def value(self) -> float | None:
|
|
165
|
+
"""Return the value."""
|
|
166
|
+
if self._dp_temperature.value is not None and self._dp_humidity.value is not None:
|
|
167
|
+
return calculate_dew_point_spread(
|
|
168
|
+
temperature=self._dp_temperature.value,
|
|
169
|
+
humidity=self._dp_humidity.value,
|
|
170
|
+
)
|
|
171
|
+
return None
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class Enthalpy(BaseClimateSensor):
|
|
175
|
+
"""Implementation of a calculated sensor for enthalpy."""
|
|
176
|
+
|
|
177
|
+
__slots__ = ()
|
|
178
|
+
|
|
179
|
+
_calculated_parameter = CalulatedParameter.ENTHALPY
|
|
180
|
+
|
|
181
|
+
def __init__(self, *, channel: hmd.Channel) -> None:
|
|
182
|
+
"""Initialize the data point."""
|
|
183
|
+
super().__init__(channel=channel)
|
|
184
|
+
self._unit = "kJ/kg"
|
|
185
|
+
|
|
186
|
+
@staticmethod
|
|
187
|
+
def is_relevant_for_model(*, channel: hmd.Channel) -> bool:
|
|
188
|
+
"""Return if this calculated data point is relevant for the model."""
|
|
189
|
+
return _is_relevant_for_model_temperature_and_humidity(channel=channel)
|
|
190
|
+
|
|
191
|
+
@state_property
|
|
192
|
+
def value(self) -> float | None:
|
|
193
|
+
"""Return the value."""
|
|
194
|
+
if self._dp_temperature.value is not None and self._dp_humidity.value is not None:
|
|
195
|
+
return calculate_enthalpy(
|
|
196
|
+
temperature=self._dp_temperature.value,
|
|
197
|
+
humidity=self._dp_humidity.value,
|
|
198
|
+
)
|
|
199
|
+
return None
|
|
200
|
+
|
|
201
|
+
|
|
144
202
|
class FrostPoint(BaseClimateSensor):
|
|
145
203
|
"""Implementation of a calculated sensor for frost point."""
|
|
146
204
|
|
|
@@ -16,9 +16,47 @@ from typing import Final
|
|
|
16
16
|
|
|
17
17
|
from aiohomematic.support import extract_exc_args
|
|
18
18
|
|
|
19
|
+
_DEFAULT_PRESSURE_HPA: Final = 1013.25
|
|
19
20
|
_LOGGER: Final = logging.getLogger(__name__)
|
|
20
21
|
|
|
21
22
|
|
|
23
|
+
def calculate_dew_point_spread(*, temperature: float, humidity: int) -> float | None:
|
|
24
|
+
"""
|
|
25
|
+
Calculate the dew point spread.
|
|
26
|
+
|
|
27
|
+
Dew point spread = Difference between current air temperature and dew point.
|
|
28
|
+
Specifies the safety margin against condensation(K).
|
|
29
|
+
"""
|
|
30
|
+
if dew_point := calculate_dew_point(temperature=temperature, humidity=humidity):
|
|
31
|
+
return round(temperature - dew_point, 2)
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def calculate_enthalpy(
|
|
36
|
+
*, temperature: float, humidity: int, pressure_hPa: float = _DEFAULT_PRESSURE_HPA
|
|
37
|
+
) -> float | None:
|
|
38
|
+
"""
|
|
39
|
+
Calculate the enthalpy based on temperature and humidity.
|
|
40
|
+
|
|
41
|
+
Calculates the specific enthalpy of humid air in kJ/kg (relative to dry air).
|
|
42
|
+
temperature: Air temperature in °C
|
|
43
|
+
humidity: Relative humidity in %
|
|
44
|
+
pressure_hPa: Air pressure (default: 1013.25 hPa)
|
|
45
|
+
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
# Saturation vapor pressure according to Magnus in hPa
|
|
49
|
+
e_s = 6.112 * math.exp((17.62 * temperature) / (243.12 + temperature))
|
|
50
|
+
e = humidity / 100.0 * e_s # aktueller Dampfdruck in hPa
|
|
51
|
+
|
|
52
|
+
# Mixing ratio (g water / kg dry air)
|
|
53
|
+
r = 622 * e / (pressure_hPa - e)
|
|
54
|
+
|
|
55
|
+
# Specific enthalpy (kJ/kg dry air)
|
|
56
|
+
h = 1.006 * temperature + r * (2501 + 1.86 * temperature) / 1000 # in kJ/kg
|
|
57
|
+
return round(h, 2)
|
|
58
|
+
|
|
59
|
+
|
|
22
60
|
def _calculate_heat_index(*, temperature: float, humidity: int) -> float:
|
|
23
61
|
"""
|
|
24
62
|
Calculate the Heat Index (feels like temperature) based on the NOAA equation.
|
|
@@ -158,10 +196,10 @@ def calculate_dew_point(*, temperature: float, humidity: int) -> float | None:
|
|
|
158
196
|
def calculate_frost_point(*, temperature: float, humidity: int) -> float | None:
|
|
159
197
|
"""Calculate the frost point."""
|
|
160
198
|
try:
|
|
161
|
-
if (
|
|
199
|
+
if (dew_point := calculate_dew_point(temperature=temperature, humidity=humidity)) is None:
|
|
162
200
|
return None
|
|
163
201
|
t = temperature + 273.15
|
|
164
|
-
td =
|
|
202
|
+
td = dew_point + 273.15
|
|
165
203
|
|
|
166
204
|
return round((td + (2671.02 / ((2954.61 / t) + 2.193665 * math.log(t) - 13.3448)) - t) - 273.15, 1)
|
|
167
205
|
except ValueError as verr:
|
|
@@ -1338,10 +1338,7 @@ class _DefinitionExporter:
|
|
|
1338
1338
|
def perform_save() -> DataOperationResult:
|
|
1339
1339
|
if not check_or_create_directory(directory=file_dir):
|
|
1340
1340
|
return DataOperationResult.NO_SAVE # pragma: no cover
|
|
1341
|
-
with open(
|
|
1342
|
-
file=os.path.join(file_dir, filename),
|
|
1343
|
-
mode="wb",
|
|
1344
|
-
) as fptr:
|
|
1341
|
+
with open(file=os.path.join(file_dir, filename), mode="wb") as fptr:
|
|
1345
1342
|
fptr.write(orjson.dumps(data, option=orjson.OPT_INDENT_2 | orjson.OPT_NON_STR_KEYS))
|
|
1346
1343
|
return DataOperationResult.SAVE_SUCCESS
|
|
1347
1344
|
|
|
@@ -43,6 +43,7 @@ from aiohomematic.const import (
|
|
|
43
43
|
NO_CACHE_ENTRY,
|
|
44
44
|
PRIMARY_CLIENT_CANDIDATE_INTERFACES,
|
|
45
45
|
TIMEOUT,
|
|
46
|
+
UTF_8,
|
|
46
47
|
CommandRxMode,
|
|
47
48
|
DeviceDescription,
|
|
48
49
|
ParamsetKey,
|
|
@@ -526,9 +527,9 @@ def hash_sha256(*, value: Any) -> str:
|
|
|
526
527
|
data = orjson.dumps(value, option=orjson.OPT_SORT_KEYS | orjson.OPT_NON_STR_KEYS)
|
|
527
528
|
except Exception:
|
|
528
529
|
# Fallback: convert to a hashable representation and use repr()
|
|
529
|
-
data = repr(_make_value_hashable(value=value)).encode()
|
|
530
|
+
data = repr(_make_value_hashable(value=value)).encode(encoding=UTF_8)
|
|
530
531
|
hasher.update(data)
|
|
531
|
-
return base64.b64encode(hasher.digest()).decode()
|
|
532
|
+
return base64.b64encode(hasher.digest()).decode(encoding=UTF_8)
|
|
532
533
|
|
|
533
534
|
|
|
534
535
|
def _make_value_hashable(*, value: Any) -> Any:
|
|
@@ -632,9 +633,7 @@ def log_boundary_error(
|
|
|
632
633
|
log_message += f" {message}"
|
|
633
634
|
|
|
634
635
|
if log_context:
|
|
635
|
-
log_message += (
|
|
636
|
-
f" ctx={orjson.dumps(_safe_log_context(context=log_context), option=orjson.OPT_SORT_KEYS).decode()}"
|
|
637
|
-
)
|
|
636
|
+
log_message += f" ctx={orjson.dumps(_safe_log_context(context=log_context), option=orjson.OPT_SORT_KEYS).decode(encoding=UTF_8)}"
|
|
638
637
|
|
|
639
638
|
# Choose level if not provided:
|
|
640
639
|
if (chosen_level := level) is None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aiohomematic
|
|
3
|
-
Version: 2025.10.
|
|
3
|
+
Version: 2025.10.8
|
|
4
4
|
Summary: Homematic interface for Home Assistant running on Python 3.
|
|
5
5
|
Home-page: https://github.com/sukramj/aiohomematic
|
|
6
6
|
Author-email: SukramJ <sukramj@icloud.com>, Daniel Perna <danielperna84@gmail.com>
|
|
@@ -519,7 +519,7 @@ async def test_data_points_by_category(
|
|
|
519
519
|
central, _, _ = central_client_factory
|
|
520
520
|
ebp_sensor = central.get_data_points(category=DataPointCategory.SENSOR)
|
|
521
521
|
assert ebp_sensor
|
|
522
|
-
assert len(ebp_sensor) ==
|
|
522
|
+
assert len(ebp_sensor) == 18
|
|
523
523
|
|
|
524
524
|
def _device_changed(self, *args: Any, **kwargs: Any) -> None:
|
|
525
525
|
"""Handle device state changes."""
|
|
@@ -527,7 +527,7 @@ async def test_data_points_by_category(
|
|
|
527
527
|
ebp_sensor[0].register_data_point_updated_callback(cb=_device_changed, custom_id="some_id")
|
|
528
528
|
ebp_sensor2 = central.get_data_points(category=DataPointCategory.SENSOR, registered=False)
|
|
529
529
|
assert ebp_sensor2
|
|
530
|
-
assert len(ebp_sensor2) ==
|
|
530
|
+
assert len(ebp_sensor2) == 17
|
|
531
531
|
|
|
532
532
|
|
|
533
533
|
@pytest.mark.asyncio
|
|
@@ -595,13 +595,13 @@ async def test_add_device(
|
|
|
595
595
|
"""Test add_device."""
|
|
596
596
|
central, _, _ = central_client_factory
|
|
597
597
|
assert len(central._devices) == 1
|
|
598
|
-
assert len(central.get_data_points(exclude_no_create=False)) ==
|
|
598
|
+
assert len(central.get_data_points(exclude_no_create=False)) == 33
|
|
599
599
|
assert len(central.device_descriptions._raw_device_descriptions.get(const.INTERFACE_ID)) == 9
|
|
600
600
|
assert len(central.paramset_descriptions._raw_paramset_descriptions.get(const.INTERFACE_ID)) == 9
|
|
601
601
|
dev_desc = helper.load_device_description(central=central, filename="HmIP-BSM.json")
|
|
602
602
|
await central.add_new_devices(interface_id=const.INTERFACE_ID, device_descriptions=dev_desc)
|
|
603
603
|
assert len(central._devices) == 2
|
|
604
|
-
assert len(central.get_data_points(exclude_no_create=False)) ==
|
|
604
|
+
assert len(central.get_data_points(exclude_no_create=False)) == 64
|
|
605
605
|
assert len(central.device_descriptions._raw_device_descriptions.get(const.INTERFACE_ID)) == 20
|
|
606
606
|
assert len(central.paramset_descriptions._raw_paramset_descriptions.get(const.INTERFACE_ID)) == 20
|
|
607
607
|
await central.add_new_devices(interface_id="NOT_ANINTERFACE_ID", device_descriptions=dev_desc)
|
|
@@ -629,13 +629,13 @@ async def test_delete_device(
|
|
|
629
629
|
"""Test device delete_device."""
|
|
630
630
|
central, _, _ = central_client_factory
|
|
631
631
|
assert len(central._devices) == 2
|
|
632
|
-
assert len(central.get_data_points(exclude_no_create=False)) ==
|
|
632
|
+
assert len(central.get_data_points(exclude_no_create=False)) == 64
|
|
633
633
|
assert len(central.device_descriptions._raw_device_descriptions.get(const.INTERFACE_ID)) == 20
|
|
634
634
|
assert len(central.paramset_descriptions._raw_paramset_descriptions.get(const.INTERFACE_ID)) == 20
|
|
635
635
|
|
|
636
636
|
await central.delete_devices(interface_id=const.INTERFACE_ID, addresses=["VCU2128127"])
|
|
637
637
|
assert len(central._devices) == 1
|
|
638
|
-
assert len(central.get_data_points(exclude_no_create=False)) ==
|
|
638
|
+
assert len(central.get_data_points(exclude_no_create=False)) == 33
|
|
639
639
|
assert len(central.device_descriptions._raw_device_descriptions.get(const.INTERFACE_ID)) == 9
|
|
640
640
|
assert len(central.paramset_descriptions._raw_paramset_descriptions.get(const.INTERFACE_ID)) == 9
|
|
641
641
|
|
|
@@ -856,7 +856,7 @@ async def test_central_direct(factory: helper.Factory) -> None:
|
|
|
856
856
|
assert central.available is False
|
|
857
857
|
assert central.system_information.serial == "0815_4711"
|
|
858
858
|
assert len(central._devices) == 2
|
|
859
|
-
assert len(central.get_data_points(exclude_no_create=False)) ==
|
|
859
|
+
assert len(central.get_data_points(exclude_no_create=False)) == 64
|
|
860
860
|
finally:
|
|
861
861
|
await central.stop()
|
|
862
862
|
|
|
@@ -27,7 +27,7 @@ async def test_central_mini(central_unit_mini) -> None:
|
|
|
27
27
|
assert central_unit_mini.get_client(interface_id=const.INTERFACE_ID).model == "PyDevCCU"
|
|
28
28
|
assert central_unit_mini.primary_client.model == "PyDevCCU"
|
|
29
29
|
assert len(central_unit_mini._devices) == 2
|
|
30
|
-
assert len(central_unit_mini.get_data_points(exclude_no_create=False)) ==
|
|
30
|
+
assert len(central_unit_mini.get_data_points(exclude_no_create=False)) == 70
|
|
31
31
|
|
|
32
32
|
usage_types: dict[DataPointUsage, int] = {}
|
|
33
33
|
for dp in central_unit_mini.get_data_points(exclude_no_create=False):
|
|
@@ -39,7 +39,7 @@ async def test_central_mini(central_unit_mini) -> None:
|
|
|
39
39
|
|
|
40
40
|
assert usage_types[DataPointUsage.NO_CREATE] == 45
|
|
41
41
|
assert usage_types[DataPointUsage.CDP_PRIMARY] == 4
|
|
42
|
-
assert usage_types[DataPointUsage.DATA_POINT] ==
|
|
42
|
+
assert usage_types[DataPointUsage.DATA_POINT] == 16
|
|
43
43
|
assert usage_types[DataPointUsage.CDP_VISIBLE] == 5
|
|
44
44
|
|
|
45
45
|
|
|
@@ -193,12 +193,12 @@ async def test_central_full(central_unit_full) -> None: # noqa: C901
|
|
|
193
193
|
assert usage_types[DataPointUsage.CDP_PRIMARY] == 272
|
|
194
194
|
assert usage_types[DataPointUsage.CDP_SECONDARY] == 162
|
|
195
195
|
assert usage_types[DataPointUsage.CDP_VISIBLE] == 141
|
|
196
|
-
assert usage_types[DataPointUsage.DATA_POINT] ==
|
|
196
|
+
assert usage_types[DataPointUsage.DATA_POINT] == 4033
|
|
197
197
|
assert usage_types[DataPointUsage.NO_CREATE] == 4291
|
|
198
198
|
|
|
199
199
|
assert len(ce_channels) == 130
|
|
200
200
|
assert len(data_point_types) == 6
|
|
201
|
-
assert len(parameters) ==
|
|
201
|
+
assert len(parameters) == 234
|
|
202
202
|
|
|
203
203
|
assert len(central_unit_full._devices) == 394
|
|
204
204
|
virtual_remotes = ["VCU4264293", "VCU0000057", "VCU0000001"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/calculated/data_point.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/model/generic/binary_sensor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/rega_scripts/fetch_all_device_data.fn
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/rega_scripts/set_program_state.fn
RENAMED
|
File without changes
|
{aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic/rega_scripts/set_system_variable.fn
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aiohomematic-2025.10.7 → aiohomematic-2025.10.8}/aiohomematic.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|