aiohomematic 2025.11.3__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 +61 -0
- aiohomematic/async_support.py +212 -0
- aiohomematic/central/__init__.py +2309 -0
- aiohomematic/central/decorators.py +155 -0
- aiohomematic/central/rpc_server.py +295 -0
- aiohomematic/client/__init__.py +1848 -0
- aiohomematic/client/_rpc_errors.py +81 -0
- aiohomematic/client/json_rpc.py +1326 -0
- aiohomematic/client/rpc_proxy.py +311 -0
- aiohomematic/const.py +1127 -0
- aiohomematic/context.py +18 -0
- aiohomematic/converter.py +108 -0
- aiohomematic/decorators.py +302 -0
- aiohomematic/exceptions.py +164 -0
- aiohomematic/hmcli.py +186 -0
- aiohomematic/model/__init__.py +140 -0
- aiohomematic/model/calculated/__init__.py +84 -0
- aiohomematic/model/calculated/climate.py +290 -0
- aiohomematic/model/calculated/data_point.py +327 -0
- aiohomematic/model/calculated/operating_voltage_level.py +299 -0
- aiohomematic/model/calculated/support.py +234 -0
- aiohomematic/model/custom/__init__.py +177 -0
- aiohomematic/model/custom/climate.py +1532 -0
- aiohomematic/model/custom/cover.py +792 -0
- aiohomematic/model/custom/data_point.py +334 -0
- aiohomematic/model/custom/definition.py +871 -0
- aiohomematic/model/custom/light.py +1128 -0
- aiohomematic/model/custom/lock.py +394 -0
- aiohomematic/model/custom/siren.py +275 -0
- aiohomematic/model/custom/support.py +41 -0
- aiohomematic/model/custom/switch.py +175 -0
- aiohomematic/model/custom/valve.py +114 -0
- aiohomematic/model/data_point.py +1123 -0
- aiohomematic/model/device.py +1445 -0
- aiohomematic/model/event.py +208 -0
- aiohomematic/model/generic/__init__.py +217 -0
- aiohomematic/model/generic/action.py +34 -0
- aiohomematic/model/generic/binary_sensor.py +30 -0
- aiohomematic/model/generic/button.py +27 -0
- aiohomematic/model/generic/data_point.py +171 -0
- aiohomematic/model/generic/dummy.py +147 -0
- aiohomematic/model/generic/number.py +76 -0
- aiohomematic/model/generic/select.py +39 -0
- aiohomematic/model/generic/sensor.py +74 -0
- aiohomematic/model/generic/switch.py +54 -0
- aiohomematic/model/generic/text.py +29 -0
- aiohomematic/model/hub/__init__.py +333 -0
- aiohomematic/model/hub/binary_sensor.py +24 -0
- aiohomematic/model/hub/button.py +28 -0
- aiohomematic/model/hub/data_point.py +340 -0
- aiohomematic/model/hub/number.py +39 -0
- aiohomematic/model/hub/select.py +49 -0
- aiohomematic/model/hub/sensor.py +37 -0
- aiohomematic/model/hub/switch.py +44 -0
- aiohomematic/model/hub/text.py +30 -0
- aiohomematic/model/support.py +586 -0
- aiohomematic/model/update.py +143 -0
- aiohomematic/property_decorators.py +496 -0
- aiohomematic/py.typed +0 -0
- aiohomematic/rega_scripts/fetch_all_device_data.fn +92 -0
- aiohomematic/rega_scripts/get_program_descriptions.fn +30 -0
- aiohomematic/rega_scripts/get_serial.fn +44 -0
- aiohomematic/rega_scripts/get_system_variable_descriptions.fn +30 -0
- aiohomematic/rega_scripts/set_program_state.fn +12 -0
- aiohomematic/rega_scripts/set_system_variable.fn +15 -0
- aiohomematic/store/__init__.py +34 -0
- aiohomematic/store/dynamic.py +551 -0
- aiohomematic/store/persistent.py +988 -0
- aiohomematic/store/visibility.py +812 -0
- aiohomematic/support.py +664 -0
- aiohomematic/validator.py +112 -0
- aiohomematic-2025.11.3.dist-info/METADATA +144 -0
- aiohomematic-2025.11.3.dist-info/RECORD +77 -0
- aiohomematic-2025.11.3.dist-info/WHEEL +5 -0
- aiohomematic-2025.11.3.dist-info/entry_points.txt +2 -0
- aiohomematic-2025.11.3.dist-info/licenses/LICENSE +21 -0
- aiohomematic-2025.11.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,812 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2021-2025
|
|
3
|
+
"""
|
|
4
|
+
Parameter visibility rules and cache for Homematic data points.
|
|
5
|
+
|
|
6
|
+
This module determines which parameters should be created, shown, hidden,
|
|
7
|
+
ignored, or un‑ignored for channels and devices. It centralizes the rules
|
|
8
|
+
that influence the visibility of data points exposed by the library.
|
|
9
|
+
|
|
10
|
+
Overview
|
|
11
|
+
- ParameterVisibilityCache: Computes and store visibility decisions per model,
|
|
12
|
+
channel number, paramset and parameter. It consolidates rules from multiple
|
|
13
|
+
sources, such as model‑specific defaults, paramset relevance, hidden lists,
|
|
14
|
+
and explicit un‑ignore directives.
|
|
15
|
+
- check_ignore_parameters_is_clean: Helper to verify that ignore/un‑ignore
|
|
16
|
+
configuration input is consistent.
|
|
17
|
+
|
|
18
|
+
Key concepts
|
|
19
|
+
- Relevant MASTER parameters: Certain MASTER paramset entries are promoted to
|
|
20
|
+
data points for selected models/channels (e.g. climate related settings), but
|
|
21
|
+
they may still be hidden by default for UI purposes.
|
|
22
|
+
- Ignored vs un‑ignored: Parameters can be broadly ignored, with exceptions
|
|
23
|
+
defined via explicit un‑ignore rules that match model/channel/paramset keys.
|
|
24
|
+
- Event suppression: For selected devices, button click events are suppressed to
|
|
25
|
+
avoid noise in event streams.
|
|
26
|
+
|
|
27
|
+
The cache avoids recomputing rule lookups by storing decisions per combination
|
|
28
|
+
of (model/channel/paramset/parameter). It is initialized on demand using data
|
|
29
|
+
available on the CentralUnit and model descriptors.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
from collections import defaultdict
|
|
35
|
+
from collections.abc import Iterable, Mapping
|
|
36
|
+
from functools import cache
|
|
37
|
+
import logging
|
|
38
|
+
import re
|
|
39
|
+
from typing import Final, TypeAlias
|
|
40
|
+
|
|
41
|
+
from aiohomematic import central as hmcu, support as hms
|
|
42
|
+
from aiohomematic.const import ADDRESS_SEPARATOR, CLICK_EVENTS, UN_IGNORE_WILDCARD, Parameter, ParamsetKey
|
|
43
|
+
from aiohomematic.model import hmd
|
|
44
|
+
from aiohomematic.model.custom import get_required_parameters
|
|
45
|
+
from aiohomematic.support import element_matches_key
|
|
46
|
+
|
|
47
|
+
_LOGGER: Final = logging.getLogger(__name__)
|
|
48
|
+
|
|
49
|
+
# Readability type aliases for internal cache structures
|
|
50
|
+
TModelName: TypeAlias = str
|
|
51
|
+
TChannelNo: TypeAlias = int | None
|
|
52
|
+
TUnIgnoreChannelNo: TypeAlias = TChannelNo | str
|
|
53
|
+
TParameterName: TypeAlias = str
|
|
54
|
+
|
|
55
|
+
# Define which additional parameters from MASTER paramset should be created as data_point.
|
|
56
|
+
# By default these are also on the _HIDDEN_PARAMETERS, which prevents these data points
|
|
57
|
+
# from being display by default. Usually these enties are used within custom data points,
|
|
58
|
+
# and not for general display.
|
|
59
|
+
# {model: (channel_no, parameter)}
|
|
60
|
+
|
|
61
|
+
_RELEVANT_MASTER_PARAMSETS_BY_CHANNEL: Final[Mapping[TChannelNo, frozenset[Parameter]]] = {
|
|
62
|
+
None: frozenset({Parameter.GLOBAL_BUTTON_LOCK, Parameter.LOW_BAT_LIMIT}),
|
|
63
|
+
0: frozenset({Parameter.GLOBAL_BUTTON_LOCK, Parameter.LOW_BAT_LIMIT}),
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
_CLIMATE_MASTER_PARAMETERS: Final[frozenset[Parameter]] = frozenset(
|
|
67
|
+
{
|
|
68
|
+
Parameter.HEATING_VALVE_TYPE,
|
|
69
|
+
Parameter.MIN_MAX_VALUE_NOT_RELEVANT_FOR_MANU_MODE,
|
|
70
|
+
Parameter.OPTIMUM_START_STOP,
|
|
71
|
+
Parameter.TEMPERATURE_MAXIMUM,
|
|
72
|
+
Parameter.TEMPERATURE_MINIMUM,
|
|
73
|
+
Parameter.TEMPERATURE_OFFSET,
|
|
74
|
+
Parameter.WEEK_PROGRAM_POINTER,
|
|
75
|
+
}
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
_RELEVANT_MASTER_PARAMSETS_BY_DEVICE: Final[Mapping[TModelName, tuple[frozenset[TChannelNo], frozenset[Parameter]]]] = {
|
|
79
|
+
"ALPHA-IP-RBG": (frozenset({1}), _CLIMATE_MASTER_PARAMETERS),
|
|
80
|
+
"ELV-SH-TACO": (frozenset({2}), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
81
|
+
"HM-CC-RT-DN": (
|
|
82
|
+
frozenset({None}),
|
|
83
|
+
_CLIMATE_MASTER_PARAMETERS,
|
|
84
|
+
),
|
|
85
|
+
"HM-CC-VG-1": (
|
|
86
|
+
frozenset({None}),
|
|
87
|
+
_CLIMATE_MASTER_PARAMETERS,
|
|
88
|
+
),
|
|
89
|
+
"HM-TC-IT-WM-W-EU": (
|
|
90
|
+
frozenset({None}),
|
|
91
|
+
_CLIMATE_MASTER_PARAMETERS,
|
|
92
|
+
),
|
|
93
|
+
"HmIP-BWTH": (frozenset({1, 8}), _CLIMATE_MASTER_PARAMETERS),
|
|
94
|
+
"HmIP-DRBLI4": (
|
|
95
|
+
frozenset({1, 2, 3, 4, 5, 6, 7, 8, 9, 13, 17, 21}),
|
|
96
|
+
frozenset({Parameter.CHANNEL_OPERATION_MODE}),
|
|
97
|
+
),
|
|
98
|
+
"HmIP-DRDI3": (frozenset({1, 2, 3}), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
99
|
+
"HmIP-DRSI1": (frozenset({1}), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
100
|
+
"HmIP-DRSI4": (frozenset({1, 2, 3, 4}), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
101
|
+
"HmIP-DSD-PCB": (frozenset({1}), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
102
|
+
"HmIP-FCI1": (frozenset({1}), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
103
|
+
"HmIP-FCI6": (frozenset(range(1, 7)), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
104
|
+
"HmIP-FSI16": (frozenset({1}), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
105
|
+
"HmIP-HEATING": (frozenset({1}), _CLIMATE_MASTER_PARAMETERS),
|
|
106
|
+
"HmIP-MIO16-PCB": (frozenset({13, 14, 15, 16}), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
107
|
+
"HmIP-MOD-RC8": (frozenset(range(1, 9)), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
108
|
+
"HmIP-RGBW": (frozenset({0}), frozenset({Parameter.DEVICE_OPERATION_MODE})),
|
|
109
|
+
"HmIP-STH": (frozenset({1}), _CLIMATE_MASTER_PARAMETERS),
|
|
110
|
+
"HmIP-WGT": (frozenset({8, 14}), _CLIMATE_MASTER_PARAMETERS),
|
|
111
|
+
"HmIP-WTH": (frozenset({1}), _CLIMATE_MASTER_PARAMETERS),
|
|
112
|
+
"HmIP-eTRV": (frozenset({1}), _CLIMATE_MASTER_PARAMETERS),
|
|
113
|
+
"HmIPW-DRBL4": (frozenset({1, 5, 9, 13}), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
114
|
+
"HmIPW-DRI16": (frozenset(range(1, 17)), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
115
|
+
"HmIPW-DRI32": (frozenset(range(1, 33)), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
116
|
+
"HmIPW-FIO6": (frozenset(range(1, 7)), frozenset({Parameter.CHANNEL_OPERATION_MODE})),
|
|
117
|
+
"HmIPW-STH": (frozenset({1}), _CLIMATE_MASTER_PARAMETERS),
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
# Ignore events for some devices
|
|
121
|
+
_IGNORE_DEVICES_FOR_DATA_POINT_EVENTS: Final[Mapping[TModelName, frozenset[Parameter]]] = {
|
|
122
|
+
"HmIP-PS": CLICK_EVENTS,
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
_IGNORE_DEVICES_FOR_DATA_POINT_EVENTS_LOWER: Final[Mapping[TModelName, frozenset[Parameter]]] = {
|
|
126
|
+
model.lower(): frozenset(events) for model, events in _IGNORE_DEVICES_FOR_DATA_POINT_EVENTS.items()
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
# data points that will be created, but should be hidden.
|
|
130
|
+
_HIDDEN_PARAMETERS: Final[frozenset[Parameter]] = frozenset(
|
|
131
|
+
{
|
|
132
|
+
Parameter.ACTIVITY_STATE,
|
|
133
|
+
Parameter.CHANNEL_OPERATION_MODE,
|
|
134
|
+
Parameter.CONFIG_PENDING,
|
|
135
|
+
Parameter.DIRECTION,
|
|
136
|
+
Parameter.ERROR,
|
|
137
|
+
Parameter.HEATING_VALVE_TYPE,
|
|
138
|
+
Parameter.LOW_BAT_LIMIT,
|
|
139
|
+
Parameter.MIN_MAX_VALUE_NOT_RELEVANT_FOR_MANU_MODE,
|
|
140
|
+
Parameter.OPTIMUM_START_STOP,
|
|
141
|
+
Parameter.SECTION,
|
|
142
|
+
Parameter.STICKY_UN_REACH,
|
|
143
|
+
Parameter.TEMPERATURE_MAXIMUM,
|
|
144
|
+
Parameter.TEMPERATURE_MINIMUM,
|
|
145
|
+
Parameter.TEMPERATURE_OFFSET,
|
|
146
|
+
Parameter.UN_REACH,
|
|
147
|
+
Parameter.UPDATE_PENDING,
|
|
148
|
+
Parameter.WORKING,
|
|
149
|
+
}
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# Parameters within the VALUES paramset for which we don't create data points.
|
|
153
|
+
_IGNORED_PARAMETERS: Final[frozenset[TParameterName]] = frozenset(
|
|
154
|
+
{
|
|
155
|
+
"ACCESS_AUTHORIZATION",
|
|
156
|
+
"ACOUSTIC_NOTIFICATION_SELECTION",
|
|
157
|
+
"ADAPTION_DRIVE",
|
|
158
|
+
"AES_KEY",
|
|
159
|
+
"ALARM_COUNT",
|
|
160
|
+
"ALL_LEDS",
|
|
161
|
+
"ARROW_DOWN",
|
|
162
|
+
"ARROW_UP",
|
|
163
|
+
"BACKLIGHT",
|
|
164
|
+
"BEEP",
|
|
165
|
+
"BELL",
|
|
166
|
+
"BLIND",
|
|
167
|
+
"BOOST_STATE",
|
|
168
|
+
"BOOST_TIME",
|
|
169
|
+
"BOOT",
|
|
170
|
+
"BULB",
|
|
171
|
+
"CLEAR_ERROR",
|
|
172
|
+
"CLEAR_WINDOW_OPEN_SYMBOL",
|
|
173
|
+
"CLOCK",
|
|
174
|
+
"CMD_RETL", # CUxD
|
|
175
|
+
"CMD_RETS", # CUxD
|
|
176
|
+
"CONTROL_DIFFERENTIAL_TEMPERATURE",
|
|
177
|
+
"DATE_TIME_UNKNOWN",
|
|
178
|
+
"DECISION_VALUE",
|
|
179
|
+
"DEVICE_IN_BOOTLOADER",
|
|
180
|
+
"DISPLAY_DATA_ALIGNMENT",
|
|
181
|
+
"DISPLAY_DATA_BACKGROUND_COLOR",
|
|
182
|
+
"DISPLAY_DATA_COMMIT",
|
|
183
|
+
"DISPLAY_DATA_ICON",
|
|
184
|
+
"DISPLAY_DATA_ID",
|
|
185
|
+
"DISPLAY_DATA_STRING",
|
|
186
|
+
"DISPLAY_DATA_TEXT_COLOR",
|
|
187
|
+
"DOOR",
|
|
188
|
+
"EXTERNAL_CLOCK",
|
|
189
|
+
"FROST_PROTECTION",
|
|
190
|
+
"HUMIDITY_LIMITER",
|
|
191
|
+
"IDENTIFICATION_MODE_KEY_VISUAL",
|
|
192
|
+
"IDENTIFICATION_MODE_LCD_BACKLIGHT",
|
|
193
|
+
"INCLUSION_UNSUPPORTED_DEVICE",
|
|
194
|
+
"INHIBIT",
|
|
195
|
+
"INSTALL_MODE",
|
|
196
|
+
"INTERVAL",
|
|
197
|
+
"LEVEL_REAL",
|
|
198
|
+
"OLD_LEVEL",
|
|
199
|
+
"OVERFLOW",
|
|
200
|
+
"OVERRUN",
|
|
201
|
+
"PARTY_SET_POINT_TEMPERATURE",
|
|
202
|
+
"PARTY_TEMPERATURE",
|
|
203
|
+
"PARTY_TIME_END",
|
|
204
|
+
"PARTY_TIME_START",
|
|
205
|
+
"PHONE",
|
|
206
|
+
"PROCESS",
|
|
207
|
+
"QUICK_VETO_TIME",
|
|
208
|
+
"RAMP_STOP",
|
|
209
|
+
"RELOCK_DELAY",
|
|
210
|
+
"SCENE",
|
|
211
|
+
"SELF_CALIBRATION",
|
|
212
|
+
"SERVICE_COUNT",
|
|
213
|
+
"SET_SYMBOL_FOR_HEATING_PHASE",
|
|
214
|
+
"SHADING_SPEED",
|
|
215
|
+
"SHEV_POS",
|
|
216
|
+
"SPEED",
|
|
217
|
+
"STATE_UNCERTAIN",
|
|
218
|
+
"SUBMIT",
|
|
219
|
+
"SWITCH_POINT_OCCURED",
|
|
220
|
+
"TEMPERATURE_LIMITER",
|
|
221
|
+
"TEMPERATURE_OUT_OF_RANGE",
|
|
222
|
+
"TEXT",
|
|
223
|
+
"USER_COLOR",
|
|
224
|
+
"USER_PROGRAM",
|
|
225
|
+
"VALVE_ADAPTION",
|
|
226
|
+
"WINDOW",
|
|
227
|
+
"WIN_RELEASE",
|
|
228
|
+
"WIN_RELEASE_ACT",
|
|
229
|
+
}
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
# Precompile Regex patterns for wildcard checks
|
|
234
|
+
# Ignore Parameter that end with
|
|
235
|
+
_IGNORED_PARAMETERS_END_RE: Final = re.compile(r".*(_OVERFLOW|_OVERRUN|_REPORTING|_RESULT|_STATUS|_SUBMIT)$")
|
|
236
|
+
# Ignore Parameter that start with
|
|
237
|
+
_IGNORED_PARAMETERS_START_RE: Final = re.compile(
|
|
238
|
+
r"^(ADJUSTING_|ERR_TTM_|HANDLE_|IDENTIFY_|PARTY_START_|PARTY_STOP_|STATUS_FLAG_|WEEK_PROGRAM_)"
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def _parameter_is_wildcard_ignored(*, parameter: TParameterName) -> bool:
|
|
243
|
+
"""Check if a parameter matches common wildcard patterns."""
|
|
244
|
+
return bool(_IGNORED_PARAMETERS_END_RE.match(parameter) or _IGNORED_PARAMETERS_START_RE.match(parameter))
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
# Parameters within the paramsets for which we create data points.
|
|
248
|
+
_UN_IGNORE_PARAMETERS_BY_DEVICE: Final[Mapping[TModelName, frozenset[Parameter]]] = {
|
|
249
|
+
"HmIP-DLD": frozenset({Parameter.ERROR_JAMMED}),
|
|
250
|
+
"HmIP-SWSD": frozenset({Parameter.SMOKE_DETECTOR_ALARM_STATUS}),
|
|
251
|
+
"HM-OU-LED16": frozenset({Parameter.LED_STATUS}),
|
|
252
|
+
"HM-Sec-Win": frozenset({Parameter.DIRECTION, Parameter.WORKING, Parameter.ERROR, Parameter.STATUS}),
|
|
253
|
+
"HM-Sec-Key": frozenset({Parameter.DIRECTION, Parameter.ERROR}),
|
|
254
|
+
"HmIP-PCBS-BAT": frozenset({Parameter.OPERATING_VOLTAGE, Parameter.LOW_BAT}), # To override ignore for HmIP-PCBS
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
_UN_IGNORE_PARAMETERS_BY_MODEL_LOWER: Final[dict[TModelName, frozenset[Parameter]]] = {
|
|
258
|
+
model.lower(): frozenset(parameters) for model, parameters in _UN_IGNORE_PARAMETERS_BY_DEVICE.items()
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
@cache
|
|
263
|
+
def _get_parameters_for_model_prefix(*, model_prefix: str | None) -> frozenset[Parameter] | None:
|
|
264
|
+
"""Return the dict value by wildcard type."""
|
|
265
|
+
if model_prefix is None:
|
|
266
|
+
return None
|
|
267
|
+
|
|
268
|
+
for model, parameters in _UN_IGNORE_PARAMETERS_BY_MODEL_LOWER.items():
|
|
269
|
+
if model.startswith(model_prefix):
|
|
270
|
+
return parameters
|
|
271
|
+
return None
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
# Parameters by device within the VALUES paramset for which we don't create data points.
|
|
275
|
+
_IGNORE_PARAMETERS_BY_DEVICE: Final[Mapping[Parameter, frozenset[TModelName]]] = {
|
|
276
|
+
Parameter.CURRENT_ILLUMINATION: frozenset(
|
|
277
|
+
{
|
|
278
|
+
"HmIP-SMI",
|
|
279
|
+
"HmIP-SMO",
|
|
280
|
+
"HmIP-SPI",
|
|
281
|
+
}
|
|
282
|
+
),
|
|
283
|
+
Parameter.LOWBAT: frozenset(
|
|
284
|
+
{
|
|
285
|
+
"HM-LC-Sw1-DR",
|
|
286
|
+
"HM-LC-Sw1-FM",
|
|
287
|
+
"HM-LC-Sw1-PCB",
|
|
288
|
+
"HM-LC-Sw1-Pl",
|
|
289
|
+
"HM-LC-Sw1-Pl-DN-R1",
|
|
290
|
+
"HM-LC-Sw1PBU-FM",
|
|
291
|
+
"HM-LC-Sw2-FM",
|
|
292
|
+
"HM-LC-Sw4-DR",
|
|
293
|
+
"HM-SwI-3-FM",
|
|
294
|
+
}
|
|
295
|
+
),
|
|
296
|
+
Parameter.LOW_BAT: frozenset({"HmIP-BWTH", "HmIP-PCBS"}),
|
|
297
|
+
Parameter.OPERATING_VOLTAGE: frozenset(
|
|
298
|
+
{
|
|
299
|
+
"ELV-SH-BS2",
|
|
300
|
+
"HmIP-BDT",
|
|
301
|
+
"HmIP-BROLL",
|
|
302
|
+
"HmIP-BS2",
|
|
303
|
+
"HmIP-BSL",
|
|
304
|
+
"HmIP-BSM",
|
|
305
|
+
"HmIP-BWTH",
|
|
306
|
+
"HmIP-DR",
|
|
307
|
+
"HmIP-FDT",
|
|
308
|
+
"HmIP-FROLL",
|
|
309
|
+
"HmIP-FSM",
|
|
310
|
+
"HmIP-MOD-OC8",
|
|
311
|
+
"HmIP-PCBS",
|
|
312
|
+
"HmIP-PDT",
|
|
313
|
+
"HmIP-PMFS",
|
|
314
|
+
"HmIP-PS",
|
|
315
|
+
"HmIP-SFD",
|
|
316
|
+
"HmIP-SMO230",
|
|
317
|
+
"HmIP-WGT",
|
|
318
|
+
}
|
|
319
|
+
),
|
|
320
|
+
Parameter.VALVE_STATE: frozenset({"HmIP-FALMOT-C8", "HmIPW-FALMOT-C12", "HmIP-FALMOT-C12"}),
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
_IGNORE_PARAMETERS_BY_DEVICE_LOWER: Final[dict[TParameterName, frozenset[TModelName]]] = {
|
|
324
|
+
parameter: frozenset(model.lower() for model in s) for parameter, s in _IGNORE_PARAMETERS_BY_DEVICE.items()
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
# Some devices have parameters on multiple channels,
|
|
328
|
+
# but we want to use it only from a certain channel.
|
|
329
|
+
_ACCEPT_PARAMETER_ONLY_ON_CHANNEL: Final[Mapping[TParameterName, int]] = {Parameter.LOWBAT: 0}
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
class ParameterVisibilityCache:
|
|
333
|
+
"""
|
|
334
|
+
Cache for parameter visibility.
|
|
335
|
+
|
|
336
|
+
The cache centralizes rules that determine whether a data point parameter is
|
|
337
|
+
created, ignored, un-ignored, or merely hidden for UI purposes. It combines
|
|
338
|
+
static rules (per-model/per-channel) with dynamic user-provided overrides and
|
|
339
|
+
memoizes decisions per (model/channel/paramset/parameter) to avoid repeated
|
|
340
|
+
computations during runtime. Behavior is unchanged; this class only clarifies
|
|
341
|
+
intent and structure through documentation and small naming improvements.
|
|
342
|
+
"""
|
|
343
|
+
|
|
344
|
+
__slots__ = (
|
|
345
|
+
"_central",
|
|
346
|
+
"_custom_un_ignore_complex",
|
|
347
|
+
"_custom_un_ignore_values_parameters",
|
|
348
|
+
"_ignore_custom_device_definition_models",
|
|
349
|
+
"_param_ignored_cache",
|
|
350
|
+
"_param_un_ignored_cache",
|
|
351
|
+
"_raw_un_ignores",
|
|
352
|
+
"_relevant_master_paramsets_by_device",
|
|
353
|
+
"_relevant_prefix_cache",
|
|
354
|
+
"_required_parameters",
|
|
355
|
+
"_storage_directory",
|
|
356
|
+
"_un_ignore_parameters_by_device_paramset_key",
|
|
357
|
+
"_un_ignore_prefix_cache",
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
def __init__(
|
|
361
|
+
self,
|
|
362
|
+
*,
|
|
363
|
+
central: hmcu.CentralUnit,
|
|
364
|
+
) -> None:
|
|
365
|
+
"""Init the parameter visibility cache."""
|
|
366
|
+
self._central = central
|
|
367
|
+
self._storage_directory: Final = central.config.storage_directory
|
|
368
|
+
self._required_parameters: Final = get_required_parameters()
|
|
369
|
+
self._raw_un_ignores: Final[frozenset[str]] = central.config.un_ignore_list or frozenset()
|
|
370
|
+
|
|
371
|
+
# un_ignore from custom un_ignore files
|
|
372
|
+
# parameter
|
|
373
|
+
self._custom_un_ignore_values_parameters: Final[set[TParameterName]] = set()
|
|
374
|
+
|
|
375
|
+
# model -> channel_no -> paramset_key -> set(parameter)
|
|
376
|
+
self._custom_un_ignore_complex: Final[
|
|
377
|
+
dict[TModelName, dict[TUnIgnoreChannelNo, dict[ParamsetKey, set[TParameterName]]]]
|
|
378
|
+
] = defaultdict(lambda: defaultdict(lambda: defaultdict(set)))
|
|
379
|
+
self._ignore_custom_device_definition_models: Final[frozenset[TModelName]] = (
|
|
380
|
+
central.config.ignore_custom_device_definition_models
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
# model, channel_no, paramset_key, set[parameter]
|
|
384
|
+
self._un_ignore_parameters_by_device_paramset_key: Final[
|
|
385
|
+
dict[TModelName, dict[TChannelNo, dict[ParamsetKey, set[TParameterName]]]]
|
|
386
|
+
] = defaultdict(lambda: defaultdict(lambda: defaultdict(set)))
|
|
387
|
+
|
|
388
|
+
# model, channel_no
|
|
389
|
+
self._relevant_master_paramsets_by_device: Final[dict[TModelName, set[TChannelNo]]] = defaultdict(set)
|
|
390
|
+
# Cache for resolving matching prefix key in _un_ignore_parameters_by_device_paramset_key
|
|
391
|
+
self._un_ignore_prefix_cache: dict[TModelName, str | None] = {}
|
|
392
|
+
# Cache for resolving matching prefix key in _relevant_master_paramsets_by_device
|
|
393
|
+
self._relevant_prefix_cache: dict[TModelName, str | None] = {}
|
|
394
|
+
# Per-instance memoization for repeated queries.
|
|
395
|
+
# Key: (model_l, channel_no, paramset_key, parameter)
|
|
396
|
+
self._param_ignored_cache: dict[tuple[TModelName, TChannelNo, ParamsetKey, TParameterName], bool] = {}
|
|
397
|
+
# Key: (model_l, channel_no, paramset_key, parameter, custom_only)
|
|
398
|
+
self._param_un_ignored_cache: dict[tuple[TModelName, TChannelNo, ParamsetKey, TParameterName, bool], bool] = {}
|
|
399
|
+
self._init()
|
|
400
|
+
|
|
401
|
+
def _init(self) -> None:
|
|
402
|
+
"""Process cache initialisation."""
|
|
403
|
+
for (
|
|
404
|
+
model,
|
|
405
|
+
channels_parameter,
|
|
406
|
+
) in _RELEVANT_MASTER_PARAMSETS_BY_DEVICE.items():
|
|
407
|
+
model_l = model.lower()
|
|
408
|
+
channel_nos, parameters = channels_parameter
|
|
409
|
+
|
|
410
|
+
def _add_channel(dt_l: str, params: frozenset[Parameter], ch_no: TChannelNo) -> None:
|
|
411
|
+
self._relevant_master_paramsets_by_device[dt_l].add(ch_no)
|
|
412
|
+
self._un_ignore_parameters_by_device_paramset_key[dt_l][ch_no][ParamsetKey.MASTER].update(params)
|
|
413
|
+
|
|
414
|
+
if channel_nos:
|
|
415
|
+
for channel_no in channel_nos:
|
|
416
|
+
_add_channel(dt_l=model_l, params=parameters, ch_no=channel_no)
|
|
417
|
+
else:
|
|
418
|
+
_add_channel(dt_l=model_l, params=parameters, ch_no=None)
|
|
419
|
+
|
|
420
|
+
self._process_un_ignore_entries(lines=self._raw_un_ignores)
|
|
421
|
+
|
|
422
|
+
def _resolve_prefix_key(
|
|
423
|
+
self, *, model_l: TModelName, mapping: Mapping[str, object], cache_dict: dict[TModelName, str | None]
|
|
424
|
+
) -> str | None:
|
|
425
|
+
"""Resolve and memoize the first key in mapping that prefixes model_l."""
|
|
426
|
+
if (dt_short_key := cache_dict.get(model_l)) is None and model_l not in cache_dict:
|
|
427
|
+
dt_short_key = next((k for k in mapping if model_l.startswith(k)), None)
|
|
428
|
+
cache_dict[model_l] = dt_short_key
|
|
429
|
+
return dt_short_key
|
|
430
|
+
|
|
431
|
+
def model_is_ignored(self, *, model: TModelName) -> bool:
|
|
432
|
+
"""Check if a model should be ignored for custom data points."""
|
|
433
|
+
return element_matches_key(
|
|
434
|
+
search_elements=self._ignore_custom_device_definition_models,
|
|
435
|
+
compare_with=model,
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
def parameter_is_ignored(
|
|
439
|
+
self,
|
|
440
|
+
*,
|
|
441
|
+
channel: hmd.Channel,
|
|
442
|
+
paramset_key: ParamsetKey,
|
|
443
|
+
parameter: TParameterName,
|
|
444
|
+
) -> bool:
|
|
445
|
+
"""Check if parameter can be ignored."""
|
|
446
|
+
model_l = channel.device.model.lower()
|
|
447
|
+
# Fast path via per-instance memoization
|
|
448
|
+
if (cache_key := (model_l, channel.no, paramset_key, parameter)) in self._param_ignored_cache:
|
|
449
|
+
return self._param_ignored_cache[cache_key]
|
|
450
|
+
|
|
451
|
+
if paramset_key == ParamsetKey.VALUES:
|
|
452
|
+
if self.parameter_is_un_ignored(
|
|
453
|
+
channel=channel,
|
|
454
|
+
paramset_key=paramset_key,
|
|
455
|
+
parameter=parameter,
|
|
456
|
+
):
|
|
457
|
+
return False
|
|
458
|
+
|
|
459
|
+
if (
|
|
460
|
+
(
|
|
461
|
+
(parameter in _IGNORED_PARAMETERS or _parameter_is_wildcard_ignored(parameter=parameter))
|
|
462
|
+
and parameter not in self._required_parameters
|
|
463
|
+
)
|
|
464
|
+
or hms.element_matches_key(
|
|
465
|
+
search_elements=_IGNORE_PARAMETERS_BY_DEVICE_LOWER.get(parameter, []),
|
|
466
|
+
compare_with=model_l,
|
|
467
|
+
)
|
|
468
|
+
or hms.element_matches_key(
|
|
469
|
+
search_elements=_IGNORE_DEVICES_FOR_DATA_POINT_EVENTS_LOWER,
|
|
470
|
+
compare_with=parameter,
|
|
471
|
+
search_key=model_l,
|
|
472
|
+
do_right_wildcard_search=False,
|
|
473
|
+
)
|
|
474
|
+
):
|
|
475
|
+
return True
|
|
476
|
+
|
|
477
|
+
if (
|
|
478
|
+
accept_channel := _ACCEPT_PARAMETER_ONLY_ON_CHANNEL.get(parameter)
|
|
479
|
+
) is not None and accept_channel != channel.no:
|
|
480
|
+
return True
|
|
481
|
+
if paramset_key == ParamsetKey.MASTER:
|
|
482
|
+
if (
|
|
483
|
+
parameters := _RELEVANT_MASTER_PARAMSETS_BY_CHANNEL.get(channel.no)
|
|
484
|
+
) is not None and parameter in parameters:
|
|
485
|
+
return False
|
|
486
|
+
|
|
487
|
+
if parameter in self._custom_un_ignore_complex[model_l][channel.no][ParamsetKey.MASTER]:
|
|
488
|
+
return False
|
|
489
|
+
|
|
490
|
+
dt_short_key = self._resolve_prefix_key(
|
|
491
|
+
model_l=model_l,
|
|
492
|
+
mapping=self._un_ignore_parameters_by_device_paramset_key,
|
|
493
|
+
cache_dict=self._un_ignore_prefix_cache,
|
|
494
|
+
)
|
|
495
|
+
if (
|
|
496
|
+
dt_short_key is not None
|
|
497
|
+
and parameter
|
|
498
|
+
not in self._un_ignore_parameters_by_device_paramset_key[dt_short_key][channel.no][ParamsetKey.MASTER]
|
|
499
|
+
):
|
|
500
|
+
result = True
|
|
501
|
+
self._param_ignored_cache[cache_key] = result
|
|
502
|
+
return result
|
|
503
|
+
|
|
504
|
+
result = False
|
|
505
|
+
self._param_ignored_cache[cache_key] = result
|
|
506
|
+
return result
|
|
507
|
+
|
|
508
|
+
def _parameter_is_un_ignored(
|
|
509
|
+
self,
|
|
510
|
+
*,
|
|
511
|
+
channel: hmd.Channel,
|
|
512
|
+
paramset_key: ParamsetKey,
|
|
513
|
+
parameter: TParameterName,
|
|
514
|
+
custom_only: bool = False,
|
|
515
|
+
) -> bool:
|
|
516
|
+
"""
|
|
517
|
+
Return if parameter is on an un_ignore list.
|
|
518
|
+
|
|
519
|
+
This can be either be the users un_ignore file, or in the
|
|
520
|
+
predefined _UN_IGNORE_PARAMETERS_BY_DEVICE.
|
|
521
|
+
"""
|
|
522
|
+
|
|
523
|
+
# check if parameter is in custom_un_ignore
|
|
524
|
+
if paramset_key == ParamsetKey.VALUES and parameter in self._custom_un_ignore_values_parameters:
|
|
525
|
+
return True
|
|
526
|
+
|
|
527
|
+
# check if parameter is in custom_un_ignore with paramset_key
|
|
528
|
+
model_l = channel.device.model.lower()
|
|
529
|
+
# Fast path via per-instance memoization
|
|
530
|
+
if (cache_key := (model_l, channel.no, paramset_key, parameter, custom_only)) in self._param_un_ignored_cache:
|
|
531
|
+
return self._param_un_ignored_cache[cache_key]
|
|
532
|
+
|
|
533
|
+
search_matrix = (
|
|
534
|
+
(
|
|
535
|
+
(model_l, channel.no),
|
|
536
|
+
(model_l, UN_IGNORE_WILDCARD),
|
|
537
|
+
(UN_IGNORE_WILDCARD, channel.no),
|
|
538
|
+
(UN_IGNORE_WILDCARD, UN_IGNORE_WILDCARD),
|
|
539
|
+
)
|
|
540
|
+
if paramset_key == ParamsetKey.VALUES
|
|
541
|
+
else ((model_l, channel.no),)
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
for ml, cno in search_matrix:
|
|
545
|
+
if parameter in self._custom_un_ignore_complex[ml][cno][paramset_key]:
|
|
546
|
+
self._param_un_ignored_cache[cache_key] = True
|
|
547
|
+
return True
|
|
548
|
+
|
|
549
|
+
# check if parameter is in _UN_IGNORE_PARAMETERS_BY_DEVICE
|
|
550
|
+
result = bool(
|
|
551
|
+
not custom_only
|
|
552
|
+
and (un_ignore_parameters := _get_parameters_for_model_prefix(model_prefix=model_l))
|
|
553
|
+
and parameter in un_ignore_parameters
|
|
554
|
+
)
|
|
555
|
+
self._param_un_ignored_cache[cache_key] = result
|
|
556
|
+
return result
|
|
557
|
+
|
|
558
|
+
def parameter_is_un_ignored(
|
|
559
|
+
self,
|
|
560
|
+
*,
|
|
561
|
+
channel: hmd.Channel,
|
|
562
|
+
paramset_key: ParamsetKey,
|
|
563
|
+
parameter: TParameterName,
|
|
564
|
+
custom_only: bool = False,
|
|
565
|
+
) -> bool:
|
|
566
|
+
"""
|
|
567
|
+
Return if parameter is on an un_ignore list.
|
|
568
|
+
|
|
569
|
+
Additionally to _parameter_is_un_ignored these parameters
|
|
570
|
+
from _RELEVANT_MASTER_PARAMSETS_BY_DEVICE are un ignored.
|
|
571
|
+
"""
|
|
572
|
+
if not custom_only:
|
|
573
|
+
model_l = channel.device.model.lower()
|
|
574
|
+
dt_short_key = self._resolve_prefix_key(
|
|
575
|
+
model_l=model_l,
|
|
576
|
+
mapping=self._un_ignore_parameters_by_device_paramset_key,
|
|
577
|
+
cache_dict=self._un_ignore_prefix_cache,
|
|
578
|
+
)
|
|
579
|
+
|
|
580
|
+
# check if parameter is in _RELEVANT_MASTER_PARAMSETS_BY_DEVICE
|
|
581
|
+
if (
|
|
582
|
+
dt_short_key is not None
|
|
583
|
+
and parameter
|
|
584
|
+
in self._un_ignore_parameters_by_device_paramset_key[dt_short_key][channel.no][paramset_key]
|
|
585
|
+
):
|
|
586
|
+
return True
|
|
587
|
+
|
|
588
|
+
return self._parameter_is_un_ignored(
|
|
589
|
+
channel=channel,
|
|
590
|
+
paramset_key=paramset_key,
|
|
591
|
+
parameter=parameter,
|
|
592
|
+
custom_only=custom_only,
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
def should_skip_parameter(
|
|
596
|
+
self,
|
|
597
|
+
*,
|
|
598
|
+
channel: hmd.Channel,
|
|
599
|
+
paramset_key: ParamsetKey,
|
|
600
|
+
parameter: TParameterName,
|
|
601
|
+
parameter_is_un_ignored: bool,
|
|
602
|
+
) -> bool:
|
|
603
|
+
"""Determine if a parameter should be skipped."""
|
|
604
|
+
if self.parameter_is_ignored(
|
|
605
|
+
channel=channel,
|
|
606
|
+
paramset_key=paramset_key,
|
|
607
|
+
parameter=parameter,
|
|
608
|
+
):
|
|
609
|
+
_LOGGER.debug(
|
|
610
|
+
"SHOULD_SKIP_PARAMETER: Ignoring parameter: %s [%s]",
|
|
611
|
+
parameter,
|
|
612
|
+
channel.address,
|
|
613
|
+
)
|
|
614
|
+
return True
|
|
615
|
+
if (
|
|
616
|
+
paramset_key == ParamsetKey.MASTER
|
|
617
|
+
and (parameters := _RELEVANT_MASTER_PARAMSETS_BY_CHANNEL.get(channel.no)) is not None
|
|
618
|
+
and parameter in parameters
|
|
619
|
+
):
|
|
620
|
+
return False
|
|
621
|
+
|
|
622
|
+
return paramset_key == ParamsetKey.MASTER and not parameter_is_un_ignored
|
|
623
|
+
|
|
624
|
+
def _process_un_ignore_entries(self, *, lines: Iterable[str]) -> None:
|
|
625
|
+
"""Batch process un_ignore entries into cache."""
|
|
626
|
+
for line in lines:
|
|
627
|
+
# ignore empty line
|
|
628
|
+
if not line.strip():
|
|
629
|
+
continue
|
|
630
|
+
|
|
631
|
+
if line_details := self._get_un_ignore_line_details(line=line):
|
|
632
|
+
if isinstance(line_details, str):
|
|
633
|
+
self._custom_un_ignore_values_parameters.add(line_details)
|
|
634
|
+
else:
|
|
635
|
+
self._add_complex_un_ignore_entry(
|
|
636
|
+
model=line_details[0],
|
|
637
|
+
channel_no=line_details[1],
|
|
638
|
+
parameter=line_details[2],
|
|
639
|
+
paramset_key=line_details[3],
|
|
640
|
+
)
|
|
641
|
+
else:
|
|
642
|
+
_LOGGER.warning(
|
|
643
|
+
"PROCESS_UN_IGNORE_ENTRY failed: No supported format detected for un ignore line '%s'. ",
|
|
644
|
+
line,
|
|
645
|
+
)
|
|
646
|
+
|
|
647
|
+
def _get_un_ignore_line_details(
|
|
648
|
+
self, *, line: str
|
|
649
|
+
) -> tuple[TModelName, TUnIgnoreChannelNo, TParameterName, ParamsetKey] | str | None:
|
|
650
|
+
"""
|
|
651
|
+
Check the format of the line for un_ignore file.
|
|
652
|
+
|
|
653
|
+
model, channel_no, paramset_key, parameter
|
|
654
|
+
"""
|
|
655
|
+
|
|
656
|
+
model: TModelName | None = None
|
|
657
|
+
channel_no: TUnIgnoreChannelNo = None
|
|
658
|
+
paramset_key: ParamsetKey | None = None
|
|
659
|
+
parameter: TParameterName | None = None
|
|
660
|
+
|
|
661
|
+
if "@" in line:
|
|
662
|
+
data = line.split("@")
|
|
663
|
+
if len(data) == 2:
|
|
664
|
+
if ADDRESS_SEPARATOR in data[0]:
|
|
665
|
+
param_data = data[0].split(ADDRESS_SEPARATOR)
|
|
666
|
+
if len(param_data) == 2:
|
|
667
|
+
parameter = param_data[0]
|
|
668
|
+
paramset_key = ParamsetKey(param_data[1])
|
|
669
|
+
else:
|
|
670
|
+
_LOGGER.warning(
|
|
671
|
+
"GET_UN_IGNORE_LINE_DETAILS failed: Could not add line '%s' to un ignore cache. "
|
|
672
|
+
"Only one ':' expected in param_data",
|
|
673
|
+
line,
|
|
674
|
+
)
|
|
675
|
+
return None
|
|
676
|
+
else:
|
|
677
|
+
_LOGGER.warning(
|
|
678
|
+
"GET_UN_IGNORE_LINE_DETAILS failed: Could not add line '%s' to un ignore cache. "
|
|
679
|
+
"No ':' before '@'",
|
|
680
|
+
line,
|
|
681
|
+
)
|
|
682
|
+
return None
|
|
683
|
+
if ADDRESS_SEPARATOR in data[1]:
|
|
684
|
+
channel_data = data[1].split(ADDRESS_SEPARATOR)
|
|
685
|
+
if len(channel_data) == 2:
|
|
686
|
+
model = channel_data[0].lower()
|
|
687
|
+
_channel_no = channel_data[1]
|
|
688
|
+
channel_no = (
|
|
689
|
+
int(_channel_no) if _channel_no.isnumeric() else None if _channel_no == "" else _channel_no
|
|
690
|
+
)
|
|
691
|
+
else:
|
|
692
|
+
_LOGGER.warning(
|
|
693
|
+
"GET_UN_IGNORE_LINE_DETAILS failed: Could not add line '%s' to un ignore cache. "
|
|
694
|
+
"Only one ':' expected in channel_data",
|
|
695
|
+
line,
|
|
696
|
+
)
|
|
697
|
+
return None
|
|
698
|
+
else:
|
|
699
|
+
_LOGGER.warning(
|
|
700
|
+
"GET_UN_IGNORE_LINE_DETAILS failed: Could not add line '%s' to un ignore cache. "
|
|
701
|
+
"No ':' after '@'",
|
|
702
|
+
line,
|
|
703
|
+
)
|
|
704
|
+
return None
|
|
705
|
+
else:
|
|
706
|
+
_LOGGER.warning(
|
|
707
|
+
"GET_UN_IGNORE_LINE_DETAILS failed: Could not add line '%s' to un ignore cache. "
|
|
708
|
+
"Only one @ expected",
|
|
709
|
+
line,
|
|
710
|
+
)
|
|
711
|
+
return None
|
|
712
|
+
elif ADDRESS_SEPARATOR in line:
|
|
713
|
+
_LOGGER.warning(
|
|
714
|
+
"GET_UN_IGNORE_LINE_DETAILS failed: No supported format detected for un ignore line '%s'. ",
|
|
715
|
+
line,
|
|
716
|
+
)
|
|
717
|
+
return None
|
|
718
|
+
if model == UN_IGNORE_WILDCARD and channel_no == UN_IGNORE_WILDCARD and paramset_key == ParamsetKey.VALUES:
|
|
719
|
+
return parameter
|
|
720
|
+
if model is not None and parameter is not None and paramset_key is not None:
|
|
721
|
+
return model, channel_no, parameter, paramset_key
|
|
722
|
+
return line
|
|
723
|
+
|
|
724
|
+
def _add_complex_un_ignore_entry(
|
|
725
|
+
self,
|
|
726
|
+
*,
|
|
727
|
+
model: TModelName,
|
|
728
|
+
channel_no: TUnIgnoreChannelNo,
|
|
729
|
+
paramset_key: ParamsetKey,
|
|
730
|
+
parameter: TParameterName,
|
|
731
|
+
) -> None:
|
|
732
|
+
"""Add line to un ignore cache."""
|
|
733
|
+
if paramset_key == ParamsetKey.MASTER:
|
|
734
|
+
if isinstance(channel_no, int) or channel_no is None:
|
|
735
|
+
# add master channel for a device to fetch paramset descriptions
|
|
736
|
+
self._relevant_master_paramsets_by_device[model].add(channel_no)
|
|
737
|
+
else:
|
|
738
|
+
_LOGGER.warning(
|
|
739
|
+
"ADD_UN_IGNORE_ENTRY: channel_no '%s' must be an integer or None for paramset_key MASTER.",
|
|
740
|
+
channel_no,
|
|
741
|
+
)
|
|
742
|
+
return
|
|
743
|
+
if model == UN_IGNORE_WILDCARD:
|
|
744
|
+
_LOGGER.warning("ADD_UN_IGNORE_ENTRY: model must be set for paramset_key MASTER.")
|
|
745
|
+
return
|
|
746
|
+
|
|
747
|
+
self._custom_un_ignore_complex[model][channel_no][paramset_key].add(parameter)
|
|
748
|
+
|
|
749
|
+
def parameter_is_hidden(
|
|
750
|
+
self,
|
|
751
|
+
*,
|
|
752
|
+
channel: hmd.Channel,
|
|
753
|
+
paramset_key: ParamsetKey,
|
|
754
|
+
parameter: TParameterName,
|
|
755
|
+
) -> bool:
|
|
756
|
+
"""
|
|
757
|
+
Return if parameter should be hidden.
|
|
758
|
+
|
|
759
|
+
This is required to determine the data_point usage.
|
|
760
|
+
Return only hidden parameters, that are no defined in the un_ignore file.
|
|
761
|
+
"""
|
|
762
|
+
return parameter in _HIDDEN_PARAMETERS and not self._parameter_is_un_ignored(
|
|
763
|
+
channel=channel,
|
|
764
|
+
paramset_key=paramset_key,
|
|
765
|
+
parameter=parameter,
|
|
766
|
+
)
|
|
767
|
+
|
|
768
|
+
def is_relevant_paramset(
|
|
769
|
+
self,
|
|
770
|
+
*,
|
|
771
|
+
channel: hmd.Channel,
|
|
772
|
+
paramset_key: ParamsetKey,
|
|
773
|
+
) -> bool:
|
|
774
|
+
"""
|
|
775
|
+
Return if a paramset is relevant.
|
|
776
|
+
|
|
777
|
+
Required to load MASTER paramsets, which are not initialized by default.
|
|
778
|
+
"""
|
|
779
|
+
if paramset_key == ParamsetKey.VALUES:
|
|
780
|
+
return True
|
|
781
|
+
if paramset_key == ParamsetKey.MASTER:
|
|
782
|
+
if channel.no in _RELEVANT_MASTER_PARAMSETS_BY_CHANNEL:
|
|
783
|
+
return True
|
|
784
|
+
model_l = channel.device.model.lower()
|
|
785
|
+
# Resolve matching device type prefix once and cache per model
|
|
786
|
+
dt_short_key = self._resolve_prefix_key(
|
|
787
|
+
model_l=model_l,
|
|
788
|
+
mapping=self._relevant_master_paramsets_by_device,
|
|
789
|
+
cache_dict=self._relevant_prefix_cache,
|
|
790
|
+
)
|
|
791
|
+
if dt_short_key is not None and channel.no in self._relevant_master_paramsets_by_device[dt_short_key]:
|
|
792
|
+
return True
|
|
793
|
+
return False
|
|
794
|
+
|
|
795
|
+
|
|
796
|
+
def check_ignore_parameters_is_clean() -> bool:
|
|
797
|
+
"""Check if a required parameter is in ignored parameters."""
|
|
798
|
+
un_ignore_parameters_by_device: list[str] = []
|
|
799
|
+
for params in _UN_IGNORE_PARAMETERS_BY_DEVICE.values():
|
|
800
|
+
un_ignore_parameters_by_device.extend(params)
|
|
801
|
+
|
|
802
|
+
return (
|
|
803
|
+
len(
|
|
804
|
+
[
|
|
805
|
+
parameter
|
|
806
|
+
for parameter in get_required_parameters()
|
|
807
|
+
if (parameter in _IGNORED_PARAMETERS or _parameter_is_wildcard_ignored(parameter=parameter))
|
|
808
|
+
and parameter not in un_ignore_parameters_by_device
|
|
809
|
+
]
|
|
810
|
+
)
|
|
811
|
+
== 0
|
|
812
|
+
)
|