Help us shape the future of Go! Take the 2026 Go Developer Survey

electrafi

package
v0.40.22 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 2, 2026 License: MIT Imports: 15 Imported by: 0

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

View Source
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

func (i *Importer) Import(ctx context.Context, path string) (Result, error)

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

func (i *Importer) ImportReader(ctx context.Context, name string, src io.Reader) (Result, error)

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL