Repository navigation
feat(vehicles): a car says its cable is in and where it is - #182
Conversation
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. |
Vehicle emits gain optional plugged, plugged_since_ms, at_site and site_distance_m, so FTW can tell which car was plugged in (FTW docs/cars.md, rule 2). host.site_location() gives a vehicle driver the site's position; the driver emits only the distance. - home_assistant_vehicle 0.1.0: new. Reads the JSON a Home Assistant automation publishes over MQTT: level, limit, charging state, and the cable (binary_sensor.<car>_charger, last_changed) and place (tracker in zone home). Levels as fractions. - tesla_cloud 0.2.0: cable from conn_charge_cable; location_data in the same read when the site has a position, charge_state alone after a 403. - teslamate_vehicle 0.2.0: plugged_in, latitude and longitude. - vag_vehicle 0.3.0: the dataset's plug state and its point time. None adds a request or wakes a car. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
ad7b87a to
332750e
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ad7b87a37b
ℹ️ 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".
| local stale = now >= expires_ms | ||
| host.emit("vehicle", { | ||
| soc = last.soc, | ||
| charge_limit = last.charge_limit, |
There was a problem hiding this comment.
Emit the charge limit using the contracted field and units
For every Home Assistant message containing a charge limit, this emits the unrecognized charge_limit key as a 0–1 fraction. The vehicle host contract and all existing vehicle drivers use charge_limit_pct with a 0–100 value, so FTW can silently drop this field or interpret 80% as 0.8%. Emit the validated incoming percentage as charge_limit_pct instead.
AGENTS.md reference: AGENTS.md:L17-L23
Useful? React with 👍 / 👎.
| firmware_versions: "" | ||
| notes: "Read-only vehicle level, charge limit, charging state, charge cable and place from a JSON message a Home Assistant automation publishes. Tested with recorded messages from a Model Y on the Tesla Custom Integration; the cable and place fields have not run on a car. Does not wake or command the car." | ||
| min_driver_version: "0.1.0" | ||
| min_host_version: "2.0.0" |
There was a problem hiding this comment.
Add the new driver's upstream documentation
The new driver depends on Home Assistant and Tesla Custom Integration entity names, attributes, and timestamp semantics, but its manifest has no upstream_docs. This prevents the repository's watcher from detecting when those source contracts change or disappear; record the durable documentation URLs used to derive the automation and mappings.
AGENTS.md reference: AGENTS.md:L76-L82
Useful? React with 👍 / 👎.
From the review of #182: - tesla_cloud and vag_vehicle send no plugged_since_ms: they read the car minutes apart, so a read that shows the cable in says nothing about when it went in. FTW counts a cable time only within a minute of a plug-in. - home_assistant_vehicle emits every 5 s, and its header says to send plugged_since only when the integration reads the cable at least every 30 s: last_changed is when Home Assistant read the change. - teslamate_vehicle dates a plugged_in change only when a healthy message comes within 10 s. A retained value redelivered after a reconnect has none. With geofence set to the site's TeslaMate geofence, the geofence topic gives at_site instead of the distance from the weather pin. - tesla_cloud asks for location_data again a day after a 403. - FTW reads a vehicle soc of 1 as 100 %. All five vehicle drivers send a level of 1 % or less as a fraction and keep percents above that, which FTW before 0.131 still reads as percents. tesla_vehicle 0.2.6. - The spec, the guide and the manifests say the 30 s rule, the 5 km band, the signed catalog gate on host.site_location and the optional vehicle_location scope. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.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>
…1660) * feat(cars): a car says when its cable went in and how far away it is 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> * fix(cars): a reading from before the plug-in does not make the only car 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> * fix(cars): the review of the car's own word - 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> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…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>
What
A vehicle driver can now tell FTW that its car's cable is in and where the car is. FTW uses that to name the car at a plug-in: rule 2, "the car says it", in FTW's
docs/cars.md. Until now every vehicle driver sent only the level, the limit and the charging state.Paired with srcfl/ftw#1660, which reads these fields in Core and adds
host.site_location().The contract (
spec/host-api.md,docs/WRITING-A-DRIVER.md)All fields are optional on the
vehicleemit:pluggedtruein,falseout.plugged_since_mslast_changedis when Home Assistant read it.at_sitetrueat this site,falseelsewhere, from the source's own idea of home (a Home Assistant zone, a TeslaMate geofence).site_distance_mat_sitewins.host.site_location()returns the site'slat, lon, ornil. FTW gives it only to a driver from the signed channel whose signed catalog entry listsvehicle. The driver measures the distance and emits only that; the car's coordinates stay in the driver. Every driver checks that the call exists, so an older FTW or Blixt L1 gets no distance.soc: a level of 1 % or less goes as a fraction. Core reads a vehiclesocof 1 or less as a fraction, so a car at 1 % read as 100 %. Sending every level as a fraction would break FTW before 0.131 (the current stable, v0.130.4), which reads every value as a percent. A percent above 1 means the same to both, so the drivers keep percents and send only 1 % or less as a fraction:0.01is 1 % to a new FTW and 0.01 % to an old one.The drivers
None adds a request or wakes a car.
home_assistant_vehicle0.1.0, new. Reads one JSON message that a Home Assistant automation publishes over MQTT, and emits every 5 s. FTW holds no Home Assistant token. This brings a site-local driver into the catalog, with the same message, so a site can drop its local copy. Added:pluggedfrombinary_sensor.<car>_charger(device class plug), andat_sitefromdevice_tracker.<car>_location_trackerin zone home. The header carries the automation for the Tesla Custom Integration. It leavesplugged_sinceout, and says to add it only when the integration reads the cable at least every 30 s.tesla_cloud0.2.0. The cable fromconn_charge_cable(<invalid>is none), else the charging state. No cable time: the car is read minutes apart. With a site position, the samevehicle_dataread asks forlocation_datatoo. A token without the optionalvehicle_locationscope gets HTTP 403; the driver then readscharge_statealone and asks again a day later. The manifest anddevices.yamlname the scope.teslamate_vehicle0.2.0.plugged_in, dated by the box's clock only when ahealthymessage comes within 10 s, so a retained value redelivered after a reconnect stays undated. Withgeofenceset to this site's TeslaMate geofence, the geofence topic givesat_site; otherwiselatitudeandlongitudegive the distance.vag_vehicle0.3.0. The Data Act plug state, with no cable time: the portal writes every 15 minutes. A value other than connected or disconnected is unknown, not unplugged. The datasets carry no position.tesla_vehicle0.2.6. Only the 1 % fix.Evidence
make test-driver ID=…: home_assistant_vehicle 40, tesla_cloud 38, teslamate_vehicle 40, vag_vehicle 39, tesla_vehicle 41 passed.make check: 4983 passed, 936 skipped.home_assistant_vehiclereplays a message recorded from a Model Y on the Tesla Custom Integration on 15 October 2026 (VIN replaced), with the entity states behind it (binary_sensor.<car>_chargeron,charging_state: Stopped,conn_charge_cable: IEC).tesla_clouduses the same recorded cable values. Positions are public landmarks (Uppsala Cathedral, Stockholm Central).location_dataasked again after a day; 1 % sent as 0.01 by all five.Not done
home_assistant_vehicleis not in FTW's bundled list; adding it is FTW's choice.🤖 Generated with Claude Code