Documentation
¶
Overview ¶
Package electrafi imports TeslaFi / ElectraFi polling-dump CSV files into Rivolt's drives and charges stores.
ElectraFi exports one row per poll (~60s cadence) with 144 columns of TeslaFi-schema snapshot data. Rivian-origin rows leave many Tesla- specific columns empty; the importer uses only the subset reliably populated by the Rivian integration:
Date, battery_level, battery_range, odometer, latitude, longitude, speed, shift_state, charging_state, charger_power, charge_rate, charge_energy_added, charge_miles_added_rated, charge_limit_soc, driveNumber, chargeNumber
Drives and charges are derived by grouping rows by driveNumber and chargeNumber. Row-level noise (empty cells, header) is tolerated.
Index ¶
Constants ¶
const DefaultPackKWh = 131.0
DefaultPackKWh is the usable capacity we assume when the operator didn't pass a value. Matches the Rivian R1T/R1S Gen-2 Large pack (~131 kWh usable); adjust via Importer.PackKWh for Standard / Max.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Importer ¶
type Importer struct {
Drives *drives.Store
Charges *charges.Store
Samples *samples.Store
// VehicleID is the stable identifier assigned to every imported
// session. The CSVs omit VIN so we synthesize one from the file
// content if the operator didn't pass --vehicle-id.
VehicleID string
// PackKWh is the usable battery capacity, used to estimate
// charger_power and charge_energy_added when those columns are
// empty (ElectraFi stopped reporting them for some Rivian sessions
// starting ~late-March 2026). Zero means "use DefaultPackKWh".
PackKWh float64
// Location is the timezone the CSV timestamps were recorded in.
// ElectraFi/TeslaFi exports are local-without-zone; parsing them
// as UTC (the pre-v0.4.2 behavior) shifts every timestamp by the
// user's offset. Nil means UTC for backwards compatibility.
Location *time.Location
// SampleFrom is the sample retention cutoff (samples.RetentionCutoff); zero
// keeps every sample. Raw samples older than it are skipped and counted in
// Result.SamplesPastRetention; the drives and charges derived from the same
// rows are unaffected.
SampleFrom time.Time
// OnProgress, when non-nil, is called periodically during a
// single file import with a phase label and a row count so an
// HTTP handler can emit heartbeats to the client. The callback
// must not block (it's called from the row loop); the API
// handler uses it to flush NDJSON lines.
OnProgress func(phase string, rows int)
// LiveCovers, when set, replaces the store lookup that decides
// whether a live session already covers [start, end) ("drive" or
// "charge"). Tests use it; nil means ask the stores.
LiveCovers func(ctx context.Context, kind string, start, end time.Time) (bool, error)
}
Importer runs against the three stores. Samples is optional — when nil, only derived sessions are written.
func (*Importer) Import ¶
Import reads path and upserts the derived drives & charges. Safe to run multiple times over the same file — session IDs are deterministic.
func (*Importer) ImportReader ¶
ImportReader is the same as Import but reads from an io.Reader rather than a file path. Used by the HTTP upload endpoint to stream an uploaded CSV without staging it to disk. `name` is used only as the seed for the synthetic vehicle ID and for error messages; the caller can pass the original upload filename or any stable label.
type Result ¶
type Result struct {
File string
Rows int
Samples int
Drives int
Charges int
SkippedRows int
// SkippedLive counts derived drives and charges not written because
// the live recorder already has a session overlapping them (the
// recorder's row is authoritative; importing it again would double
// count the trip or the kWh).
SkippedLive int
// SamplesPastRetention counts raw samples left out because they are older
// than the install's sample retention (Importer.SampleFrom): importing them
// would recreate a dropped partition only for the next sweep to delete it.
// Drives and charges derived from those rows are still imported.
SamplesPastRetention int `json:"samples_past_retention,omitempty"`
}
Result summarizes what an import produced.