Skip to content

Report glucose sensor unavailable when data is stale (#53) - #60

Open
proscar87 wants to merge 1 commit into
PTST:mainfrom
proscar87:53-max-data-age-unavailable
Open

Report glucose sensor unavailable when data is stale (#53)#60
proscar87 wants to merge 1 commit into
PTST:mainfrom
proscar87:53-max-data-age-unavailable

Conversation

@proscar87

Copy link
Copy Markdown

Fixes #53

Problem

The LibreView API always returns the latest known reading — it never signals "no current data". GlucoseSensor in sensor.py doesn't override available, so the entity never becomes unavailable, even if the CGM has been disconnected for hours. In practice this means a glucose alert automation can silently stop working while the entity looks perfectly healthy — a trust failure in a health-related integration, not a cosmetic one.

Design (proposed by the maintainer in the issue thread)

"Would it satisfy your needs if you could configure a max age of sensor data before the integration reports unavailable? Let's say you configure it to 5 minutes, then whenever the latest reported data is more than 5 minutes old, then we would stop reporting data."

This PR implements exactly that.

Implementation

  • const.py: new CONF_MAX_DATA_AGE = "max_data_age" and DEFAULT_MAX_DATA_AGE = 0, added right next to CONF_SENSOR_DURATION following the same naming/placement convention.
  • config_flow.py: added CONF_MAX_DATA_AGE as a vol.Required(..., default=...) int field to both the initial setup step (async_step_options) and the options/reconfigure step (LibreViewOptionsFlowHandler.async_step_init) — mirroring CONF_SENSOR_DURATION in both defaulting logic and schema placement.
  • sensor.py: GlucoseSensor now takes a max_data_age constructor argument (minutes) and overrides available:
    • super().available is still respected (coordinator failure still means unavailable).
    • If max_data_age is 0, behaves exactly as before (always available) — see "Default" below.
    • Otherwise, compares gcm.factory_timestamp (UTC) against now; unavailable once the reading is older than max_data_age minutes.
    • A missing/unparsable timestamp does not crash the entity: _measurement_timestamp catches AttributeError/TypeError/ValueError/IndexError from the underlying GlucoseMeasurement.parse_dt and fails open (reports available, logs at debug level), since blowing up a health-data entity over a parsing edge case seemed worse than the alternative.
    • async_setup_entry reads the new option with entry.data.get(CONF_MAX_DATA_AGE, DEFAULT_MAX_DATA_AGE) rather than direct indexing (unlike the other options), because existing config entries created before this PR won't have this key until the user re-saves the options flow — direct indexing would KeyError on every existing installation at startup.
  • strings.json / translations/en.json: added the new field description next to sensor_duration in both the config and options steps. No other languages touched.

Default: disabled (0)

Libre sensors report roughly every 1–5 minutes depending on model. A too-short threshold would flip the entity unavailable on every normal sync gap; too-long and it stops protecting anything. Rather than guess a "safe" active default (there's a real range of opinions in the issue thread — 1x vs 3-5x the reporting interval), I defaulted to disabled (0) so existing installations see zero behavior change until a user explicitly opts in and picks a value that fits their sensor/automation setup. This seemed like the safer default for a health-adjacent integration where an unexpected "unavailable" could itself break an automation. Happy to change the default if the maintainer prefers an active one (e.g. 15 min) instead.

What I validated

This repo has no unit test suite (CI is Hassfest + HACS + lint only), so I wrote a standalone script that imports the real custom_components.libreview.sensor module and instantiates the real GlucoseSensor class against a synthetic-but-structurally-real LibreView.models.Connection/GlucoseMeasurement/Sensor object graph (same field layout as the real 0.3.0 package — I had to stub the LibreView package itself for local testing only, because the published 0.3.0 wheel is currently broken against modern dataclass-wizard releases due to a json_field import that no longer exists there; that's a pre-existing, unrelated upstream packaging issue, not something this PR touches). This is not a bundled test — it's a local-only validation script since the repo has no test infra.

Confirmed via git stash:

  • Without the fix: a reading timestamped 3 hours ago still reports available=True — reproducing the bug.
  • With the fix: the same stale reading with max_data_age=5 reports available=False; a 1-minute-old reading with the same setting stays available=True; max_data_age=0 (default) keeps stale data available=True (no behavior change); a malformed timestamp does not raise and reports available=True (fail-open).
  • Ran black, isort --profile black, and pylint (matching this repo's CI) — clean except for three pre-existing pylint warnings unrelated to this change (verified identical on main).

What I could not validate: real-world CGM disconnect behavior. I don't have a way to simulate a real Libre sensor going out of range for hours and observe what FactoryTimestamp LibreView's API actually returns in that situation (frozen at last-seen time vs. something else). The reporter of this issue has a real FreeStyle Libre and offered to test — that real-world confirmation would be valuable before merging, especially to sanity-check the timestamp field genuinely stops updating (rather than e.g. reflecting the LibreView cloud sync time) when the sensor drops off.

🤖 Generated with Claude Code

…TST#53)

The LibreView API always returns the most recent known reading, even
when the CGM has been disconnected for hours, so GlucoseSensor never
became unavailable and alert automations kept firing on stale data.

Adds a CONF_MAX_DATA_AGE option (minutes), following the existing
CONF_SENSOR_DURATION pattern in const.py/config_flow.py, and overrides
GlucoseSensor.available to compare the reading's factory_timestamp
against it. Defaults to 0 (disabled) to preserve current behaviour for
existing installations until they opt in.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missing sensor data represented as latest value in graphs

1 participant