Skip to content

feat(cars): a car says when its cable went in and how far away it is - #1660

Merged
frahlg merged 4 commits into
masterfrom
claude/car-plugged-here
Oct 16, 2026
Merged

frahlg merged 4 commits into
masterfrom
claude/car-plugged-here

Conversation

@frahlg

@frahlg frahlg commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

Paired with srcfl/device-drivers#182, whose drivers send the fields this reads. #1650 is merged; this is rebased on master.

What

#1650 names a car at a plug-in by its own word (rule 2 in docs/cars.md) and reads plugged and at_site from a vehicle emit. No driver sent them; srcfl/device-drivers#182 makes four drivers send them. This PR adds what those drivers need from Core, and the fixes the review found.

New fields and host call

  • When the cable went in. A vehicle emit may carry plugged_since_ms, the time the source read the cable change. Rule 2 uses it in place of the time the box first saw the source say so. Without it, the box dates a change it saw itself only when the source's previous reading was less than a minute older. Further apart, the cable could have gone in at any time between, and the change does not count.
  • How far away the car is. site_distance_m: up to 300 m is here, over 5 km is elsewhere, and between is unknown. The site's position is the weather pin, set for forecasts at zoom 11.
  • host.site_location(). Returns the weather pin's lat, lon to a driver from the signed channel whose signed catalog entry lists vehicle. RuntimePolicy now carries the signed entry's capabilities. A bundled or local file gets nil, whatever its own DRIVER table says. The pin nobody moved (59.3293, 18.0686) and 0,0 are no position. The driver emits only the distance. The call follows the live config.

Identification

  • Rule 2 across two chargers. A cable time is no answer when another charger saw a plug-in within the same minute: the car could be on either. Test: the Volvo on the Easee, the Tesla 30 s later in the garage, the Tesla's report first. Before, the cars came out swapped; now both chargers ask.
  • Rule 2 per car. One source of a car that says it is elsewhere rules the car out.
  • An away car is no candidate. A car read elsewhere, or with its cable out, after the plug-in does not count for rule 3. With two cars and one away, the other is the only car. With every car away, the session is a visitor's.
  • A reading from before the plug-in says nothing about it. Rule 3's "not here" used the time a report arrived. A replayed old "cable out" made the only car a visitor for the whole session. The reading's own time counts now.
  • A tap over the box's visitor verdict is final. Before, the next newer "elsewhere" undid it.

Leaks and display

  • Home Assistant bridge. vehicle_json goes to the LAN broker; it now leaves out at_site, site_distance_m and plugged_since_ms.
  • Driver card. The box page showed a driver's fraction soc (0.14) as 0 %. It now shows the stored level ×100 and reads charge_limit as a fraction.

Docs: docs/writing-a-driver.md (host API table, "Which car is plugged in") and rules 2 and 3 in docs/cars.md.

Evidence

  • GOFLAGS=-p=2 make verify: clean on 250c787 (tests, vet, build).
  • internal/cars/carsays_test.go, each failing on the code before its fix: the car's own clock names it after a late report; two cars claiming is no answer; a car away is never named; with no place only the minute counts; a replayed old reading leaves the only car named; two plug-ins within a minute ask on both chargers; a tap over the visitor verdict stays; an away car is no candidate; a change between readings five minutes apart has no time.
  • internal/drivers/vehicle_plugged_test.go runs testdata/home_assistant_vehicle.lua, tesla_cloud.lua and teslamate_vehicle.lua (copies of feat(settings): add EV tab with charge plans (loadpoints) #182, as zaptec_cloud.lua is) in this host, through to telemetry.VehicleObservations. It checks the cable, the place, the cable time and that no coordinates reach the stored reading. lua_site_location_test.go: the call answers only a signed vehicle entry.
  • internal/ha: vehicle_json without the three keys. internal/api: the card shows 14 % and 80 % for both a fraction and a percent driver. internal/driverrepo: the policy carries the signed capabilities.

Not done

🤖 Generated with Claude Code

frahlg and others added 3 commits October 16, 2026 09:56
Vehicle drivers may add plugged_since_ms, the time their source saw the
cable take its state, and site_distance_m, the car's distance from the
site. Core reads up to 300 m as here and over 1 km as elsewhere, with no
answer between. Rule 2 uses the car's own time, so a source that reports
minutes after the plug-in, or the first report after a restart, still
names its car when the cable went in within the minute.

host.site_location() gives the weather pin to vehicle drivers only. The
pin nobody moved and 0,0 are no position: a car measured against central
Stockholm would turn the only car into a visitor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ar a visitor

Rule 3's "not here" took the time a report arrived. A source that
replays a reading from before the plug-in, or one that has not read the
car since it came home, would then say "cable out" or "elsewhere"
within the minute, and the only car would become a visitor for the whole
session. No driver emitted plugged before; the Home Assistant, Tesla,
TeslaMate and VAG drivers now do. The reading's own time counts instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Rule 2 gives no cable answer when another charger saw a plug-in within
  the same minute: the car could be on either. Rule 2 is per car: one
  source that says the car is elsewhere rules it out.
- A change the box saw between readings more than a minute apart has no
  time. A car read elsewhere after the plug-in is no rule 3 candidate.
- A tap over the box's own visitor verdict is final.
- site_distance_m reads as elsewhere only beyond 5 km: the site is a
  forecast pin.
- host.site_location answers only a driver whose signed catalog entry
  lists vehicle; RuntimePolicy carries the signed capabilities.
- Home Assistant's vehicle_json leaves out at_site, site_distance_m and
  plugged_since_ms.
- The driver card shows the stored level x100 and reads charge_limit as a
  fraction.
- testdata copies of the Home Assistant, TeslaMate and Fleet API drivers
  from srcfl/device-drivers#182 run end to end in this host.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@frahlg
frahlg force-pushed the claude/car-plugged-here branch from 87525b8 to 250c787 Compare October 16, 2026 08:13
@frahlg
frahlg marked this pull request as ready for review October 16, 2026 08:13
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 16, 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-16T08:18:12.534214Z 250c787 Draft marked ready
ℹ️ 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: 250c7879c3

ℹ️ 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 thread go/internal/cars/tick.go
Comment on lines +523 to 526
at := o.At
if at.Before(plugAt) || at.Sub(plugAt) > IdentifyWindow {
continue
}

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 Use the cable observation time for unplug evidence

When a source reports plugged=false without a fresh SoC, o.At remains the previous SoC measurement time even though PluggedSince records the new cable-state observation; VehicleObservation explicitly permits cable changes without a new level. If that SoC predates the current plug-in, this check discards the fresh unplug evidence, so a visitor can be assigned to the household's only known car (or an unavailable car remains a candidate). Evaluate the cable-out condition against o.PluggedSince, while retaining the appropriate source time for location evidence.

Useful? React with 👍 / 👎.

@frahlg
frahlg merged commit 4675e4d into master Oct 16, 2026
18 checks passed
frahlg added a commit that referenced this pull request Oct 16, 2026
…ged in (#1665)

Move the device-drivers pin from 76ddd58 to 5cc7bb1a (srcfl/device-drivers#182)
and bundle home_assistant_vehicle, tesla_cloud, teslamate_vehicle and
vag_vehicle beside tesla_vehicle. Between the two pins only those five
drivers changed. fleetping's shipped list is regenerated from the pin.

The end-to-end vehicle test now runs the bundled copies, and the testdata
copies from #1660 go.

Supersedes #1449.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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