Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions irc2phpbb/data/marvin_strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,40 @@
"station_url": "https://opendata-download-metobs.smhi.se/api/version/1.0/parameter/13/station/65090/period/latest-hour/data.json",
"current_weather_url": "https://opendata-download-metobs.smhi.se/api/version/1.0/parameter/1/station/65090/period/latest-hour/data.json",
"weather_codes_url": "https://opendata-download-metobs.smhi.se/api/version/1.0/parameter/13/codes.json",
"wind_direction_url": "https://opendata-download-metobs.smhi.se/api/version/1.0/parameter/3/station/65090/period/latest-hour/data.json",
"wind_speed_url": "https://opendata-download-metobs.smhi.se/api/version/1.0/parameter/4/station/65090/period/latest-hour/data.json",
"forecast_url": "https://opendata-download-metfcst.smhi.se/api/category/snow1g/version/1/geotype/point/lon/15.589/lat/56.15/data.json?parameters=air_temperature,symbol_code,wind_from_direction,wind_speed&timeseries=8",
"no_significant_weather": "Inget signifikant väder observerat",
"compass": ["↑", "↗", "→", "↘", "↓", "↙", "←", "↖"],
"symbols": {
"1": "klart",
"2": "nästan klart",
"3": "molnväxling",
"4": "halvklart",
"5": "molnigt",
"6": "mulet",
"7": "dimma",
"8": "lätta skurar",
"9": "måttliga skurar",
"10": "kraftiga skurar",
"11": "åskväder",
"12": "lätta snöblandade skurar",
"13": "måttliga snöblandade skurar",
"14": "kraftiga snöblandade skurar",
"15": "lätta snöbyar",
"16": "måttliga snöbyar",
"17": "kraftiga snöbyar",
"18": "lätt regn",
"19": "måttligt regn",
"20": "kraftigt regn",
"21": "åska",
"22": "lätt snöblandat",
"23": "måttligt snöblandat",
"24": "kraftigt snöblandat",
"25": "lätt snöfall",
"26": "måttligt snöfall",
"27": "kraftigt snöfall"
},
"failed": "Något har hänt med SMHI, de svarar inte."
},

Expand Down
88 changes: 75 additions & 13 deletions irc2phpbb/marvin_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import logging
import random
import re
import zoneinfo

from importlib import resources as impresources

Expand Down Expand Up @@ -289,30 +290,61 @@ def marvinSun(row):
return msg


def windDirectionToCompass(degrees):
"""
Convert a wind direction in degrees to a compass arrow (↑, ↗, →, ...).
"""
compass = getString("smhi")["compass"]
index = round(degrees / (360 / len(compass))) % len(compass)
return compass[index]


def getCurrentWeather():
"""
Fetch the current temperature, wind and weather observation for Karlskrona.
"""
station_req = requests.get(getString("smhi", "station_url"), timeout=5)
weather_code: int = int(station_req.json().get("value")[0].get("value"))

weather_codes_req = requests.get(getString("smhi", "weather_codes_url"), timeout=5)
weather_codes_arr: list = weather_codes_req.json().get("entry")

current_weather_req = requests.get(getString("smhi", "current_weather_url"), timeout=5)
temperature: str = current_weather_req.json().get("value")[0].get("value")

wind_direction_req = requests.get(getString("smhi", "wind_direction_url"), timeout=5)
wind_direction: float = float(wind_direction_req.json().get("value")[0].get("value"))

wind_speed_req = requests.get(getString("smhi", "wind_speed_url"), timeout=5)
wind_speed: str = wind_speed_req.json().get("value")[0].get("value")

observation = ""
for code in weather_codes_arr:
if code.get("key") == weather_code:
observation = code.get("value")

return temperature, wind_speed, windDirectionToCompass(wind_direction), observation


def marvinWeather(row):
"""
Check what the weather prognosis looks like.
"""
msg = ""
if any(r in row for r in ["väder", "vädret", "prognos", "prognosen", "smhi"]):
temperature = ""
observation = ""

