aiohomematic 2025.8.7__py3-none-any.whl → 2025.8.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/caches/dynamic.py +8 -12
- aiohomematic/caches/persistent.py +27 -22
- aiohomematic/caches/visibility.py +275 -252
- aiohomematic/central/__init__.py +26 -31
- aiohomematic/const.py +103 -76
- aiohomematic/model/calculated/__init__.py +3 -3
- aiohomematic/model/calculated/data_point.py +5 -1
- aiohomematic/model/custom/data_point.py +4 -0
- aiohomematic/model/data_point.py +16 -3
- aiohomematic/model/device.py +35 -64
- aiohomematic/model/hub/__init__.py +5 -9
- aiohomematic/model/hub/data_point.py +4 -0
- aiohomematic/model/update.py +4 -0
- aiohomematic/rega_scripts/fetch_all_device_data.fn +48 -31
- aiohomematic/support.py +15 -2
- {aiohomematic-2025.8.7.dist-info → aiohomematic-2025.8.9.dist-info}/METADATA +1 -1
- {aiohomematic-2025.8.7.dist-info → aiohomematic-2025.8.9.dist-info}/RECORD +20 -20
- {aiohomematic-2025.8.7.dist-info → aiohomematic-2025.8.9.dist-info}/WHEEL +0 -0
- {aiohomematic-2025.8.7.dist-info → aiohomematic-2025.8.9.dist-info}/licenses/LICENSE +0 -0
- {aiohomematic-2025.8.7.dist-info → aiohomematic-2025.8.9.dist-info}/top_level.txt +0 -0
aiohomematic/central/__init__.py
CHANGED
|
@@ -298,14 +298,14 @@ class CentralUnit(PayloadMixin):
|
|
|
298
298
|
)
|
|
299
299
|
|
|
300
300
|
@property
|
|
301
|
-
def interface_ids(self) ->
|
|
301
|
+
def interface_ids(self) -> frozenset[str]:
|
|
302
302
|
"""Return all associated interface ids."""
|
|
303
|
-
return
|
|
303
|
+
return frozenset(self._clients)
|
|
304
304
|
|
|
305
305
|
@property
|
|
306
|
-
def interfaces(self) ->
|
|
306
|
+
def interfaces(self) -> frozenset[Interface]:
|
|
307
307
|
"""Return all associated interfaces."""
|
|
308
|
-
return
|
|
308
|
+
return frozenset(client.interface for client in self._clients.values())
|
|
309
309
|
|
|
310
310
|
@property
|
|
311
311
|
def is_alive(self) -> bool:
|
|
@@ -1009,19 +1009,19 @@ class CentralUnit(PayloadMixin):
|
|
|
1009
1009
|
return
|
|
1010
1010
|
|
|
1011
1011
|
async with self._device_add_semaphore:
|
|
1012
|
-
#
|
|
1013
|
-
|
|
1014
|
-
dev_desc["ADDRESS"]
|
|
1015
|
-
for dev_desc in self._device_descriptions.get_raw_device_descriptions(interface_id=interface_id)
|
|
1016
|
-
)
|
|
1012
|
+
# Use mapping membership to avoid rebuilding known addresses and allow O(1) checks.
|
|
1013
|
+
existing_map = self._device_descriptions.get_device_descriptions(interface_id=interface_id)
|
|
1017
1014
|
client = self._clients[interface_id]
|
|
1018
1015
|
save_paramset_descriptions = False
|
|
1019
1016
|
save_device_descriptions = False
|
|
1020
1017
|
for dev_desc in device_descriptions:
|
|
1021
1018
|
try:
|
|
1019
|
+
address = dev_desc["ADDRESS"]
|
|
1020
|
+
# Check existence before mutating cache to ensure we detect truly new addresses.
|
|
1021
|
+
is_new_address = address not in existing_map
|
|
1022
1022
|
self._device_descriptions.add_device(interface_id=interface_id, device_description=dev_desc)
|
|
1023
1023
|
save_device_descriptions = True
|
|
1024
|
-
if
|
|
1024
|
+
if is_new_address:
|
|
1025
1025
|
await client.fetch_paramset_descriptions(device_description=dev_desc)
|
|
1026
1026
|
save_paramset_descriptions = True
|
|
1027
1027
|
except Exception as exc: # pragma: no cover
|
|
@@ -1043,7 +1043,7 @@ class CentralUnit(PayloadMixin):
|
|
|
1043
1043
|
await self._create_devices(new_device_addresses=new_device_addresses)
|
|
1044
1044
|
|
|
1045
1045
|
def _check_for_new_device_addresses(self) -> Mapping[str, set[str]]:
|
|
1046
|
-
"""Check if there are new devices
|
|
1046
|
+
"""Check if there are new devices that need to be created."""
|
|
1047
1047
|
new_device_addresses: dict[str, set[str]] = {}
|
|
1048
1048
|
for interface_id in self.interface_ids:
|
|
1049
1049
|
if not self._paramset_descriptions.has_interface_id(interface_id=interface_id):
|
|
@@ -1053,21 +1053,16 @@ class CentralUnit(PayloadMixin):
|
|
|
1053
1053
|
)
|
|
1054
1054
|
continue
|
|
1055
1055
|
|
|
1056
|
-
if
|
|
1057
|
-
|
|
1058
|
-
|
|
1056
|
+
# Build the set locally and assign only if non-empty to avoid add-then-delete pattern
|
|
1057
|
+
new_set: set[str] = set()
|
|
1059
1058
|
for device_address in self._device_descriptions.get_addresses(interface_id=interface_id):
|
|
1060
1059
|
if device_address not in self._devices:
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
del new_device_addresses[interface_id]
|
|
1060
|
+
new_set.add(device_address)
|
|
1061
|
+
if new_set:
|
|
1062
|
+
new_device_addresses[interface_id] = new_set
|
|
1065
1063
|
|
|
1066
1064
|
if _LOGGER.isEnabledFor(level=DEBUG):
|
|
1067
|
-
count
|
|
1068
|
-
for item in new_device_addresses.values():
|
|
1069
|
-
count += len(item)
|
|
1070
|
-
|
|
1065
|
+
count = sum(len(item) for item in new_device_addresses.values())
|
|
1071
1066
|
_LOGGER.debug(
|
|
1072
1067
|
"CHECK_FOR_NEW_DEVICE_ADDRESSES: %s: %i.",
|
|
1073
1068
|
"Found new device addresses" if new_device_addresses else "Did not find any new device addresses",
|
|
@@ -1298,7 +1293,7 @@ class CentralUnit(PayloadMixin):
|
|
|
1298
1293
|
full_format: bool = False,
|
|
1299
1294
|
un_ignore_candidates_only: bool = False,
|
|
1300
1295
|
use_channel_wildcard: bool = False,
|
|
1301
|
-
) ->
|
|
1296
|
+
) -> tuple[str, ...]:
|
|
1302
1297
|
"""
|
|
1303
1298
|
Return all parameters from VALUES paramset.
|
|
1304
1299
|
|
|
@@ -1368,7 +1363,7 @@ class CentralUnit(PayloadMixin):
|
|
|
1368
1363
|
else:
|
|
1369
1364
|
parameters.add(f"{parameter}:{paramset_key}@{model}:{channel_repr}")
|
|
1370
1365
|
|
|
1371
|
-
return
|
|
1366
|
+
return tuple(parameters)
|
|
1372
1367
|
|
|
1373
1368
|
def _get_virtual_remote(self, device_address: str) -> Device | None:
|
|
1374
1369
|
"""Get the virtual remote for the Client."""
|
|
@@ -1811,8 +1806,8 @@ class CentralConfig:
|
|
|
1811
1806
|
enable_program_scan: bool = DEFAULT_ENABLE_PROGRAM_SCAN,
|
|
1812
1807
|
enable_sysvar_scan: bool = DEFAULT_ENABLE_SYSVAR_SCAN,
|
|
1813
1808
|
hm_master_poll_after_send_intervals: tuple[int, ...] = DEFAULT_HM_MASTER_POLL_AFTER_SEND_INTERVALS,
|
|
1814
|
-
ignore_custom_device_definition_models:
|
|
1815
|
-
interfaces_requiring_periodic_refresh:
|
|
1809
|
+
ignore_custom_device_definition_models: frozenset[str] = DEFAULT_IGNORE_CUSTOM_DEVICE_DEFINITION_MODELS,
|
|
1810
|
+
interfaces_requiring_periodic_refresh: frozenset[Interface] = INTERFACES_REQUIRING_PERIODIC_REFRESH,
|
|
1816
1811
|
json_port: int | None = None,
|
|
1817
1812
|
listen_ip_addr: str | None = None,
|
|
1818
1813
|
listen_port: int | None = None,
|
|
@@ -1823,7 +1818,7 @@ class CentralConfig:
|
|
|
1823
1818
|
sys_scan_interval: int = DEFAULT_SYS_SCAN_INTERVAL,
|
|
1824
1819
|
sysvar_markers: tuple[DescriptionMarker | str, ...] = DEFAULT_SYSVAR_MARKERS,
|
|
1825
1820
|
tls: bool = DEFAULT_TLS,
|
|
1826
|
-
un_ignore_list:
|
|
1821
|
+
un_ignore_list: frozenset[str] = DEFAULT_UN_IGNORES,
|
|
1827
1822
|
verify_tls: bool = DEFAULT_VERIFY_TLS,
|
|
1828
1823
|
) -> None:
|
|
1829
1824
|
"""Init the client config."""
|
|
@@ -1838,8 +1833,8 @@ class CentralConfig:
|
|
|
1838
1833
|
self.enable_sysvar_scan: Final = enable_sysvar_scan
|
|
1839
1834
|
self.hm_master_poll_after_send_intervals: Final = hm_master_poll_after_send_intervals
|
|
1840
1835
|
self.host: Final = host
|
|
1841
|
-
self.ignore_custom_device_definition_models: Final = ignore_custom_device_definition_models
|
|
1842
|
-
self.interfaces_requiring_periodic_refresh: Final = interfaces_requiring_periodic_refresh
|
|
1836
|
+
self.ignore_custom_device_definition_models: Final = frozenset(ignore_custom_device_definition_models or ())
|
|
1837
|
+
self.interfaces_requiring_periodic_refresh: Final = frozenset(interfaces_requiring_periodic_refresh or ())
|
|
1843
1838
|
self.json_port: Final = json_port
|
|
1844
1839
|
self.listen_ip_addr: Final = listen_ip_addr
|
|
1845
1840
|
self.listen_port: Final = listen_port
|
|
@@ -1877,9 +1872,9 @@ class CentralConfig:
|
|
|
1877
1872
|
return 443 if self.tls else 80
|
|
1878
1873
|
|
|
1879
1874
|
@property
|
|
1880
|
-
def enabled_interface_configs(self) ->
|
|
1875
|
+
def enabled_interface_configs(self) -> frozenset[hmcl.InterfaceConfig]:
|
|
1881
1876
|
"""Return the interface configs."""
|
|
1882
|
-
return
|
|
1877
|
+
return frozenset(ic for ic in self._interface_configs if ic.enabled is True)
|
|
1883
1878
|
|
|
1884
1879
|
@property
|
|
1885
1880
|
def use_caches(self) -> bool:
|
aiohomematic/const.py
CHANGED
|
@@ -9,9 +9,10 @@ from enum import Enum, IntEnum, StrEnum
|
|
|
9
9
|
import os
|
|
10
10
|
import re
|
|
11
11
|
import sys
|
|
12
|
-
from
|
|
12
|
+
from types import MappingProxyType
|
|
13
|
+
from typing import Any, Final, NamedTuple, Required, TypeAlias, TypedDict
|
|
13
14
|
|
|
14
|
-
VERSION: Final = "2025.8.
|
|
15
|
+
VERSION: Final = "2025.8.9"
|
|
15
16
|
|
|
16
17
|
# Detect test speedup mode via environment
|
|
17
18
|
_TEST_SPEEDUP: Final = (
|
|
@@ -24,7 +25,7 @@ DEFAULT_ENABLE_DEVICE_FIRMWARE_CHECK: Final = False
|
|
|
24
25
|
DEFAULT_ENABLE_PROGRAM_SCAN: Final = True
|
|
25
26
|
DEFAULT_ENABLE_SYSVAR_SCAN: Final = True
|
|
26
27
|
DEFAULT_HM_MASTER_POLL_AFTER_SEND_INTERVALS: Final = (5,)
|
|
27
|
-
DEFAULT_IGNORE_CUSTOM_DEVICE_DEFINITION_MODELS: Final[
|
|
28
|
+
DEFAULT_IGNORE_CUSTOM_DEVICE_DEFINITION_MODELS: Final[frozenset[str]] = frozenset()
|
|
28
29
|
DEFAULT_INCLUDE_INTERNAL_PROGRAMS: Final = False
|
|
29
30
|
DEFAULT_INCLUDE_INTERNAL_SYSVARS: Final = True
|
|
30
31
|
DEFAULT_MAX_READ_WORKERS: Final = 1
|
|
@@ -35,7 +36,7 @@ DEFAULT_PROGRAM_MARKERS: Final[tuple[DescriptionMarker | str, ...]] = ()
|
|
|
35
36
|
DEFAULT_SYSVAR_MARKERS: Final[tuple[DescriptionMarker | str, ...]] = ()
|
|
36
37
|
DEFAULT_SYS_SCAN_INTERVAL: Final = 30
|
|
37
38
|
DEFAULT_TLS: Final = False
|
|
38
|
-
DEFAULT_UN_IGNORES: Final[
|
|
39
|
+
DEFAULT_UN_IGNORES: Final[frozenset[str]] = frozenset()
|
|
39
40
|
DEFAULT_VERIFY_TLS: Final = False
|
|
40
41
|
|
|
41
42
|
# Default encoding for json service calls, persistent cache
|
|
@@ -52,22 +53,24 @@ CHANNEL_ADDRESS_PATTERN: Final = re.compile(r"^[0-9a-zA-Z-]{5,20}:[0-9]{1,3}$")
|
|
|
52
53
|
DEVICE_ADDRESS_PATTERN: Final = re.compile(r"^[0-9a-zA-Z-]{5,20}$")
|
|
53
54
|
ALLOWED_HOSTNAME_PATTERN: Final = re.compile(r"(?!-)[a-z0-9-]{1,63}(?<!-)$", re.IGNORECASE)
|
|
54
55
|
HTMLTAG_PATTERN: Final = re.compile(r"<.*?>|&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-f]{1,6});")
|
|
55
|
-
SCHEDULER_PROFILE_PATTERN = re.compile(
|
|
56
|
+
SCHEDULER_PROFILE_PATTERN: Final = re.compile(
|
|
56
57
|
r"^P[1-6]_(ENDTIME|TEMPERATURE)_(MONDAY|TUESDAY|WEDNESDAY|THURSDAY|FRIDAY|SATURDAY|SUNDAY)_([1-9]|1[0-3])$"
|
|
57
58
|
)
|
|
58
|
-
SCHEDULER_TIME_PATTERN = re.compile(r"^(([0-1]{0,1}[0-9])|(2[0-4])):[0-5][0-9]")
|
|
59
|
-
|
|
60
|
-
ALWAYS_ENABLE_SYSVARS_BY_ID: Final = "40", "41"
|
|
61
|
-
RENAME_SYSVAR_BY_NAME: Final =
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
59
|
+
SCHEDULER_TIME_PATTERN: Final = re.compile(r"^(([0-1]{0,1}[0-9])|(2[0-4])):[0-5][0-9]")
|
|
60
|
+
|
|
61
|
+
ALWAYS_ENABLE_SYSVARS_BY_ID: Final[frozenset[str]] = frozenset({"40", "41"})
|
|
62
|
+
RENAME_SYSVAR_BY_NAME: Final[Mapping[str, str]] = MappingProxyType(
|
|
63
|
+
{
|
|
64
|
+
"${sysVarAlarmMessages}": "ALARM_MESSAGES",
|
|
65
|
+
"${sysVarPresence}": "PRESENCE",
|
|
66
|
+
"${sysVarServiceMessages}": "SERVICE_MESSAGES",
|
|
67
|
+
}
|
|
68
|
+
)
|
|
66
69
|
|
|
67
|
-
SYSVAR_ENABLE_DEFAULT: Final = "40", "41"
|
|
70
|
+
SYSVAR_ENABLE_DEFAULT: Final[frozenset[str]] = frozenset({"40", "41"})
|
|
68
71
|
|
|
69
72
|
ADDRESS_SEPARATOR: Final = ":"
|
|
70
|
-
BLOCK_LOG_TIMEOUT = 60
|
|
73
|
+
BLOCK_LOG_TIMEOUT: Final = 60
|
|
71
74
|
CACHE_PATH: Final = "cache"
|
|
72
75
|
CONF_PASSWORD: Final = "password"
|
|
73
76
|
CONF_USERNAME: Final = "username"
|
|
@@ -79,7 +82,7 @@ DEVICE_DESCRIPTIONS_DIR: Final = "export_device_descriptions"
|
|
|
79
82
|
DEVICE_FIRMWARE_CHECK_INTERVAL: Final = 21600 # 6h
|
|
80
83
|
DEVICE_FIRMWARE_DELIVERING_CHECK_INTERVAL: Final = 3600 # 1h
|
|
81
84
|
DEVICE_FIRMWARE_UPDATING_CHECK_INTERVAL: Final = 300 # 5m
|
|
82
|
-
DUMMY_SERIAL = "SN0815"
|
|
85
|
+
DUMMY_SERIAL: Final = "SN0815"
|
|
83
86
|
FILE_DEVICES: Final = "homematic_devices.json"
|
|
84
87
|
FILE_PARAMSETS: Final = "homematic_paramsets.json"
|
|
85
88
|
HUB_PATH: Final = "hub"
|
|
@@ -87,7 +90,7 @@ IDENTIFIER_SEPARATOR: Final = "@"
|
|
|
87
90
|
INIT_DATETIME: Final = datetime.strptime("01.01.1970 00:00:00", DATETIME_FORMAT)
|
|
88
91
|
IP_ANY_V4: Final = "0.0.0.0"
|
|
89
92
|
JSON_SESSION_AGE: Final = 90
|
|
90
|
-
KWARGS_ARG_DATA_POINT = "data_point"
|
|
93
|
+
KWARGS_ARG_DATA_POINT: Final = "data_point"
|
|
91
94
|
LAST_COMMAND_SEND_STORE_TIMEOUT: Final = 60
|
|
92
95
|
LOCAL_HOST: Final = "127.0.0.1"
|
|
93
96
|
MAX_CACHE_AGE: Final = 10
|
|
@@ -113,7 +116,7 @@ WAIT_FOR_CALLBACK: Final[int | None] = None
|
|
|
113
116
|
SCHEDULER_NOT_STARTED_SLEEP: Final = 0.05 if _TEST_SPEEDUP else 10
|
|
114
117
|
SCHEDULER_LOOP_SLEEP: Final = 0.05 if _TEST_SPEEDUP else 5
|
|
115
118
|
|
|
116
|
-
CALLBACK_WARN_INTERVAL = CONNECTION_CHECKER_INTERVAL * 40
|
|
119
|
+
CALLBACK_WARN_INTERVAL: Final = CONNECTION_CHECKER_INTERVAL * 40
|
|
117
120
|
|
|
118
121
|
# Path
|
|
119
122
|
PROGRAM_SET_PATH_ROOT: Final = "program/set"
|
|
@@ -125,7 +128,7 @@ SYSVAR_STATE_PATH_ROOT: Final = "sysvar/status"
|
|
|
125
128
|
VIRTDEV_SET_PATH_ROOT: Final = "virtdev/set"
|
|
126
129
|
VIRTDEV_STATE_PATH_ROOT: Final = "virtdev/status"
|
|
127
130
|
|
|
128
|
-
CALLBACK_TYPE = Callable[[], None] | None
|
|
131
|
+
CALLBACK_TYPE: TypeAlias = Callable[[], None] | None
|
|
129
132
|
|
|
130
133
|
|
|
131
134
|
class Backend(StrEnum):
|
|
@@ -536,22 +539,26 @@ class ParameterType(StrEnum):
|
|
|
536
539
|
EMPTY = ""
|
|
537
540
|
|
|
538
541
|
|
|
539
|
-
CLICK_EVENTS: Final[
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
542
|
+
CLICK_EVENTS: Final[frozenset[Parameter]] = frozenset(
|
|
543
|
+
{
|
|
544
|
+
Parameter.PRESS,
|
|
545
|
+
Parameter.PRESS_CONT,
|
|
546
|
+
Parameter.PRESS_LOCK,
|
|
547
|
+
Parameter.PRESS_LONG,
|
|
548
|
+
Parameter.PRESS_LONG_RELEASE,
|
|
549
|
+
Parameter.PRESS_LONG_START,
|
|
550
|
+
Parameter.PRESS_SHORT,
|
|
551
|
+
Parameter.PRESS_UNLOCK,
|
|
552
|
+
}
|
|
548
553
|
)
|
|
549
554
|
|
|
550
555
|
DEVICE_ERROR_EVENTS: Final[tuple[Parameter, ...]] = (Parameter.ERROR, Parameter.SENSOR_ERROR)
|
|
551
556
|
|
|
552
|
-
DATA_POINT_EVENTS: Final[
|
|
553
|
-
|
|
554
|
-
|
|
557
|
+
DATA_POINT_EVENTS: Final[frozenset[EventType]] = frozenset(
|
|
558
|
+
{
|
|
559
|
+
EventType.IMPULSE,
|
|
560
|
+
EventType.KEYPRESS,
|
|
561
|
+
}
|
|
555
562
|
)
|
|
556
563
|
|
|
557
564
|
|
|
@@ -567,26 +574,32 @@ class DataPointKey(NamedTuple):
|
|
|
567
574
|
type DP_KEY_VALUE = tuple[DataPointKey, Any]
|
|
568
575
|
type SYSVAR_TYPE = bool | float | int | str | None
|
|
569
576
|
|
|
570
|
-
HMIP_FIRMWARE_UPDATE_IN_PROGRESS_STATES: Final[
|
|
571
|
-
|
|
572
|
-
|
|
577
|
+
HMIP_FIRMWARE_UPDATE_IN_PROGRESS_STATES: Final[frozenset[DeviceFirmwareState]] = frozenset(
|
|
578
|
+
{
|
|
579
|
+
DeviceFirmwareState.DO_UPDATE_PENDING,
|
|
580
|
+
DeviceFirmwareState.PERFORMING_UPDATE,
|
|
581
|
+
}
|
|
573
582
|
)
|
|
574
583
|
|
|
575
|
-
HMIP_FIRMWARE_UPDATE_READY_STATES: Final[
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
584
|
+
HMIP_FIRMWARE_UPDATE_READY_STATES: Final[frozenset[DeviceFirmwareState]] = frozenset(
|
|
585
|
+
{
|
|
586
|
+
DeviceFirmwareState.READY_FOR_UPDATE,
|
|
587
|
+
DeviceFirmwareState.DO_UPDATE_PENDING,
|
|
588
|
+
DeviceFirmwareState.PERFORMING_UPDATE,
|
|
589
|
+
}
|
|
579
590
|
)
|
|
580
591
|
|
|
581
|
-
IMPULSE_EVENTS: Final[
|
|
592
|
+
IMPULSE_EVENTS: Final[frozenset[Parameter]] = frozenset({Parameter.SEQUENCE_OK})
|
|
582
593
|
|
|
583
|
-
KEY_CHANNEL_OPERATION_MODE_VISIBILITY: Final[Mapping[str,
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
}
|
|
594
|
+
KEY_CHANNEL_OPERATION_MODE_VISIBILITY: Final[Mapping[str, frozenset[str]]] = MappingProxyType(
|
|
595
|
+
{
|
|
596
|
+
Parameter.STATE: frozenset({"BINARY_BEHAVIOR"}),
|
|
597
|
+
Parameter.PRESS_LONG: frozenset({"KEY_BEHAVIOR", "SWITCH_BEHAVIOR"}),
|
|
598
|
+
Parameter.PRESS_LONG_RELEASE: frozenset({"KEY_BEHAVIOR", "SWITCH_BEHAVIOR"}),
|
|
599
|
+
Parameter.PRESS_LONG_START: frozenset({"KEY_BEHAVIOR", "SWITCH_BEHAVIOR"}),
|
|
600
|
+
Parameter.PRESS_SHORT: frozenset({"KEY_BEHAVIOR", "SWITCH_BEHAVIOR"}),
|
|
601
|
+
}
|
|
602
|
+
)
|
|
590
603
|
|
|
591
604
|
HUB_CATEGORIES: Final[tuple[DataPointCategory, ...]] = (
|
|
592
605
|
DataPointCategory.HUB_BINARY_SENSOR,
|
|
@@ -616,42 +629,54 @@ CATEGORIES: Final[tuple[DataPointCategory, ...]] = (
|
|
|
616
629
|
DataPointCategory.VALVE,
|
|
617
630
|
)
|
|
618
631
|
|
|
619
|
-
PRIMARY_CLIENT_CANDIDATE_INTERFACES: Final = (
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
632
|
+
PRIMARY_CLIENT_CANDIDATE_INTERFACES: Final[frozenset[Interface]] = frozenset(
|
|
633
|
+
{
|
|
634
|
+
Interface.HMIP_RF,
|
|
635
|
+
Interface.BIDCOS_RF,
|
|
636
|
+
Interface.BIDCOS_WIRED,
|
|
637
|
+
}
|
|
623
638
|
)
|
|
624
639
|
|
|
625
|
-
RELEVANT_INIT_PARAMETERS: Final[
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
640
|
+
RELEVANT_INIT_PARAMETERS: Final[frozenset[Parameter]] = frozenset(
|
|
641
|
+
{
|
|
642
|
+
Parameter.CONFIG_PENDING,
|
|
643
|
+
Parameter.STICKY_UN_REACH,
|
|
644
|
+
Parameter.UN_REACH,
|
|
645
|
+
}
|
|
629
646
|
)
|
|
630
647
|
|
|
631
|
-
INTERFACES_SUPPORTING_FIRMWARE_UPDATES: Final[
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
648
|
+
INTERFACES_SUPPORTING_FIRMWARE_UPDATES: Final[frozenset[Interface]] = frozenset(
|
|
649
|
+
{
|
|
650
|
+
Interface.BIDCOS_RF,
|
|
651
|
+
Interface.BIDCOS_WIRED,
|
|
652
|
+
Interface.HMIP_RF,
|
|
653
|
+
}
|
|
635
654
|
)
|
|
636
655
|
|
|
637
|
-
INTERFACES_SUPPORTING_XML_RPC: Final[
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
656
|
+
INTERFACES_SUPPORTING_XML_RPC: Final[frozenset[Interface]] = frozenset(
|
|
657
|
+
{
|
|
658
|
+
Interface.BIDCOS_RF,
|
|
659
|
+
Interface.BIDCOS_WIRED,
|
|
660
|
+
Interface.HMIP_RF,
|
|
661
|
+
Interface.VIRTUAL_DEVICES,
|
|
662
|
+
}
|
|
642
663
|
)
|
|
643
664
|
|
|
644
|
-
INTERFACES_REQUIRING_PERIODIC_REFRESH: Final[
|
|
645
|
-
|
|
646
|
-
|
|
665
|
+
INTERFACES_REQUIRING_PERIODIC_REFRESH: Final[frozenset[Interface]] = frozenset(
|
|
666
|
+
{
|
|
667
|
+
Interface.CCU_JACK,
|
|
668
|
+
Interface.CUXD,
|
|
669
|
+
}
|
|
647
670
|
)
|
|
648
671
|
|
|
649
672
|
DEFAULT_USE_PERIODIC_SCAN_FOR_INTERFACES: Final = True
|
|
650
673
|
|
|
651
|
-
IGNORE_FOR_UN_IGNORE_PARAMETERS: Final[
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
674
|
+
IGNORE_FOR_UN_IGNORE_PARAMETERS: Final[frozenset[Parameter]] = frozenset(
|
|
675
|
+
{
|
|
676
|
+
Parameter.CONFIG_PENDING,
|
|
677
|
+
Parameter.STICKY_UN_REACH,
|
|
678
|
+
Parameter.UN_REACH,
|
|
679
|
+
}
|
|
655
680
|
)
|
|
656
681
|
|
|
657
682
|
|
|
@@ -659,12 +684,14 @@ IGNORE_FOR_UN_IGNORE_PARAMETERS: Final[tuple[Parameter, ...]] = (
|
|
|
659
684
|
_IGNORE_ON_INITIAL_LOAD_PARAMETERS_END_RE: Final = re.compile(r".*(_ERROR)$")
|
|
660
685
|
# Ignore Parameter on initial load that start with
|
|
661
686
|
_IGNORE_ON_INITIAL_LOAD_PARAMETERS_START_RE: Final = re.compile(r"^(ERROR_|RSSI_)")
|
|
662
|
-
_IGNORE_ON_INITIAL_LOAD_PARAMETERS: Final = (
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
687
|
+
_IGNORE_ON_INITIAL_LOAD_PARAMETERS: Final[frozenset[Parameter]] = frozenset(
|
|
688
|
+
{
|
|
689
|
+
Parameter.DUTY_CYCLE,
|
|
690
|
+
Parameter.DUTYCYCLE,
|
|
691
|
+
Parameter.LOW_BAT,
|
|
692
|
+
Parameter.LOWBAT,
|
|
693
|
+
Parameter.OPERATING_VOLTAGE,
|
|
694
|
+
}
|
|
668
695
|
)
|
|
669
696
|
|
|
670
697
|
|
|
@@ -60,6 +60,6 @@ _LOGGER: Final = logging.getLogger(__name__)
|
|
|
60
60
|
@inspector()
|
|
61
61
|
def create_calculated_data_points(channel: hmd.Channel) -> None:
|
|
62
62
|
"""Decides which data point category should be used, and creates the required data points."""
|
|
63
|
-
for
|
|
64
|
-
if
|
|
65
|
-
channel.add_data_point(data_point=
|
|
63
|
+
for dp in _CALCULATED_DATA_POINTS:
|
|
64
|
+
if dp.is_relevant_for_model(channel=channel):
|
|
65
|
+
channel.add_data_point(data_point=dp(channel=channel))
|
|
@@ -280,6 +280,10 @@ class CalculatedDataPoint[ParameterT: GenericParameterType](BaseDataPoint):
|
|
|
280
280
|
"""Generate the usage for the data point."""
|
|
281
281
|
return DataPointUsage.DATA_POINT
|
|
282
282
|
|
|
283
|
+
def _get_signature(self) -> str:
|
|
284
|
+
"""Return the signature of the data_point."""
|
|
285
|
+
return f"{self._category}/{self._channel.device.model}/{self._calculated_parameter}"
|
|
286
|
+
|
|
283
287
|
async def load_data_point_value(self, call_source: CallSource, direct_call: bool = False) -> None:
|
|
284
288
|
"""Init the data point values."""
|
|
285
289
|
for dp in self._readable_data_points:
|
|
@@ -300,7 +304,7 @@ class CalculatedDataPoint[ParameterT: GenericParameterType](BaseDataPoint):
|
|
|
300
304
|
@property
|
|
301
305
|
def _should_fire_data_point_updated_callback(self) -> bool:
|
|
302
306
|
"""Check if a data point has been updated or refreshed."""
|
|
303
|
-
if self.fired_recently:
|
|
307
|
+
if self.fired_recently:
|
|
304
308
|
return False
|
|
305
309
|
|
|
306
310
|
if (relevant_values_data_point := self._relevant_values_data_points) is not None and len(
|
|
@@ -177,6 +177,10 @@ class CustomDataPoint(BaseDataPoint):
|
|
|
177
177
|
return DataPointUsage.CDP_PRIMARY
|
|
178
178
|
return DataPointUsage.CDP_SECONDARY
|
|
179
179
|
|
|
180
|
+
def _get_signature(self) -> str:
|
|
181
|
+
"""Return the signature of the data_point."""
|
|
182
|
+
return f"{self._category}/{self._channel.device.model}/{self.data_point_name_postfix}"
|
|
183
|
+
|
|
180
184
|
async def load_data_point_value(self, call_source: CallSource, direct_call: bool = False) -> None:
|
|
181
185
|
"""Init the data point values."""
|
|
182
186
|
for dp in self._readable_data_points:
|
aiohomematic/model/data_point.py
CHANGED
|
@@ -146,6 +146,7 @@ class CallbackDataPoint(ABC):
|
|
|
146
146
|
"_modified_at",
|
|
147
147
|
"_path_data",
|
|
148
148
|
"_refreshed_at",
|
|
149
|
+
"_signature",
|
|
149
150
|
"_temporary_modified_at",
|
|
150
151
|
"_temporary_refreshed_at",
|
|
151
152
|
"_unique_id",
|
|
@@ -164,6 +165,7 @@ class CallbackDataPoint(ABC):
|
|
|
164
165
|
self._fired_at: datetime = INIT_DATETIME
|
|
165
166
|
self._modified_at: datetime = INIT_DATETIME
|
|
166
167
|
self._refreshed_at: datetime = INIT_DATETIME
|
|
168
|
+
self._signature: Final = self._get_signature()
|
|
167
169
|
self._temporary_modified_at: datetime = INIT_DATETIME
|
|
168
170
|
self._temporary_refreshed_at: datetime = INIT_DATETIME
|
|
169
171
|
|
|
@@ -252,6 +254,11 @@ class CallbackDataPoint(ABC):
|
|
|
252
254
|
def name(self) -> str:
|
|
253
255
|
"""Return the name of the data_point."""
|
|
254
256
|
|
|
257
|
+
@property
|
|
258
|
+
def signature(self) -> str:
|
|
259
|
+
"""Return the data_point signature."""
|
|
260
|
+
return self._signature
|
|
261
|
+
|
|
255
262
|
@config_property
|
|
256
263
|
def unique_id(self) -> str:
|
|
257
264
|
"""Return the unique_id."""
|
|
@@ -325,6 +332,10 @@ class CallbackDataPoint(ABC):
|
|
|
325
332
|
def _get_path_data(self) -> PathData:
|
|
326
333
|
"""Return the path data."""
|
|
327
334
|
|
|
335
|
+
@abstractmethod
|
|
336
|
+
def _get_signature(self) -> str:
|
|
337
|
+
"""Return the signature of the data_point."""
|
|
338
|
+
|
|
328
339
|
def _unregister_data_point_updated_callback(self, cb: Callable, custom_id: str) -> None:
|
|
329
340
|
"""Unregister data_point updated callback."""
|
|
330
341
|
if cb in self._data_point_updated_callbacks:
|
|
@@ -840,6 +851,10 @@ class BaseParameterDataPoint[
|
|
|
840
851
|
return multiplier
|
|
841
852
|
return DEFAULT_MULTIPLIER
|
|
842
853
|
|
|
854
|
+
def _get_signature(self) -> str:
|
|
855
|
+
"""Return the signature of the data_point."""
|
|
856
|
+
return f"{self._category}/{self._channel.device.model}/{self._parameter}"
|
|
857
|
+
|
|
843
858
|
@abstractmethod
|
|
844
859
|
async def event(self, value: Any, received_at: datetime | None = None) -> None:
|
|
845
860
|
"""Handle event for which this handler has subscribed."""
|
|
@@ -861,9 +876,7 @@ class BaseParameterDataPoint[
|
|
|
861
876
|
|
|
862
877
|
self.write_value(
|
|
863
878
|
value=await self._device.value_cache.get_value(
|
|
864
|
-
|
|
865
|
-
paramset_key=self._paramset_key,
|
|
866
|
-
parameter=self._parameter,
|
|
879
|
+
dpk=self.dpk,
|
|
867
880
|
call_source=call_source,
|
|
868
881
|
direct_call=direct_call,
|
|
869
882
|
),
|