From 321fd4a372029dbef1eaf662e18acd9817985f7e Mon Sep 17 00:00:00 2001 From: Colin Powell Date: Fri, 24 Oct 2025 10:47:33 -0400 Subject: [PATCH] Add chart.js dashboard --- main.go | 114 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 88 insertions(+), 26 deletions(-) diff --git a/main.go b/main.go index a69940f..e2c1b39 100644 --- a/main.go +++ b/main.go @@ -64,11 +64,9 @@ func estimateCloudCover(url string) (float64, error) { defer valMask.Close() defer cloudMask.Close() - // s < 60 (low saturation) + // Low saturation, high brightness = likely clouds gocv.InRangeWithScalar(s, gocv.NewScalar(0, 0, 0, 0), gocv.NewScalar(60, 0, 0, 0), &satMask) - // v > 120 (bright) gocv.InRangeWithScalar(v, gocv.NewScalar(120, 0, 0, 0), gocv.NewScalar(255, 0, 0, 0), &valMask) - gocv.BitwiseAnd(satMask, valMask, &cloudMask) totalPixels := cloudMask.Rows() * cloudMask.Cols() @@ -92,9 +90,8 @@ func backgroundCollector() { trendLock.Lock() trendHistory = append(trendHistory, result) - // Keep last 100 entries - if len(trendHistory) > 100 { - trendHistory = trendHistory[len(trendHistory)-100:] + if len(trendHistory) > 200 { + trendHistory = trendHistory[len(trendHistory)-200:] } trendLock.Unlock() @@ -122,28 +119,93 @@ func getEnvInt(key string, def int) int { func main() { go backgroundCollector() - http.HandleFunc("/cloudcover", func(w http.ResponseWriter, r *http.Request) { - trendLock.Lock() - defer trendLock.Unlock() - - if len(trendHistory) == 0 { - http.Error(w, "No data collected yet", http.StatusServiceUnavailable) - return - } - latest := trendHistory[len(trendHistory)-1] - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(latest) - }) - - http.HandleFunc("/cloudtrend", func(w http.ResponseWriter, r *http.Request) { - trendLock.Lock() - defer trendLock.Unlock() - - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(trendHistory) - }) + http.HandleFunc("/", dashboardHandler) + http.HandleFunc("/cloudcover", handleLatest) + http.HandleFunc("/cloudtrend", handleTrend) fmt.Printf("☁️ Cloud cover API started at http://localhost:8080\n") fmt.Printf("🔁 Refresh interval: %ds | Source: %s\n", refreshSecs, imageURL) log.Fatal(http.ListenAndServe(":8080", nil)) } + +func handleLatest(w http.ResponseWriter, r *http.Request) { + trendLock.Lock() + defer trendLock.Unlock() + + if len(trendHistory) == 0 { + http.Error(w, "No data collected yet", http.StatusServiceUnavailable) + return + } + latest := trendHistory[len(trendHistory)-1] + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(latest) +} + +func handleTrend(w http.ResponseWriter, r *http.Request) { + trendLock.Lock() + defer trendLock.Unlock() + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(trendHistory) +} + +func dashboardHandler(w http.ResponseWriter, r *http.Request) { + html := ` + + + + + ☁️ Cloud Cover Dashboard + + + + +

☁️ Cloud Cover Trend

+

Live view of sky cloudiness from ` + imageURL + `

+ + + + + +` + w.Header().Set("Content-Type", "text/html") + w.Write([]byte(html)) +}