This commit is contained in:
flo_isea committed 2026-04-20 11:07:30 +02:00
1 parent 1275e5a821
commit ff9860023b
3 files changed
+50 -6

No files matched your search

+31 -3
View File
@@ -157,7 +157,7 @@ func (s *server) handleIndex(w http.ResponseWriter, r *http.Request) {
updatedLabel = updatedAt.Format("02.01.2006 15:04:05") updatedLabel = updatedAt.Format("02.01.2006 15:04:05")
} }
if err := page.Execute(w, map[string]any{ if err := page.Execute(w, map[string]any{
"Watts": watts, "Watts": formatGermanNumber(watts),
"UpdatedAt": updatedLabel, "UpdatedAt": updatedLabel,
"HistoryChart": buildHistoryChart(history, s.chartMax), "HistoryChart": buildHistoryChart(history, s.chartMax),
}); err != nil { }); err != nil {
@@ -165,6 +165,34 @@ func (s *server) handleIndex(w http.ResponseWriter, r *http.Request) {
} }
} }
func formatGermanNumber(n int) string {
s := strconv.Itoa(n)
if len(s) <= 3 {
return s
}
neg := false
if s[0] == '-' {
neg = true
s = s[1:]
}
var b strings.Builder
if neg {
b.WriteByte('-')
}
rem := len(s) % 3
if rem == 0 {
rem = 3
}
b.WriteString(s[:rem])
for i := rem; i < len(s); i += 3 {
b.WriteByte('.')
b.WriteString(s[i : i+3])
}
return b.String()
}
func buildHistoryChart(history []sample, maxWatts int) template.HTML { func buildHistoryChart(history []sample, maxWatts int) template.HTML {
const ( const (
width = 640 width = 640
@@ -242,8 +270,8 @@ func buildHistoryChart(history []sample, maxWatts int) template.HTML {
fmt.Fprintf(&b, `<path d="M %.1f %.1f V %.1f"/>`, x1, yTop, yBottom) fmt.Fprintf(&b, `<path d="M %.1f %.1f V %.1f"/>`, x1, yTop, yBottom)
b.WriteString(`</g>`) b.WriteString(`</g>`)
b.WriteString(`<g fill="#64748b" font-size="12" font-family="system-ui, sans-serif">`) b.WriteString(`<g fill="#64748b" font-size="12" font-family="system-ui, sans-serif">`)
fmt.Fprintf(&b, `<text x="%.1f" y="%d" text-anchor="end">%d W</text>`, x0-10, int(yTop)+14, maxV) fmt.Fprintf(&b, `<text x="%.1f" y="%d" text-anchor="end">%s W</text>`, x0-10, int(yTop)+14, formatGermanNumber(maxV))
fmt.Fprintf(&b, `<text x="%.1f" y="%d" text-anchor="end">%d W</text>`, x0-10, int(yMid)+14, midV) fmt.Fprintf(&b, `<text x="%.1f" y="%d" text-anchor="end">%s W</text>`, x0-10, int(yMid)+14, formatGermanNumber(midV))
fmt.Fprintf(&b, `<text x="%.1f" y="%d" text-anchor="end">0 W</text>`, x0-10, int(yBottom)+14) fmt.Fprintf(&b, `<text x="%.1f" y="%d" text-anchor="end">0 W</text>`, x0-10, int(yBottom)+14)
fmt.Fprintf(&b, `<text x="%.1f" y="%d" text-anchor="middle">%s</text>`, x0, int(height)-10, start.Format("15:04")) fmt.Fprintf(&b, `<text x="%.1f" y="%d" text-anchor="middle">%s</text>`, x0, int(height)-10, start.Format("15:04"))
fmt.Fprintf(&b, `<text x="%.1f" y="%d" text-anchor="middle">%s</text>`, x0+(plotW/2), int(height)-10, midT.Format("15:04")) fmt.Fprintf(&b, `<text x="%.1f" y="%d" text-anchor="middle">%s</text>`, x0+(plotW/2), int(height)-10, midT.Format("15:04"))
+18 -1
View File
@@ -31,6 +31,23 @@ func TestHandleIndexRendersWatts(t *testing.T) {
} }
} }
func TestFormatGermanNumber(t *testing.T) {
tests := map[int]string{
0: "0",
12: "12",
999: "999",
1000: "1.000",
10000: "10.000",
1234567: "1.234.567",
-1000: "-1.000",
}
for input, want := range tests {
if got := formatGermanNumber(input); got != want {
t.Fatalf("formatGermanNumber(%d) = %q, want %q", input, got, want)
}
}
}
func TestHandleWriteRequiresSecret(t *testing.T) { func TestHandleWriteRequiresSecret(t *testing.T) {
srv := &server{secret: "secret", store: &store{}} srv := &server{secret: "secret", store: &store{}}
req := httptest.NewRequest(http.MethodPost, "/write", bytes.NewBufferString(`{"watts":456}`)) req := httptest.NewRequest(http.MethodPost, "/write", bytes.NewBufferString(`{"watts":456}`))
@@ -116,7 +133,7 @@ func TestBuildHistoryChartRendersPolyline(t *testing.T) {
func TestBuildHistoryChartUsesConfiguredMax(t *testing.T) { func TestBuildHistoryChartUsesConfiguredMax(t *testing.T) {
history := []sample{{at: time.Date(2026, 4, 20, 12, 0, 0, 0, time.UTC), value: 250}} history := []sample{{at: time.Date(2026, 4, 20, 12, 0, 0, 0, time.UTC), value: 250}}
chart := buildHistoryChart(history, 1000) chart := buildHistoryChart(history, 1000)
if !strings.Contains(string(chart), "1000 W") { if !strings.Contains(string(chart), "1.000 W") {
t.Fatalf("chart did not include configured max: %s", string(chart)) t.Fatalf("chart did not include configured max: %s", string(chart))
} }
if strings.Contains(string(chart), "250 W") { if strings.Contains(string(chart), "250 W") {
+1 -2
View File
@@ -18,8 +18,7 @@
</head> </head>
<body> <body>
<main> <main>
<div class="value">{{.Watts}}</div> <div class="value">{{.Watts}} W</div>
<div class="unit">watts</div>
<div class="updated-at">Last updated: {{.UpdatedAt}}</div> <div class="updated-at">Last updated: {{.UpdatedAt}}</div>
<div class="chart">{{.HistoryChart}}</div> <div class="chart">{{.HistoryChart}}</div>
</main> </main>