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

dcfcrates

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: 7 Imported by: 0

Documentation

Overview

Package dcfcrates owns the DCFC network table used by trip planning. It lives in its own package so the settings layer (which stores user-edited overrides) and the tripadvice layer (which consumes both) can share it without the settings -> tripadvice -> rivian -> settings import cycle that the older single-package layout produced.

Index

Constants

View Source
const (
	StateEstimated         = "estimated"
	StateNeedsConfirmation = "needs_confirmation"
	StateConfirmed         = "confirmed"
)

Verify states (charges.network_state).

View Source
const (
	SiteMemoryRadiusM = 100.0 // a verified charge of the user's own at the same site
	NearRadiusM       = 150.0 // one network this close: confident
	SiteRadiusM       = 250.0 // stations this close count as the site
)

Detection radii, metres.

View Source
const (
	KeyTesla            = "tesla"
	KeyTeslaDestination = "tesla_destination"
	KeyRivian           = "rivian"
	KeyRivianWaypoints  = "rivian_waypoints"
	KeyElectrifyAmerica = "electrify_america"
	KeyEVgo             = "evgo"
	KeyChargePoint      = "chargepoint"
	KeyIONNA            = "ionna"
	KeyShellRecharge    = "shell_recharge"
)

Network keys. "" is "no network recognised".

View Source
const ConfidentAt = 0.85

ConfidentAt is the confidence an estimate needs to stand without asking the user (state estimated rather than needs_confirmation).

View Source
const ObservedSource = "DCFC Tracker"

ObservedSource is the tracker's name, shown with every figure.

Variables

View Source
var Networks = []Network{
	{
		Slug:          "ea",
		DisplayName:   "Electrify America",
		MatchPatterns: []string{"electrify america"},
		GuestRate:     0.48,
		MemberRate:    f64(0.36),
		MemberPlan:    "Pass+ - $7/mo",
		RivianIDs:     []string{"10027"},
	},
	{
		Slug:        "tesla_sc",
		DisplayName: "Tesla Supercharger",

		MatchPatterns: []string{"[tesla]", "tesla supercharger", "supercharger"},
		GuestRate:     0.55,
		MemberRate:    f64(0.40),
		MemberPlan:    "Supercharging Membership - $12.99/mo",
		RivianIDs:     []string{"10050"},
	},
	{
		Slug:          "ran",
		DisplayName:   "Rivian Adventure Network",
		MatchPatterns: []string{"rivian adventure", "adventure network", "rivian ran"},

		GuestRate:           0.60,
		MemberRate:          f64(0.54),
		MemberPlan:          "Rivian owner discount",
		RivianIDs:           []string{"10001", "10002"},
		DefaultMemberActive: true,
	},
	{
		Slug:          "evgo",
		DisplayName:   "EVgo",
		MatchPatterns: []string{"evgo"},
		GuestRate:     0.42,
		MemberRate:    f64(0.34),
		MemberPlan:    "EVgo Plus - $6.99/mo",
		RivianIDs:     []string{"10031"},
	},
	{
		Slug:          "shell_recharge",
		DisplayName:   "Shell Recharge",
		MatchPatterns: []string{"shell recharge", "shell ev", "greenlots"},
		GuestRate:     0.43,
		MemberRate:    f64(0.40),
		MemberPlan:    "Recharge Rewards - free tier",
		RivianIDs:     []string{"10045"},
	},
	{
		Slug:          "chargepoint",
		DisplayName:   "ChargePoint",
		MatchPatterns: []string{"chargepoint"},
		GuestRate:     0.45,
		MemberRate:    nil,
		MemberPlan:    "",
		RivianIDs:     []string{"10025"},
	},
	{
		Slug:          "ionna",
		DisplayName:   "Ionna",
		MatchPatterns: []string{"ionna"},
		GuestRate:     0.40,
		MemberRate:    nil,
		MemberPlan:    "",
		RivianIDs:     []string{"10056"},
	},
	{
		Slug:          "flo",
		DisplayName:   "Flo",
		MatchPatterns: []string{"[flo]", "flo charging", " flo "},
		GuestRate:     0.35,
		MemberRate:    nil,
		MemberPlan:    "",
		RivianIDs:     []string{"10032", "10033"},
	},
	{
		Slug:          "ev_connect",
		DisplayName:   "EV Connect",
		MatchPatterns: []string{"ev connect", "evconnect"},
		GuestRate:     0.40,
		MemberRate:    nil,
		MemberPlan:    "",
		RivianIDs:     []string{"10030"},
	},
}

