Skip to content

feat(notifications): tell the owner when a device does not follow FTW - #1972

Merged
frahlg merged 1 commit into
masterfrom
notify-not-following
Nov 5, 2026
Merged

frahlg merged 1 commit into
masterfrom
notify-not-following

Conversation

@frahlg

@frahlg frahlg commented Nov 5, 2026

Copy link
Copy Markdown
Member

Problem

xorath, 5 Nov 2026: a battery charged 2.3 kW while FTW asked it to discharge 812 W. The box's page said Not following, and no phone heard. Core works out control_feedback only when someone asks for /api/status, and no notification rule read it.

Change

  • A new catalogue-worded rule, control.not_following (go/internal/notifications/control.go):
    • On the HealthTick it reads the box's own answer, at most every 30 s. api.Server.ControlStates is the same builder /api/status serves, so a push never says what the box's page does not.
    • It fires once a device function has been not_following for the rule's threshold (default 600 s). It fires once until that device follows again, with the rule's cooldown on top (default 3600 s). The key is driver and kind.
    • The words come from the catalogue: "A device is not following FTW" / "{name} does not do what FTW asks. Open FTW to see why." {name} is the driver, as in driver.offline.
  • The rule is on by default for owners who have notifications on (Fredrik, 5 Nov 2026). A box whose stored rules predate the kind takes the default, as FTW Cloud's own notices do. GET /api/notifications/rules already offers missing kinds at their default, so the phones' switches show it on.
  • Settings → Notifications shows a threshold field for a stored control.not_following rule, as it does for the fuse rule.
  • The changeset is minor, for a visible new notification. Fredrik asked for it ("kör på").

#1864 also edits service.go, but in dispatch/deliver. This PR touches the rule lists, the struct and Subscribe, so the two do not overlap.

Pairs

srcfl/ftw-webapp#117 holds the catalogue and the switch's label, and goes first. srcfl/ftw-app follows, with the copy, the phone's words and the iPhone's choices.

Contract-pair: srcfl/ftw-webapp@66f0797

Evidence

  • make verify is clean on this machine.
  • New tests:
    • TestControlNotFollowingTellsTheOwnerOnceAfterTenMinutes: quiet for ten minutes, one message with the catalogue's words, nothing more while still not following, the cooldown holds after a brief recovery, a second message after the hour, and at most one read in 20 s of ticks.
    • TestControlNotFollowingStaysQuietWhenTurnedOff: the rule off, or notifications off.
    • TestControlNotFollowingIsARuleThePhonesCanSet.
    • TestControlStatesAreTheStatusDocumentsVerdicts: a battery charging 4.4 kW against a 5 kW discharge reads not_following (power_wrong_direction) in both.
  • TestRulesPutSeedsDefaultsOnFirstTouch now lists the kind among those on by default.
  • I rendered the settings tab locally in a browser. The rule shows Threshold, Priority and Cooldown, like fuse.over_limit.

🤖 Generated with Claude Code

xorath, 5 Nov 2026: a battery charged 2.3 kW while FTW asked it to
discharge 812 W. The box's page said "Not following"; no phone heard.

The new control.not_following rule reads the box's own answer to "Are
we in control?" (the control_feedback GET /api/status serves) at most
every 30 s on the HealthTick, and fires once a device function has been
not_following for ten minutes. It fires once until the device follows
again, with an hour's cooldown on top. On by default for owners with
notifications on (Fredrik, 5 Nov 2026); a box whose stored rules predate
the kind takes the default. The words come from the push catalogue.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-05T14:03:09.470832Z 4fc3194 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4fc3194c60

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +48 to +50
if s.cfg == nil || !s.cfg.Enabled || s.controlReader == nil || now.Sub(s.controlCheckedAt) < controlCheckEvery {
s.mu.Unlock()
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reset the control observation window when notifications are disabled

If notifications are disabled after this rule has observed a device not following, this early return preserves controlFirstAt throughout the disabled period. Service.Reload also leaves that map intact, so reenabling notifications can immediately count the entire disabled interval toward the ten-minute threshold—even if the device recovered and only just started failing again. This can send an alert without the required sustained observation window. Clear the control observation state when disabling notifications or reset it on reload, while retaining lastFired for cooldown enforcement.

Useful? React with 👍 / 👎.

@frahlg
frahlg merged commit f0a7202 into master Nov 5, 2026
23 of 24 checks passed
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.

1 participant