Demonstrates external HTTP via curl + jq, forEach over a forecast array, and multi-binding interpolation.
Requires jq (brew install jq). Uses the free wttr.in service — no auth needed but the response is heavyweight, so we extract only what we render.
yaml
id: weather
interval: 900 # 15 minutes
order: 60
location: tray
click:
url: "https://wttr.in"
command: |
curl -fsSL --max-time 6 "https://wttr.in/?format=j1" 2>/dev/null \
| jq -c '{
temp_f: (.current_condition[0].temp_F | tonumber),
temp_c: (.current_condition[0].temp_C | tonumber),
condition: .current_condition[0].weatherDesc[0].value,
humidity: (.current_condition[0].humidity | tonumber),
city: .nearest_area[0].areaName[0].value,
forecast: [.weather[0:3] | .[] | {
date: .date,
max_f: (.maxtempF | tonumber),
min_f: (.mintempF | tonumber),
cond: .hourly[4].weatherDesc[0].value
}]
}' \
|| echo '{"temp_f":null,"condition":"offline"}'
view:
hstack:
spacing: 4
children:
- symbol: { name: cloud.sun, size: 13, color: "#5AC8FA" }
- text:
content: "${temp_f}°"
size: 11
weight: semibold
design: rounded
monospacedDigit: true
hover:
vstack:
spacing: 10
alignment: leading
frame: { width: 240 }
children:
- hstack:
children:
- vstack:
alignment: leading
spacing: 0
children:
- text: { content: "${city}", size: 11, color: secondary, weight: medium }
- text: { content: "${condition}", size: 13 }
- spacer: {}
- text:
content: "${temp_f}°"
size: 28
weight: semibold
design: rounded
monospacedDigit: true
- hstack:
spacing: 12
children:
- text: { content: "${temp_c:%.0f}°C", size: 11, color: secondary }
- text: { content: "Humidity ${humidity}%", size: 11, color: secondary }
- divider: {}
- text: { content: "Forecast", size: 10, weight: medium, color: secondary }
- forEach:
in: "${forecast}"
stack: vstack
spacing: 6
alignment: leading
template:
hstack:
spacing: 8
children:
- text: { content: "${item.date}", size: 11, design: monospaced }
- text: { content: "${item.cond}", size: 11, color: secondary, lineLimit: 1 }
- spacer: {}
- text:
content: "${item.max_f:%.0f}° / ${item.min_f:%.0f}°"
size: 11
weight: medium
design: rounded
monospacedDigit: true
What this teaches
- Async HTTP —
curlreturns JSON,jqreshapes it, the widget binds to the reshaped fields. - Graceful failure — the
|| echo '{"temp_f":null,"condition":"offline"}'fallback means a network outage shows "offline" instead of breaking the widget. forEachfor forecast rows — a single template renders three days from${forecast}.- Multi-binding text —
"${item.max_f:%.0f}° / ${item.min_f:%.0f}°"interpolates two values into one string with format specs each.
Building on this
- Pin to a specific city by appending
?location=Berlinor a postcode to the wttr.in URL. - Switch to Celsius primary by swapping
temp_fandtemp_cbindings. - Add a "feels like" row to the popover by extracting
.current_condition[0].FeelsLikeFin the jq pipeline.