Skip to content

iPhone: the plan says where the battery ends up (plan pass 2) - #58

Closed
frahlg wants to merge 1 commit into
claude/ios-plan-whyfrom
claude/ios-plan-level
Closed

frahlg wants to merge 1 commit into
claude/ios-plan-whyfrom
claude/ios-plan-level

Conversation

@frahlg

@frahlg frahlg commented Oct 14, 2026

Copy link
Copy Markdown
Member

Plan pass, change 2 of 5: the plan says where the battery ends up. Stacked on change 1 (#57). The box half is srcfl/ftw#1630, merged and in FTW v0.143.0-beta.1: each plan.get slot now carries socPermille, the level the planner expects at the slot's end.

What changes

A finger on the strip or the dial reads the level with the rest of the quarter:

08:45 · 122 öre · ger ström · 66 %

The sentence on Now says the level only when it is news: a charge that ends full (98 % or more), or a discharge that ends as low as the battery gets in the hours ahead. Else the line stays as short as it was.

The story under the dial says it for every move:

Batteriet lagrar 22:30–24:00 för 59,0 öre, och når 94 %. … Batteriet ger ström 05:00–09:00, när elen kostar upp till 176 öre, och lämnar 66 %.

The lanes draw the level as a dashed line above the bars, with "batteriets nivå" in the legend.

Where the level comes from

The wire first: PlanSlot.socPermille from plan.get. A box older than v0.143.0-beta.1 sends none, so the app falls back to the planner's soc in /api/mpc/plan, which it reads already. With neither, nothing says a level; the app never guesses one. The registry owns no plan field names, so nothing changes in protocol/registry.yaml.

Evidence

  • swift test: FTWShared 217 and FTWKit 367 tests green. New: a finger reads where the battery is; the level is said where it matters and not elsewhere; the dial's middle says it; socPermille round-trips on the wire; the entry takes the level from the wire or else the planner.
  • UI tests on the iPhone SE (3rd generation), iOS 26.5: NowFitsTests 11 of 11, PlanStripTests 3 of 3, PlanDialTests 3 of 3.
  • Pictures below, from the demo in Swedish.

Pictures

The plan view, dark, light and the largest text: each move says where it leaves the battery.

The plan view with levels in the story

A finger across the strip on Now, then round the dial: the readout ends with the level.

Reading the level with a finger

The lanes, dark and light, with the level as a dashed line.

The lanes with the level line

🤖 Generated with Claude Code

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

chatgpt-codex-connector Bot commented Oct 14, 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-10-14T19:28:38.881813Z b72ec48 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: b72ec48a44

ℹ️ 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".

public var entry: PlanEntry {
PlanEntry.live(plan: plan.plan, points: loadpoints.points, windows: loadpoints.windows, prices: prices.prices, timeZone: loadpoints.zone.identifier, daylight: daylight)
PlanEntry.live(plan: plan.plan, points: loadpoints.points, windows: loadpoints.windows, prices: prices.prices, timeZone: loadpoints.zone.identifier, daylight: daylight,
levels: plan.forecast.compactMap { f in f.levelPct.map { (f.startMs, f.startMs + f.durationMs, $0) } })

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 Do not apply cached levels to a fresh plan

On boxes that predate socPermille, reopening the app within the cached forecast horizon can combine a newly received plan with stale battery levels: PlanModel initializes forecastStamp from the persisted forecast, while Now calls plan.activate() without requesting a new forecast. Because this line matches that cached document to the fresh plan solely by timestamps, Now can report percentages from an earlier plan after the mode or forecast has changed. Only use a current-session forecast that corresponds to this plan, or omit the fallback level when its provenance cannot be established.

Useful? React with 👍 / 👎.

frahlg added a commit that referenced this pull request Oct 14, 2026
…nst guessed, and the planning style as a scale (#58, #59, #60, #61)

One squash for four stacked PRs, changes 2–5 of the plan pass Fredrik approved on 14 Oct 2026 (change 1 was #57):
- #58: the planned battery level in the scrub, a dashed level line in the lanes and the dial's story (socPermille from srcfl/ftw#1630).
- #59: one honest saving under the dial, against no battery, only when the plan ends near its start level.
- #60: known against guessed: the dashed sun, hatched unpriced quarters, the style named when it is not balanced.
- #61: the two levers at the foot of the plan view, the planning style as a detented scale (Fredrik's note 21), one command on release with the receipt, and only moved quarters cross-faded.

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

frahlg commented Oct 14, 2026

Copy link
Copy Markdown
Member Author

Included in #61, merged as 36af0e6.

@frahlg frahlg closed this Oct 14, 2026
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