aiohomematic 2025.10.7__py3-none-any.whl → 2025.10.9__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 +3 -3
- aiohomematic/async_support.py +1 -1
- aiohomematic/central/__init__.py +59 -31
- aiohomematic/central/decorators.py +1 -1
- aiohomematic/central/rpc_server.py +1 -1
- aiohomematic/client/__init__.py +19 -13
- aiohomematic/client/_rpc_errors.py +1 -1
- aiohomematic/client/json_rpc.py +29 -3
- aiohomematic/client/rpc_proxy.py +20 -2
- aiohomematic/const.py +25 -6
- aiohomematic/context.py +1 -1
- aiohomematic/converter.py +1 -1
- aiohomematic/decorators.py +1 -1
- aiohomematic/exceptions.py +1 -1
- aiohomematic/hmcli.py +1 -1
- aiohomematic/model/__init__.py +1 -1
- aiohomematic/model/calculated/__init__.py +21 -4
- aiohomematic/model/calculated/climate.py +59 -1
- aiohomematic/model/calculated/data_point.py +1 -1
- aiohomematic/model/calculated/operating_voltage_level.py +1 -1
- aiohomematic/model/calculated/support.py +41 -3
- aiohomematic/model/custom/__init__.py +1 -1
- aiohomematic/model/custom/climate.py +7 -4
- aiohomematic/model/custom/const.py +1 -1
- aiohomematic/model/custom/cover.py +1 -1
- aiohomematic/model/custom/data_point.py +1 -1
- aiohomematic/model/custom/definition.py +1 -1
- aiohomematic/model/custom/light.py +1 -1
- aiohomematic/model/custom/lock.py +1 -1
- aiohomematic/model/custom/siren.py +1 -1
- aiohomematic/model/custom/support.py +1 -1
- aiohomematic/model/custom/switch.py +1 -1
- aiohomematic/model/custom/valve.py +1 -1
- aiohomematic/model/data_point.py +3 -2
- aiohomematic/model/device.py +10 -13
- aiohomematic/model/event.py +1 -1
- aiohomematic/model/generic/__init__.py +1 -1
- aiohomematic/model/generic/action.py +1 -1
- aiohomematic/model/generic/binary_sensor.py +1 -1
- aiohomematic/model/generic/button.py +1 -1
- aiohomematic/model/generic/data_point.py +1 -1
- aiohomematic/model/generic/number.py +1 -1
- aiohomematic/model/generic/select.py +1 -1
- aiohomematic/model/generic/sensor.py +1 -1
- aiohomematic/model/generic/switch.py +1 -1
- aiohomematic/model/generic/text.py +1 -1
- aiohomematic/model/hub/__init__.py +1 -1
- aiohomematic/model/hub/binary_sensor.py +1 -1
- aiohomematic/model/hub/button.py +1 -1
- aiohomematic/model/hub/data_point.py +1 -1
- aiohomematic/model/hub/number.py +1 -1
- aiohomematic/model/hub/select.py +1 -1
- aiohomematic/model/hub/sensor.py +1 -1
- aiohomematic/model/hub/switch.py +1 -1
- aiohomematic/model/hub/text.py +1 -1
- aiohomematic/model/support.py +1 -1
- aiohomematic/model/update.py +1 -1
- aiohomematic/property_decorators.py +2 -2
- aiohomematic/store/__init__.py +34 -0
- aiohomematic/{caches → store}/dynamic.py +4 -4
- aiohomematic/store/persistent.py +933 -0
- aiohomematic/{caches → store}/visibility.py +4 -4
- aiohomematic/support.py +20 -17
- aiohomematic/validator.py +1 -1
- {aiohomematic-2025.10.7.dist-info → aiohomematic-2025.10.9.dist-info}/METADATA +1 -1
- aiohomematic-2025.10.9.dist-info/RECORD +78 -0
- aiohomematic_support/client_local.py +2 -2
- aiohomematic/caches/__init__.py +0 -12
- aiohomematic/caches/persistent.py +0 -478
- aiohomematic-2025.10.7.dist-info/RECORD +0 -78
- {aiohomematic-2025.10.7.dist-info → aiohomematic-2025.10.9.dist-info}/WHEEL +0 -0
- {aiohomematic-2025.10.7.dist-info → aiohomematic-2025.10.9.dist-info}/licenses/LICENSE +0 -0
- {aiohomematic-2025.10.7.dist-info → aiohomematic-2025.10.9.dist-info}/top_level.txt +0 -0
aiohomematic/const.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
|
2
|
-
# Copyright (c) 2021-2025
|
|
2
|
+
# Copyright (c) 2021-2025
|
|
3
3
|
"""
|
|
4
4
|
Constants used by aiohomematic.
|
|
5
5
|
|
|
@@ -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.9"
|
|
23
23
|
|
|
24
24
|
# Detect test speedup mode via environment
|
|
25
25
|
_TEST_SPEEDUP: Final = (
|
|
@@ -27,7 +27,7 @@ _TEST_SPEEDUP: Final = (
|
|
|
27
27
|
)
|
|
28
28
|
|
|
29
29
|
# default
|
|
30
|
-
|
|
30
|
+
DEFAULT_STORAGE_DIRECTORY: Final = "aiohomematic_storage"
|
|
31
31
|
DEFAULT_CUSTOM_ID: Final = "custom_id"
|
|
32
32
|
DEFAULT_DELAY_NEW_DEVICE_CREATION: Final = False
|
|
33
33
|
DEFAULT_ENABLE_DEVICE_FIRMWARE_CHECK: Final = False
|
|
@@ -49,6 +49,12 @@ DEFAULT_UN_IGNORES: Final[frozenset[str]] = frozenset()
|
|
|
49
49
|
DEFAULT_USE_GROUP_CHANNEL_FOR_COVER_STATE: Final = True
|
|
50
50
|
DEFAULT_VERIFY_TLS: Final = False
|
|
51
51
|
|
|
52
|
+
MANU_TEMP_CUSTOM_ID: Final = "manu_temp"
|
|
53
|
+
INTERNAL_CUSTOM_IDS: Final[tuple[str, ...]] = (
|
|
54
|
+
DEFAULT_CUSTOM_ID,
|
|
55
|
+
MANU_TEMP_CUSTOM_ID,
|
|
56
|
+
)
|
|
57
|
+
|
|
52
58
|
# Default encoding for json service calls, persistent cache
|
|
53
59
|
UTF_8: Final = "utf-8"
|
|
54
60
|
# Default encoding for xmlrpc service calls and script files
|
|
@@ -82,7 +88,7 @@ SYSVAR_ENABLE_DEFAULT: Final[frozenset[str]] = ALWAYS_ENABLE_SYSVARS_BY_ID
|
|
|
82
88
|
|
|
83
89
|
ADDRESS_SEPARATOR: Final = ":"
|
|
84
90
|
BLOCK_LOG_TIMEOUT: Final = 60
|
|
85
|
-
|
|
91
|
+
CONTENT_PATH: Final = "cache"
|
|
86
92
|
CONF_PASSWORD: Final = "password"
|
|
87
93
|
CONF_USERNAME: Final = "username"
|
|
88
94
|
|
|
@@ -94,8 +100,12 @@ DEVICE_FIRMWARE_CHECK_INTERVAL: Final = 21600 # 6h
|
|
|
94
100
|
DEVICE_FIRMWARE_DELIVERING_CHECK_INTERVAL: Final = 3600 # 1h
|
|
95
101
|
DEVICE_FIRMWARE_UPDATING_CHECK_INTERVAL: Final = 300 # 5m
|
|
96
102
|
DUMMY_SERIAL: Final = "SN0815"
|
|
97
|
-
FILE_DEVICES: Final = "homematic_devices
|
|
98
|
-
FILE_PARAMSETS: Final = "homematic_paramsets
|
|
103
|
+
FILE_DEVICES: Final = "homematic_devices"
|
|
104
|
+
FILE_PARAMSETS: Final = "homematic_paramsets"
|
|
105
|
+
FILE_SESSION_RECORDER: Final = "homematic_session_recorder"
|
|
106
|
+
FILE_NAME_TS_PATTERN: Final = "%Y%m%d_%H%M%S"
|
|
107
|
+
SUB_DIRECTORY_CACHE: Final = "cache"
|
|
108
|
+
SUB_DIRECTORY_SESSION: Final = "session"
|
|
99
109
|
HUB_PATH: Final = "hub"
|
|
100
110
|
IDENTIFIER_SEPARATOR: Final = "@"
|
|
101
111
|
INIT_DATETIME: Final = datetime.strptime("01.01.1970 00:00:00", DATETIME_FORMAT)
|
|
@@ -179,6 +189,8 @@ class CalulatedParameter(StrEnum):
|
|
|
179
189
|
|
|
180
190
|
APPARENT_TEMPERATURE = "APPARENT_TEMPERATURE"
|
|
181
191
|
DEW_POINT = "DEW_POINT"
|
|
192
|
+
DEW_POINT_SPREAD = "DEW_POINT_SPREAD"
|
|
193
|
+
ENTHALPY = "ENTHALPY"
|
|
182
194
|
FROST_POINT = "FROST_POINT"
|
|
183
195
|
OPERATING_VOLTAGE_LEVEL = "OPERATING_VOLTAGE_LEVEL"
|
|
184
196
|
VAPOR_CONCENTRATION = "VAPOR_CONCENTRATION"
|
|
@@ -497,6 +509,13 @@ class RegaScript(StrEnum):
|
|
|
497
509
|
SET_SYSTEM_VARIABLE: Final = "set_system_variable.fn"
|
|
498
510
|
|
|
499
511
|
|
|
512
|
+
class RPCType(StrEnum):
|
|
513
|
+
"""Enum with Homematic rpc types."""
|
|
514
|
+
|
|
515
|
+
XML_RPC = "xmlrpc"
|
|
516
|
+
JSON_RPC = "jsonrpc"
|
|
517
|
+
|
|
518
|
+
|
|
500
519
|
class Interface(StrEnum):
|
|
501
520
|
"""Enum with Homematic interfaces."""
|
|
502
521
|
|
aiohomematic/context.py
CHANGED
aiohomematic/converter.py
CHANGED
aiohomematic/decorators.py
CHANGED
aiohomematic/exceptions.py
CHANGED
aiohomematic/hmcli.py
CHANGED
aiohomematic/model/__init__.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
|
2
|
-
# Copyright (c) 2021-2025
|
|
2
|
+
# Copyright (c) 2021-2025
|
|
3
3
|
"""
|
|
4
4
|
Calculated (derived) data points for AioHomematic.
|
|
5
5
|
|
|
@@ -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
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
|
2
|
-
# Copyright (c) 2021-2025
|
|
2
|
+
# Copyright (c) 2021-2025
|
|
3
3
|
"""Module for calculating the apparent temperature in the sensor category."""
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
@@ -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
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
|
2
|
-
# Copyright (c) 2021-2025
|
|
2
|
+
# Copyright (c) 2021-2025
|
|
3
3
|
"""
|
|
4
4
|
A number of functions used to calculate values based on existing data.
|
|
5
5
|
|
|
@@ -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:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
|
2
|
-
# Copyright (c) 2021-2025
|
|
2
|
+
# Copyright (c) 2021-2025
|
|
3
3
|
"""Module for data points implemented using the climate category."""
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
@@ -12,6 +12,7 @@ import logging
|
|
|
12
12
|
from typing import Any, Final, cast
|
|
13
13
|
|
|
14
14
|
from aiohomematic.const import (
|
|
15
|
+
MANU_TEMP_CUSTOM_ID,
|
|
15
16
|
SCHEDULER_PROFILE_PATTERN,
|
|
16
17
|
SCHEDULER_TIME_PATTERN,
|
|
17
18
|
DataPointCategory,
|
|
@@ -233,7 +234,9 @@ class BaseCustomDpClimate(CustomDataPoint):
|
|
|
233
234
|
field=Field.TEMPERATURE_MINIMUM, data_point_type=DpFloat
|
|
234
235
|
)
|
|
235
236
|
self._unregister_callbacks.append(
|
|
236
|
-
self._dp_setpoint.register_data_point_updated_callback(
|
|
237
|
+
self._dp_setpoint.register_data_point_updated_callback(
|
|
238
|
+
cb=self._manu_temp_changed, custom_id=MANU_TEMP_CUSTOM_ID
|
|
239
|
+
)
|
|
237
240
|
)
|
|
238
241
|
|
|
239
242
|
@abstractmethod
|
|
@@ -799,7 +802,7 @@ class CustomDpRfThermostat(BaseCustomDpClimate):
|
|
|
799
802
|
|
|
800
803
|
self._unregister_callbacks.append(
|
|
801
804
|
self._dp_control_mode.register_data_point_updated_callback(
|
|
802
|
-
cb=self._manu_temp_changed, custom_id=
|
|
805
|
+
cb=self._manu_temp_changed, custom_id=MANU_TEMP_CUSTOM_ID
|
|
803
806
|
)
|
|
804
807
|
)
|
|
805
808
|
|
|
@@ -1044,7 +1047,7 @@ class CustomDpIpThermostat(BaseCustomDpClimate):
|
|
|
1044
1047
|
|
|
1045
1048
|
self._unregister_callbacks.append(
|
|
1046
1049
|
self._dp_set_point_mode.register_data_point_updated_callback(
|
|
1047
|
-
cb=self._manu_temp_changed, custom_id=
|
|
1050
|
+
cb=self._manu_temp_changed, custom_id=MANU_TEMP_CUSTOM_ID
|
|
1048
1051
|
)
|
|
1049
1052
|
)
|
|
1050
1053
|
|
aiohomematic/model/data_point.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
|
2
|
-
# Copyright (c) 2021-2025
|
|
2
|
+
# Copyright (c) 2021-2025
|
|
3
3
|
"""
|
|
4
4
|
Core data point model for AioHomematic.
|
|
5
5
|
|
|
@@ -43,6 +43,7 @@ from aiohomematic.const import (
|
|
|
43
43
|
DEFAULT_MULTIPLIER,
|
|
44
44
|
DP_KEY_VALUE,
|
|
45
45
|
INIT_DATETIME,
|
|
46
|
+
INTERNAL_CUSTOM_IDS,
|
|
46
47
|
KEY_CHANNEL_OPERATION_MODE_VISIBILITY,
|
|
47
48
|
KWARGS_ARG_CUSTOM_ID,
|
|
48
49
|
KWARGS_ARG_DATA_POINT,
|
|
@@ -312,7 +313,7 @@ class CallbackDataPoint(ABC, LogContextMixin):
|
|
|
312
313
|
|
|
313
314
|
def register_data_point_updated_callback(self, *, cb: Callable, custom_id: str) -> CALLBACK_TYPE:
|
|
314
315
|
"""Register data_point updated callback."""
|
|
315
|
-
if custom_id
|
|
316
|
+
if custom_id not in INTERNAL_CUSTOM_IDS:
|
|
316
317
|
if self._custom_id is not None and self._custom_id != custom_id:
|
|
317
318
|
raise AioHomematicException(
|
|
318
319
|
f"REGISTER_data_point_updated_CALLBACK failed: hm_data_point: {self.full_name} is already registered by {self._custom_id}"
|
aiohomematic/model/device.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
|
2
|
-
# Copyright (c) 2021-2025
|
|
2
|
+
# Copyright (c) 2021-2025
|
|
3
3
|
"""
|
|
4
4
|
Device and channel model for AioHomematic.
|
|
5
5
|
|
|
@@ -671,7 +671,7 @@ class Device(LogContextMixin, PayloadMixin):
|
|
|
671
671
|
channel_address=channel_address,
|
|
672
672
|
paramset_key=paramset_key,
|
|
673
673
|
)
|
|
674
|
-
await self._central.
|
|
674
|
+
await self._central.save_files(save_paramset_descriptions=True)
|
|
675
675
|
for dp in self.generic_data_points:
|
|
676
676
|
dp.update_parameter_data()
|
|
677
677
|
self.fire_device_updated_callback()
|
|
@@ -1240,7 +1240,7 @@ class _ValueCache:
|
|
|
1240
1240
|
*,
|
|
1241
1241
|
dpk: DataPointKey,
|
|
1242
1242
|
) -> Any:
|
|
1243
|
-
"""Load data from
|
|
1243
|
+
"""Load data from store."""
|
|
1244
1244
|
# Try to get data from central cache
|
|
1245
1245
|
if (
|
|
1246
1246
|
dpk.paramset_key == ParamsetKey.VALUES
|
|
@@ -1269,14 +1269,14 @@ class _DefinitionExporter:
|
|
|
1269
1269
|
"_device_address",
|
|
1270
1270
|
"_interface_id",
|
|
1271
1271
|
"_random_id",
|
|
1272
|
-
"
|
|
1272
|
+
"_storage_directory",
|
|
1273
1273
|
)
|
|
1274
1274
|
|
|
1275
1275
|
def __init__(self, *, device: Device) -> None:
|
|
1276
1276
|
"""Init the device exporter."""
|
|
1277
1277
|
self._client: Final = device.client
|
|
1278
1278
|
self._central: Final = device.client.central
|
|
1279
|
-
self.
|
|
1279
|
+
self._storage_directory: Final = self._central.config.storage_directory
|
|
1280
1280
|
self._interface_id: Final = device.interface_id
|
|
1281
1281
|
self._device_address: Final = device.address
|
|
1282
1282
|
self._random_id: Final[str] = f"VCU{int(random.randint(1000000, 9999999))}"
|
|
@@ -1315,14 +1315,14 @@ class _DefinitionExporter:
|
|
|
1315
1315
|
|
|
1316
1316
|
# Save device_descriptions for device to file.
|
|
1317
1317
|
await self._save(
|
|
1318
|
-
|
|
1318
|
+
directory=f"{self._storage_directory}/{DEVICE_DESCRIPTIONS_DIR}",
|
|
1319
1319
|
filename=filename,
|
|
1320
1320
|
data=anonymize_device_descriptions,
|
|
1321
1321
|
)
|
|
1322
1322
|
|
|
1323
1323
|
# Save device_descriptions for device to file.
|
|
1324
1324
|
await self._save(
|
|
1325
|
-
|
|
1325
|
+
directory=f"{self._storage_directory}/{PARAMSET_DESCRIPTIONS_DIR}",
|
|
1326
1326
|
filename=filename,
|
|
1327
1327
|
data=anonymize_paramset_descriptions,
|
|
1328
1328
|
)
|
|
@@ -1332,16 +1332,13 @@ class _DefinitionExporter:
|
|
|
1332
1332
|
address_parts[0] = self._random_id
|
|
1333
1333
|
return ADDRESS_SEPARATOR.join(address_parts)
|
|
1334
1334
|
|
|
1335
|
-
async def _save(self, *,
|
|
1335
|
+
async def _save(self, *, directory: str, filename: str, data: Any) -> DataOperationResult:
|
|
1336
1336
|
"""Save file to disk."""
|
|
1337
1337
|
|
|
1338
1338
|
def perform_save() -> DataOperationResult:
|
|
1339
|
-
if not check_or_create_directory(directory=
|
|
1339
|
+
if not check_or_create_directory(directory=directory):
|
|
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(directory, 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
|
|
aiohomematic/model/event.py
CHANGED