First minor since v0.50.0. Three threads landing together:
Device battery
- New bundled
device_batterywidget puts every battery-reporting device on a dashboard tile, sorted lowest-charge-first with critical/low/ok tone colouring and an optional days-to-empty estimate. - Persistent battery history: new
BatteryHistorySQLite store atdata/core/battery_history.dbwrites one row per heartbeat. Both MQTT (transport_wiring) and TRMNL HTTP-pull (trmnl_api) hook into it, so every device kind that reports battery accumulates history. /devices/batteryadmin page: per-device card with Chart.js trace tied to--t-accent(theme-reactive), stats table (samples, drain rate %/day, projected days-to-20%, days-to-0%), selectable window (1d / 3d / 7d / 14d / 30d / 90d). Status dot in the top-right corner conveys tone without a screaming rail.- Entry point wired through the existing top-bar battery indicator: single-device case becomes a direct link to the admin page, multi-device popover gains a "View charts & trends" footer link, mobile drawer's Batteries item is now a proper navigation link.
- Linear-regression projection with an 8-sample minimum. Flat/charging batteries report the slope alone (no divide-by-near-zero "ten years" surprises).
Rotation polish
- The projection bar at the top of each rotation card now respects conditions: bands show the step the picker would actually pick at that minute, not the time-naive cycle. Held slots get a diagonal stripe; walked-past slots get a small amber underline so "the cycle shifted here" is visible at a glance.
- Palette refresh: monochromatic ladder built from
--t-accentat five intensities (replacing the previous warm-everything terra / ochre / sage / rose / mauve set). Reads as a single coherent strip and tracks the active theme.
Condition decision logging
- Every rotation tick that evaluates step conditions, and every schedule fire involving conditions, now writes one structured
type="conditions"event row with per-condition observed values, pass/fail, the time-slot step vs the actually picked step, and any fail-open reason. - Surfaced as a new filter chip on
/events?type=conditionswith a structured "why" panel under each row (per-step ✓/✗ chips, the configured comparison, the observed HA state, the picked step). - Debounced: identical decisions in successive ticks emit only one row, so a healthy quiet rotation doesn't write 2880 rows a day.
Verified
- New
test_autonomous_tick_skips_conditioned_step_in_full_cyclepins the existing rotation-tick behaviour by replaying the "3D Print step gated onbinary_sensor.octoprint_printing == onwhile printer is off" scenario through a full 20-minute cycle and asserting the gated page never appears in the fired-pages list. - New
test_condition_decision_event_emitted_on_rotation_tickcovers the new logging path including the debounce. - 844 tests across the suite, all green.
Full Changelog: v0.50.3...v0.51.0