Fix false recharge-cycle detection from a single noisy battery reading
Build and push server image / build-and-push (push) Successful in 38s
Build and push server image / build-and-push (push) Successful in 38s
A report was flagged as "the battery got recharged" (resetting battery_history and stats_recharge_cycles, and re-arming the low-battery alert) whenever it came in >= RECHARGE_JUMP_PCT above the single immediately-previous report. That's exactly what a real recharge looks like, but it's also exactly what a normal reading looks like right after one noisy low report: e.g. 60, 59, 58, then a stray 53, then back to a perfectly normal 58 -- 58 >= 53+5 falsely read as a recharge. Now compared against the max of the last RECHARGE_LOOKBACK (3) reports instead of just the one before it, so a lone stray reading doesn't get to set the bar a normal reading then trips. A real recharge still needs to clear all of them, so genuine recharges are still caught immediately (verified: 18% -> 90% still triggers, history still resets). Paired with the firmware-side battery.c change (trimmed-mean ADC sampling) that reduces how often a stray reading like the 53 above happens in the first place.
This commit is contained in:
@@ -21,7 +21,16 @@ logger = logging.getLogger(__name__)
|
||||
# Battery-history / estimate tuning (see /frame/battery and battery_estimate_s).
|
||||
BATTERY_HISTORY_MAX = 500 # ~20 days at hourly reports
|
||||
BATTERY_LOG_MAX = 20000 # ~2 years at hourly reports -- cap on the battery_log table per frame
|
||||
RECHARGE_JUMP_PCT = 5 # a report this much above the previous one = battery was recharged
|
||||
RECHARGE_JUMP_PCT = 5 # a report this much above the recent baseline = battery was recharged
|
||||
# How many of the most recent reports make up that baseline. A lone noisy
|
||||
# reading (ADC/regulator glitch -- see firmware/main/battery.c) can still
|
||||
# dip or spike a single report; comparing against just the one immediately
|
||||
# previous report meant that a normal reading right after a noisy dip
|
||||
# looked like a 5%+ jump and falsely registered as a recharge. Comparing
|
||||
# against the max of the last few reports instead means an actual recharge
|
||||
# still needs to clear all of them, while a single stray low one doesn't
|
||||
# get to set the bar.
|
||||
RECHARGE_LOOKBACK = 3
|
||||
MIN_ESTIMATE_SPAN_S = 2 * 3600 # need at least this much observed time...
|
||||
MIN_ESTIMATE_DROP_PCT = 2 # ...and this much observed drop before estimating
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ from .common import (
|
||||
BATTERY_HISTORY_MAX,
|
||||
BATTERY_LOG_MAX,
|
||||
RECHARGE_JUMP_PCT,
|
||||
RECHARGE_LOOKBACK,
|
||||
immich_client_for,
|
||||
immich_creds,
|
||||
list_assets,
|
||||
@@ -202,7 +203,12 @@ def frame_battery(
|
||||
alert_frame_name = ""
|
||||
with frame_locked(db, frame.id) as locked:
|
||||
locked.stats_battery_reports += 1
|
||||
if locked.battery_history and body.percent >= locked.battery_history[-1][1] + RECHARGE_JUMP_PCT:
|
||||
# See RECHARGE_LOOKBACK: compared against the max of the last few
|
||||
# reports, not just the single previous one, so a lone noisy dip
|
||||
# can't make the next normal reading look like a recharge.
|
||||
recent = locked.battery_history[-RECHARGE_LOOKBACK:]
|
||||
recent_max = max((pct for _, pct in recent), default=None)
|
||||
if recent_max is not None and body.percent >= recent_max + RECHARGE_JUMP_PCT:
|
||||
# Percent jumped up meaningfully -- the battery was recharged
|
||||
# (or swapped). Start a fresh discharge cycle so runtime and
|
||||
# discharge-rate estimates never span a charge -- and let a
|
||||
|
||||
Reference in New Issue
Block a user