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
- Variables
- func DisplayNetwork(known string) string
- func IsDCOnlyNode(poi *PoiLike) bool
- func NetworkKey(parts ...string) string
- func StationQuery(nrelNetwork, stationName string) string
- type Candidate
- type DetectInput
- type Detection
- type Network
- type NetworkOverride
- type PinChoice
- type PoiLike
- type ResolvedRate
- type SiteMemory
Constants ¶
const ( StateEstimated = "estimated" StateNeedsConfirmation = "needs_confirmation" StateConfirmed = "confirmed" )
Verify states (charges.network_state).
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.
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".
const ConfidentAt = 0.85
ConfidentAt is the confidence an estimate needs to stand without asking the user (state estimated rather than needs_confirmation).
Variables ¶
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.
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
DisplayNetwork is a friendly canonical name, else the string itself.
func IsDCOnlyNode ¶ added in v0.33.0
IsDCOnlyNode reports a node an AC session can't be at.
func NetworkKey ¶ added in v0.33.0
NetworkKey reads a free-form network, operator, brand or name string.
func StationQuery ¶ added in v0.32.0
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 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 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 ¶
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 ¶
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 PinChoice ¶ added in v0.33.0
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
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 SiteMemory ¶ added in v0.33.0
SiteMemory is the nearest DC fast charge of the user's own whose network they confirmed or set themselves.