Weather Widget (Open-Meteo)

I wanted to spruce up my dashboard, so I made a quick weather widget!
It fetches current weather data using the Open-Meteo API.
It requires no API key but relies on net.proxyFetch, so your silverbullet instance must allow outbound requests.

-- Weather icon mapping (WMO Weather interpretation codes)
local weatherIcons = {
  [0] = "☀️", [1] = "🌤️", [2] = "⛅", [3] = "☁️",
  [45] = "🌫️", [48] = "🌫️", [51] = "🌦️", [53] = "🌦️", [55] = "🌧️",
  [56] = "🌧️", [57] = "🌧️", [61] = "🌧️", [63] = "🌧️", [65] = "🌧️",
  [66] = "🌧️", [67] = "🌧️", [71] = "🌨️", [73] = "🌨️", [75] = "❄️",
  [77] = "🌨️", [80] = "🌦️", [81] = "🌧️", [82] = "🌧️",
  [85] = "🌨️", [86] = "❄️", [95] = "⛈️", [96] = "⛈️", [99] = "⛈️"
}

-- Helper to get coordinates for a city
local function getCoords(city)
  local geoUrl = "https://geocoding-api.open-meteo.com/v1/search?count=1&name=" .. city
  local geoResp = net.proxyFetch(geoUrl)
  if not geoResp.ok then return nil, "Geo Error" end
  local geoData = geoResp.body
  if not geoData.results or #geoData.results == 0 then return nil, "City not found" end
  local loc = geoData.results[1]
  return loc.latitude, loc.longitude
end

-- Simple weather: icon + city + temp (inline friendly)
function weather(city) --City as a string, e.g. "Dubai"
  local lat, lon = getCoords(city)
  if not lat then return "⚠️ " .. lon end
  local url = "https://api.open-meteo.com/v1/forecast?current=temperature_2m,weather_code&latitude=" .. lat .. "&longitude=" .. lon
  local resp = net.proxyFetch(url)
  if not resp.ok then return "⚠️" end
  
  local c = resp.body.current
  local icon = weatherIcons[c.weather_code] or ""
  return icon .. " " .. city .. " " .. c.temperature_2m .. "°C"
end

-- Extended weather: icon + city + temp + humidity 
function weatherExtended(city)
  local lat, lon = getCoords(city)
  if not lat then return "⚠️ " .. lon end
  
  local url = "https://api.open-meteo.com/v1/forecast?current=temperature_2m,relative_humidity_2m,weather_code&latitude=" .. lat .. "&longitude=" .. lon
  local resp = net.proxyFetch(url)
  if not resp.ok then return "⚠️" end
  
  local c = resp.body.current
  local icon = weatherIcons[c.weather_code] or ""
  return icon .. " " .. city .. " " .. c.temperature_2m .. "°C |💧" .. c.relative_humidity_2m .. "%"
end

-- Compact weather: just icon + temp 
function weatherCompact(city)
  local lat, lon = getCoords(city)
  if not lat then return "⚠️" end
  
  local url = "https://api.open-meteo.com/v1/forecast?current=temperature_2m,weather_code&latitude=" .. lat .. "&longitude=" .. lon
  local resp = net.proxyFetch(url)
  if not resp.ok then return "⚠️" end
  
  local c = resp.body.current
  local icon = weatherIcons[c.weather_code] or ""
  return icon .. " " .. c.temperature_2m .. "°C"
end
5 Likes

Really nice widget — clean, readable, and Open-Meteo is a great choice (no API key :+1:).

A couple of small tweaks you might consider to make it even more robust in daily dashboards:

URL-encode the city name
Cities with spaces or accents (e.g. New York, Liège, São Paulo) can break the geocoding request unless encoded.

Add timezone=auto to the forecast call
This avoids subtle mismatches between local time and returned “current” values.

Light caching (optional)
Even a tiny in-memory cache (e.g. 10 minutes for weather, 24h for geocoding) prevents hitting the API on every render.

Slightly stronger error guards
Checking resp.body and resp.body.current avoids edge-case crashes if Open-Meteo returns an empty payload.

Overall though: great dashboard candy, very SB-friendly, and the WMO icon mapping is :ok_hand:
Thanks for sharing!