Add Tesla Fleet API connector for vehicle data sync - #1210
Conversation
Adds `moltis-connector-tesla`, a read-only adapter that keeps a local copy
of Tesla vehicle data in the shared connector snapshot store.
A Tesla dataset takes one of two shapes, because the connector store's
snapshot semantics retire any item a run does not re-observe:
- State datasets key items by VIN, so each sync replaces the stored row and
the dataset always holds the latest reading.
- History datasets key items by `{vin}:{rfc3339}` and carry forward the most
recent `maxSamples` observations per vehicle. Older samples are left
unobserved, which is what retires them on commit.
Requesting vehicle data from a sleeping Tesla wakes it and drains the
battery, and Fleet API rate-limits the endpoint, so the connector reads each
vehicle's connectivity state first and skips any car that is not online
rather than waking it. A state dataset carries the previous reading forward
untouched; a history dataset records no sample. The first sync of an
unreachable vehicle still writes a row so the vehicle is visible with its
connectivity state and no data.
Moltis does not run the authorization-code flow: that redirect URI belongs
to a developer application registered against a domain the operator
controls, so the account stores a user-supplied refresh token instead. Fleet
API answers 403/412 for a client whose partner registration is incomplete,
which maps to a dedicated error naming that setup step rather than a generic
failure.
Supporting changes:
- `PASSWORD_FIELD` becomes `SECRET_FIELDS` and covers `refreshToken`, so the
token gets the same vault encryption CalDAV passwords already get. Editing
a connection without retyping the token preserves the stored one.
- `account_view` swaps an eleven-element tuple for a named struct; adding two
more positional fields to it was no longer readable.
- Vault credential handling moves from `connectors.rs` to
`connectors_secrets.rs`, and the Tesla gateway tests to
`connectors_tests/tesla.rs`, keeping both files under the size limit.
`SaveButton` defaults to `type="button"`, so the Tesla connection modal's save control never submitted its form: filling in every field and clicking save did nothing, sending no RPC and showing no error. Pass `type="submit"` as the other connector modals do, and use the same create-versus-save label convention so a new connection reads "Add connection". Also fixes `tesla_connector`'s `get_vehicle`, which asked the reader for a single row filtered by full-text VIN match and then checked the VIN exactly. Full-text matching is fuzzy, so another vehicle's reading could rank first and get filtered out, reporting a stored vehicle as absent. It now scans a bounded window of candidates and returns the newest reading whose VIN matches. Both were caught by the new E2E and tool tests added alongside them.
Greptile SummaryAdds a read-only Tesla Fleet API connector with encrypted refresh-token storage, state and history synchronization, agent querying, gateway lifecycle integration, web configuration, localization, tests, and setup documentation.
Confidence Score: 5/5The PR appears safe to merge with no concrete blocking or independently actionable non-blocking issue identified. The Tesla connector validates external inputs, bounds API responses, avoids waking unreachable vehicles, commits synchronization results atomically, preserves credentials during edits, and consistently integrates vault, gateway, tool, UI, test, and documentation paths.
|
| Filename | Overview |
|---|---|
| crates/connector-tesla/src/client.rs | Implements bounded, timed Fleet API requests and refresh-token authentication against fixed regional endpoints. |
| crates/connector-tesla/src/config.rs | Defines and validates Tesla account, region, dataset mode, VIN, endpoint, and retention configuration. |
| crates/connector-tesla/src/connector.rs | Implements atomic state and history synchronization, VIN selection, unreachable-vehicle carry-forward, and sample retention. |
| crates/connector-tesla/src/tool.rs | Adds read-only agent operations for listing and searching locally synchronized Tesla data. |
| crates/gateway/src/connectors.rs | Integrates Tesla account lifecycle, connection testing, dataset validation, and synchronization into the connector manager. |
| crates/gateway/src/connectors_secrets.rs | Extends structured connector-secret encryption and migration handling to Tesla refresh tokens. |
| crates/web/ui/src/pages/connectors/TeslaConnectionForm.tsx | Adds create and edit flows that preserve stored refresh tokens unless the user supplies a replacement. |
| crates/web/ui/src/pages/connectors/TeslaDatasetFields.tsx | Adds typed Tesla dataset controls and payload construction for mode, VINs, endpoints, and retention. |
| docs/src/tesla.md | Documents Fleet API application setup, authorization scopes, account configuration, data retention, and security considerations. |
Sequence Diagram
sequenceDiagram
participant UI as Connectors UI
participant GW as Gateway
participant Vault
participant Tesla as Tesla Fleet API
participant Store as Connector Store
participant Agent as Tesla Agent Tool
UI->>GW: Create account and dataset
GW->>Vault: Encrypt refresh token
GW->>Store: Persist account and dataset
GW->>Vault: Decrypt refresh token for sync
GW->>Tesla: Refresh OAuth access token
GW->>Tesla: List vehicles
loop Reachable selected vehicles
GW->>Tesla: Request configured vehicle data
end
GW->>Store: Commit state or retained history snapshot
Agent->>Store: Query datasets, vehicles, or readings
Store-->>Agent: Return synchronized local data
Reviews (1): Last reviewed commit: "fix(connectors): make the Tesla connecti..." | Re-trigger Greptile
Merging this PR will not alter performance
Comparing Footnotes
|
ffd7c4a to
9ea04e1
Compare
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
Summary
Adds
moltis-connector-tesla, a read-only adapter that keeps a local copy of Tesla vehicle data in the shared connector snapshot store. It never sends vehicle commands and never wakes a sleeping car.Two dataset shapes. The connector store retires any item a run does not re-observe, which is right for "current state" and wrong for history. So:
{vin}:{rfc3339}and carry forward the most recentmaxSamplesobservations per vehicle. Older samples are left unobserved, which is what retires them on commit.Sleeping vehicles are skipped, not woken. Requesting vehicle data from a sleeping Tesla wakes it and drains the battery, and Fleet API rate-limits the endpoint. The connector reads each vehicle's connectivity state first and skips any car that is not online: a state dataset carries the previous reading forward untouched (keeping its original
observedAt, since it is stale), a history dataset records no sample. The first sync of an unreachable vehicle still writes a row so the vehicle is visible with its connectivity state and no data.Credentials. Moltis does not run the authorization-code flow: that redirect URI belongs to a developer application registered against a domain the operator controls, so the account stores a user-supplied refresh token instead. Fleet API answers 403/412 for a client whose partner registration is incomplete, which maps to a dedicated error naming that setup step rather than a generic failure.
Supporting changes
PASSWORD_FIELDbecomesSECRET_FIELDSand coversrefreshToken, so the token gets the same vault encryption CalDAV passwords already get. Editing a connection without retyping the token preserves the stored one.account_viewswaps an eleven-element tuple for a named struct; adding two more positional fields to it was no longer readable.connectors.rstoconnectors_secrets.rs, and the Tesla gateway tests toconnectors_tests/tesla.rs, keeping both files under the 1500-line limit.tesla_connectoragent tool (trusted-only, read-only):list_datasets,list_vehicles,get_vehicle,search_readings.en,fr,zh,zh-TW;docs/src/tesla.mdplusconnectors.mdandSUMMARY.mdentries.Bugs caught by the new tests
charging_statenever parsed — Tesla returns it PascalCase ("Charging") inside an otherwise snake_case payload, so every value silently becameUnknown.SaveButtondefaults totype="button", so filling every field and clicking save sent no RPC and showed no error.get_vehiclecould report a stored car as absent — it asked for one row by fuzzy full-text VIN match, then checked the VIN exactly, so another vehicle could rank first and get filtered out.Validation
Completed
cargo fmt --all -- --checkcargo clippy -p moltis-connector-tesla --all-targets(clean)cargo clippy -p moltis-gateway --all-targets(clean)./scripts/check-file-size.shbash scripts/check-changelog-guard.sh origin/main HEADcargo test -p moltis-connector-tesla— 35 tests (26 unit + 9 sync integration)cargo test -p moltis-gateway connectors— 40 testscargo check -p moltis-connector-tesla --no-default-featuresand--features metricscd crates/web/ui && npx tsc --noEmit(0 errors)cd crates/web/ui && npx @biomejs/biome check src/pages/connectors/ src/types/connector.ts src/locales/(no new diagnostics)cd crates/web/ui && npm run buildcd crates/web/ui && npx playwright test e2e/specs/settings-connectors.spec.js— 10/10 passing, including 3 new Tesla specsRemaining
CLAUDE.md./scripts/local-validate.sh 1210— not run; the sandbox hit its disk allowance mid-session, so the release binary never built and E2E ran against a debug build insteadManual QA
Everything below needs a real Tesla developer application: registered at developer.tesla.com, public key hosted at
https://<your-domain>/.well-known/appspecific/com.tesla.3p.public-key.pem, partner registration completed, and a refresh token withopenid offline_access vehicle_device_data(plusvehicle_locationfor coordinates).statemode with the default endpoints. Sync twice while the car is awake and confirm the item count stays at one per vehicle andobservedAtadvances.historymode withmaxSamplesset low (say 3). Sync four or more times and confirm the item count caps at 3 per vehicle and the oldest reading drops out.asleepin the Tesla app, then run a sync. Confirm the car does not wake, the state dataset still shows the previous reading with its originalobservedAt, and the history dataset gains no sample. The gateway logs an info line naming the skipped count.tesla_connectorwithout contacting Tesla.refreshTokenis stored encrypted in<data_dir>/connectors.db; with the vault sealed, confirm syncing fails with an actionable "vault is sealed" message rather than a panic.