From 6a97d392c97fe89870c750a04e74b5937dd62bb7 Mon Sep 17 00:00:00 2001 From: AlexFullmoon Date: Sat, 27 Aug 2022 13:30:17 +0300 Subject: [PATCH] Added OpenWeatherMap widget. --- src/components/widget.jsx | 2 + .../widgets/openweathermap/icon.jsx | 7 + .../widgets/openweathermap/weather.jsx | 44 ++ src/pages/api/widgets/openweathermap.js | 9 + src/skeleton/widgets.yaml | 7 + src/utils/owm-condition-map.js | 413 ++++++++++++++++++ 6 files changed, 482 insertions(+) create mode 100644 src/components/widgets/openweathermap/icon.jsx create mode 100644 src/components/widgets/openweathermap/weather.jsx create mode 100644 src/pages/api/widgets/openweathermap.js create mode 100644 src/utils/owm-condition-map.js diff --git a/src/components/widget.jsx b/src/components/widget.jsx index 415cc7d40..0dd26535e 100644 --- a/src/components/widget.jsx +++ b/src/components/widget.jsx @@ -1,8 +1,10 @@ import Weather from "components/widgets/weather/weather"; +import OpenWeatherMap from "components/widgets/openweathermap/weather"; import Resources from "components/widgets/resources/resources"; const widgetMappings = { weather: Weather, + openweathermap: OpenWeatherMap, resources: Resources, }; diff --git a/src/components/widgets/openweathermap/icon.jsx b/src/components/widgets/openweathermap/icon.jsx new file mode 100644 index 000000000..975c1f7ef --- /dev/null +++ b/src/components/widgets/openweathermap/icon.jsx @@ -0,0 +1,7 @@ +import mapIcon from "utils/owm-condition-map"; + +export default function Icon({ condition, timeOfDay }) { + const Icon = mapIcon(condition, timeOfDay); + + return ; +} diff --git a/src/components/widgets/openweathermap/weather.jsx b/src/components/widgets/openweathermap/weather.jsx new file mode 100644 index 000000000..9f0a61ac5 --- /dev/null +++ b/src/components/widgets/openweathermap/weather.jsx @@ -0,0 +1,44 @@ +import useSWR from "swr"; +import { BiError } from "react-icons/bi"; + +import Icon from "./icon"; + +export default function OpenWeatherMap({ options }) { + const { data, error } = useSWR( + `/api/widgets/openweathermap?lat=${options.latitude}&lon=${options.longitude}&apiKey=${options.apiKey}&duration=${options.cache}` + ); + + if (error) { + return ( +
+ +
+ API + Error +
+
+ ); + } + + if (!data) { + return
; + } + + if (data.error) { + return
; + } + // OpenWeatherMap returns temperature in Kelvins + var temp_c = data.main.temp - 273.15; + var temp_f = temp_c * 9 / 5 + 32; + return ( +
+ data.sys.sunrise) && (data.dt < data.sys.sundown) ? "day" : "night"} /> +
+ + {options.units === "metric" ? temp_c.toFixed(0) : temp_f.toFixed(0)}° + + {data.weather[0].description} +
+
+ ); +} diff --git a/src/pages/api/widgets/openweathermap.js b/src/pages/api/widgets/openweathermap.js new file mode 100644 index 000000000..471f69df9 --- /dev/null +++ b/src/pages/api/widgets/openweathermap.js @@ -0,0 +1,9 @@ +import cachedFetch from "utils/cached-fetch"; + +export default async function handler(req, res) { + const { lat, lon, apiKey, duration } = req.query; + + const api_url = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${apiKey}`; + + res.send(await cachedFetch(api_url, duration)); +} diff --git a/src/skeleton/widgets.yaml b/src/skeleton/widgets.yaml index e366a7996..011433b67 100644 --- a/src/skeleton/widgets.yaml +++ b/src/skeleton/widgets.yaml @@ -5,6 +5,13 @@ apiKey: weather_api_key # get from https://www.weatherapi.com/ cache: 5 # cache time in minutes +- openweathermap: + latitude: 51.5072 # widget configuration + longitude: 0.1275 + units: metric + apiKey: openweathermap_api_key # get from https://openweathermap.org/api + cache: 10 # cache time in minutes + - resources: cpu: true memory: true diff --git a/src/utils/owm-condition-map.js b/src/utils/owm-condition-map.js new file mode 100644 index 000000000..ab5658f2b --- /dev/null +++ b/src/utils/owm-condition-map.js @@ -0,0 +1,413 @@ +import * as Icons from "react-icons/wi"; + +const conditions = [ + { + code: 200, + icon: { + day: Icons.WiDayStormShowers, + night: Icons.WiNightAltStormShowers, + }, + }, + { + code: 201, + icon: { + day: Icons.WiDayThunderstorm, + night: Icons.WiNightAltThunderstorm, + }, + }, + { + code: 202, + icon: { + day: Icons.WiDayThunderstorm, + night: Icons.WiNightAltThunderstorm, + }, + }, + { + code: 210, + icon: { + day: Icons.WiDayStormShowers, + night: Icons.WiNightAltStormShowers, + }, + }, + { + code: 211, + icon: { + day: Icons.WiDayThunderstorm, + night: Icons.WiNightAltThunderstorm, + }, + }, + { + code: 212, + icon: { + day: Icons.WiDayThunderstorm, + night: Icons.WiNightAltThunderstorm, + }, + }, + { + code: 221, + icon: { + day: Icons.WiDayThunderstorm, + night: Icons.WiNightAltThunderstorm, + }, + }, + { + code: 230, + icon: { + day: Icons.WiDayStormShowers, + night: Icons.WiNightAltStormShowers, + }, + }, + { + code: 231, + icon: { + day: Icons.WiDayStormShowers, + night: Icons.WiNightAltStormShowers, + }, + }, + { + code: 232, + icon: { + day: Icons.WiDayThunderstorm, + night: Icons.WiNightAltThunderstorm, + }, + }, + + { + code: 300, + icon: { + day: Icons.WiDaySprinkle, + night: Icons.WiNightAltSprinkle, + }, + }, + { + code: 301, + icon: { + day: Icons.WiDaySprinkle, + night: Icons.WiNightAltSprinkle, + }, + }, + { + code: 302, + icon: { + day: Icons.WiDayRain, + night: Icons.WiNightAltRain, + }, + }, + { + code: 310, + icon: { + day: Icons.WiDaySprinkle, + night: Icons.WiNightAltSprinkle, + }, + }, + { + code: 311, + icon: { + day: Icons.WiDayRain, + night: Icons.WiNightAltRain, + }, + }, + { + code: 312, + icon: { + day: Icons.WiDayRain, + night: Icons.WiNightAltRain, + }, + }, + { + code: 313, + icon: { + day: Icons.WiDayShowers, + night: Icons.WiNightAltShowers, + }, + }, + { + code: 314, + icon: { + day: Icons.WiDayShowers, + night: Icons.WiNightAltShowers, + }, + }, + { + code: 321, + icon: { + day: Icons.WiDayShowers, + night: Icons.WiNightAltShowers, + }, + }, + + { + code: 500, + icon: { + day: Icons.WiDayRain, + night: Icons.WiNightAltRain, + }, + }, + { + code: 501, + icon: { + day: Icons.WiDayRain, + night: Icons.WiNightAltRain, + }, + }, + { + code: 502, + icon: { + day: Icons.WiDayRain, + night: Icons.WiNightAltRain, + }, + }, + { + code: 503, + icon: { + day: Icons.WiDayRain, + night: Icons.WiNightAltRain, + }, + }, + { + code: 504, + icon: { + day: Icons.WiDayRain, + night: Icons.WiNightAltRain, + }, + }, + { + code: 511, + icon: { + day: Icons.WiDaySleet, + night: Icons.WiNightAltSleet, + }, + }, + { + code: 520, + icon: { + day: Icons.WiDayShowers, + night: Icons.WiNightAltShowers, + }, + }, + { + code: 521, + icon: { + day: Icons.WiDayShowers, + night: Icons.WiNightAltShowers, + }, + }, + { + code: 522, + icon: { + day: Icons.WiDayShowers, + night: Icons.WiNightAltShowers, + }, + }, + { + code: 531, + icon: { + day: Icons.WiDayShowers, + night: Icons.WiNightAltShowers, + }, + }, + + { + code: 600, + icon: { + day: Icons.WiDaySnow, + night: Icons.WiNightAltSnow, + }, + }, + { + code: 601, + icon: { + day: Icons.WiDaySnow, + night: Icons.WiNightAltSnow, + }, + }, + { + code: 602, + icon: { + day: Icons.WiDaySnow, + night: Icons.WiNightAltSnow, + }, + }, + { + code: 611, + icon: { + day: Icons.WiDaySleet, + night: Icons.WiNightAltSleet, + }, + }, + { + code: 612, + icon: { + day: Icons.WiDaySleet, + night: Icons.WiNightAltSleet, + }, + }, + { + code: 613, + icon: { + day: Icons.WiDaySleet, + night: Icons.WiNightAltSleet, + }, + }, + { + code: 615, + icon: { + day: Icons.WiDayRainMix, + night: Icons.WiNightAltRainMix, + }, + }, + { + code: 616, + icon: { + day: Icons.WiDayRainMix, + night: Icons.WiNightAltRainMix, + }, + }, + { + code: 620, + icon: { + day: Icons.WiDaySnow, + night: Icons.WiNightAltSnow, + }, + }, + { + code: 621, + icon: { + day: Icons.WiDaySnow, + night: Icons.WiNightAltSnow, + }, + }, + { + code: 622, + icon: { + day: Icons.WiDaySnow, + night: Icons.WiNightAltSnow, + }, + }, + + { + code: 701, + icon: { + day: Icons.WiDayFog, + night: Icons.WiNightFog, + }, + }, + { + code: 711, + icon: { + day: Icons.WiSmoke, + night: Icons.WiSmoke, + }, + }, + { + code: 721, + icon: { + day: Icons.WiDayHaze, + night: Icons.WiWindy, + }, + }, + { + code: 731, + icon: { + day: Icons.WiDust, + night: Icons.WiDust, + }, + }, + { + code: 741, + icon: { + day: Icons.WiDayFog, + night: Icons.WiNightFog, + }, + }, + { + code: 751, + icon: { + day: Icons.WiDust, + night: Icons.WiDust, + }, + }, + { + code: 761, + icon: { + day: Icons.WiSandstorm, + night: Icons.WiSandstorm, + }, + }, + { + code: 762, + icon: { + day: Icons.WiDust, + night: Icons.WiDust, + }, + }, + { + code: 771, + icon: { + day: Icons.WiStrongWind, + night: Icons.WiStrongWind, + }, + }, + + { + code: 781, + icon: { + day: Icons.WiTornado, + night: Icons.WiTornado, + }, + }, + + { + code: 800, + icon: { + day: Icons.WiDaySunny, + night: Icons.WiNightClear, + }, + }, + + { + code: 801, + icon: { + day: Icons.WiDayCloudy, + night: Icons.WiNightAltCloudy, + }, + }, + { + code: 802, + icon: { + day: Icons.WiDayCloudy, + night: Icons.WiNightAltCloudy, + }, + }, + { + code: 803, + icon: { + day: Icons.WiDayCloudy, + night: Icons.WiNightAltCloudy, + }, + }, + { + code: 804, + icon: { + day: Icons.WiCloudy, + night: Icons.WiCloudy, + }, + }, + +]; + +export default function mapIcon(weatherStatusCode, timeOfDay) { + const mapping = conditions.find( + (condition) => condition.code === weatherStatusCode + ); + + if (mapping) { + if (timeOfDay === "day") { + return mapping.icon.day; + } else if (timeOfDay === "night") { + return mapping.icon.night; + } + } + + return Icons.WiDaySunny; +}