Networks is the lookup table, ordered most-specific-first so the substring matcher returns the right hit. Last entry (slug "unmatched") is the fallthrough - its patterns never match, but MatchNetwork returns it when nothing else does.

View Source
var UnmatchedNetwork = Network{
	Slug:        "unmatched",
	DisplayName: "Unknown DCFC",
	GuestRate:   0.46,
	MemberRate:  nil,
	MemberPlan:  "",
}

UnmatchedNetwork is the fallback when MatchNetwork can't pin a stop to a known operator. Pinned at "unknown" rather than guessing - every per-stop quote should be traceable to a row in this file.

Functions

func DisplayNetwork added in v0.33.0

func DisplayNetwork(known string) string

DisplayNetwork is a friendly canonical name, else the string itself.

func IsDCOnlyNode added in v0.33.0

func IsDCOnlyNode(poi *PoiLike) bool

IsDCOnlyNode reports a node an AC session can't be at.

func NetworkKey added in v0.33.0

func NetworkKey(parts ...string) string

NetworkKey reads a free-form network, operator, brand or name string.

func StationQuery added in v0.32.0

func StationQuery(nrelNetwork, stationName string) string

StationQuery turns an NREL station's network code and name into the text ResolveRate matches, so a recorded DCFC session priced from the station it happened at resolves the same rows a planned stop does.

Types

type Candidate added in v0.33.0

type Candidate struct {
	Name       string  // station name
	Network    string  // the archive's network field (NREL ev_network, renamed)
	Operator   string  // the archive's operator field (often a URL)
	DistM      float64 // from the charge position
	MaxPowerKW float64 // 0 when unknown
}

Candidate is an archive DC fast charging station near the charge.

type DetectInput added in v0.33.0

type DetectInput struct {
	RivianCharger bool        // Rivian reported isRivianCharger for this DC session
	Site          *SiteMemory // nil when none within SiteMemoryRadiusM
	Candidates    []Candidate // DC stations within SiteRadiusM, any order
	PeakKW        float64     // the session's peak power, 0 when unknown
}

DetectInput is everything Detect looks at.

type Detection added in v0.33.0

type Detection struct {
	Query      string
	Network    string // display name of the detected network ("" when none)
	State      string
	Confidence float64
	Evidence   json.RawMessage
}

Detection is Detect's verdict. Query is what to price with (ResolveRate(Query, user overrides)); empty Query means nothing to price: the charge stays unpriced and needs the user.

func Detect added in v0.33.0

func Detect(in DetectInput, pos string) Detection

Detect classifies a DC fast charge Rivian didn't bill. Signals, first decisive one wins: Rivian's isRivianCharger, the user's own verified charge at the same site, then archive stations by distance. At a site with more than one network the nearest is the estimate, a peak only one candidate could deliver can pick another, and either way the user is asked. pos labels where the charge position came from ("open" or "last") for the evidence.

type MembershipTerm added in v0.38.10

type MembershipTerm struct {
	Slug      string // the network's slug in Networks
	Plan      string
	Terms     string // as the operator states it
	SourceURL string // the operator page
	// ReadVia says how the text was read: "page" (the operator's page was fetched) or
	// "search snippet" (the page itself could not be fetched; the text is what the
	// operator's own page showed in search results).
	ReadVia string
	Checked string // YYYY-MM-DD
}

MembershipTerm is one plan of one network.

type Network

