Skip to content

Commit 8f3033e

Browse files
committed
Move update frequency handling to package
1 parent 4fcdb26 commit 8f3033e

2 files changed

Lines changed: 35 additions & 105 deletions

File tree

custom_components/victron_mqtt/common.py

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import logging
2-
import time
2+
from typing import Any
33
from homeassistant.helpers.device_registry import DeviceInfo
44
from homeassistant.helpers.entity import Entity
55
from homeassistant.components.sensor.const import SensorDeviceClass, SensorStateClass
@@ -22,7 +22,6 @@ def __init__(
2222
metric: VictronVenusMetric,
2323
device_info: DeviceInfo,
2424
type: str,
25-
update_frequency_seconds: int,
2625
) -> None:
2726
"""Initialize the sensor based on detauls in the metric."""
2827
self._device = device
@@ -39,8 +38,6 @@ def __init__(
3938
self._attr_suggested_display_precision = metric.precision
4039
self._attr_translation_key = metric.generic_short_id.replace('{', '').replace('}', '') # same as in merge_topics.py
4140
self._attr_translation_placeholders = metric.key_values
42-
self._update_frequency_seconds = update_frequency_seconds
43-
self._last_update = None
4441
_LOGGER.info("%s %s added. Based on: %s", type, self, repr(metric))
4542

4643
def __repr__(self) -> str:
@@ -51,35 +48,14 @@ def __repr__(self) -> str:
5148
f"metric={self._metric.short_id}, "
5249
f"translation_key={self._attr_translation_key}, "
5350
f"translation_placeholders={self._attr_translation_placeholders}, "
54-
f"value={self._attr_native_value}, "
55-
f"update_frequency_seconds={self._update_frequency_seconds})"
51+
f"value={self._attr_native_value})"
5652
)
5753

58-
def _on_update(self, metric: VictronVenusMetric):
59-
self._update_internal(metric)
60-
61-
def update(self) -> bool:
62-
if not isinstance(self._metric.value, (float, int)):
63-
return False
64-
return self._update_internal(self._metric)
65-
66-
def _update_internal(self, metric: VictronVenusMetric) -> bool:
54+
def _on_update(self, metric: VictronVenusMetric, value: Any) -> None:
6755
# Might be that the entity was removed or not added yet
6856
if self.hass is None:
69-
return False
70-
# Only apply update frequency logic for float values
71-
if isinstance(metric.value, (float, int)):
72-
now = time.time()
73-
if self._last_update is not None:
74-
elapsed = now - self._last_update
75-
if elapsed < self._update_frequency_seconds:
76-
_LOGGER.debug(
77-
"Update for %s skipped due to frequency limit (%.2fs < %ds)",
78-
self._attr_unique_id, elapsed, self._update_frequency_seconds
79-
)
80-
return False
81-
self._last_update = now
82-
return self._on_update_task(metric)
57+
return
58+
self._on_update_task(value)
8359

8460
async def async_added_to_hass(self) -> None:
8561
"""Run when entity about to be added to hass."""

custom_components/victron_mqtt/hub.py

Lines changed: 30 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -77,33 +77,18 @@ def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
7777
serial=config.get(CONF_SERIAL, "noserial"),
7878
topic_prefix=config.get(CONF_ROOT_TOPIC_PREFIX) or None,
7979
operation_mode=operation_mode,
80-
device_type_exclude_filter=excluded_device_types
80+
device_type_exclude_filter=excluded_device_types,
81+
update_frequency_seconds=config.get(CONF_UPDATE_FREQUENCY_SECONDS, DEFAULT_UPDATE_FREQUENCY_SECONDS)
8182
)
8283
self._hub.on_new_metric = self.on_new_metric
83-
self.update_frequency_seconds = config.get(CONF_UPDATE_FREQUENCY_SECONDS, DEFAULT_UPDATE_FREQUENCY_SECONDS)
8484
self.add_entities_map: dict[MetricKind, AddEntitiesCallback] = {}
8585

86-
# Track all entities for periodic updates
87-
self.entities: list[VictronBaseEntity] = []
88-
self._update_task_unsub = None
89-
9086
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self.stop)
9187

