@@ -75,8 +75,9 @@ def __init__(
7575 self ._api_validator : APIValidator | None = None
7676 # Cache of validated hot water parameter id -> name.
7777 self ._hot_water_param_cache : dict [str , str ] = {}
78- # Track which hot water param groups have been validated.
78+ # Track which hot water param groups have been fully validated.
7979 self ._validated_hot_water_groups : set [str ] = set ()
80+ self ._validated_hot_water_parameters : dict [str , set [str ]] = {}
8081 # Locks to prevent concurrent validation of the same section/group.
8182 self ._section_locks : dict [str , asyncio .Lock ] = {}
8283 self ._hot_water_group_locks : dict [str , asyncio .Lock ] = {}
@@ -192,6 +193,11 @@ async def ensure_section_validated(
192193 raise BSBLANError (ErrorMsg .API_VALIDATOR_NOT_INITIALIZED )
193194
194195 api_validator = self ._api_validator
196+ if api_validator .is_section_validated (section ):
197+ return
198+
199+ def _requested_parameter_ids () -> set [str ]:
200+ return set (self ._get_section_params (section , include ))
195201
196202 async def _validate () -> None :
197203 logger .debug ("Lazy loading section: %s" , section )
@@ -205,7 +211,9 @@ async def _validate() -> None:
205211 await self ._run_once_locked (
206212 section ,
207213 self ._section_locks ,
208- lambda : api_validator .is_section_validated (section ),
214+ lambda : api_validator .are_parameters_validated (
215+ section , _requested_parameter_ids ()
216+ ),
209217 _validate ,
210218 )
211219
@@ -231,49 +239,38 @@ async def ensure_hot_water_group_validated(
231239 If provided, only these parameters will be validated.
232240
233241 """
242+ if group_name in self ._validated_hot_water_groups :
243+ return
234244
235- async def _validate () -> None :
236- if not self ._api_validator :
237- raise BSBLANError (ErrorMsg .API_VALIDATOR_NOT_INITIALIZED )
245+ def _group_params () -> dict [str , str ]:
246+ return self ._get_hot_water_group_params (param_filter , include )
238247
239- api_data = self ._get_api_data ()
240- if not api_data :
241- raise BSBLANError (ErrorMsg .API_DATA_NOT_INITIALIZED )
248+ def _requested_parameter_ids () -> set [str ]:
249+ return set (_group_params ())
242250
251+ async def _validate () -> None :
243252 logger .debug ("Lazy loading hot water group: %s" , group_name )
244-
245- # Get the base hot water params from API config
246- section_data = api_data .get ("hot_water" , {})
247-
248- # Filter to only the params in this group
249- group_params = {
250- param_id : param_name
251- for param_id , param_name in section_data .items ()
252- if param_id in param_filter
253+ group_params = _group_params ()
254+ uncovered_params = {
255+ param_id : name
256+ for param_id , name in group_params .items ()
257+ if param_id
258+ not in self ._validated_hot_water_parameters .get (group_name , set ())
253259 }
254260
255- # Apply include filter if specified - only validate requested params
256- if include is not None :
257- group_params = {
258- param_id : name
259- for param_id , name in group_params .items ()
260- if name in include
261- }
262-
263- if not group_params :
261+ if not uncovered_params :
264262 logger .debug ("No parameters to validate for group %s" , group_name )
265- self ._validated_hot_water_groups .add (group_name )
266263 return
267264
268265 # Request only these specific parameters from the device
269- params = self ._extract_params_summary (group_params )
266+ params = self ._extract_params_summary (uncovered_params )
270267 response_data = await self ._request (
271268 params = {"Parameter" : params ["string_par" ]}
272269 )
273270
274271 # Validate and filter out unsupported params
275272 params_to_remove = []
276- for param_id , param_name in group_params .items ():
273+ for param_id , param_name in uncovered_params .items ():
277274 if param_id not in response_data :
278275 logger .info (
279276 "Hot water param %s (%s) not found in response" ,
@@ -294,26 +291,87 @@ async def _validate() -> None:
294291 params_to_remove .append (param_id )
295292
296293 # Update the cache with validated params for this group
297- for param_id , param_name in group_params .items ():
294+ for param_id , param_name in uncovered_params .items ():
298295 if param_id not in params_to_remove :
299296 self ._hot_water_param_cache [param_id ] = param_name
300297
301- # Mark this group as validated
302- self ._validated_hot_water_groups .add (group_name )
298+ validated_ids = self ._validated_hot_water_parameters .setdefault (
299+ group_name , set ()
300+ )
301+ validated_ids .update (uncovered_params )
302+ if (
303+ self ._get_hot_water_group_params (param_filter , None ).keys ()
304+ <= validated_ids
305+ ):
306+ self ._validated_hot_water_groups .add (group_name )
307+ else :
308+ self ._validated_hot_water_groups .discard (group_name )
303309 logger .debug (
304310 "Validated hot water group '%s': %d params, removed %d unsupported" ,
305311 group_name ,
306- len (group_params ),
312+ len (uncovered_params ),
307313 len (params_to_remove ),
308314 )
309315
310316 await self ._run_once_locked (
311317 group_name ,
312318 self ._hot_water_group_locks ,
313- lambda : group_name in self ._validated_hot_water_groups ,
319+ lambda : self ._are_hot_water_parameters_validated (
320+ group_name , _requested_parameter_ids ()
321+ ),
314322 _validate ,
315323 )
316324
325+ def _get_section_params (
326+ self , section : SectionLiteral , include : list [str ] | None
327+ ) -> dict [str , str ]:
328+ """Get the configured parameters selected for a section request."""
329+ api_data = self ._get_api_data ()
330+ if not api_data :
331+ raise BSBLANError (ErrorMsg .API_DATA_NOT_INITIALIZED )
332+
333+ try :
334+ section_data = api_data [section ]
335+ except KeyError as err :
336+ msg = ErrorMsg .SECTION_NOT_FOUND .format (section )
337+ raise BSBLANError (msg ) from err
338+
339+ if include is None :
340+ return section_data
341+ return {
342+ param_id : name for param_id , name in section_data .items () if name in include
343+ }
344+
345+ def _get_hot_water_group_params (
346+ self , param_filter : set [str ], include : list [str ] | None
347+ ) -> dict [str , str ]:
348+ """Get configured parameters selected for a hot water group request."""
349+ if not self ._api_validator :
350+ raise BSBLANError (ErrorMsg .API_VALIDATOR_NOT_INITIALIZED )
351+
352+ api_data = self ._get_api_data ()
353+ if not api_data :
354+ raise BSBLANError (ErrorMsg .API_DATA_NOT_INITIALIZED )
355+
356+ group_params = {
357+ param_id : param_name
358+ for param_id , param_name in api_data .get ("hot_water" , {}).items ()
359+ if param_id in param_filter
360+ }
361+ if include is None :
362+ return group_params
363+ return {
364+ param_id : name for param_id , name in group_params .items () if name in include
365+ }
366+
367+ def _are_hot_water_parameters_validated (
368+ self , group_name : str , parameter_ids : set [str ]
369+ ) -> bool :
370+ """Check whether every requested hot water parameter ID was validated."""
371+ return group_name in self ._validated_hot_water_groups or parameter_ids .issubset (
372+ self ._validated_hot_water_parameters .get (group_name , set ())
373+ )
374+
317375 async def _validate_api_section (
318376 self , section : SectionLiteral , include : list [str ] | None = None
319377 ) -> dict [str , Any ] | None :
@@ -340,36 +398,31 @@ async def _validate_api_section(
340398 if not api_data :
341399 raise BSBLANError (ErrorMsg .API_DATA_NOT_INITIALIZED )
342400
343- # Assign to local variable after asserting it's not None
344401 api_validator = self ._api_validator
345-
346402 if api_validator .is_section_validated (section ):
347403 return None
348404
349- # Get parameters for the section
350- try :
351- section_data = api_data [ section ]
352- except KeyError as err :
353- msg = ErrorMsg . SECTION_NOT_FOUND . format (section )
354- raise BSBLANError ( msg ) from err
405+ section_data = self . _get_section_params ( section , include )
406+ uncovered_params = {
407+ param_id : name
408+ for param_id , name in section_data . items ()
409+ if not api_validator . are_parameters_validated (section , { param_id } )
410+ }
355411
356- # Filter to only included params if specified
357- if include is not None :
358- section_data = {
359- param_id : name
360- for param_id , name in section_data .items ()
361- if name in include
362- }
412+ if not uncovered_params :
413+ return None
363414
364415 try :
365416 # Request data from device for validation
366- params = self ._extract_params_summary (section_data )
417+ params = self ._extract_params_summary (uncovered_params )
367418 response_data = await self ._request (
368419 params = {"Parameter" : params ["string_par" ]}
369420 )
370421
371422 # Validate the section against actual device response
372- api_validator .validate_section (section , response_data , include )
423+ api_validator .validate_section (
424+ section , response_data , parameter_ids = set (uncovered_params )
425+ )
373426 # Update API data with validated configuration
374427 api_data [section ] = api_validator .get_section_params (section )
375428
0 commit comments