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.
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user