Last active
October 22, 2024 22:22
-
-
Save sirlancelot/1583689478612435fe9af4ea3990ded2 to your computer and use it in GitHub Desktop.
Obsidian QuickAdd Macro to fetch the weather from OpenWeatherMap
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//@ts-check | |
/** | |
* Fetch weather data from OpenWeatherMap API | |
* | |
* A free OpenWeatherMap API Key is required and can be entered using the Quick | |
* Add Macro settings dialog. | |
* | |
* You should know how to install and use Quick Add Macros before using this. | |
* See https://quickadd.obsidian.guide/docs/Choices/MacroChoice | |
* | |
* Available variables: | |
* | |
* - {{VALUE:clouds}} = `${result.clouds?.all}%` | |
* - {{VALUE:feelsLike}} = `${feels_like.toFixed(0)}°` | |
* - {{VALUE:humidity}} = `${humidity}%` | |
* - {{VALUE:icon}} = parseOpenWeatherMapIcon(weather.icon) | |
* - {{VALUE:image}} = `https://openweathermap.org/img/wn/${icon}.png` | |
* - {{VALUE:image2x}} = `https://openweathermap.org/img/wn/${icon}@2x.png` | |
* - {{VALUE:main}} = weather.main | |
* - {{VALUE:place}} = `${result.name}` | |
* - {{VALUE:pressure}} = `${pressure} hPa` | |
* - {{VALUE:sunrise}} = moment(result.sys.sunrise).format("HH:mm:ss") | |
* - {{VALUE:sunset}} = moment(result.sys.sunset).format("HH:mm:ss") | |
* - {{VALUE:temp}} = `${temp.toFixed(0)}°` | |
* - {{VALUE:tempHigh}} = `${temp_max.toFixed(0)}°` | |
* - {{VALUE:tempLow}} = `${temp_min.toFixed(0)}°` | |
* - {{VALUE:wind}} = result.wind | |
* | |
* If you like this, please let me know: https://ko-fi.com/sirlancelot 🐱👤 | |
*/ | |
/** @type {string} */ | |
const API_BASE_URL = "https://api.openweathermap.org/data/2.5/weather" | |
/** | |
* @typedef {Object} Weather | |
* @property {number} id | |
* @property {string} main | |
* @property {string} description | |
* @property {`${number}${"d"|"n"}`} icon | |
*/ | |
/** | |
* @typedef {Object} Main | |
* @property {number} temp | |
* @property {number} feels_like | |
* @property {number} temp_min | |
* @property {number} temp_max | |
* @property {number} pressure | |
* @property {number} humidity | |
* @property {number} [sea_level] | |
* @property {number} [grnd_level] | |
*/ | |
/** | |
* @typedef {Object} Wind | |
* @property {number} speed | |
* @property {number} deg | |
*/ | |
/** | |
* @typedef {Object} Clouds | |
* @property {number} all | |
*/ | |
/** | |
* @typedef {Object} Sys | |
* @property {number} type | |
* @property {number} id | |
* @property {string} country | |
* @property {number} sunrise | |
* @property {number} sunset | |
*/ | |
/** | |
* @typedef {Object} ApiResponse | |
* @property {{ lat: number; lon: number }} coord | |
* @property {Weather[]} weather | |
* @property {string} base | |
* @property {Main} main | |
* @property {number} visibility | |
* @property {Wind} wind | |
* @property {Clouds} clouds | |
* @property {number} dt | |
* @property {Sys} sys | |
* @property {number} timezone | |
* @property {number} id | |
* @property {string} name | |
* @property {number} cod | |
* @property {string} message error message | |
*/ | |
const API_KEY_OPTION = "OpenWeatherMap API Key" | |
const CITY_OPTION = "City" | |
const UNITS_OPTION = "Units" | |
/** | |
* Convert OpenWeatherMap icon to an emoji character | |
* | |
* @see https://openweathermap.org/weather-conditions | |
* @param {Weather['icon']} icon | |
*/ | |
function parseOpenWeatherMapIcon(icon) { | |
return ( | |
{ | |
"01d": "☀️", | |
"01n": "🌙", | |
"02d": "⛅", | |
"02n": "⛅", | |
"03d": "☁️", | |
"03n": "☁️", | |
"04d": "☁️", | |
"04n": "☁️", | |
"09d": "🌧️", | |
"09n": "🌧️", | |
"10d": "🌦️", | |
"10n": "🌦️", | |
"11d": "⛈️", | |
"11n": "⛈️", | |
"13d": "❄️", | |
"13n": "❄️", | |
"50d": "🌫️", | |
"50n": "🌫️", | |
}[icon] || icon | |
) | |
} | |
/** | |
* @param {{ variables: Record<string, any> }} context | |
* @param {{ "OpenWeatherMap API Key": string; City: string; Units: string }} settings | |
*/ | |
async function entry({ variables }, settings) { | |
const { | |
[API_KEY_OPTION]: appid, | |
[CITY_OPTION]: city, | |
[UNITS_OPTION]: units, | |
} = settings | |
if (!appid) throw new Error("No API Key provided.") | |
if (!city) throw new Error("No City provided.") | |
if (!units) throw new Error("No Units provided.") | |
const query = new URLSearchParams({ appid, units }) | |
query.set(Number(city) ? "id" : "q", city) | |
/** @type {ApiResponse} */ | |
const result = JSON.parse( | |
// @ts-expect-error - `request` is provided by the environment | |
await request({ | |
contentType: "application/json", | |
method: "GET", | |
throw: false, | |
url: `${API_BASE_URL}?${query}`, | |
}) | |
) | |
if (result.cod !== 200) throw new Error(result.message) | |
const [weather] = result.weather | |
const { icon } = weather | |
const { temp, feels_like, temp_min, temp_max, humidity, pressure } = | |
result.main | |
const suffix = units === "imperial" ? "°F" : units === "metric" ? "°C" : "K" | |
Object.assign(variables, { | |
clouds: `${result.clouds?.all}%`, | |
feelsLike: `${feels_like.toFixed(0)}${suffix}`, | |
humidity: `${humidity}%`, | |
icon: parseOpenWeatherMapIcon(icon), | |
image: `https://openweathermap.org/img/wn/${icon}.png`, | |
image2x: `https://openweathermap.org/img/wn/${icon}@2x.png`, | |
main: weather.main, | |
place: `${result.name}`, | |
pressure: `${pressure} hPa`, | |
// @ts-expect-error - `moment` is provided by the environment | |
sunrise: moment(result.sys.sunrise).format("HH:mm:ss"), | |
// @ts-expect-error - `moment` is provided by the environment | |
sunset: moment(result.sys.sunset).format("HH:mm:ss"), | |
temp: `${temp.toFixed(0)}${suffix}`, | |
tempHigh: `${temp_max.toFixed(0)}${suffix}`, | |
tempLow: `${temp_min.toFixed(0)}${suffix}`, | |
wind: result.wind, | |
}) | |
} | |
module.exports = { | |
entry, | |
settings: { | |
name: "Weather Retrieval", | |
author: "Matthew Pietz", | |
options: { | |
[API_KEY_OPTION]: { | |
type: "text", | |
defaultValue: "", | |
placeholder: "", | |
description: | |
"Visit https://openweathermap.org/ to get an API key", | |
}, | |
[CITY_OPTION]: { | |
type: "text", | |
defaultValue: "Milwaukie, OR, US", | |
placeholder: "City,State,Country", | |
description: "Comma separated city name, state code, and country code", | |
}, | |
[UNITS_OPTION]: { | |
type: "dropdown", | |
defaultValue: "imperial", | |
options: ["imperial", "metric", "standard"], | |
description: | |
"Imperial = Fahrenheit, Metric = Celsius, Standard = Kelvin", | |
}, | |
}, | |
}, | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment