Repository navigation
feat(notifications): tell the owner when a device does not follow FTW - #1972
Conversation
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>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 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".
| if s.cfg == nil || !s.cfg.Enabled || s.controlReader == nil || now.Sub(s.controlCheckedAt) < controlCheckEvery { | ||
| s.mu.Unlock() | ||
| return |
There was a problem hiding this comment.
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 👍 / 👎.
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_feedbackonly when someone asks for/api/status, and no notification rule read it.Change
control.not_following(go/internal/notifications/control.go):HealthTickit reads the box's own answer, at most every 30 s.api.Server.ControlStatesis the same builder/api/statusserves, so a push never says what the box's page does not.not_followingfor 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.{name}is the driver, as indriver.offline.GET /api/notifications/rulesalready offers missing kinds at their default, so the phones' switches show it on.control.not_followingrule, as it does for the fuse rule.minor, for a visible new notification. Fredrik asked for it ("kör på").#1864also editsservice.go, but indispatch/deliver. This PR touches the rule lists, the struct andSubscribe, 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 verifyis clean on this machine.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 readsnot_following(power_wrong_direction) in both.TestRulesPutSeedsDefaultsOnFirstTouchnow lists the kind among those on by default.fuse.over_limit.🤖 Generated with Claude Code