88 BSBLAN ,
99 BSBLANAuthError ,
1010 BSBLANConnectionError ,
11+ HotWaterConfig ,
12+ HotWaterSchedule ,
1113 HotWaterState ,
1214 Sensor ,
1315 State ,
1921from homeassistant .exceptions import ConfigEntryAuthFailed
2022from homeassistant .helpers .update_coordinator import DataUpdateCoordinator , UpdateFailed
2123
22- from .const import DOMAIN , LOGGER , SCAN_INTERVAL
24+ from .const import DOMAIN , LOGGER , SCAN_INTERVAL_FAST , SCAN_INTERVAL_SLOW
2325
2426
2527@dataclass
26- class BSBLanCoordinatorData :
27- """BSBLan data stored in the Home Assistant data object ."""
28+ class BSBLanFastData :
29+ """BSBLan fast-polling data."""
2830
2931 state : State
3032 sensor : Sensor
3133 dhw : HotWaterState
3234
3335
34- class BSBLanUpdateCoordinator (DataUpdateCoordinator [BSBLanCoordinatorData ]):
35- """The BSB-Lan update coordinator."""
36+ @dataclass
37+ class BSBLanSlowData :
38+ """BSBLan slow-polling data."""
39+
40+ dhw_config : HotWaterConfig | None = None
41+ dhw_schedule : HotWaterSchedule | None = None
42+
43+
44+ class BSBLanCoordinator [T ](DataUpdateCoordinator [T ]):
45+ """Base BSB-Lan coordinator."""
3646
3747 config_entry : ConfigEntry
3848
@@ -41,44 +51,122 @@ def __init__(
4151 hass : HomeAssistant ,
4252 config_entry : ConfigEntry ,
4353 client : BSBLAN ,
54+ name : str ,
55+ update_interval : timedelta ,
4456 ) -> None :
4557 """Initialize the BSB-Lan coordinator."""
4658 super ().__init__ (
4759 hass ,
4860 logger = LOGGER ,
4961 config_entry = config_entry ,
50- name = f" { DOMAIN } _ { config_entry . data [ CONF_HOST ] } " ,
51- update_interval = self . _get_update_interval () ,
62+ name = name ,
63+ update_interval = update_interval ,
5264 )
5365 self .client = client
5466
67+
68+ class BSBLanFastCoordinator (BSBLanCoordinator [BSBLanFastData ]):
69+ """The BSB-Lan fast update coordinator for frequently changing data."""
70+
71+ def __init__ (
72+ self ,
73+ hass : HomeAssistant ,
74+ config_entry : ConfigEntry ,
75+ client : BSBLAN ,
76+ ) -> None :
77+ """Initialize the BSB-Lan fast coordinator."""
78+ super ().__init__ (
79+ hass ,
80+ config_entry ,
81+ client ,
82+ name = f"{ DOMAIN } _fast_{ config_entry .data [CONF_HOST ]} " ,
83+ update_interval = self ._get_update_interval (),
84+ )
85+
5586 def _get_update_interval (self ) -> timedelta :
5687 """Get the update interval with a random offset.
5788
58- Use the default scan interval and add a random number of seconds to avoid timeouts when
89+ Add a random number of seconds to avoid timeouts when
5990 the BSB-Lan device is already/still busy retrieving data,
6091 e.g. for MQTT or internal logging.
6192 """
62- return SCAN_INTERVAL + timedelta (seconds = randint (1 , 8 ))
93+ return SCAN_INTERVAL_FAST + timedelta (seconds = randint (1 , 8 ))
6394
64- async def _async_update_data (self ) -> BSBLanCoordinatorData :
65- """Get state and sensor data from BSB-Lan device."""
95+ async def _async_update_data (self ) -> BSBLanFastData :
96+ """Fetch fast-changing data from the BSB-Lan device."""
6697 try :
67- # initialize the client, this is cached and will only be called once
68- await self .client .initialize ()
69-
98+ # Client is already initialized in async_setup_entry
99+ # Fetch fast-changing data (state, sensor, DHW state)
70100 state = await self .client .state ()
71101 sensor = await self .client .sensor ()
72102 dhw = await self .client .hot_water_state ()
103+
73104 except BSBLANAuthError as err :
74105 raise ConfigEntryAuthFailed (
75106 "Authentication failed for BSB-Lan device"
76107 ) from err
77108 except BSBLANConnectionError as err :
78- host = self .config_entry .data [CONF_HOST ] if self . config_entry else "unknown"
109+ host = self .config_entry .data [CONF_HOST ]
79110 raise UpdateFailed (
80111 f"Error while establishing connection with BSB-Lan device at { host } "
81112 ) from err
82113
114+ # Update the interval with random jitter for next update
83115 self .update_interval = self ._get_update_interval ()
84- return BSBLanCoordinatorData (state = state , sensor = sensor , dhw = dhw )
116+
117+ return BSBLanFastData (
118+ state = state ,
119+ sensor = sensor ,
120+ dhw = dhw ,
121+ )
122+
123+
124+ class BSBLanSlowCoordinator (BSBLanCoordinator [BSBLanSlowData ]):
125+ """The BSB-Lan slow update coordinator for infrequently changing data."""
126+
127+ def __init__ (
128+ self ,
129+ hass : HomeAssistant ,
130+ config_entry : ConfigEntry ,
131+ client : BSBLAN ,
132+ ) -> None :
133+ """Initialize the BSB-Lan slow coordinator."""
134+ super ().__init__ (
135+ hass ,
136+ config_entry ,
137+ client ,
138+ name = f"{ DOMAIN } _slow_{ config_entry .data [CONF_HOST ]} " ,
139+ update_interval = SCAN_INTERVAL_SLOW ,
140+ )
141+
142+ async def _async_update_data (self ) -> BSBLanSlowData :
143+ """Fetch slow-changing data from the BSB-Lan device."""
144+ try :
145+ # Client is already initialized in async_setup_entry
146+ # Fetch slow-changing configuration data
147+ dhw_config = await self .client .hot_water_config ()
148+ dhw_schedule = await self .client .hot_water_schedule ()
149+
150+ except AttributeError :
151+ # Device does not support DHW functionality
152+ LOGGER .debug (
153+ "DHW (Domestic Hot Water) not available on device at %s" ,
154+ self .config_entry .data [CONF_HOST ],
155+ )
156+ return BSBLanSlowData ()
157+ except (BSBLANConnectionError , BSBLANAuthError ) as err :
158+ # If config update fails, keep existing data
159+ LOGGER .debug (
160+ "Failed to fetch DHW config from %s: %s" ,
161+ self .config_entry .data [CONF_HOST ],
162+ err ,
163+ )
164+ if self .data :
165+ return self .data
166+ # First fetch failed, return empty data
167+ return BSBLanSlowData ()
168+
169+ return BSBLanSlowData (
170+ dhw_config = dhw_config ,
171+ dhw_schedule = dhw_schedule ,
172+ )
0 commit comments