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..43edc0918 --- /dev/null +++ b/src/components/widgets/openweathermap/weather.jsx @@ -0,0 +1,41 @@ +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}&units=${options.units}` + ); + + if (error) { + return ( +
+ +
+ API + Error +
+
+ ); + } + + if (!data) { + return
; + } + + if (data.error) { + return
; + } + return ( +
+ data.sys.sunrise) && (data.dt < data.sys.sundown) ? "day" : "night"} /> +
+ + {data.main.temp.toFixed(1)}° + + {data.weather[0].description.charAt(0).toUpperCase() + data.weather[0].description.slice(1)} +
+
+ ); +} diff --git a/src/pages/api/widgets/openweathermap.js b/src/pages/api/widgets/openweathermap.js new file mode 100644 index 000000000..07dc91399 --- /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, units } = req.query; + + const api_url = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${apiKey}&units=${units}`; + + 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; +}