9288
async def start(self):
93-
_LOGGER.info("Starting hub. Update frequency: %s seconds", self.update_frequency_seconds)
89+
_LOGGER.info("Starting hub.")
9490
try:
9591
await self._hub.connect()
96-
97-
if self.update_frequency_seconds > 0:
98-
# Start the periodic update task
99-
full_refresh_interval = self.update_frequency_seconds * 2
100-
self._update_task_unsub = async_track_time_interval(
101-
self.hass,
102-
self._periodic_update_task,
103-
timedelta(seconds=full_refresh_interval)
104-
)
105-
_LOGGER.info("Started periodic update task to run every %d seconds(s)", full_refresh_interval)
106-
10792
except CannotConnectError as connect_error:
10893
_LOGGER.error("Cannot connect to the hub")
10994
raise ConfigEntryNotReady("Device is offline") from connect_error
@@ -125,9 +110,6 @@ def on_new_metric(self, hub: VictronVenusHub, device: VictronVenusDevice, metric
125110
device_info = Hub._map_device_info(device)
126111
entity = self.creatre_entity(device, metric, device_info)
127112

128-
# Track the entity for periodic updates
129-
self.entities.append(entity)
130-
131113
# Add entity dynamically to the platform
132114
self.add_entities_map[metric.metric_kind]([entity])
133115

@@ -147,37 +129,19 @@ def register_add_entities_callback(self, async_add_entities: AddEntitiesCallback
147129
_LOGGER.info("Registering AddEntitiesCallback. kind: %s, AddEntitiesCallback: %s", kind, async_add_entities)
148130
self.add_entities_map[kind] = async_add_entities
149131

150-
async def _periodic_update_task(self, now=None):
151-
"""Periodic task to update all tracked entities with their latest metric values."""
152-
if not self.entities:
153-
_LOGGER.debug("No entities to update")
154-
return
155-
156-
_LOGGER.debug("Running periodic update task for %d entities", len(self.entities))
157-
158-
updated_count = 0
159-
160-
for entity in self.entities:
161-
if entity.update():
162-
#_LOGGER.info("Entity updated: %s", entity)
163-
updated_count += 1
164-
165-
if updated_count > 0:
166-
_LOGGER.debug("Periodic update completed: %d entities updated", updated_count)
167-
168132
def creatre_entity(self, device: VictronVenusDevice, metric: VictronVenusMetric, info: DeviceInfo) -> VictronBaseEntity:
169133
"""Create a VictronBaseEntity from a device and metric."""
170134
if metric.metric_kind == MetricKind.SENSOR:
171-
return VictronSensor(device, metric, info, self.update_frequency_seconds)
135+
return VictronSensor(device, metric, info)
172136
elif metric.metric_kind == MetricKind.BINARY_SENSOR:
173-
return VictronBinarySensor(device, metric, info, self.update_frequency_seconds)
137+
return VictronBinarySensor(device, metric, info)
174138
assert isinstance(metric, VictronVenusWritableMetric), f"Expected metric to be a VictronVenusWritableMetric. Got {type(metric)}"
175139
if metric.metric_kind == MetricKind.SWITCH:
176-
return VictronSwitch(device, metric, info, self.update_frequency_seconds)
140+
return VictronSwitch(device, metric, info)
177141
elif metric.metric_kind == MetricKind.NUMBER:
178-
return VictronNumber(device, metric, info, self.update_frequency_seconds)
142+
return VictronNumber(device, metric, info)
179143
elif metric.metric_kind == MetricKind.SELECT:
180-
return VictronSelect(device, metric, info, self.update_frequency_seconds)
144+
return VictronSelect(device, metric, info)
181145
else:
182146
raise ValueError(f"Unsupported metric kind: {metric.metric_kind}")
183147

@@ -199,22 +163,20 @@ def __init__(
199163
device: VictronVenusDevice,
200164
metric: VictronVenusMetric,
201165
device_info: DeviceInfo,
202-
update_frequency_seconds: int,
203166
) -> None:
204167
"""Initialize the sensor based on detauls in the metric."""
205168
self._attr_native_value = metric.value
206-
super().__init__(device, metric, device_info, "sensor", update_frequency_seconds)
169+
super().__init__(device, metric, device_info, "sensor")
207170

208171
def __repr__(self) -> str:
209172
"""Return a string representation of the sensor."""
210173
return f"VictronSensor({super().__repr__()})"
211174

212-
def _on_update_task(self, metric: VictronVenusMetric) -> bool:
213-
if self._attr_native_value == metric.value:
214-
return False
215-
self._attr_native_value = metric.value
175+
def _on_update_task(self, value: Any) -> None:
176+
if self._attr_native_value == value:
177+
return
178+
self._attr_native_value = value
216179
self.schedule_update_ha_state()
217-
return True
218180

219181
class VictronSwitch(VictronBaseEntity, SwitchEntity):
220182
"""Implementation of a Victron Venus multiple state select using SelectEntity."""
@@ -224,25 +186,23 @@ def __init__(
224186
device: VictronVenusDevice,
225187
writable_metric: VictronVenusWritableMetric,
226188
device_info: DeviceInfo,
227-
update_frequency_seconds: int,
228189
) -> None:
229190
"""Initialize the switch."""
230191
self._attr_is_on = writable_metric.value == GenericOnOff.On
231-
super().__init__(device, writable_metric, device_info, "switch", update_frequency_seconds)
192+
super().__init__(device, writable_metric, device_info, "switch")
232193

233194
def __repr__(self) -> str:
234195
"""Return a string representation of the sensor."""
235196
return (
236197
f"VictronSwitch({super().__repr__()}, is_on={self._attr_is_on})"
237198
)
238199

239-
def _on_update_task(self, metric: VictronVenusMetric) -> bool:
240-
new_val = metric.value == GenericOnOff.On
200+
def _on_update_task(self, value: Any) -> None:
201+
new_val = value == GenericOnOff.On
241202
if self._attr_is_on == new_val:
242-
return False
203+
return
243204
self._attr_is_on = new_val
244205
self.schedule_update_ha_state()
245-
return True
246206

247207
async def async_turn_on(self, **kwargs: Any) -> None:
248208
"""Turn the switch on."""
@@ -266,7 +226,6 @@ def __init__(
266226
device: VictronVenusDevice,
267227
writable_metric: VictronVenusWritableMetric,
268228
device_info: DeviceInfo,
269-
update_frequency_seconds: int,
270229
) -> None:
271230
"""Initialize the number entity."""
272231
self._attr_native_value = writable_metric.value
@@ -276,18 +235,17 @@ def __init__(
276235
self._attr_native_max_value = writable_metric.max_value
277236
if isinstance(writable_metric.step, int) or isinstance(writable_metric.step, float):
278237
self._attr_native_step = writable_metric.step
279-
super().__init__(device, writable_metric, device_info, "number", update_frequency_seconds)
238+
super().__init__(device, writable_metric, device_info, "number")
280239

281240
def __repr__(self) -> str:
282241
"""Return a string representation of the sensor."""
283242
return f"VictronNumber({super().__repr__()}, native_value={self._attr_native_value})"
284243

285-
def _on_update_task(self, metric: VictronVenusMetric) -> bool:
286-
if self._attr_native_value == metric.value:
287-
return False
288-
self._attr_native_value = metric.value
244+
def _on_update_task(self, value: Any) -> None:
245+
if self._attr_native_value == value:
246+
return
247+
self._attr_native_value = value
289248
self.schedule_update_ha_state()
290-
return True
291249

292250
@property
293251
def native_value(self):
@@ -310,22 +268,20 @@ def __init__(
310268
device: VictronVenusDevice,
311269
metric: VictronVenusMetric,
312270
device_info: DeviceInfo,
313-
update_frequency_seconds: int,
314271
) -> None:
315272
self._attr_is_on = bool(metric.value)
316-
super().__init__(device, metric, device_info, "binary_sensor", update_frequency_seconds)
273+
super().__init__(device, metric, device_info, "binary_sensor")
317274

318275
def __repr__(self) -> str:
319276
"""Return a string representation of the sensor."""
320277
return f"VictronBinarySensor({super().__repr__()}), is_on={self._attr_is_on})"
321278

322-
def _on_update_task(self, metric: VictronVenusMetric) -> bool:
323-
new_val = metric.value == GenericOnOff.On
279+
def _on_update_task(self, value: Any) -> None:
280+
new_val = value == GenericOnOff.On
324281
if self._attr_is_on == new_val:
325-
return False
282+
return
326283
self._attr_is_on = new_val
327284
self.schedule_update_ha_state()
328-
return True
329285

330286
@property
331287
def is_on(self) -> bool:
@@ -339,24 +295,22 @@ def __init__(
339295
device: VictronVenusDevice,
340296
writable_metric: VictronVenusWritableMetric,
341297
device_info: DeviceInfo,
342-
update_frequency_seconds: int,
343298
) -> None:
344299
"""Initialize the switch."""
345300
self._attr_options = writable_metric.enum_values
346301
self._attr_current_option = self._map_value_to_state(writable_metric.value)
347-
super().__init__(device, writable_metric, device_info, "select", update_frequency_seconds)
302+
super().__init__(device, writable_metric, device_info, "select")
348303

349304
def __repr__(self) -> str:
350305
"""Return a string representation of the sensor."""
351306
return f"VictronSelect({super().__repr__()}, current_option={self._attr_current_option}, options={self._attr_options})"
352307

353-
def _on_update_task(self, metric: VictronVenusMetric) -> bool:
354-
new_val = self._map_value_to_state(metric.value)
308+
def _on_update_task(self, value: Any) -> None:
309+
new_val = self._map_value_to_state(value)
355310
if self._attr_current_option == new_val:
356-
return False
311+
return
357312
self._attr_current_option = new_val
358313
self.schedule_update_ha_state()
359-
return True
360314

361315
async def async_select_option(self, option: str) -> None:
362316
"""Change the selected option."""

0 commit comments

Comments
 (0)