type Network struct {
	Slug        string
	DisplayName string
	// MatchPatterns are substrings checked (case-insensitive) against
	// the charger name returned by Rivian's planTrip waypoint.
	// Order matters: more specific patterns must come first. The
	// Tesla / Tesla Supercharger pair is the canonical example -
	// "tesla supercharger" appears before "tesla" so destination
	// chargers don't accidentally claim the Supercharger rate.
	MatchPatterns []string
	GuestRate     float64
	// MemberRate is the rate after paying for the network's
	// subscription. Nil = no membership tier exists (ChargePoint,
	// Francis, etc.) or the user is automatically a member (RAN -
	// every Rivolt user is by definition a Rivian owner).
	MemberRate *float64
	// MemberPlan is the human-readable plan name + monthly cost
	// surfaced in the cost-strip tooltip later. Empty when no plan.
	MemberPlan string
	// RivianIDs are the values passed to Rivian's planTrip2
	// networkPreferences[].networkId field. Some networks span
	// multiple operator IDs (RAN = Adventure + Wayfinder; FLO =
	// FLOC + FLOU for Canada/US), so this is a list. Mapping of the
	// network ids Rivian reports:
	//
	//   10001, 10002 → RAN (Adventure + Wayfinder, "RAN, RWN")
	//   10025         → ChargePoint
	//   10027         → Electrify America
	//   10030         → EV Connect
	//   10031         → EVgo
	//   10032, 10033 → FLO ("FLOC, FLOU" — Canada + US)
	//   10045         → Shell Recharge
	//   10050         → Tesla
	//   10056         → IONNA
	//
	// Networks outside that enum (Blink, bp pulse, Francis Energy)
	// leave RivianIDs empty — Rivian's planner doesn't recognise
	// them, so sending an arbitrary ID would risk being treated as
	// "exclude everything else." Custom user-added rows also stay
	// empty.
	RivianIDs []string
	// DefaultMemberActive is the initial MemberActive state for a
	// freshly-seeded ChargingNetwork row. True for networks every
	// Rivolt user qualifies for by definition (RAN — every user
	// owns a Rivian). False for opt-in subscriptions (EA Pass+,
	// Tesla SC Membership, EVgo Plus).
	DefaultMemberActive bool
}

Network is one DCFC operator with a guest rate and, where it exists, a member rate. The "is the user a member?" question is deliberately not modeled here - we show BOTH totals on the planner and let the user do the comparison. Keeps the data model trivial (no settings panel, no migration) and the savings line reads as a one-glance number.

Rates are USD/kWh for US deployments at time of writing (mid-2025). They will go stale - Rivolt's user-history pricing (deferred slice C) is the long-term fix; this table is the best-effort fallback when we have no per-user data for that network yet.

func MatchNetwork

func MatchNetwork(name string) Network

MatchNetwork returns the network entry whose first MatchPattern appears (case-insensitive) in the charger name. Falls back to UnmatchedNetwork - caller never has to nil-check.

The match is on the bare charger name as Rivian's planner returned it (e.g. "Electrify America - Pflugerville Crossing"). Whitespace around the user-visible name varies; lowercasing both sides plus padding the haystack with a space on each end lets " flo "-style boundary patterns work without a regex.

func (Network) MemberRateOrGuest

func (n Network) MemberRateOrGuest() float64

MemberRateOrGuest returns the network's MemberRate when set, otherwise its GuestRate. Used by the cost estimator's "with-all-memberships" total - networks that don't have a membership simply contribute their guest rate to both totals.

type NetworkOverride

type NetworkOverride struct {
	Slug         string
	Name         string
	GuestRate    float64
	MemberRate   float64 // 0 = no member tier
	MemberActive bool
	MemberPlan   string
}

NetworkOverride is the bridge type the user-settings layer passes to the cost estimator so we can wire user-edited rates + per-membership-toggle into trip planning without tripadvice having to import the settings package. Slug ties the override to a row in the built-in Networks table so the match-pattern lookup keeps working; Name is the fallback substring for user-added custom rows that have no slug.

type ObservedPrice added in v0.38.7

type ObservedPrice struct {
	Slug string
	// Low and High are USD per kWh (see the note above).
	Low, High float64
	// Month is the observation month, "2006-01" form (the month of the tracker report).
	Month string
	// URL is the tracker page the figure was copied from.
	URL string
	// Note says what Low and High are, for whoever re-checks the row.
	Note string
	// LowNote and HighNote say what each end is, for the UI, when Low and High differ.
	LowNote, HighNote string
}

ObservedPrice is one network's hand-copied observation.

func ObservedFor added in v0.38.7

func ObservedFor(slug string, now time.Time) (ObservedPrice, bool)

ObservedFor returns the observed price for a network slug when there is one and it is not over 12 months old at now. The month counts from its first day, so a figure observed in 2026-10 stops showing on 2027-10-01: earlier than the day it was copied, never later.

func ObservedRateAt added in v0.38.8

func ObservedRateAt(slug string, sessionStart time.Time, member bool) (rate float64, o ObservedPrice, ok bool)

