nicer plot

This commit is contained in:
flo_isea committed 2026-04-20 11:01:59 +02:00
1 parent 624ff1da42
commit 1275e5a821
2 files changed
+72 -14

No files matched your search

+36 -11
View File
@@ -25,6 +25,8 @@ type store struct {
history []sample
}
const maxHistoryPoints = 10000
type sample struct {
at time.Time
value int
@@ -42,6 +44,11 @@ func (s *store) set(v int) {
now := time.Now()
s.updatedAt = now
s.history = append(s.history, sample{at: now, value: v})
s.trimHistoryLocked(now)
s.mu.Unlock()
}
func (s *store) trimHistoryLocked(now time.Time) {
cutoff := now.Add(-12 * time.Hour)
keep := s.history[:0]
for _, point := range s.history {
@@ -49,8 +56,10 @@ func (s *store) set(v int) {
keep = append(keep, point)
}
}
if len(keep) > maxHistoryPoints {
keep = keep[len(keep)-maxHistoryPoints:]
}
s.history = keep
s.mu.Unlock()
}
func (s *store) snapshot() (int, time.Time, []sample) {
@@ -63,6 +72,7 @@ func (s *store) snapshot() (int, time.Time, []sample) {
func main() {
secret := os.Getenv("WRITE_SECRET")
addr := os.Getenv("ADDR")
chartMax := 0
if cfg, err := loadConfig("config.json"); err != nil {
if !errors.Is(err, os.ErrNotExist) {
log.Fatalf("load config: %v", err)
@@ -74,6 +84,9 @@ func main() {
if addr == "" {
addr = cfg.Addr
}
if cfg.ChartMaxWatts > 0 {
chartMax = cfg.ChartMaxWatts
}
}
if secret == "" {
log.Fatal("WRITE_SECRET is required")
@@ -83,8 +96,9 @@ func main() {
}
app := &server{
secret: secret,
store: &store{},
secret: secret,
chartMax: chartMax,
store: &store{},
}
log.Printf("listening on %s", addr)
@@ -92,13 +106,15 @@ func main() {
}
type server struct {
secret string
store *store
secret string
chartMax int
store *store
}
type config struct {
WriteSecret string `json:"write_secret"`
Addr string `json:"addr"`
WriteSecret string `json:"write_secret"`
Addr string `json:"addr"`
ChartMaxWatts int `json:"chart_max_watts"`
}
func loadConfig(path string) (config, error) {
@@ -143,17 +159,17 @@ func (s *server) handleIndex(w http.ResponseWriter, r *http.Request) {
if err := page.Execute(w, map[string]any{
"Watts": watts,
"UpdatedAt": updatedLabel,
"HistoryChart": buildHistoryChart(history),
"HistoryChart": buildHistoryChart(history, s.chartMax),
}); err != nil {
http.Error(w, "render error", http.StatusInternalServerError)
}
}
func buildHistoryChart(history []sample) template.HTML {
func buildHistoryChart(history []sample, maxWatts int) template.HTML {
const (
width = 640
height = 220
padL = 52
padL = 72
padR = 18
padT = 18
padB = 42
@@ -164,10 +180,13 @@ func buildHistoryChart(history []sample) template.HTML {
}
minV, maxV := 0, history[0].value
if maxWatts > 0 {
maxV = maxWatts
}
start := history[0].at
end := history[len(history)-1].at
for _, point := range history[1:] {
if point.value > maxV {
if maxWatts <= 0 && point.value > maxV {
maxV = point.value
}
if point.at.Before(start) {
@@ -193,6 +212,12 @@ func buildHistoryChart(history []sample) template.HTML {
for _, point := range history {
x := float64(padL) + plotW*point.at.Sub(start).Seconds()/span.Seconds()
y := float64(height-padB) - plotH*(float64(point.value-minV)/float64(maxV-minV))
if y < float64(padT) {
y = float64(padT)
}
if y > float64(height-padB) {
y = float64(height - padB)
}
points = append(points, fmt.Sprintf("%.1f,%.1f", x, y))
lastX, lastY = x, y
}