try:
station_req = requests.get(getString("smhi", "station_url"), timeout=5)
weather_code: int = int(station_req.json().get("value")[0].get("value"))
temperature, wind_speed, compass_direction, observation = getCurrentWeather()
current_symbol, forecast = getWeatherForecast()

weather_codes_req = requests.get(getString("smhi", "weather_codes_url"), timeout=5)
weather_codes_arr: list = weather_codes_req.json().get("entry")
parts = [f"Karlskrona {temperature}° {current_symbol} "
f"{wind_speed} m/s {compass_direction}"]

current_weather_req = requests.get(getString("smhi", "current_weather_url"), timeout=5)
temperature: str = current_weather_req.json().get("value")[0].get("value")
if observation and observation != getString("smhi", "no_significant_weather"):
parts.append(observation)

for code in weather_codes_arr:
if code.get("key") == weather_code:
observation = code.get("value")
current = ". ".join(parts)

msg = f"Karlskrona just nu: {temperature} °C. {observation}."
msg = f"{current} · {forecast}." if forecast else f"{current}."

except Exception as e:
LOG.error("Failed to get weather: %s", e)
Expand All @@ -321,6 +353,36 @@ def marvinWeather(row):
return msg


def getWeatherForecast():
"""
Get the current sky symbol plus a short summary of the weather forecast
for the coming hours.
"""
symbols = getString("smhi")["symbols"]
forecast_req = requests.get(getString("smhi", "forecast_url"), timeout=5)
time_series = forecast_req.json().get("timeSeries")

current_symbol = symbols.get(str(time_series[0].get("data").get("symbol_code")))

# Pick two points a few hours apart instead of showing every hour.
step_indices = [3, 7]
selected_steps = [time_series[i] for i in step_indices if i < len(time_series)]

stockholm = zoneinfo.ZoneInfo("Europe/Stockholm")
steps = []
for step in selected_steps:
data = step.get("data")
local_time = datetime.datetime.fromisoformat(step.get("time")).astimezone(stockholm)
temperature = data.get("air_temperature")
symbol = symbols.get(str(data.get("symbol_code")))
wind_speed = data.get("wind_speed")
wind_direction = windDirectionToCompass(data.get("wind_from_direction"))
steps.append(f"{local_time:%H:%M} {temperature}° {symbol} "
f"{wind_speed} m/s {wind_direction}")

return current_symbol, " · ".join(steps)


