Skip to content

feat: system metrics in DIY, starting with battery level - #1467

Open
jawzf wants to merge 2 commits into
sameerasw:developfrom
jawzf:feat/009-value-battery-level
Open

jawzf wants to merge 2 commits into
sameerasw:developfrom
jawzf:feat/009-value-battery-level

Conversation

@jawzf

@jawzf jawzf commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Description

First step of System metrics, as discussed: DIY automations can now react when the battery level meets a numeric condition, as a Trigger (one-shot) or a State (entry and exit actions). Each metric gets its own tile in its domain section: Battery level sits in Battery & Power in both lists. Later metrics (volume, temperatures, Wi-Fi/BT battery) only add a source, an enum entry and a tile in their own section.

  • Per-domain, no new module. Battery level lives in PowerModule. The shared logic is a plain, pure-Kotlin SystemMetricTracker that each module holds, with unit tests.
  • Operators: above, at least, below, at most, equals, not equal, between, outside (triggers and states); rises above, falls below, changes by, increases by, decreases by (triggers only).
  • No loops or flapping: event-driven only (no polling), duplicate readings ignored, edge-triggered, a hysteresis margin (default 2%) so 79↔80 jitter can't flap, optional "stay true for" hold, cooldown for triggers (default 60 s, minimum 5 s), and no trigger fires on the first reading.
  • Loop breaker: more than 5 fires/entries in 60 s turns the automation off and posts a notification with a Re-enable button (non-exported receiver, immutable PendingIntent). The row says "Paused: kept triggering itself" until it's turned back on.
  • Every opened state closes: the value moving out of range, disabling, deleting, editing the condition, the module stopping and the loop breaker all run exit actions exactly once. Open states are persisted so they survive process death and reboots without running entry actions twice.
  • Replaces the old Battery level state tile (one Battery level tile per list now). Saved Battery level states keep loading and running unchanged; opening one shows the new tile with the same range, and saving converts it without re-running any actions. This also fixes a bug: disabling or deleting an active Battery level state never ran its exit actions.
  • ACTION_BATTERY_CHANGED filtering: only integer-percent changes reach the tracker, so temperature/voltage/status broadcasts (and charge-limit status flapping) are ignored.
  • Export/import: automations export as before. Open states and the paused flag live in a new diy_runtime_prefs file that's deliberately left out of export and backup (it describes this device's current state, like battery_history_points). After an import, changed states close and imported ones are re-checked against the current level.

Type of change

  • Bug fix
  • New feature
  • UI / visual update
  • Performance improvement
  • Refactoring / cleanup
  • Translations
  • Build / CI tooling

UI changes

A Battery level tile in Battery & Power in both the trigger and state lists (replacing the old state-only tile). Its sheet has a live "Now: 64%" reading, operator chips (state-only operators for states), a slider or range slider, and a collapsed Advanced section (stay true for, margin, and cooldown for triggers). Rows show a summary like "Battery level at most 20%".

0_state_list_battery_level

State list: one Battery level tile in Battery & Power

0b_trigger_list_battery_level

Trigger list

1_battery_level_sheet

Battery level sheet for a state (an old Battery level state opened, range kept)

1b_battery_level_trigger_sheet

Battery level sheet for a trigger

2_loop_breaker_notification

Loop breaker notification with Re-enable

3_rows_and_paused

Row summaries, and a row paused by the loop breaker

Checklist

Before submitting, please make sure:

  • The base branch is set to develop (not main).
  • The app builds cleanly locally with ./gradlew assembleDebug.
  • Unit tests pass with ./gradlew testDebugUnitTest.
  • You tested the changes on a device or emulator (including Shizuku or Root if applicable).
  • New preferences use SettingsRepository for keys and getter/setter methods. (No user settings. Runtime DIY state uses its own diy_runtime_prefs file next to diy_automations_prefs, so it stays out of export.)
  • New user-facing settings are registered in FeatureRegistry.kt for search indexing. (N/A, no new settings.)
  • New UI follows the design system in ui/core/ (RoundedCardContainer, IconToggleItem, etc.).
  • Strings are added to res/values/strings.xml and drawables use rounded variants (R.drawable.rounded_*).

Tested on a Pixel emulator with dumpsys battery: a simulated charge limiter flapping 79↔80% with status and temperature changes for a minute fired nothing; triggers fire once and re-arm past the margin; the loop breaker trips on the 6th fire, Re-enable restores it and it trips again; disabling or deleting an active state (new and old Battery level) runs its exit actions; an old Battery level state converts on save without running actions; after a reboot or force-stop, active states stay active without re-running entry actions, and ones that became false exit; export, edit, import closes and re-opens states correctly. Release APK +36 KB, no dependency changes.

🤖 Generated with Claude Code

Adds a Battery level trigger and state in Battery & Power, built on a
generic SystemMetric condition with numeric operators and a pure
SystemMetricTracker (de-dupe, edge triggering, hysteresis, hold,
cooldown, loop breaker, baseline) that guarantees every opened state
closes. Open states and loop-breaker pauses live in diy_runtime_prefs,
which is not exported. PowerModule forwards battery level only when the
percent changes. The old Battery level state tile is replaced; saved
BatteryLevel automations keep running and convert when edited, and
disabling or deleting an open one now runs its exit actions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jawzf

jawzf commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

I am working on an enhancement to this for future modules as well, which lets you decide the time period for an increases by or decreases by condition. For e.g. if battery reduces 5% within 30 min.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

1 participant