aiohomematic 2025.8.8__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/persistent.py +27 -22
- aiohomematic/caches/visibility.py +275 -252
- aiohomematic/central/__init__.py +26 -31
- aiohomematic/const.py +103 -76
- aiohomematic/model/calculated/data_point.py +5 -1
- aiohomematic/model/custom/data_point.py +4 -0
- aiohomematic/model/data_point.py +15 -0
- aiohomematic/model/device.py +7 -5
- aiohomematic/model/hub/data_point.py +4 -0
- aiohomematic/model/update.py +4 -0
- aiohomematic/support.py +15 -2
- {aiohomematic-2025.8.8.dist-info → aiohomematic-2025.8.9.dist-info}/METADATA +1 -1
- {aiohomematic-2025.8.8.dist-info → aiohomematic-2025.8.9.dist-info}/RECORD +16 -16
- {aiohomematic-2025.8.8.dist-info → aiohomematic-2025.8.9.dist-info}/WHEEL +0 -0
- {aiohomematic-2025.8.8.dist-info → aiohomematic-2025.8.9.dist-info}/licenses/LICENSE +0 -0
- {aiohomematic-2025.8.8.dist-info → aiohomematic-2025.8.9.dist-info}/top_level.txt +0 -0
|
@@ -30,11 +30,11 @@ available on the CentralUnit and model descriptors.
|
|
|
30
30
|
from __future__ import annotations
|
|
31
31
|
|
|
32
32
|
from collections import defaultdict
|
|
33
|
-
from collections.abc import Mapping
|
|
33
|
+
from collections.abc import Iterable, Mapping
|
|
34
34
|
from functools import cache
|
|
35
35
|
import logging
|
|
36
36
|
import re
|
|
37
|
-
from typing import Final
|
|
37
|
+
from typing import Final, TypeAlias
|
|
38
38
|
|
|
39
39
|
from aiohomematic import central as hmcu, support as hms
|
|
40
40
|
from aiohomematic.const import ADDRESS_SEPARATOR, CLICK_EVENTS, UN_IGNORE_WILDCARD, Parameter, ParamsetKey
|
|
@@ -43,7 +43,12 @@ from aiohomematic.model.custom import get_required_parameters
|
|
|
43
43
|
from aiohomematic.support import element_matches_key
|
|
44
44
|
|
|
45
45
|
_LOGGER: Final = logging.getLogger(__name__)
|
|
46
|
-
|
|
46
|
+
|
|
47
|
+
# Readability type aliases for internal cache structures
|
|
48
|
+
TModelName: TypeAlias = str
|
|
49
|
+
TChannelNo: TypeAlias = int | None
|
|
50
|
+
TUnIgnoreChannelNo: TypeAlias = TChannelNo | str
|
|
51
|
+
TParameterName: TypeAlias = str
|
|
47
52
|
|
|
48
53
|
# Define which additional parameters from MASTER paramset should be created as data_point.
|
|
49
54
|
# By default these are also on the _HIDDEN_PARAMETERS, which prevents these data points
|
|
@@ -51,172 +56,174 @@ _CACHE_KEY_TYPE = tuple[str, int, ParamsetKey, str]
|
|
|
51
56
|
# and not for general display.
|
|
52
57
|
# {model: (channel_no, parameter)}
|
|
53
58
|
|
|
54
|
-
_RELEVANT_MASTER_PARAMSETS_BY_CHANNEL: Final[Mapping[
|
|
55
|
-
None: (Parameter.GLOBAL_BUTTON_LOCK, Parameter.LOW_BAT_LIMIT),
|
|
56
|
-
0: (Parameter.GLOBAL_BUTTON_LOCK, Parameter.LOW_BAT_LIMIT),
|
|
59
|
+
_RELEVANT_MASTER_PARAMSETS_BY_CHANNEL: Final[Mapping[TChannelNo, frozenset[Parameter]]] = {
|
|
60
|
+
None: frozenset({Parameter.GLOBAL_BUTTON_LOCK, Parameter.LOW_BAT_LIMIT}),
|
|
61
|
+
0: frozenset({Parameter.GLOBAL_BUTTON_LOCK, Parameter.LOW_BAT_LIMIT}),
|
|
57
62
|
}
|
|
58
63
|
|
|
59
|
-
_CLIMATE_MASTER_PARAMETERS: Final[
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
_CLIMATE_MASTER_PARAMETERS: Final[frozenset[Parameter]] = frozenset(
|
|
65
|
+
{
|
|
66
|
+
Parameter.HEATING_VALVE_TYPE,
|
|
67
|
+
Parameter.MIN_MAX_VALUE_NOT_RELEVANT_FOR_MANU_MODE,
|
|
68
|
+
Parameter.OPTIMUM_START_STOP,
|
|
69
|
+
Parameter.TEMPERATURE_MAXIMUM,
|
|
70
|
+
Parameter.TEMPERATURE_MINIMUM,
|
|
71
|
+
Parameter.TEMPERATURE_OFFSET,
|
|
72
|
+
Parameter.WEEK_PROGRAM_POINTER,
|
|
73
|
+
}
|
|
67
74
|
)
|
|
68
75
|
|
|
69
|
-
_RELEVANT_MASTER_PARAMSETS_BY_DEVICE: Final[Mapping[
|
|
70
|
-
"ALPHA-IP-RBG": ((1
|
|
71
|
-
"ELV-SH-TACO": ((2
|
|
76
|
+
_RELEVANT_MASTER_PARAMSETS_BY_DEVICE: Final[Mapping[TModelName, tuple[frozenset[TChannelNo], frozenset[Parameter]]]] = {
|
|
77
|
+
"ALPHA-IP-RBG": (frozenset({1}), _CLIMATE_MASTER_PARAMETERS),
|
|
78
|
+
"ELV-SH-TACO": (frozenset({2}), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
72
79
|
"HM-CC-RT-DN": (
|
|
73
|
-
(None
|
|
80
|
+
frozenset({None}),
|
|
74
81
|
_CLIMATE_MASTER_PARAMETERS,
|
|
75
82
|
),
|
|
76
83
|
"HM-CC-VG-1": (
|
|
77
|
-
(None
|
|
84
|
+
frozenset({None}),
|
|
78
85
|
_CLIMATE_MASTER_PARAMETERS,
|
|
79
86
|
),
|
|
80
87
|
"HM-TC-IT-WM-W-EU": (
|
|
81
|
-
(None
|
|
88
|
+
frozenset({None}),
|
|
82
89
|
_CLIMATE_MASTER_PARAMETERS,
|
|
83
90
|
),
|
|
84
|
-
"HmIP-BWTH": ((1, 8), _CLIMATE_MASTER_PARAMETERS),
|
|
91
|
+
"HmIP-BWTH": (frozenset({1, 8}), _CLIMATE_MASTER_PARAMETERS),
|
|
85
92
|
"HmIP-DRBLI4": (
|
|
86
|
-
(1, 2, 3, 4, 5, 6, 7, 8, 9, 13, 17, 21),
|
|
87
|
-
(Parameter.CHANNEL_OPERATION_MODE
|
|
93
|
+
frozenset({1, 2, 3, 4, 5, 6, 7, 8, 9, 13, 17, 21}),
|
|
94
|
+
frozenset({Parameter.CHANNEL_OPERATION_MODE}),
|
|
88
95
|
),
|
|
89
|
-
"HmIP-DRDI3": ((1, 2, 3), (Parameter.CHANNEL_OPERATION_MODE
|
|
90
|
-
"HmIP-DRSI1": ((1
|
|
91
|
-
"HmIP-DRSI4": ((1, 2, 3, 4), (Parameter.CHANNEL_OPERATION_MODE
|
|
92
|
-
"HmIP-DSD-PCB": ((1
|
|
93
|
-
"HmIP-FCI1": ((1
|
|
94
|
-
"HmIP-FCI6": (
|
|
95
|
-
"HmIP-FSI16": ((1
|
|
96
|
-
"HmIP-HEATING": ((1
|
|
97
|
-
"HmIP-MIO16-PCB": ((13, 14, 15, 16), (Parameter.CHANNEL_OPERATION_MODE
|
|
98
|
-
"HmIP-MOD-RC8": (
|
|
99
|
-
"HmIP-RGBW": ((0
|
|
100
|
-
"HmIP-STH": ((1
|
|
101
|
-
"HmIP-WGT": ((8, 14), _CLIMATE_MASTER_PARAMETERS),
|
|
102
|
-
"HmIP-WTH": ((1
|
|
103
|
-
"HmIP-eTRV": ((1
|
|
104
|
-
"HmIPW-DRBL4": ((1, 5, 9, 13), (Parameter.CHANNEL_OPERATION_MODE
|
|
105
|
-
"HmIPW-DRI16": (
|
|
106
|
-
"HmIPW-DRI32": (
|
|
107
|
-
"HmIPW-FIO6": (
|
|
108
|
-
"HmIPW-STH": ((1
|
|
96
|
+
"HmIP-DRDI3": (frozenset({1, 2, 3}), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
97
|
+
"HmIP-DRSI1": (frozenset({1}), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
98
|
+
"HmIP-DRSI4": (frozenset({1, 2, 3, 4}), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
99
|
+
"HmIP-DSD-PCB": (frozenset({1}), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
100
|
+
"HmIP-FCI1": (frozenset({1}), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
101
|
+
"HmIP-FCI6": (frozenset(range(1, 7)), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
102
|
+
"HmIP-FSI16": (frozenset({1}), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
103
|
+
"HmIP-HEATING": (frozenset({1}), _CLIMATE_MASTER_PARAMETERS),
|
|
104
|
+
"HmIP-MIO16-PCB": (frozenset({13, 14, 15, 16}), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
105
|
+
"HmIP-MOD-RC8": (frozenset(range(1, 9)), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
106
|
+
"HmIP-RGBW": (frozenset({0}), frozenset({Parameter.DEVICE_OPERATION_MODE})),
|
|
107
|
+
"HmIP-STH": (frozenset({1}), _CLIMATE_MASTER_PARAMETERS),
|
|
108
|
+
"HmIP-WGT": (frozenset({8, 14}), _CLIMATE_MASTER_PARAMETERS),
|
|
109
|
+
"HmIP-WTH": (frozenset({1}), _CLIMATE_MASTER_PARAMETERS),
|
|
110
|
+
"HmIP-eTRV": (frozenset({1}), _CLIMATE_MASTER_PARAMETERS),
|
|
111
|
+
"HmIPW-DRBL4": (frozenset({1, 5, 9, 13}), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
112
|
+
"HmIPW-DRI16": (frozenset(range(1, 17)), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
113
|
+
"HmIPW-DRI32": (frozenset(range(1, 33)), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
114
|
+
"HmIPW-FIO6": (frozenset(range(1, 7)), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
115
|
+
"HmIPW-STH": (frozenset({1}), _CLIMATE_MASTER_PARAMETERS),
|
|
109
116
|
}
|
|
110
117
|
|
|
111
118
|
# Ignore events for some devices
|
|
112
|
-
_IGNORE_DEVICES_FOR_DATA_POINT_EVENTS: Final[Mapping[
|
|
119
|
+
_IGNORE_DEVICES_FOR_DATA_POINT_EVENTS: Final[Mapping[TModelName, frozenset[Parameter]]] = {
|
|
113
120
|
"HmIP-PS": CLICK_EVENTS,
|
|
114
121
|
}
|
|
115
122
|
|
|
116
|
-
_IGNORE_DEVICES_FOR_DATA_POINT_EVENTS_LOWER: Final[
|
|
117
|
-
model.lower():
|
|
123
|
+
_IGNORE_DEVICES_FOR_DATA_POINT_EVENTS_LOWER: Final[Mapping[TModelName, frozenset[Parameter]]] = {
|
|
124
|
+
model.lower(): frozenset(events) for model, events in _IGNORE_DEVICES_FOR_DATA_POINT_EVENTS.items()
|
|
118
125
|
}
|
|
119
126
|
|
|
120
127
|
# data points that will be created, but should be hidden.
|
|
121
|
-
_HIDDEN_PARAMETERS: Final[
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
128
|
+
_HIDDEN_PARAMETERS: Final[frozenset[Parameter]] = frozenset(
|
|
129
|
+
{
|
|
130
|
+
Parameter.ACTIVITY_STATE,
|
|
131
|
+
Parameter.CHANNEL_OPERATION_MODE,
|
|
132
|
+
Parameter.CONFIG_PENDING,
|
|
133
|
+
Parameter.DIRECTION,
|
|
134
|
+
Parameter.ERROR,
|
|
135
|
+
Parameter.HEATING_VALVE_TYPE,
|
|
136
|
+
Parameter.LOW_BAT_LIMIT,
|
|
137
|
+
Parameter.MIN_MAX_VALUE_NOT_RELEVANT_FOR_MANU_MODE,
|
|
138
|
+
Parameter.OPTIMUM_START_STOP,
|
|
139
|
+
Parameter.SECTION,
|
|
140
|
+
Parameter.STICKY_UN_REACH,
|
|
141
|
+
Parameter.TEMPERATURE_MAXIMUM,
|
|
142
|
+
Parameter.TEMPERATURE_MINIMUM,
|
|
143
|
+
Parameter.TEMPERATURE_OFFSET,
|
|
144
|
+
Parameter.UN_REACH,
|
|
145
|
+
Parameter.UPDATE_PENDING,
|
|
146
|
+
Parameter.WORKING,
|
|
147
|
+
}
|
|
139
148
|
)
|
|
140
|
-
# Fast membership for hidden parameters
|
|
141
|
-
_HIDDEN_PARAMETERS_SET: Final[set[Parameter]] = set(_HIDDEN_PARAMETERS)
|
|
142
149
|
|
|
143
150
|
# Parameters within the VALUES paramset for which we don't create data points.
|
|
144
|
-
_IGNORED_PARAMETERS: Final[
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
151
|
+
_IGNORED_PARAMETERS: Final[frozenset[TParameterName]] = frozenset(
|
|
152
|
+
{
|
|
153
|
+
"ACCESS_AUTHORIZATION",
|
|
154
|
+
"ACOUSTIC_NOTIFICATION_SELECTION",
|
|
155
|
+
"ADAPTION_DRIVE",
|
|
156
|
+
"AES_KEY",
|
|
157
|
+
"ALARM_COUNT",
|
|
158
|
+
"ALL_LEDS",
|
|
159
|
+
"ARROW_DOWN",
|
|
160
|
+
"ARROW_UP",
|
|
161
|
+
"BACKLIGHT",
|
|
162
|
+
"BEEP",
|
|
163
|
+
"BELL",
|
|
164
|
+
"BLIND",
|
|
165
|
+
"BOOST_STATE",
|
|
166
|
+
"BOOST_TIME",
|
|
167
|
+
"BOOT",
|
|
168
|
+
"BULB",
|
|
169
|
+
"CLEAR_ERROR",
|
|
170
|
+
"CLEAR_WINDOW_OPEN_SYMBOL",
|
|
171
|
+
"CLOCK",
|
|
172
|
+
"CONTROL_DIFFERENTIAL_TEMPERATURE",
|
|
173
|
+
"DATE_TIME_UNKNOWN",
|
|
174
|
+
"DECISION_VALUE",
|
|
175
|
+
"DEVICE_IN_BOOTLOADER",
|
|
176
|
+
"DISPLAY_DATA_ALIGNMENT",
|
|
177
|
+
"DISPLAY_DATA_BACKGROUND_COLOR",
|
|
178
|
+
"DISPLAY_DATA_COMMIT",
|
|
179
|
+
"DISPLAY_DATA_ICON",
|
|
180
|
+
"DISPLAY_DATA_ID",
|
|
181
|
+
"DISPLAY_DATA_STRING",
|
|
182
|
+
"DISPLAY_DATA_TEXT_COLOR",
|
|
183
|
+
"DOOR",
|
|
184
|
+
"EXTERNAL_CLOCK",
|
|
185
|
+
"FROST_PROTECTION",
|
|
186
|
+
"HUMIDITY_LIMITER",
|
|
187
|
+
"IDENTIFICATION_MODE_KEY_VISUAL",
|
|
188
|
+
"IDENTIFICATION_MODE_LCD_BACKLIGHT",
|
|
189
|
+
"INCLUSION_UNSUPPORTED_DEVICE",
|
|
190
|
+
"INHIBIT",
|
|
191
|
+
"INSTALL_MODE",
|
|
192
|
+
"INTERVAL",
|
|
193
|
+
"LEVEL_REAL",
|
|
194
|
+
"OLD_LEVEL",
|
|
195
|
+
"OVERFLOW",
|
|
196
|
+
"OVERRUN",
|
|
197
|
+
"PARTY_SET_POINT_TEMPERATURE",
|
|
198
|
+
"PARTY_TEMPERATURE",
|
|
199
|
+
"PARTY_TIME_END",
|
|
200
|
+
"PARTY_TIME_START",
|
|
201
|
+
"PHONE",
|
|
202
|
+
"PROCESS",
|
|
203
|
+
"QUICK_VETO_TIME",
|
|
204
|
+
"RAMP_STOP",
|
|
205
|
+
"RELOCK_DELAY",
|
|
206
|
+
"SCENE",
|
|
207
|
+
"SELF_CALIBRATION",
|
|
208
|
+
"SERVICE_COUNT",
|
|
209
|
+
"SET_SYMBOL_FOR_HEATING_PHASE",
|
|
210
|
+
"SHADING_SPEED",
|
|
211
|
+
"SHEV_POS",
|
|
212
|
+
"SPEED",
|
|
213
|
+
"STATE_UNCERTAIN",
|
|
214
|
+
"SUBMIT",
|
|
215
|
+
"SWITCH_POINT_OCCURED",
|
|
216
|
+
"TEMPERATURE_LIMITER",
|
|
217
|
+
"TEMPERATURE_OUT_OF_RANGE",
|
|
218
|
+
"TEXT",
|
|
219
|
+
"USER_COLOR",
|
|
220
|
+
"USER_PROGRAM",
|
|
221
|
+
"VALVE_ADAPTION",
|
|
222
|
+
"WINDOW",
|
|
223
|
+
"WIN_RELEASE",
|
|
224
|
+
"WIN_RELEASE_ACT",
|
|
225
|
+
}
|
|
217
226
|
)
|
|
218
|
-
# Fast membership for ignored VALUE parameters
|
|
219
|
-
_IGNORED_PARAMETERS_SET: Final[set[str]] = set(_IGNORED_PARAMETERS)
|
|
220
227
|
|
|
221
228
|
|
|
222
229
|
# Precompile Regex patterns for wildcard checks
|
|
@@ -228,31 +235,28 @@ _IGNORED_PARAMETERS_START_RE: Final = re.compile(
|
|
|
228
235
|
)
|
|
229
236
|
|
|
230
237
|
|
|
231
|
-
def _parameter_is_wildcard_ignored(parameter:
|
|
238
|
+
def _parameter_is_wildcard_ignored(parameter: TParameterName) -> bool:
|
|
232
239
|
"""Check if a parameter matches common wildcard patterns."""
|
|
233
240
|
return bool(_IGNORED_PARAMETERS_END_RE.match(parameter) or _IGNORED_PARAMETERS_START_RE.match(parameter))
|
|
234
241
|
|
|
235
242
|
|
|
236
243
|
# Parameters within the paramsets for which we create data points.
|
|
237
|
-
_UN_IGNORE_PARAMETERS_BY_DEVICE: Final[Mapping[
|
|
238
|
-
"HmIP-DLD": (Parameter.ERROR_JAMMED
|
|
239
|
-
"HmIP-SWSD": (Parameter.SMOKE_DETECTOR_ALARM_STATUS
|
|
240
|
-
"HM-OU-LED16": (Parameter.LED_STATUS
|
|
241
|
-
"HM-Sec-Win": (Parameter.DIRECTION, Parameter.WORKING, Parameter.ERROR, Parameter.STATUS),
|
|
242
|
-
"HM-Sec-Key": (Parameter.DIRECTION, Parameter.ERROR),
|
|
243
|
-
"HmIP-PCBS-BAT": (
|
|
244
|
-
Parameter.OPERATING_VOLTAGE,
|
|
245
|
-
Parameter.LOW_BAT,
|
|
246
|
-
), # To override ignore for HmIP-PCBS
|
|
244
|
+
_UN_IGNORE_PARAMETERS_BY_DEVICE: Final[Mapping[TModelName, frozenset[Parameter]]] = {
|
|
245
|
+
"HmIP-DLD": frozenset({Parameter.ERROR_JAMMED}),
|
|
246
|
+
"HmIP-SWSD": frozenset({Parameter.SMOKE_DETECTOR_ALARM_STATUS}),
|
|
247
|
+
"HM-OU-LED16": frozenset({Parameter.LED_STATUS}),
|
|
248
|
+
"HM-Sec-Win": frozenset({Parameter.DIRECTION, Parameter.WORKING, Parameter.ERROR, Parameter.STATUS}),
|
|
249
|
+
"HM-Sec-Key": frozenset({Parameter.DIRECTION, Parameter.ERROR}),
|
|
250
|
+
"HmIP-PCBS-BAT": frozenset({Parameter.OPERATING_VOLTAGE, Parameter.LOW_BAT}), # To override ignore for HmIP-PCBS
|
|
247
251
|
}
|
|
248
252
|
|
|
249
|
-
_UN_IGNORE_PARAMETERS_BY_MODEL_LOWER: Final[dict[
|
|
250
|
-
model.lower(): parameters for model, parameters in _UN_IGNORE_PARAMETERS_BY_DEVICE.items()
|
|
253
|
+
_UN_IGNORE_PARAMETERS_BY_MODEL_LOWER: Final[dict[TModelName, frozenset[Parameter]]] = {
|
|
254
|
+
model.lower(): frozenset(parameters) for model, parameters in _UN_IGNORE_PARAMETERS_BY_DEVICE.items()
|
|
251
255
|
}
|
|
252
256
|
|
|
253
257
|
|
|
254
258
|
@cache
|
|
255
|
-
def _get_parameters_for_model_prefix(model_prefix: str | None) ->
|
|
259
|
+
def _get_parameters_for_model_prefix(model_prefix: str | None) -> frozenset[Parameter] | None:
|
|
256
260
|
"""Return the dict value by wildcard type."""
|
|
257
261
|
if model_prefix is None:
|
|
258
262
|
return None
|
|
@@ -264,59 +268,74 @@ def _get_parameters_for_model_prefix(model_prefix: str | None) -> tuple[str, ...
|
|
|
264
268
|
|
|
265
269
|
|
|
266
270
|
# Parameters by device within the VALUES paramset for which we don't create data points.
|
|
267
|
-
_IGNORE_PARAMETERS_BY_DEVICE: Final[Mapping[Parameter,
|
|
268
|
-
Parameter.CURRENT_ILLUMINATION: (
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
271
|
+
_IGNORE_PARAMETERS_BY_DEVICE: Final[Mapping[Parameter, frozenset[TModelName]]] = {
|
|
272
|
+
Parameter.CURRENT_ILLUMINATION: frozenset(
|
|
273
|
+
{
|
|
274
|
+
"HmIP-SMI",
|
|
275
|
+
"HmIP-SMO",
|
|
276
|
+
"HmIP-SPI",
|
|
277
|
+
}
|
|
272
278
|
),
|
|
273
|
-
Parameter.LOWBAT: (
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
279
|
+
Parameter.LOWBAT: frozenset(
|
|
280
|
+
{
|
|
281
|
+
"HM-LC-Sw1-DR",
|
|
282
|
+
"HM-LC-Sw1-FM",
|
|
283
|
+
"HM-LC-Sw1-PCB",
|
|
284
|
+
"HM-LC-Sw1-Pl",
|
|
285
|
+
"HM-LC-Sw1-Pl-DN-R1",
|
|
286
|
+
"HM-LC-Sw1PBU-FM",
|
|
287
|
+
"HM-LC-Sw2-FM",
|
|
288
|
+
"HM-LC-Sw4-DR",
|
|
289
|
+
"HM-SwI-3-FM",
|
|
290
|
+
}
|
|
283
291
|
),
|
|
284
|
-
Parameter.LOW_BAT: ("HmIP-BWTH", "HmIP-PCBS"),
|
|
285
|
-
Parameter.OPERATING_VOLTAGE: (
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
292
|
+
Parameter.LOW_BAT: frozenset({"HmIP-BWTH", "HmIP-PCBS"}),
|
|
293
|
+
Parameter.OPERATING_VOLTAGE: frozenset(
|
|
294
|
+
{
|
|
295
|
+
"ELV-SH-BS2",
|
|
296
|
+
"HmIP-BDT",
|
|
297
|
+
"HmIP-BROLL",
|
|
298
|
+
"HmIP-BS2",
|
|
299
|
+
"HmIP-BSL",
|
|
300
|
+
"HmIP-BSM",
|
|
301
|
+
"HmIP-BWTH",
|
|
302
|
+
"HmIP-DR",
|
|
303
|
+
"HmIP-FDT",
|
|
304
|
+
"HmIP-FROLL",
|
|
305
|
+
"HmIP-FSM",
|
|
306
|
+
"HmIP-MOD-OC8",
|
|
307
|
+
"HmIP-PCBS",
|
|
308
|
+
"HmIP-PDT",
|
|
309
|
+
"HmIP-PMFS",
|
|
310
|
+
"HmIP-PS",
|
|
311
|
+
"HmIP-SFD",
|
|
312
|
+
"HmIP-SMO230",
|
|
313
|
+
"HmIP-WGT",
|
|
314
|
+
}
|
|
305
315
|
),
|
|
306
|
-
Parameter.VALVE_STATE: ("HmIPW-FALMOT-C12", "HmIP-FALMOT-C12"),
|
|
316
|
+
Parameter.VALVE_STATE: frozenset({"HmIPW-FALMOT-C12", "HmIP-FALMOT-C12"}),
|
|
307
317
|
}
|
|
308
318
|
|
|
309
|
-
_IGNORE_PARAMETERS_BY_DEVICE_LOWER: Final[dict[
|
|
310
|
-
parameter:
|
|
319
|
+
_IGNORE_PARAMETERS_BY_DEVICE_LOWER: Final[dict[TParameterName, frozenset[TModelName]]] = {
|
|
320
|
+
parameter: frozenset(model.lower() for model in s) for parameter, s in _IGNORE_PARAMETERS_BY_DEVICE.items()
|
|
311
321
|
}
|
|
312
322
|
|
|
313
323
|
# Some devices have parameters on multiple channels,
|
|
314
324
|
# but we want to use it only from a certain channel.
|
|
315
|
-
_ACCEPT_PARAMETER_ONLY_ON_CHANNEL: Final[Mapping[
|
|
325
|
+
_ACCEPT_PARAMETER_ONLY_ON_CHANNEL: Final[Mapping[TParameterName, int]] = {Parameter.LOWBAT: 0}
|
|
316
326
|
|
|
317
327
|
|
|
318
328
|
class ParameterVisibilityCache:
|
|
319
|
-
"""
|
|
329
|
+
"""
|
|
330
|
+
Cache for parameter visibility.
|
|
331
|
+
|
|
332
|
+
The cache centralizes rules that determine whether a data point parameter is
|
|
333
|
+
created, ignored, un-ignored, or merely hidden for UI purposes. It combines
|
|
334
|
+
static rules (per-model/per-channel) with dynamic user-provided overrides and
|
|
335
|
+
memoizes decisions per (model/channel/paramset/parameter) to avoid repeated
|
|
336
|
+
computations during runtime. Behavior is unchanged; this class only clarifies
|
|
337
|
+
intent and structure through documentation and small naming improvements.
|
|
338
|
+
"""
|
|
320
339
|
|
|
321
340
|
__slots__ = (
|
|
322
341
|
"_central",
|
|
@@ -342,36 +361,36 @@ class ParameterVisibilityCache:
|
|
|
342
361
|
self._central = central
|
|
343
362
|
self._storage_folder: Final = central.config.storage_folder
|
|
344
363
|
self._required_parameters: Final = get_required_parameters()
|
|
345
|
-
self._raw_un_ignores: Final[
|
|
364
|
+
self._raw_un_ignores: Final[frozenset[str]] = central.config.un_ignore_list or frozenset()
|
|
346
365
|
|
|
347
366
|
# un_ignore from custom un_ignore files
|
|
348
367
|
# parameter
|
|
349
|
-
self._custom_un_ignore_values_parameters: Final[set[
|
|
368
|
+
self._custom_un_ignore_values_parameters: Final[set[TParameterName]] = set()
|
|
350
369
|
|
|
351
|
-
# model
|
|
352
|
-
self._custom_un_ignore_complex: Final[
|
|
353
|
-
|
|
354
|
-
)
|
|
355
|
-
self._ignore_custom_device_definition_models: Final[
|
|
370
|
+
# model -> channel_no -> paramset_key -> set(parameter)
|
|
371
|
+
self._custom_un_ignore_complex: Final[
|
|
372
|
+
dict[TModelName, dict[TUnIgnoreChannelNo, dict[ParamsetKey, set[TParameterName]]]]
|
|
373
|
+
] = defaultdict(lambda: defaultdict(lambda: defaultdict(set)))
|
|
374
|
+
self._ignore_custom_device_definition_models: Final[frozenset[TModelName]] = (
|
|
356
375
|
central.config.ignore_custom_device_definition_models
|
|
357
376
|
)
|
|
358
377
|
|
|
359
378
|
# model, channel_no, paramset_key, set[parameter]
|
|
360
379
|
self._un_ignore_parameters_by_device_paramset_key: Final[
|
|
361
|
-
dict[
|
|
380
|
+
dict[TModelName, dict[TChannelNo, dict[ParamsetKey, set[TParameterName]]]]
|
|
362
381
|
] = defaultdict(lambda: defaultdict(lambda: defaultdict(set)))
|
|
363
382
|
|
|
364
383
|
# model, channel_no
|
|
365
|
-
self._relevant_master_paramsets_by_device: Final[dict[
|
|
384
|
+
self._relevant_master_paramsets_by_device: Final[dict[TModelName, set[TChannelNo]]] = defaultdict(set)
|
|
366
385
|
# Cache for resolving matching prefix key in _un_ignore_parameters_by_device_paramset_key
|
|
367
|
-
self._un_ignore_prefix_cache: dict[
|
|
386
|
+
self._un_ignore_prefix_cache: dict[TModelName, str | None] = {}
|
|
368
387
|
# Cache for resolving matching prefix key in _relevant_master_paramsets_by_device
|
|
369
|
-
self._relevant_prefix_cache: dict[
|
|
388
|
+
self._relevant_prefix_cache: dict[TModelName, str | None] = {}
|
|
370
389
|
# Per-instance memoization for repeated queries.
|
|
371
390
|
# Key: (model_l, channel_no, paramset_key, parameter)
|
|
372
|
-
self._param_ignored_cache: dict[tuple[
|
|
391
|
+
self._param_ignored_cache: dict[tuple[TModelName, TChannelNo, ParamsetKey, TParameterName], bool] = {}
|
|
373
392
|
# Key: (model_l, channel_no, paramset_key, parameter, custom_only)
|
|
374
|
-
self._param_un_ignored_cache: dict[tuple[
|
|
393
|
+
self._param_un_ignored_cache: dict[tuple[TModelName, TChannelNo, ParamsetKey, TParameterName, bool], bool] = {}
|
|
375
394
|
self._init()
|
|
376
395
|
|
|
377
396
|
def _init(self) -> None:
|
|
@@ -383,7 +402,7 @@ class ParameterVisibilityCache:
|
|
|
383
402
|
model_l = model.lower()
|
|
384
403
|
channel_nos, parameters = channels_parameter
|
|
385
404
|
|
|
386
|
-
def _add_channel(dt_l: str, params:
|
|
405
|
+
def _add_channel(dt_l: str, params: frozenset[Parameter], ch_no: TChannelNo) -> None:
|
|
387
406
|
self._relevant_master_paramsets_by_device[dt_l].add(ch_no)
|
|
388
407
|
self._un_ignore_parameters_by_device_paramset_key[dt_l][ch_no][ParamsetKey.MASTER].update(params)
|
|
389
408
|
|
|
@@ -395,7 +414,17 @@ class ParameterVisibilityCache:
|
|
|
395
414
|
|
|
396
415
|
self._process_un_ignore_entries(lines=self._raw_un_ignores)
|
|
397
416
|
|
|
398
|
-
def
|
|
417
|
+
def _resolve_prefix_key(
|
|
418
|
+
self, model_l: TModelName, mapping: Mapping[str, object], cache_dict: dict[TModelName, str | None]
|
|
419
|
+
) -> str | None:
|
|
420
|
+
"""Resolve and memoize the first key in mapping that prefixes model_l."""
|
|
421
|
+
dt_short_key = cache_dict.get(model_l)
|
|
422
|
+
if dt_short_key is None and model_l not in cache_dict:
|
|
423
|
+
dt_short_key = next((k for k in mapping if model_l.startswith(k)), None)
|
|
424
|
+
cache_dict[model_l] = dt_short_key
|
|
425
|
+
return dt_short_key
|
|
426
|
+
|
|
427
|
+
def model_is_ignored(self, model: TModelName) -> bool:
|
|
399
428
|
"""Check if a model should be ignored for custom data points."""
|
|
400
429
|
return element_matches_key(
|
|
401
430
|
search_elements=self._ignore_custom_device_definition_models,
|
|
@@ -406,13 +435,12 @@ class ParameterVisibilityCache:
|
|
|
406
435
|
self,
|
|
407
436
|
channel: hmd.Channel,
|
|
408
437
|
paramset_key: ParamsetKey,
|
|
409
|
-
parameter:
|
|
438
|
+
parameter: TParameterName,
|
|
410
439
|
) -> bool:
|
|
411
440
|
"""Check if parameter can be ignored."""
|
|
412
441
|
model_l = channel.device.model.lower()
|
|
413
442
|
# Fast path via per-instance memoization
|
|
414
|
-
|
|
415
|
-
if (cache_key := (model_l, ch_no_key, paramset_key, parameter)) in self._param_ignored_cache:
|
|
443
|
+
if (cache_key := (model_l, channel.no, paramset_key, parameter)) in self._param_ignored_cache:
|
|
416
444
|
return self._param_ignored_cache[cache_key]
|
|
417
445
|
|
|
418
446
|
if paramset_key == ParamsetKey.VALUES:
|
|
@@ -425,7 +453,7 @@ class ParameterVisibilityCache:
|
|
|
425
453
|
|
|
426
454
|
if (
|
|
427
455
|
(
|
|
428
|
-
(parameter in
|
|
456
|
+
(parameter in _IGNORED_PARAMETERS or _parameter_is_wildcard_ignored(parameter=parameter))
|
|
429
457
|
and parameter not in self._required_parameters
|
|
430
458
|
)
|
|
431
459
|
or hms.element_matches_key(
|
|
@@ -454,14 +482,11 @@ class ParameterVisibilityCache:
|
|
|
454
482
|
if parameter in self._custom_un_ignore_complex[model_l][channel.no][ParamsetKey.MASTER]:
|
|
455
483
|
return False # pragma: no cover
|
|
456
484
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
(k for k in self._un_ignore_parameters_by_device_paramset_key if model_l.startswith(k)), None
|
|
463
|
-
)
|
|
464
|
-
self._un_ignore_prefix_cache[model_l] = dt_short_key
|
|
485
|
+
dt_short_key = self._resolve_prefix_key(
|
|
486
|
+
model_l=model_l,
|
|
487
|
+
mapping=self._un_ignore_parameters_by_device_paramset_key,
|
|
488
|
+
cache_dict=self._un_ignore_prefix_cache,
|
|
489
|
+
)
|
|
465
490
|
if (
|
|
466
491
|
dt_short_key is not None
|
|
467
492
|
and parameter
|
|
@@ -479,7 +504,7 @@ class ParameterVisibilityCache:
|
|
|
479
504
|
self,
|
|
480
505
|
channel: hmd.Channel,
|
|
481
506
|
paramset_key: ParamsetKey,
|
|
482
|
-
parameter:
|
|
507
|
+
parameter: TParameterName,
|
|
483
508
|
custom_only: bool = False,
|
|
484
509
|
) -> bool:
|
|
485
510
|
"""
|
|
@@ -496,8 +521,7 @@ class ParameterVisibilityCache:
|
|
|
496
521
|
# check if parameter is in custom_un_ignore with paramset_key
|
|
497
522
|
model_l = channel.device.model.lower()
|
|
498
523
|
# Fast path via per-instance memoization
|
|
499
|
-
|
|
500
|
-
if (cache_key := (model_l, ch_no_key, paramset_key, parameter, custom_only)) in self._param_un_ignored_cache:
|
|
524
|
+
if (cache_key := (model_l, channel.no, paramset_key, parameter, custom_only)) in self._param_un_ignored_cache:
|
|
501
525
|
return self._param_un_ignored_cache[cache_key]
|
|
502
526
|
|
|
503
527
|
search_matrix = (
|
|
@@ -529,7 +553,7 @@ class ParameterVisibilityCache:
|
|
|
529
553
|
self,
|
|
530
554
|
channel: hmd.Channel,
|
|
531
555
|
paramset_key: ParamsetKey,
|
|
532
|
-
parameter:
|
|
556
|
+
parameter: TParameterName,
|
|
533
557
|
custom_only: bool = False,
|
|
534
558
|
) -> bool:
|
|
535
559
|
"""
|
|
@@ -540,13 +564,11 @@ class ParameterVisibilityCache:
|
|
|
540
564
|
"""
|
|
541
565
|
if not custom_only:
|
|
542
566
|
model_l = channel.device.model.lower()
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
)
|
|
549
|
-
self._un_ignore_prefix_cache[model_l] = dt_short_key
|
|
567
|
+
dt_short_key = self._resolve_prefix_key(
|
|
568
|
+
model_l=model_l,
|
|
569
|
+
mapping=self._un_ignore_parameters_by_device_paramset_key,
|
|
570
|
+
cache_dict=self._un_ignore_prefix_cache,
|
|
571
|
+
)
|
|
550
572
|
|
|
551
573
|
# check if parameter is in _RELEVANT_MASTER_PARAMSETS_BY_DEVICE
|
|
552
574
|
if (
|
|
@@ -564,7 +586,7 @@ class ParameterVisibilityCache:
|
|
|
564
586
|
)
|
|
565
587
|
|
|
566
588
|
def should_skip_parameter(
|
|
567
|
-
self, channel: hmd.Channel, paramset_key: ParamsetKey, parameter:
|
|
589
|
+
self, channel: hmd.Channel, paramset_key: ParamsetKey, parameter: TParameterName, parameter_is_un_ignored: bool
|
|
568
590
|
) -> bool:
|
|
569
591
|
"""Determine if a parameter should be skipped."""
|
|
570
592
|
if self.parameter_is_ignored(
|
|
@@ -587,7 +609,7 @@ class ParameterVisibilityCache:
|
|
|
587
609
|
|
|
588
610
|
return paramset_key == ParamsetKey.MASTER and not parameter_is_un_ignored
|
|
589
611
|
|
|
590
|
-
def _process_un_ignore_entries(self, lines:
|
|
612
|
+
def _process_un_ignore_entries(self, lines: Iterable[str]) -> None:
|
|
591
613
|
"""Batch process un_ignore entries into cache."""
|
|
592
614
|
for line in lines:
|
|
593
615
|
# ignore empty line
|
|
@@ -610,17 +632,19 @@ class ParameterVisibilityCache:
|
|
|
610
632
|
line,
|
|
611
633
|
)
|
|
612
634
|
|
|
613
|
-
def _get_un_ignore_line_details(
|
|
635
|
+
def _get_un_ignore_line_details(
|
|
636
|
+
self, line: str
|
|
637
|
+
) -> tuple[TModelName, TUnIgnoreChannelNo, TParameterName, ParamsetKey] | str | None:
|
|
614
638
|
"""
|
|
615
639
|
Check the format of the line for un_ignore file.
|
|
616
640
|
|
|
617
641
|
model, channel_no, paramset_key, parameter
|
|
618
642
|
"""
|
|
619
643
|
|
|
620
|
-
model:
|
|
621
|
-
channel_no:
|
|
644
|
+
model: TModelName | None = None
|
|
645
|
+
channel_no: TUnIgnoreChannelNo = None
|
|
622
646
|
paramset_key: ParamsetKey | None = None
|
|
623
|
-
parameter:
|
|
647
|
+
parameter: TParameterName | None = None
|
|
624
648
|
|
|
625
649
|
if "@" in line:
|
|
626
650
|
data = line.split("@")
|
|
@@ -687,10 +711,10 @@ class ParameterVisibilityCache:
|
|
|
687
711
|
|
|
688
712
|
def _add_complex_un_ignore_entry(
|
|
689
713
|
self,
|
|
690
|
-
model:
|
|
691
|
-
channel_no:
|
|
714
|
+
model: TModelName,
|
|
715
|
+
channel_no: TUnIgnoreChannelNo,
|
|
692
716
|
paramset_key: ParamsetKey,
|
|
693
|
-
parameter:
|
|
717
|
+
parameter: TParameterName,
|
|
694
718
|
) -> None:
|
|
695
719
|
"""Add line to un ignore cache."""
|
|
696
720
|
if paramset_key == ParamsetKey.MASTER:
|
|
@@ -713,7 +737,7 @@ class ParameterVisibilityCache:
|
|
|
713
737
|
self,
|
|
714
738
|
channel: hmd.Channel,
|
|
715
739
|
paramset_key: ParamsetKey,
|
|
716
|
-
parameter:
|
|
740
|
+
parameter: TParameterName,
|
|
717
741
|
) -> bool:
|
|
718
742
|
"""
|
|
719
743
|
Return if parameter should be hidden.
|
|
@@ -721,7 +745,7 @@ class ParameterVisibilityCache:
|
|
|
721
745
|
This is required to determine the data_point usage.
|
|
722
746
|
Return only hidden parameters, that are no defined in the un_ignore file.
|
|
723
747
|
"""
|
|
724
|
-
return parameter in
|
|
748
|
+
return parameter in _HIDDEN_PARAMETERS and not self._parameter_is_un_ignored(
|
|
725
749
|
channel=channel,
|
|
726
750
|
paramset_key=paramset_key,
|
|
727
751
|
parameter=parameter,
|
|
@@ -744,12 +768,11 @@ class ParameterVisibilityCache:
|
|
|
744
768
|
return True
|
|
745
769
|
model_l = channel.device.model.lower()
|
|
746
770
|
# Resolve matching device type prefix once and cache per model
|
|
747
|
-
dt_short_key = self.
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
self._relevant_prefix_cache[model_l] = dt_short_key
|
|
771
|
+
dt_short_key = self._resolve_prefix_key(
|
|
772
|
+
model_l=model_l,
|
|
773
|
+
mapping=self._relevant_master_paramsets_by_device,
|
|
774
|
+
cache_dict=self._relevant_prefix_cache,
|
|
775
|
+
)
|
|
753
776
|
if dt_short_key is not None and channel.no in self._relevant_master_paramsets_by_device[dt_short_key]:
|
|
754
777
|
return True
|
|
755
778
|
return False
|
|
@@ -766,7 +789,7 @@ def check_ignore_parameters_is_clean() -> bool:
|
|
|
766
789
|
[
|
|
767
790
|
parameter
|
|
768
791
|
for parameter in get_required_parameters()
|
|
769
|
-
if (parameter in
|
|
792
|
+
if (parameter in _IGNORED_PARAMETERS or _parameter_is_wildcard_ignored(parameter=parameter))
|
|
770
793
|
and parameter not in un_ignore_parameters_by_device
|
|
771
794
|
]
|
|
772
795
|
)
|