def marvinStrip(row):
"""
Get a comic strip.
Expand Down
12 changes: 12 additions & 0 deletions tests/resources/weather/codes_significant.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"entry": [
{
"key": 68,
"value": "Lätt regn"
},
{
"key": 100,
"value": "Inget signifikant väder observerat"
}
]
}
90 changes: 90 additions & 0 deletions tests/resources/weather/forecast.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
{
"createdTime": "2026-08-23T21:15:00Z",
"referenceTime": "2026-08-23T21:15:00Z",
"geometry": {
"type": "Point",
"coordinates": [[15.589, 56.15]]
},
"timeSeries": [
{
"time": "2026-08-23T22:00:00Z",
"intervalParametersStartTime": "2026-08-23T21:00:00Z",
"data": {
"air_temperature": 15.8,
"symbol_code": 4,
"wind_from_direction": 340,
"wind_speed": 2.6
}
},
{
"time": "2026-08-23T23:00:00Z",
"intervalParametersStartTime": "2026-08-23T22:00:00Z",
"data": {
"air_temperature": 15.5,
"symbol_code": 4,
"wind_from_direction": 335,
"wind_speed": 2.8
}
},
{
"time": "2026-08-24T00:00:00Z",
"intervalParametersStartTime": "2026-08-23T23:00:00Z",
"data": {
"air_temperature": 15.3,
"symbol_code": 4,
"wind_from_direction": 330,
"wind_speed": 3.0
}
},
{
"time": "2026-08-24T01:00:00Z",
"intervalParametersStartTime": "2026-08-24T00:00:00Z",
"data": {
"air_temperature": 15.2,
"symbol_code": 5,
"wind_from_direction": 320,
"wind_speed": 3.1
}
},
{
"time": "2026-08-24T02:00:00Z",
"intervalParametersStartTime": "2026-08-24T01:00:00Z",
"data": {
"air_temperature": 15.0,
"symbol_code": 5,
"wind_from_direction": 315,
"wind_speed": 3.2
}
},
{
"time": "2026-08-24T03:00:00Z",
"intervalParametersStartTime": "2026-08-24T02:00:00Z",
"data": {
"air_temperature": 14.8,
"symbol_code": 6,
"wind_from_direction": 310,
"wind_speed": 3.3
}
},
{
"time": "2026-08-24T04:00:00Z",
"intervalParametersStartTime": "2026-08-24T03:00:00Z",
"data": {
"air_temperature": 14.7,
"symbol_code": 6,
"wind_from_direction": 305,
"wind_speed": 3.3
}
},
{
"time": "2026-08-24T05:00:00Z",
"intervalParametersStartTime": "2026-08-24T04:00:00Z",
"data": {
"air_temperature": 14.6,
"symbol_code": 6,
"wind_from_direction": 300,
"wind_speed": 3.4
}
}
]
}
40 changes: 40 additions & 0 deletions tests/resources/weather/station_significant.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"updated": 1729620000000,
"parameter": {
"key": "13",
"name": "Rådande väder",
"summary": "momentanvärde, 1 gång/tim resp 8 gånger/dygn",
"unit": "kod"
},
"station": {
"key": "65090",
"name": "Karlskrona-Söderstjerna",
"owner": "Försvarsmakten",
"ownerCategory": "CLIMATE",
"measuringStations": "CORE",
"height": 2.0
},
"period": {
"key": "latest-hour",
"from": 1729616401000,
"to": 1729620000000,
"summary": "Data från senaste timmen",
"sampling": "Ej angivet"
},
"position": [
{
"from": 1267401600000,
"to": 1729621220000,
"height": 2.4,
"latitude": 56.15,
"longitude": 15.589
}
],
"value": [
{
"date": 1729620000000,
"value": "68",
"quality": "G"
}
]
}
40 changes: 40 additions & 0 deletions tests/resources/weather/winddirection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"updated": 1787346000000,
"parameter": {
"key": "3",
"name": "Vindriktning",
"summary": "medelvärde 10 min, 1 gång/tim",
"unit": "grader"
},
"station": {
"key": "65090",
"name": "Karlskrona-Söderstjerna",
"owner": "Försvarsmakten",
"ownerCategory": "CLIMATE",
"measuringStations": "CORE",
"height": 10.0
},
"period": {
"key": "latest-hour",
"from": 1787342401000,
"to": 1787346000000,
"summary": "Data från senaste timmen",
"sampling": "10 minuter"
},
"position": [
{
"from": 1267401600000,
"to": 1787347212000,
"height": 10.0,
"latitude": 56.15,
"longitude": 15.589
}
],
"value": [
{
"date": 1787346000000,
"value": "225",
"quality": "G"
}
]
}
40 changes: 40 additions & 0 deletions tests/resources/weather/windspeed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"updated": 1787346000000,
"parameter": {
"key": "4",
"name": "Vindhastighet",
"summary": "medelvärde 10 min, 1 gång/tim",
"unit": "meter per sekund"
},
"station": {
"key": "65090",
"name": "Karlskrona-Söderstjerna",
"owner": "Försvarsmakten",
"ownerCategory": "CLIMATE",
"measuringStations": "CORE",
"height": 10.0
},
"period": {
"key": "latest-hour",
"from": 1787342401000,
"to": 1787346000000,
"summary": "Data från senaste timmen",
"sampling": "10 minuter"
},
"position": [
{
"from": 1267401600000,
"to": 1787347212000,
"height": 10.0,
"latitude": 56.15,
"longitude": 15.589
}
],
"value": [
{
"date": 1787346000000,
"value": "3.2",
"quality": "G"
}
]
}
Loading
Loading