diff --git a/main.go b/main.go index 08da81b..e371db4 100644 --- a/main.go +++ b/main.go @@ -12,7 +12,7 @@ import ( "sync" "time" - _ "modernc.org/sqlite" // lightweight pure-Go SQLite driver + _ "modernc.org/sqlite" "gocv.io/x/gocv" ) @@ -23,6 +23,15 @@ type CloudResult struct { Error string `json:"error,omitempty"` } +type Summary struct { + From time.Time `json:"from"` + To time.Time `json:"to"` + Avg float64 `json:"avg"` + Min float64 `json:"min"` + Max float64 `json:"max"` + SampleCount int `json:"sample_count"` +} + var ( imageURL = getEnv("CLOUD_IMAGE_URL", "http://clouds.local:8082/snapshot") refreshSecs = getEnvInt("CLOUD_REFRESH_SECONDS", 30) @@ -39,6 +48,7 @@ func main() { http.HandleFunc("/", dashboardHandler) http.HandleFunc("/cloudcover", handleLatest) http.HandleFunc("/cloudtrend", handleTrend) + http.HandleFunc("/cloudsummary", handleSummary) fmt.Printf("☁️ Cloud cover API started at http://localhost:8080\n") fmt.Printf("🔁 Refresh every %ds | Source: %s | DB: %s\n", refreshSecs, imageURL, dbPath) @@ -104,7 +114,6 @@ func backgroundCollector() { if err != nil { result.Error = err.Error() } - storeResult(result) fmt.Printf("[%s] Cloud cover: %.2f%%\n", result.Timestamp.Format("15:04:05"), result.CloudCover) time.Sleep(time.Duration(refreshSecs) * time.Second) @@ -170,13 +179,42 @@ func getAll(limit int) ([]CloudResult, error) { results = append(results, r) } - // Reverse to chronological order + // reverse chronological order for i, j := 0, len(results)-1; i < j; i, j = i+1, j-1 { results[i], results[j] = results[j], results[i] } return results, nil } +func getSummary() (*Summary, error) { + since := time.Now().Add(-24 * time.Hour) + rows := db.QueryRow(` + SELECT + AVG(cloud_cover), + MIN(cloud_cover), + MAX(cloud_cover), + COUNT(*) + FROM cloud_trend + WHERE timestamp > ? + `, since.Format(time.RFC3339)) + + var s Summary + var avg, min, max sql.NullFloat64 + var count int + if err := rows.Scan(&avg, &min, &max, &count); err != nil { + return nil, err + } + s.From = since + s.To = time.Now() + s.SampleCount = count + if avg.Valid { + s.Avg = avg.Float64 + s.Min = min.Float64 + s.Max = max.Float64 + } + return &s, nil +} + func handleLatest(w http.ResponseWriter, r *http.Request) { result, err := getLatest() if err != nil { @@ -197,6 +235,16 @@ func handleTrend(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(results) } +func handleSummary(w http.ResponseWriter, r *http.Request) { + summary, err := getSummary() + if err != nil { + http.Error(w, err.Error(), 500) + return + } + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(summary) +} + func dashboardHandler(w http.ResponseWriter, r *http.Request) { html := ` @@ -207,50 +255,67 @@ func dashboardHandler(w http.ResponseWriter, r *http.Request) {

☁️ Cloud Cover Trend

Source: ` + imageURL + `

+ +
+

Loading summary...

+
+ @@ -259,6 +324,7 @@ func dashboardHandler(w http.ResponseWriter, r *http.Request) { w.Write([]byte(html)) } + func getEnv(key, def string) string { if v, ok := os.LookupEnv(key); ok { return v