From 8de6f1e46ece914eef3c2d30e331256c25115637 Mon Sep 17 00:00:00 2001 From: nopil3os Date: Wed, 8 Apr 2026 17:59:02 +0200 Subject: [PATCH 1/6] with help from gemini: replace deprecated smhi pm3g api for new SNOW api --- backends/smhi.go | 94 ++++++++++++++++++++++++------------------------ 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/backends/smhi.go b/backends/smhi.go index 733ff12..b6caf68 100644 --- a/backends/smhi.go +++ b/backends/smhi.go @@ -15,25 +15,29 @@ import ( type smhiConfig struct { } -type smhiDataPoint struct { - Level int `json:"level"` - LevelType string `json:"levelType"` - Name string `json:"name"` - Unit string `json:"unit"` - Values []interface{} `json:"values"` +type smhiData struct { + AirTemperature float64 `json:"air_temperature"` + WindFromDirection float64 `json:"wind_from_direction"` + WindSpeed float64 `json:"wind_speed"` + WindSpeedOfGust float64 `json:"wind_speed_of_gust"` + RelativeHumidity float64 `json:"relative_humidity"` + VisibilityInAir float64 `json:"visibility_in_air"` + PrecipitationAmountMean float64 `json:"precipitation_amount_mean"` + SymbolCode float64 `json:"symbol_code"` } type smhiTimeSeries struct { - ValidTime string `json:"validTime"` - Parameters []*smhiDataPoint `json:"parameters"` + Time string `json:"time"` + IntervalParametersStartTime string `json:"intervalParametersStartTime"` + Data smhiData `json:"data"` } type smhiGeometry struct { - Coordinates [][]float32 `json:"coordinates"` + Coordinates []float32 `json:"coordinates"` } type smhiResponse struct { - ApprovedTime string `json:"approvedTime"` + CreatedTime string `json:"createdTime"` ReferenceTime string `json:"referenceTime"` Geometry smhiGeometry `json:"geometry"` TimeSeries []*smhiTimeSeries `json:"timeSeries"` @@ -45,8 +49,9 @@ type smhiCondition struct { } const ( - // see http://opendata.smhi.se/apidocs/metfcst/index.html - smhiWuri = "https://opendata-download-metfcst.smhi.se/api/category/pmp3g/version/2/geotype/point/lon/%s/lat/%s/data.json" + // Ensure this URL is still valid for the new API version. Left unchanged based on prompt context. + //smhiWuri = "https://opendata-download-metfcst.smhi.se/api/category/pmp3g/version/2/geotype/point/lon/%s/lat/%s/data.json" + smhiWuri = "https://opendata-download-metfcst.smhi.se/api/category/snow1g/version/1/geotype/point/lon/%s/lat/%s/data.json" ) var ( @@ -109,7 +114,6 @@ func (c *smhiConfig) fetch(url string) (*smhiResponse, error) { return nil, fmt.Errorf("Unable to parse response (%s): %v", url, err) } return &response, nil - } func (c *smhiConfig) Fetch(location string, numDays int) (ret iface.Data) { @@ -128,10 +132,11 @@ func (c *smhiConfig) Fetch(location string, numDays int) (ret iface.Data) { ret.Current = c.parseCurrent(resp) ret.Forecast = c.parseForecast(resp, numDays) coordinates := resp.Geometry.Coordinates - ret.GeoLoc = &iface.LatLon{Latitude: coordinates[0][1], Longitude: coordinates[0][0]} + ret.GeoLoc = &iface.LatLon{Latitude: coordinates[1], Longitude: coordinates[0]} ret.Location = location + " (Forecast provided by SMHI)" return ret } + func (c *smhiConfig) parseForecast(response *smhiResponse, numDays int) (days []iface.Day) { if numDays > 10 { numDays = 10 @@ -147,7 +152,7 @@ func (c *smhiConfig) parseForecast(response *smhiResponse, numDays int) (days [] break } - ts, err := time.Parse(time.RFC3339, prediction.ValidTime) + ts, err := time.Parse(time.RFC3339, prediction.Time) if err != nil { log.Fatalf("Failed to parse timestamp: %v\n", err) } @@ -172,7 +177,7 @@ func (c *smhiConfig) parseCurrent(forecast *smhiResponse) (cnd iface.Cond) { var currentTime time.Time = time.Now().UTC() for _, prediction := range forecast.TimeSeries { - ts, err := time.Parse(time.RFC3339, prediction.ValidTime) + ts, err := time.Parse(time.RFC3339, prediction.Time) if err != nil { log.Fatalf("Failed to parse timestamp: %v\n", err) } @@ -185,43 +190,36 @@ func (c *smhiConfig) parseCurrent(forecast *smhiResponse) (cnd iface.Cond) { } func (c *smhiConfig) parsePrediction(prediction *smhiTimeSeries) (cnd iface.Cond) { - ts, err := time.Parse(time.RFC3339, prediction.ValidTime) + ts, err := time.Parse(time.RFC3339, prediction.Time) if err != nil { log.Fatalf("Failed to parse timestamp: %v\n", err) } cnd.Time = ts - for _, param := range prediction.Parameters { - switch param.Name { - case "pmean": - precip := float32(param.Values[0].(float64) / 1000) // Convert mm/h to m/h - cnd.PrecipM = &precip - case "vis": - vis := float32(param.Values[0].(float64) * 1000) // Convert km to m - cnd.VisibleDistM = &vis - case "t": - temp := float32(param.Values[0].(float64)) - cnd.TempC = &temp - case "Wsymb2": - condition := weatherConditions[int(param.Values[0].(float64))] - cnd.Code = condition.WeatherCode - cnd.Desc = condition.Description - case "ws": - windSpeed := float32(param.Values[0].(float64) * 3.6) // convert m/s to km/h - cnd.WindspeedKmph = &windSpeed - case "gust": - gustSpeed := float32(param.Values[0].(float64) * 3.6) // convert m/s to km/h - cnd.WindGustKmph = &gustSpeed - case "wd": - val := int(param.Values[0].(float64)) - cnd.WinddirDegree = &val - case "r": - val := int(param.Values[0].(float64)) - cnd.Humidity = &val - default: - continue - } - } + precip := float32(prediction.Data.PrecipitationAmountMean / 1000) // Convert mm/h to m/h + cnd.PrecipM = &precip + + vis := float32(prediction.Data.VisibilityInAir * 1000) // Convert km to m + cnd.VisibleDistM = &vis + + temp := float32(prediction.Data.AirTemperature) + cnd.TempC = &temp + + condition := weatherConditions[int(prediction.Data.SymbolCode)] + cnd.Code = condition.WeatherCode + cnd.Desc = condition.Description + + windSpeed := float32(prediction.Data.WindSpeed * 3.6) // convert m/s to km/h + cnd.WindspeedKmph = &windSpeed + + gustSpeed := float32(prediction.Data.WindSpeedOfGust * 3.6) // convert m/s to km/h + cnd.WindGustKmph = &gustSpeed + + wd := int(prediction.Data.WindFromDirection) + cnd.WinddirDegree = &wd + + humidity := int(prediction.Data.RelativeHumidity) + cnd.Humidity = &humidity return cnd } From 85c61203bc399a9ef3450ed68b52e660cb51d175 Mon Sep 17 00:00:00 2001 From: nopil3os Date: Fri, 10 Apr 2026 16:44:33 +0200 Subject: [PATCH 2/6] remove unit conversation as it is now in km/m2 = mm/m2 --- backends/smhi.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backends/smhi.go b/backends/smhi.go index b6caf68..9b7710d 100644 --- a/backends/smhi.go +++ b/backends/smhi.go @@ -49,7 +49,6 @@ type smhiCondition struct { } const ( - // Ensure this URL is still valid for the new API version. Left unchanged based on prompt context. //smhiWuri = "https://opendata-download-metfcst.smhi.se/api/category/pmp3g/version/2/geotype/point/lon/%s/lat/%s/data.json" smhiWuri = "https://opendata-download-metfcst.smhi.se/api/category/snow1g/version/1/geotype/point/lon/%s/lat/%s/data.json" ) @@ -196,7 +195,7 @@ func (c *smhiConfig) parsePrediction(prediction *smhiTimeSeries) (cnd iface.Cond } cnd.Time = ts - precip := float32(prediction.Data.PrecipitationAmountMean / 1000) // Convert mm/h to m/h + precip := float32(prediction.Data.PrecipitationAmountMean) // no need to convert this anymore cnd.PrecipM = &precip vis := float32(prediction.Data.VisibilityInAir * 1000) // Convert km to m From a3b5e9b0e33809582d6766e1e8b98c2fdbd2a8cf Mon Sep 17 00:00:00 2001 From: nopil3os Date: Sat, 11 Apr 2026 11:09:28 +0200 Subject: [PATCH 3/6] Revert "remove unit conversation as it is now in km/m2 = mm/m2" This reverts commit 85c61203bc399a9ef3450ed68b52e660cb51d175. --- backends/smhi.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backends/smhi.go b/backends/smhi.go index 9b7710d..b6caf68 100644 --- a/backends/smhi.go +++ b/backends/smhi.go @@ -49,6 +49,7 @@ type smhiCondition struct { } const ( + // Ensure this URL is still valid for the new API version. Left unchanged based on prompt context. //smhiWuri = "https://opendata-download-metfcst.smhi.se/api/category/pmp3g/version/2/geotype/point/lon/%s/lat/%s/data.json" smhiWuri = "https://opendata-download-metfcst.smhi.se/api/category/snow1g/version/1/geotype/point/lon/%s/lat/%s/data.json" ) @@ -195,7 +196,7 @@ func (c *smhiConfig) parsePrediction(prediction *smhiTimeSeries) (cnd iface.Cond } cnd.Time = ts - precip := float32(prediction.Data.PrecipitationAmountMean) // no need to convert this anymore + precip := float32(prediction.Data.PrecipitationAmountMean / 1000) // Convert mm/h to m/h cnd.PrecipM = &precip vis := float32(prediction.Data.VisibilityInAir * 1000) // Convert km to m From a8a73d6035117bfb07f74fce6c97771a5e1c7f7b Mon Sep 17 00:00:00 2001 From: nopil3os Date: Sun, 12 Apr 2026 11:30:51 +0200 Subject: [PATCH 4/6] removed gemini comment --- backends/smhi.go | 1 - 1 file changed, 1 deletion(-) diff --git a/backends/smhi.go b/backends/smhi.go index 3ac4b17..cba0c27 100644 --- a/backends/smhi.go +++ b/backends/smhi.go @@ -49,7 +49,6 @@ type smhiCondition struct { } const ( - // Ensure this URL is still valid for the new API version. Left unchanged based on prompt context. //smhiWuri = "https://opendata-download-metfcst.smhi.se/api/category/pmp3g/version/2/geotype/point/lon/%s/lat/%s/data.json" smhiWuri = "https://opendata-download-metfcst.smhi.se/api/category/snow1g/version/1/geotype/point/lon/%s/lat/%s/data.json" ) From 45f1d66a02f407397aba31dbfae182ba104ec5b2 Mon Sep 17 00:00:00 2001 From: Kordian Bruck Date: Sun, 12 Apr 2026 12:44:42 +0200 Subject: [PATCH 5/6] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- backends/smhi.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/backends/smhi.go b/backends/smhi.go index cba0c27..fbc194b 100644 --- a/backends/smhi.go +++ b/backends/smhi.go @@ -204,9 +204,14 @@ func (c *smhiConfig) parsePrediction(prediction *smhiTimeSeries) (cnd iface.Cond temp := float32(prediction.Data.AirTemperature) cnd.TempC = &temp - condition := weatherConditions[int(prediction.Data.SymbolCode)] - cnd.Code = condition.WeatherCode - cnd.Desc = condition.Description + symbolCode := int(prediction.Data.SymbolCode) + if condition, ok := weatherConditions[symbolCode]; ok { + cnd.Code = condition.WeatherCode + cnd.Desc = condition.Description + } else { + cnd.Code = iface.CodeUnknown + cnd.Desc = fmt.Sprintf("Unknown weather condition (symbol code %d)", symbolCode) + } windSpeed := float32(prediction.Data.WindSpeed * 3.6) // convert m/s to km/h cnd.WindspeedKmph = &windSpeed From e7d05dc76d5c6c655ab25d7733b1ccbe31ba68c0 Mon Sep 17 00:00:00 2001 From: Kordian Bruck Date: Sun, 12 Apr 2026 12:47:41 +0200 Subject: [PATCH 6/6] Update SMHI API endpoint for snow data --- backends/smhi.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/smhi.go b/backends/smhi.go index fbc194b..077b8f3 100644 --- a/backends/smhi.go +++ b/backends/smhi.go @@ -49,7 +49,7 @@ type smhiCondition struct { } const ( - //smhiWuri = "https://opendata-download-metfcst.smhi.se/api/category/pmp3g/version/2/geotype/point/lon/%s/lat/%s/data.json" + // API Spec: https://opendata.smhi.se/metfcst/snow1gv1/parameters smhiWuri = "https://opendata-download-metfcst.smhi.se/api/category/snow1g/version/1/geotype/point/lon/%s/lat/%s/data.json" )