Skip to content

Commit

Permalink
Merge branch 'hnnweb_smhi'
Browse files Browse the repository at this point in the history
  • Loading branch information
mendhak committed Feb 5, 2023
2 parents 99226cd + e50fef1 commit 3670b48
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 3 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 2023-02-04
* Add SMHI (Sweden) as weather provider. Added by [hnnweb](https://github.com/mendhak/waveshare-epaper-display/pull/51)

## 2023-01-29
* Add code to display on the [7.5 inch B version 2 screen](https://www.waveshare.com/product/displays/e-paper/epaper-1/7.5inch-e-paper-hat-b.htm)

Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ The screen will display date, time, weather icon with high and low, and calendar
- [Weather.gov (US)](#weathergov-us)
- [Climacell (tomorrow.io)](#climacell-tomorrowio)
- [VisualCrossing](#visualcrossing)
- [SMHI (Sweden)](#smhi-sweden)
- [Pick a severe weather warning provider](#pick-a-severe-weather-warning-provider)
- [Met Office (UK)](#met-office-uk-1)
- [Weather.gov (US)](#weathergov-us-1)
Expand Down Expand Up @@ -177,7 +178,11 @@ Register on [VisualCrossing](https://www.visualcrossing.com/). Under Account Det

export VISUALCROSSING_APIKEY=XXXXXXXXXXXXXXXXXXXXXX

### SMHI (Sweden)

SMHI requires you to identify yourself. Just set your own email,

export SMHI_SELF_IDENTIFICATION=you@example.com

## Pick a severe weather warning provider

Expand Down
2 changes: 2 additions & 0 deletions env.sh.sample
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
# export WEATHER_MET_EIREANN=1
# Or, weather.gov self identification
# export WEATHERGOV_SELF_IDENTIFICATION=you@example.com
# Or, SMHI self identification
# export SMHI_SELF_IDENTIFICATION=you@example.com

# Your latitude and longitude to pass to weather providers
export WEATHER_LATITUDE=51.5077
Expand Down
15 changes: 12 additions & 3 deletions screen-weather-get.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import sys
import os
import logging
from weather_providers import climacell, openweathermap, metofficedatahub, metno, meteireann, accuweather, visualcrossing, weathergov
from weather_providers import climacell, openweathermap, metofficedatahub, metno, meteireann, accuweather, visualcrossing, weathergov, smhi
from alert_providers import metofficerssfeed, weathergovalerts
from alert_providers import meteireann as meteireannalertprovider
from utility import update_svg, configure_logging
Expand All @@ -24,6 +24,7 @@ def format_weather_description(weather_description):
weather_dict[2] = splits[1] if len(splits) > 1 else ''
return weather_dict


def get_weather(location_lat, location_long, units):

# gather relevant environment configs
Expand All @@ -37,6 +38,7 @@ def get_weather(location_lat, location_long, units):
visualcrossing_apikey = os.getenv("VISUALCROSSING_APIKEY")
use_met_eireann = os.getenv("WEATHER_MET_EIREANN")
weathergov_self_id = os.getenv("WEATHERGOV_SELF_IDENTIFICATION")
smhi_self_id = os.getenv("SMHI_SELF_IDENTIFICATION")

if (
not climacell_apikey
Expand All @@ -47,6 +49,7 @@ def get_weather(location_lat, location_long, units):
and not visualcrossing_apikey
and not use_met_eireann
and not weathergov_self_id
and not smhi_self_id
):
logging.error("No weather provider has been configured (Climacell, OpenWeatherMap, Weather.gov, MetOffice, AccuWeather, Met.no, Met Eireann, VisualCrossing...)")
sys.exit(1)
Expand Down Expand Up @@ -93,13 +96,19 @@ def get_weather(location_lat, location_long, units):
logging.info("Getting weather from Climacell")
weather_provider = climacell.Climacell(climacell_apikey, location_lat, location_long, units)

elif smhi_self_id:
logging.info("Getting weather from SMHI")
weather_provider = smhi.SMHI(smhi_self_id, location_lat, location_long, units)

weather = weather_provider.get_weather()
logging.info("weather - {}".format(weather))
return weather


def format_alert_description(alert_message):
return html.escape(alert_message)


def get_alert_message(location_lat, location_long):
alert_message = ""
alert_metoffice_feed_url = os.getenv("ALERT_METOFFICE_FEED_URL")
Expand Down Expand Up @@ -149,7 +158,7 @@ def main():

alert_message = get_alert_message(location_lat, location_long)
alert_message = format_alert_description(alert_message)

output_dict = {
'LOW_ONE': "{}{}".format(str(round(weather['temperatureMin'])), degrees),
'HIGH_ONE': "{}{}".format(str(round(weather['temperatureMax'])), degrees),
Expand All @@ -167,7 +176,7 @@ def main():
logging.debug("main() - {}".format(output_dict))

logging.info("Updating SVG")

template_svg_filename = f'screen-template.{template_name}.svg'
output_svg_filename = 'screen-output-weather.svg'
update_svg(template_svg_filename, output_svg_filename, output_dict)
Expand Down
137 changes: 137 additions & 0 deletions weather_providers/smhi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import logging
from weather_providers.base_provider import BaseWeatherProvider


class SMHI(BaseWeatherProvider):
def __init__(self, smhi_self_id, location_lat, location_long, units):
self.smhi_self_id = smhi_self_id
self.location_lat = location_lat
self.location_long = location_long
self.units = units

# Map SMHI icons to local icons
# Reference: https://opendata.smhi.se/apidocs/metfcst/parameters.html#parameter-wsymb
def get_icon_from_smhi_weathercode(self, weathercode, is_daytime):

icon_dict = {
1: "clear_sky_day" if is_daytime else "clearnight", # Clear sky
2: "clear_sky_day" if is_daytime else "clearnight", # Nearly clear sky
3: "few_clouds" if is_daytime else "partlycloudynight", # Variable cloudiness
4: "scattered_clouds" if is_daytime else "partlycloudynight", # Halfclear sky
5: "mostly_cloudy" if is_daytime else "mostly_cloudy_night", # Cloudy sky
6: "overcast", # Overcast
7: "climacell_fog", # Fog
8: 'climacell_rain_light' if is_daytime else 'rain_night_light', # Light rain showers
9: "climacell_rain" if is_daytime else "rain_night", # Moderate rain showers
10: "climacell_rain_heavy" if is_daytime else "rain_night_heavy", # Heavy rain showers
11: "thundershower_rain", # Thunderstorm
12: "sleet", # Light sleet showers
13: "sleet", # Moderate sleet showers
14: "sleet", # Heavy sleet showers
15: "climacell_snow_light", # Light snow showers
16: "snow", # Moderate snow showers
17: "snow", # Heavy snow showers
18: 'climacell_rain_light' if is_daytime else 'rain_night_light', # Light rain
19: "climacell_rain" if is_daytime else "rain_night", # Moderate rain
20: "climacell_rain_heavy" if is_daytime else "rain_night_heavy", # Heavy rain
21: "thundershower_rain", # Thunder
22: "sleet", # Light sleet
23: "sleet", # Moderate sleet
24: "sleet", # Heavy sleet
25: "climacell_snow_light", # Light snowfall
26: "snow", # Moderate snowfall
27: "snow", # Heavy snowfall
}

icon = icon_dict[weathercode]
logging.debug(
"get_icon_by_weathercode({}) - {}"
.format(weathercode, icon))

return icon

def get_description_from_smhi_weathercode(self, weathercode):
description_dict = {
1: "Clear sky",
2: "Nearly clear sky",
3: "Variable cloudiness",
4: "Halfclear sky",
5: "Cloudy sky",
6: "Overcast",
7: "Fog",
8: "Light rain showers",
9: "Moderate rain showers",
10: "Heavy rain showers",
11: "Thunderstorm",
12: "Light sleet showers",
13: "Moderate sleet showers",
14: "Heavy sleet showers",
15: "Light snow showers",
16: "Moderate snow showers",
17: "Heavy snow showers",
18: "Light rain",
19: "Moderate rain",
20: "Heavy rain",
21: "Thunder",
22: "Light sleet",
23: "Moderate sleet",
24: "Heavy sleet",
25: "Light snowfall",
26: "Moderate snowfall",
27: "Heavy snowfall",
}
description = description_dict[weathercode]

logging.debug(
"get_description_by_weathercode({}) - {}"
.format(weathercode, description))

return description.title()

# Get weather from SMHI API
# https://opendata.smhi.se/apidocs/metfcst/get-forecast.html#get-point-forecast
# The API response is a complete forecast approximately 10 days ahead of the latest current forecast.
# All times in the answer given in UTC.
# Precipitation parameters have a distribution in time (a time interval) until the valid time for current data.
# The interval starts at the time step before. At the beginning of the forecast, the interval is one hour.
# Later in the forecast, the time interval increases (eg 3, 6 and 12 h). Unit remains mm / h.
# So, current hour is index 0, next hour is index 1
# The API accepts 6 decimals in the lon/lat. With more decimals, it returns 404.

def get_weather(self):

url = ("https://opendata-download-metfcst.smhi.se/api/category/pmp3g/version/2/geotype/point/lon/{}/lat/{}/data.json"
.format(self.location_long, self.location_lat))

headers = {"User-Agent": self.smhi_self_id}

response_data = self.get_response_json(url, headers=headers)
logging.debug(response_data)
weather_data = response_data["timeSeries"]
logging.debug("get_weather() - {}".format(weather_data))

# Get the weather code of the first item.
for data in weather_data[0]["parameters"]:
if data["name"] == "Wsymb2":
weather_code = data["values"][0]

daytime = self.is_daytime(self.location_lat, self.location_long)

# { "temperatureMin": "2.0", "temperatureMax": "15.1", "icon": "mostly_cloudy", "description": "Cloudy with light breezes" }
# No Min or Max here. We just get the estimated temperature for the hour.
# Since we get the forecast for several hours and days, get the min/max for the next 12 hours?
weather = {}
temp_list = []

for item in range(0, 12):
for param in weather_data[item]['parameters']:
if param['name'] == 't':
temp = param['values'][0]
temp_list.append(temp)

weather["temperatureMin"] = min(temp_list)
weather["temperatureMax"] = max(temp_list)
weather["icon"] = self.get_icon_from_smhi_weathercode(weather_code, daytime)
weather["description"] = self.get_description_from_smhi_weathercode(weather_code)
logging.debug(weather)
return weather

0 comments on commit 3670b48

Please sign in to comment.