Skip to content

Commit

Permalink
Fix: Global sensor update rate and refpower calibration update (#383)
Browse files Browse the repository at this point in the history
* fix: Slowed global diag entity updates fixes [Rate-limit] Global visible device count [and others] #377

- Added _cached_ratelimit to BermudaGlobalEntity and applied to sensors
- Added ref_power_changed stamp and tied between ref_power changes and cache invalidation in entities, so calibration that causes increased distances show up immediately.
- allowed specifying custom interval to main _cached_rateliimit as well as global.
  • Loading branch information
agittins authored Nov 13, 2024
1 parent 8c50b57 commit 435c62a
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 6 deletions.
4 changes: 4 additions & 0 deletions custom_components/bermuda/bermuda_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def __init__(self, address, options) -> None:
self.prefname: str | None = None # "preferred" name - ideally local_name
self.address: str = address
self.ref_power: float = 0 # If non-zero, use in place of global ref_power.
self.ref_power_changed: float = 0 # Stamp for last change to ref_power, for cache zapping.
self.options = options
self.unique_id: str | None = None # mac address formatted.
self.address_type = BDADDR_TYPE_UNKNOWN
Expand Down Expand Up @@ -152,6 +153,9 @@ def set_ref_power(self, new_ref_power: float):
# gets applied.
# if nearest_scanner is not None:
self.apply_scanner_selection(nearest_scanner)
# Update the stamp so that the BermudaEntity can clear the cache and show the
# new measurement(s) immediately.
self.ref_power_changed = MONOTONIC_TIME()

def apply_scanner_selection(self, closest_scanner: BermudaDeviceScanner | None):
"""
Expand Down
26 changes: 24 additions & 2 deletions custom_components/bermuda/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,22 @@ def __init__(
self.bermuda_last_state: Any = 0
self.bermuda_last_stamp: float = 0

def _cached_ratelimit(self, statevalue: Any, fast_falling=True, fast_rising=False):
def _cached_ratelimit(self, statevalue: Any, fast_falling=True, fast_rising=False, interval=None):
"""
Uses the CONF_UPDATE_INTERVAL and other logic to return either the given statevalue
or an older, cached value. Helps to reduce excess sensor churn without compromising latency.
Only suitable for MEASUREMENTS, as numerical comparison is used.
Mostly suitable for MEASUREMENTS, but should work with strings, too.
If interval is specified the cache will use that (in seconds), otherwise the deafult is
the CONF_UPPDATE_INTERVAL (typically suitable for fast-close slow-far sensors)
"""
if interval is not None:
self.bermuda_update_interval = interval

nowstamp = MONOTONIC_TIME()
if (
(self.bermuda_last_stamp < nowstamp - self.bermuda_update_interval) # Cache is stale
or (self._device.ref_power_changed > nowstamp + 2) # ref power changed in last 2sec
or (self.bermuda_last_state is None) # Nothing compares to you.
or (statevalue is None) # or you.
or (fast_falling and statevalue < self.bermuda_last_state) # (like Distance)
Expand Down Expand Up @@ -165,6 +171,9 @@ def __init__(
super().__init__(coordinator)
self.coordinator = coordinator
self.config_entry = config_entry
self._cache_ratelimit_value = None
self._cache_ratelimit_stamp: float = 0
self._cache_ratelimit_interval = 60

@callback
def _handle_coordinator_update(self) -> None:
Expand All @@ -175,6 +184,19 @@ def _handle_coordinator_update(self) -> None:
"""
self.async_write_ha_state()

def _cached_ratelimit(self, statevalue: Any, interval:int|None=None):
"""A simple way to rate-limit sensor updates."""
if interval is not None:
self._cache_ratelimit_interval = interval
nowstamp = MONOTONIC_TIME()

if nowstamp > self._cache_ratelimit_stamp + self._cache_ratelimit_interval:
self._cache_ratelimit_stamp = nowstamp
self._cache_ratelimit_value = statevalue
return statevalue
else:
return self._cache_ratelimit_value

@property
def device_info(self):
"""Implementing this creates an entry in the device registry."""
Expand Down
8 changes: 4 additions & 4 deletions custom_components/bermuda/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ def unique_id(self):
@property
def native_value(self) -> int:
"""Gets the number of proxies we have access to."""
return len(self.coordinator.scanner_list)
return self._cached_ratelimit(len(self.coordinator.scanner_list)) or 0

@property
def name(self):
Expand All @@ -375,7 +375,7 @@ def unique_id(self):
@property
def native_value(self) -> int:
"""Gets the number of proxies we have access to."""
return self.coordinator.count_active_scanners()
return self._cached_ratelimit(self.coordinator.count_active_scanners()) or 0

@property
def name(self):
Expand All @@ -399,7 +399,7 @@ def unique_id(self):
@property
def native_value(self) -> int:
"""Gets the amount of devices we have seen."""
return len(self.coordinator.devices)
return self._cached_ratelimit(len(self.coordinator.devices)) or 0

@property
def name(self):
Expand All @@ -423,7 +423,7 @@ def unique_id(self):
@property
def native_value(self) -> int:
"""Gets the amount of devices that are active."""
return self.coordinator.count_active_devices()
return self._cached_ratelimit(self.coordinator.count_active_devices()) or 0

@property
def name(self):
Expand Down

0 comments on commit 435c62a

Please sign in to comment.