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

leftopen

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

Documentation

Overview

Package leftopen sends the left-open alert: an opt-in push when a door, the frunk, the liftgate, the tailgate or the tonneau has stayed open for OpenFor after the truck was parked and left (DA's ruling, Sprint 8 item 4).

The rule is a "walked away" proxy built only from state we already receive; nothing here calls Rivian, wakes the truck or asks it anything. Unknown inputs mean no alert: a missed alert is acceptable, one sent for lack of knowledge is not. A truck that sleeps with the frunk open stops sending frames, so a timer (not the arrival of a frame) drives the check, reading the monitor's latest state.

  • Closures: only what the state positively says is open (a door, the frunk, the liftgate, the tailgate, the tonneau). Windows are not part of v1.
  • An episode starts when something is first seen open on a fresh state, and ends when everything is closed.
  • The alert fires once per episode, after OpenFor, when the truck is in P (stated, not assumed), not charging, its power state is known, and it is asleep or standby, or awake but locked on a fresh state.
  • The text is fixed (internal/push.LeftOpenPayload): the model, what is open, how long. No place.

The marker (left_open_alerts) records only that a vehicle's episode was alerted, so a restart or a second pod never repeats it. A pod that does not run the vehicle's monitor sees no state for it and does nothing.

Index

Constants

View Source
const (
	// CheckEvery is the timer.
	CheckEvery = time.Minute
	// FreshFrame is how recent a state must be to start an episode, and to vouch for an awake, locked truck.
	FreshFrame = 10 * time.Minute
	// MaxAge: a state older than this (a sleeping truck's last frame) no longer says anything.
	MaxAge = 12 * time.Hour
	// PruneAfter is how long a marker is kept.
	PruneAfter = 30 * 24 * time.Hour
	// Cooldown: no second alert for a vehicle within this long of its last.
	Cooldown = 2 * time.Hour
)
View Source
const OpenFor = 15 * time.Minute

OpenFor is how long something must stay open (the owner's N). One constant: changing it is a one-line change.

Variables

This section is empty.

Functions

func OpenClosures

func OpenClosures(st *rivian.State) []string

OpenClosures names what the state positively reports open, as the kinds internal/push.JoinClosures knows. A state with no real data (nothing has been read yet) reports nothing.

func WalkedAway

func WalkedAway(st *rivian.State, at, now time.Time) bool

WalkedAway says whether the state shows the truck parked and left: in P (stated), not charging, with a known power state that is asleep or standby, or awake but locked on a fresh state. at is when the state was last updated.

Types

type Job

type Job struct {
	Source Source
	// Deliver sends the push to the account's devices that have the alert on.
	Deliver func(ctx context.Context, uid uuid.UUID, model string, kinds []string, minutes int) error
	Now     func() time.Time
	Logger  *slog.Logger
	// Fence says what this pod may do for a vehicle (the group lease, lead mode). Nil, or Off, is as
	// before. An alert is made only while the pod holds the vehicle's group; the claim then carries
	// the epoch. A pod that does not hold it, or is still warming up, skips the tick (the owner
	// alerts; this pod would only race it for the marker).
	Fence func(rivianID string) (fence.Token, fence.State)
	// contains filtered or unexported fields
}

Job is the timer.

func (*Job) Run

func (j *Job) Run(ctx context.Context)

Run ticks every CheckEvery until ctx ends.

func (*Job) Tick

func (j *Job) Tick(ctx context.Context)

Tick checks every candidate vehicle once.

type LatestFunc

type LatestFunc func(uid uuid.UUID, rivianID string) (*rivian.State, time.Time)

LatestFunc reads a vehicle's newest state from the monitor this pod runs (nil state: not this pod's).

type Source

type Source interface {
	// Vehicles are the trucks of accounts with the alert on for some device.
	Vehicles(ctx context.Context) ([]Vehicle, error)
	// Latest is the monitor's newest state for the vehicle and when it was last updated; nil when this pod does not
	// run that vehicle's monitor.
	Latest(uid uuid.UUID, rivianID string) (*rivian.State, time.Time)
	// Claim records the episode as alerted and reports whether this call was the one (false: already alerted).
	Claim(ctx context.Context, uid, vehicleID uuid.UUID, episodeStart time.Time) (bool, error)
	// Prune deletes markers older than the cutoff.
	Prune(ctx context.Context, before time.Time) (int64, error)
}

Source reads what the job needs.

func NewSource

func NewSource(db *sql.DB, latest LatestFunc, skip uuid.UUID) Source

NewSource reads candidates and records markers in Postgres, and states from latest. skip is excluded (the demo user).

type Vehicle

type Vehicle struct {
	UserID    uuid.UUID
	VehicleID uuid.UUID // the database id
	RivianID  string
	Model     string
}

Vehicle is a truck whose owner has the alert on.

Jump to

Keyboard shortcuts

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