aiohomematic 2025.10.8__py3-none-any.whl → 2025.10.10__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 +69 -30
- aiohomematic/central/decorators.py +1 -1
- aiohomematic/central/rpc_server.py +1 -1
- aiohomematic/client/__init__.py +22 -14
- aiohomematic/client/_rpc_errors.py +1 -1
- aiohomematic/client/json_rpc.py +29 -3
- aiohomematic/client/rpc_proxy.py +20 -2
- aiohomematic/const.py +33 -7
- 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 +1 -1
- aiohomematic/model/calculated/climate.py +1 -1
- aiohomematic/model/calculated/data_point.py +1 -1
- aiohomematic/model/calculated/operating_voltage_level.py +1 -1
- aiohomematic/model/calculated/support.py +1 -1
- 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 +4 -4
- aiohomematic/model/device.py +13 -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 +3 -3
- aiohomematic/property_decorators.py +2 -2
- aiohomematic/store/__init__.py +34 -0
- aiohomematic/{caches → store}/dynamic.py +4 -4
- aiohomematic/store/persistent.py +970 -0
- aiohomematic/{caches → store}/visibility.py +4 -4
- aiohomematic/support.py +16 -12
- aiohomematic/validator.py +1 -1
- {aiohomematic-2025.10.8.dist-info → aiohomematic-2025.10.10.dist-info}/METADATA +1 -1
- aiohomematic-2025.10.10.dist-info/RECORD +78 -0
- aiohomematic_support/client_local.py +8 -8
- aiohomematic/caches/__init__.py +0 -12
- aiohomematic/caches/persistent.py +0 -478
- aiohomematic-2025.10.8.dist-info/RECORD +0 -78
- {aiohomematic-2025.10.8.dist-info → aiohomematic-2025.10.10.dist-info}/WHEEL +0 -0
- {aiohomematic-2025.10.8.dist-info → aiohomematic-2025.10.10.dist-info}/licenses/LICENSE +0 -0
- {aiohomematic-2025.10.8.dist-info → aiohomematic-2025.10.10.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.10"
|
|
23
23
|
|
|
24
24
|
# Detect test speedup mode via environment
|
|
25
25
|
_TEST_SPEEDUP: Final = (
|
|
@@ -27,8 +27,6 @@ _TEST_SPEEDUP: Final = (
|
|
|
27
27
|
)
|
|
28
28
|
|
|
29
29
|
# default
|
|
30
|
-
DEFAULT_STORAGE_FOLDER: Final = "aiohomematic_storage"
|
|
31
|
-
DEFAULT_CUSTOM_ID: Final = "custom_id"
|
|
32
30
|
DEFAULT_DELAY_NEW_DEVICE_CREATION: Final = False
|
|
33
31
|
DEFAULT_ENABLE_DEVICE_FIRMWARE_CHECK: Final = False
|
|
34
32
|
DEFAULT_ENABLE_PROGRAM_SCAN: Final = True
|
|
@@ -40,8 +38,11 @@ DEFAULT_INCLUDE_INTERNAL_SYSVARS: Final = True
|
|
|
40
38
|
DEFAULT_MAX_READ_WORKERS: Final = 1
|
|
41
39
|
DEFAULT_MAX_WORKERS: Final = 1
|
|
42
40
|
DEFAULT_MULTIPLIER: Final = 1.0
|
|
41
|
+
DEFAULT_OPTIONAL_SETTINGS: Final[tuple[OptionalSettings | str, ...]] = ()
|
|
43
42
|
DEFAULT_PERIODIC_REFRESH_INTERVAL: Final = 15
|
|
44
43
|
DEFAULT_PROGRAM_MARKERS: Final[tuple[DescriptionMarker | str, ...]] = ()
|
|
44
|
+
DEFAULT_SESSION_RECORDER_START_FOR_SECONDS: Final = 120
|
|
45
|
+
DEFAULT_STORAGE_DIRECTORY: Final = "aiohomematic_storage"
|
|
45
46
|
DEFAULT_SYSVAR_MARKERS: Final[tuple[DescriptionMarker | str, ...]] = ()
|
|
46
47
|
DEFAULT_SYS_SCAN_INTERVAL: Final = 30
|
|
47
48
|
DEFAULT_TLS: Final = False
|
|
@@ -82,7 +83,7 @@ SYSVAR_ENABLE_DEFAULT: Final[frozenset[str]] = ALWAYS_ENABLE_SYSVARS_BY_ID
|
|
|
82
83
|
|
|
83
84
|
ADDRESS_SEPARATOR: Final = ":"
|
|
84
85
|
BLOCK_LOG_TIMEOUT: Final = 60
|
|
85
|
-
|
|
86
|
+
CONTENT_PATH: Final = "cache"
|
|
86
87
|
CONF_PASSWORD: Final = "password"
|
|
87
88
|
CONF_USERNAME: Final = "username"
|
|
88
89
|
|
|
@@ -94,8 +95,12 @@ DEVICE_FIRMWARE_CHECK_INTERVAL: Final = 21600 # 6h
|
|
|
94
95
|
DEVICE_FIRMWARE_DELIVERING_CHECK_INTERVAL: Final = 3600 # 1h
|
|
95
96
|
DEVICE_FIRMWARE_UPDATING_CHECK_INTERVAL: Final = 300 # 5m
|
|
96
97
|
DUMMY_SERIAL: Final = "SN0815"
|
|
97
|
-
FILE_DEVICES: Final = "homematic_devices
|
|
98
|
-
FILE_PARAMSETS: Final = "homematic_paramsets
|
|
98
|
+
FILE_DEVICES: Final = "homematic_devices"
|
|
99
|
+
FILE_PARAMSETS: Final = "homematic_paramsets"
|
|
100
|
+
FILE_SESSION_RECORDER: Final = "homematic_session_recorder"
|
|
101
|
+
FILE_NAME_TS_PATTERN: Final = "%Y%m%d_%H%M%S"
|
|
102
|
+
SUB_DIRECTORY_CACHE: Final = "cache"
|
|
103
|
+
SUB_DIRECTORY_SESSION: Final = "session"
|
|
99
104
|
HUB_PATH: Final = "hub"
|
|
100
105
|
IDENTIFIER_SEPARATOR: Final = "@"
|
|
101
106
|
INIT_DATETIME: Final = datetime.strptime("01.01.1970 00:00:00", DATETIME_FORMAT)
|
|
@@ -204,6 +209,13 @@ class CommandRxMode(StrEnum):
|
|
|
204
209
|
WAKEUP = "WAKEUP"
|
|
205
210
|
|
|
206
211
|
|
|
212
|
+
class InternalCustomID(StrEnum):
|
|
213
|
+
"""Enum for Homematic internal custom IDs."""
|
|
214
|
+
|
|
215
|
+
DEFAULT = "cid_default"
|
|
216
|
+
MANU_TEMP = "cid_manu_temp"
|
|
217
|
+
|
|
218
|
+
|
|
207
219
|
class DataOperationResult(Enum):
|
|
208
220
|
"""Enum with data operation results."""
|
|
209
221
|
|
|
@@ -342,6 +354,13 @@ class Operations(IntEnum):
|
|
|
342
354
|
EVENT = 4
|
|
343
355
|
|
|
344
356
|
|
|
357
|
+
class OptionalSettings(StrEnum):
|
|
358
|
+
"""Enum with aiohomematic optional settings."""
|
|
359
|
+
|
|
360
|
+
SR_DISABLE_RANDOMIZE_OUTPUT = "SR_DISABLE_RANDOMIZED_OUTPUT"
|
|
361
|
+
SR_RECORD_SYSTEM_INIT = "SR_RECORD_SYSTEM_INIT"
|
|
362
|
+
|
|
363
|
+
|
|
345
364
|
class Parameter(StrEnum):
|
|
346
365
|
"""Enum with Homematic parameters."""
|
|
347
366
|
|
|
@@ -499,6 +518,13 @@ class RegaScript(StrEnum):
|
|
|
499
518
|
SET_SYSTEM_VARIABLE: Final = "set_system_variable.fn"
|
|
500
519
|
|
|
501
520
|
|
|
521
|
+
class RPCType(StrEnum):
|
|
522
|
+
"""Enum with Homematic rpc types."""
|
|
523
|
+
|
|
524
|
+
XML_RPC = "xmlrpc"
|
|
525
|
+
JSON_RPC = "jsonrpc"
|
|
526
|
+
|
|
527
|
+
|
|
502
528
|
class Interface(StrEnum):
|
|
503
529
|
"""Enum with Homematic interfaces."""
|
|
504
530
|
|
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
|
"""Module for data points implemented using the climate category."""
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
@@ -15,6 +15,7 @@ from aiohomematic.const import (
|
|
|
15
15
|
SCHEDULER_PROFILE_PATTERN,
|
|
16
16
|
SCHEDULER_TIME_PATTERN,
|
|
17
17
|
DataPointCategory,
|
|
18
|
+
InternalCustomID,
|
|
18
19
|
Parameter,
|
|
19
20
|
ParamsetKey,
|
|
20
21
|
ProductGroup,
|
|
@@ -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=InternalCustomID.MANU_TEMP
|
|
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=InternalCustomID.MANU_TEMP
|
|
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=InternalCustomID.MANU_TEMP
|
|
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
|
|
|
@@ -39,7 +39,6 @@ from aiohomematic import central as hmcu, client as hmcl, support as hms, valida
|
|
|
39
39
|
from aiohomematic.async_support import loop_check
|
|
40
40
|
from aiohomematic.const import (
|
|
41
41
|
CALLBACK_TYPE,
|
|
42
|
-
DEFAULT_CUSTOM_ID,
|
|
43
42
|
DEFAULT_MULTIPLIER,
|
|
44
43
|
DP_KEY_VALUE,
|
|
45
44
|
INIT_DATETIME,
|
|
@@ -54,6 +53,7 @@ from aiohomematic.const import (
|
|
|
54
53
|
DataPointUsage,
|
|
55
54
|
EventKey,
|
|
56
55
|
Flag,
|
|
56
|
+
InternalCustomID,
|
|
57
57
|
Operations,
|
|
58
58
|
Parameter,
|
|
59
59
|
ParameterData,
|
|
@@ -308,11 +308,11 @@ class CallbackDataPoint(ABC, LogContextMixin):
|
|
|
308
308
|
|
|
309
309
|
def register_internal_data_point_updated_callback(self, *, cb: Callable) -> CALLBACK_TYPE:
|
|
310
310
|
"""Register internal data_point updated callback."""
|
|
311
|
-
return self.register_data_point_updated_callback(cb=cb, custom_id=
|
|
311
|
+
return self.register_data_point_updated_callback(cb=cb, custom_id=InternalCustomID.DEFAULT)
|
|
312
312
|
|
|
313
313
|
def register_data_point_updated_callback(self, *, cb: Callable, custom_id: str) -> CALLBACK_TYPE:
|
|
314
314
|
"""Register data_point updated callback."""
|
|
315
|
-
if custom_id
|
|
315
|
+
if custom_id not in InternalCustomID:
|
|
316
316
|
if self._custom_id is not None and self._custom_id != custom_id:
|
|
317
317
|
raise AioHomematicException(
|
|
318
318
|
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))}"
|
|
@@ -1293,7 +1293,7 @@ class _DefinitionExporter:
|
|
|
1293
1293
|
str, dict[ParamsetKey, dict[str, ParameterData]]
|
|
1294
1294
|
] = await self._client.get_all_paramset_descriptions(device_descriptions=tuple(device_descriptions.values()))
|
|
1295
1295
|
model = device_descriptions[self._device_address]["TYPE"]
|
|
1296
|
-
|
|
1296
|
+
file_name = f"{model}.json"
|
|
1297
1297
|
|
|
1298
1298
|
# anonymize device_descriptions
|
|
1299
1299
|
anonymize_device_descriptions: list[DeviceDescription] = []
|
|
@@ -1315,15 +1315,15 @@ class _DefinitionExporter:
|
|
|
1315
1315
|
|
|
1316
1316
|
# Save device_descriptions for device to file.
|
|
1317
1317
|
await self._save(
|
|
1318
|
-
|
|
1319
|
-
|
|
1318
|
+
directory=f"{self._storage_directory}/{DEVICE_DESCRIPTIONS_DIR}",
|
|
1319
|
+
file_name=file_name,
|
|
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
|
-
|
|
1326
|
-
|
|
1325
|
+
directory=f"{self._storage_directory}/{PARAMSET_DESCRIPTIONS_DIR}",
|
|
1326
|
+
file_name=file_name,
|
|
1327
1327
|
data=anonymize_paramset_descriptions,
|
|
1328
1328
|
)
|
|
1329
1329
|
|
|
@@ -1332,13 +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, file_name: 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(file=os.path.join(
|
|
1341
|
+
with open(file=os.path.join(directory, file_name), mode="wb") as fptr:
|
|
1342
1342
|
fptr.write(orjson.dumps(data, option=orjson.OPT_INDENT_2 | orjson.OPT_NON_STR_KEYS))
|
|
1343
1343
|
return DataOperationResult.SAVE_SUCCESS
|
|
1344
1344
|
|
aiohomematic/model/event.py
CHANGED
aiohomematic/model/hub/button.py
CHANGED
aiohomematic/model/hub/number.py
CHANGED
aiohomematic/model/hub/select.py
CHANGED
aiohomematic/model/hub/sensor.py
CHANGED
aiohomematic/model/hub/switch.py
CHANGED
aiohomematic/model/hub/text.py
CHANGED
aiohomematic/model/support.py
CHANGED
aiohomematic/model/update.py
CHANGED
|
@@ -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 update category."""
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
@@ -11,11 +11,11 @@ from typing import Final
|
|
|
11
11
|
|
|
12
12
|
from aiohomematic.const import (
|
|
13
13
|
CALLBACK_TYPE,
|
|
14
|
-
DEFAULT_CUSTOM_ID,
|
|
15
14
|
HMIP_FIRMWARE_UPDATE_IN_PROGRESS_STATES,
|
|
16
15
|
HMIP_FIRMWARE_UPDATE_READY_STATES,
|
|
17
16
|
DataPointCategory,
|
|
18
17
|
Interface,
|
|
18
|
+
InternalCustomID,
|
|
19
19
|
)
|
|
20
20
|
from aiohomematic.decorators import inspector
|
|
21
21
|
from aiohomematic.exceptions import AioHomematicException
|
|
@@ -114,7 +114,7 @@ class DpUpdate(CallbackDataPoint, PayloadMixin):
|
|
|
114
114
|
|
|
115
115
|
def register_data_point_updated_callback(self, *, cb: Callable, custom_id: str) -> CALLBACK_TYPE:
|
|
116
116
|
"""Register update callback."""
|
|
117
|
-
if custom_id !=
|
|
117
|
+
if custom_id != InternalCustomID.DEFAULT:
|
|
118
118
|
if self._custom_id is not None:
|
|
119
119
|
raise AioHomematicException(
|
|
120
120
|
f"REGISTER_UPDATE_CALLBACK failed: hm_data_point: {self.full_name} is already registered by {self._custom_id}"
|