dcfcrates

package
v0.21.2 Latest Latest
Warning

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

Go to latest
Published: May 19, 2026 License: MIT Imports: 1 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

This section is empty.

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 (10% off)",
		RivianIDs:           []string{"10001", "10002"},
		DefaultMemberActive: true,
	},
	{
		Slug:          "evgo",
		DisplayName:   "EVgo",
		MatchPatterns: []string{"evgo"},
		GuestRate:     0.42,
		MemberRate:    f64(0.34),
		MemberPlan:    "Rewards+ - $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:    "GO+ - free",
		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

This section is empty.

Types

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 is
	// lifted from the Android app's InternalNetwork enum
	// (apk decompile, 2026-05-18):
	//
	//   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 Rewards+).
	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 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.

Jump to

Keyboard shortcuts

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