diff --git a/src/components/widget.jsx b/src/components/widget.jsx
index 415cc7d4..0dd26535 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 00000000..975c1f7e
--- /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 00000000..9f0a61ac
--- /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 (
+
+ );
+ }
+
+ 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 00000000..471f69df
--- /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 e366a799..011433b6 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 00000000..ab5658f2
--- /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;
+}