Files
x1208-exporter/internal/sensor/sensor.go
T
mokhtar e4c34784c8 rewrite as a daemon with shutdown on low battery
the oneshot+timer produced 8367 journal lines a day of systemd
start/stop noise. now Type=notify with an internal ticker, logging
transitions only.

hardware sits behind sensor.Sensor; internal/monitor is pure, so the
shutdown state machine is tested without a pi.

four guards, each tested, because a false poweroff of a box with no
physical access is worse than a missed one:
- a failed read resets the arming window rather than pausing it
- voltage must actually fall across the window, else it is a stuck
  sensor. this is the only defence against the ac line reading 0
  while mains is connected, so it is load-bearing
- a settle period stops a restart loop acting on early readings
- flapping ac cannot accumulate

verified on hardware 2026-08-09: after poweroff on battery the x1208
starts the pi again when mains returns, with no button press. that
was the risk that could have made this feature unsafe.

a failed tick rewrites the previous sample with sensor_healthy 0 and
the failure counter, so last_update still freezes for the staleness
alert while the failure stays visible.

module path moved to git.mial.net. debian units and the compose
overlay deleted; deployment lives in the infra repo.
2026-08-09 02:01:19 +02:00

27 lines
845 B
Go

// Package sensor defines the hardware seam. Everything above this interface is
// pure and testable on any machine; everything below it touches the Pi.
package sensor
import "time"
// Reading is one complete sample. A Reading only exists if every channel was
// read successfully — there is no partial Reading, because a partial sample
// cannot be reasoned about downstream.
type Reading struct {
ACPresent bool
Volts float64
SOC float64
At time.Time
}
// Sensor reads the UPS. Read returns an error if any channel fails; callers
// must treat the Reading as invalid in that case.
//
// Implementations may hold OS handles open across calls. A Read that returns
// an error must leave the Sensor usable: the next Read reacquires whatever it
// needs.
type Sensor interface {
Read() (Reading, error)
Close() error
}