ObservedRateAt is the figure a past or live session may be priced at: the observed price for slug when the session started on or after the observation month's first day and before 12 months later (a session before the report is not covered by it; a session after the 12 months is not either). The rate is the higher end of the range (what a non-member pays, where the tracker reports both) unless member is true, then the lower end.

func (ObservedPrice) Label added in v0.38.7

func (o ObservedPrice) Label() string

Label is the exact wording for the figure.

type PinChoice added in v0.33.0

type PinChoice struct {
	Snap  bool   `json:"snap"`
	Label string `json:"label"`
}

PinChoice is what a charge's pin says: Snap uses the node as-is; otherwise the charge's own location is labelled.

func ChoosePin added in v0.33.0

func ChoosePin(poi *PoiLike, known string, isDC bool) PinChoice

ChoosePin decides whether a nearby node agrees with the known network (Snap) and what to call the charge. Never a wrong brand:

  • a non-DC session never snaps to a DC-only node;
  • unknown network: snap with the node's name, or a generic label;
  • the node agrees: snap;
  • it disagrees, or can't be verified (no network info): the known network at the charge's own location.

type PoiLike added in v0.33.0

type PoiLike struct {
	Name       string  `json:"name,omitempty"`
	Operator   string  `json:"operator,omitempty"`
	Network    string  `json:"network,omitempty"`
	Brand      string  `json:"brand,omitempty"`
	MaxPowerKW float64 `json:"maxPowerKW,omitempty"`
}

PoiLike is a charging node's descriptive fields (OSM or the archive).

type ResolvedRate

type ResolvedRate struct {
	Slug          string
	DisplayName   string
	GuestRate     float64
	UserRate      float64 // honours the user's MemberActive toggle
	AllMemberRate float64 // hypothetical "you have every plan"
	MemberPlan    string
}

ResolvedRate is what the cost estimator works with per stop. Three rates so the strip can show guest / your-memberships / max-if-you-joined-everything; DisplayName + Slug feed the breakdown attribution line.

func ResolveRate

func ResolveRate(stopName string, overrides []NetworkOverride) ResolvedRate

ResolveRate picks the right rate for a planned stop. First the user's overrides (slug-keyed against the built-in match patterns, falling back to substring against Name for custom rows), then the built-in Networks defaults, then UnmatchedNetwork.

func ResolveRateBySlug added in v0.33.2

func ResolveRateBySlug(slug string, overrides []NetworkOverride) (ResolvedRate, bool)

ResolveRateBySlug is ResolveRate for a network the caller already identified by slug (the user picked it, or it came from detection evidence): the user's override for that slug first, then the built-in default. ok is false for a slug that names no built-in network and has no override row.

type ShownMembershipTerm added in v0.38.10

type ShownMembershipTerm struct {
	MembershipTerm
	Network string
	Label   string
}

ShownMembershipTerm is a row as the API gives it: the term, the network's display name, and the label every row carries.

func MembershipTermsAt added in v0.38.10

func MembershipTermsAt(now time.Time) []ShownMembershipTerm

MembershipTermsAt returns the rows that are still shown at now: checked less than 12 months ago. Each carries the label "As listed by <network>; may be out of date; last checked <date>.".

type SiteMemory added in v0.33.0

type SiteMemory struct {
	Network string // display name as stored on that charge
	DistM   float64
}

SiteMemory is the nearest DC fast charge of the user's own whose network they confirmed or set themselves.

type StopNetwork added in v0.38.7

type StopNetwork struct {
	Slug        string
	DisplayName string
	MemberPlan  string
	// UserPerKWh is what the user's saved price book says they pay (their active
	// membership applied), and ok only when it is THEIR number: a custom row, or a
	// built-in row whose rate differs from the built-in default. A row still holding
	// the default (the seed the Settings page offers) is the table figure, not theirs.
	UserPerKWh float64
	UserSet    bool
}

StopNetwork is the network a planned stop belongs to, and the price the USER set for it, if they did. The built-in table's rates are deliberately not here: they are dated estimates (mid-2025) and the planner no longer shows them as dollar figures.

func ResolveStop added in v0.38.7

func ResolveStop(stopName string, overrides []NetworkOverride) StopNetwork

ResolveStop identifies a stop's network and the user's own price for it.

Jump to

Keyboard shortcuts

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