pyrtan / sourcev0.19.0 · main

pyrtan / examples / weather / weather.toml

4,038 bytes · last changed 2026-10-26 in The catalog grows up: namespaces, publishing by pull request through the gate, gap reports gossiped back to authors; release 0.8.0

1# Weather for a place, from Open-Meteo (no key needed). Two hops: geocode
2# the name, then fetch the forecast for the coordinates. Each hop has its
3# own decision so a failure says which hop failed.
4
5name = "weather"
6version = 2
7namespace = "pyrtan"
8description = "Current weather for a place, from Open-Meteo."
9tags = ["weather", "read-only"]
10license = "MIT"
11hosts = ["geocoding-api.open-meteo.com", "api.open-meteo.com"]
12start = "geocode"
13
14# "forecast in Oslo" and "temperature in Oslo" read as "weather in Oslo".
15[language]
16synonyms = { weather = ["forecast", "temperature"] }
17
18[[triggers]]
19pattern = "[what's the] weather in {place}"
20[[triggers]]
21pattern = "[what's the] weather for {place}"
22[[triggers]]
23pattern = "how [hot|cold|warm] is it in {place}"
24
25[inputs.place]
26type = "str"
27description = "A town or city name"
28
29[inputs.units]
30type = "str"
31default = "celsius"
32one_of = ["celsius", "fahrenheit"]
33
34[nodes.geocode]
35kind = "observe"
36http = { method = "GET", url = "https://geocoding-api.open-meteo.com/v1/search?name={{ inputs.place | url }}&count=1&language=en&format=json" }
37into = "geo"
38extract = [
39 { into = "hit", json = "json.results[0]" },
40 { into = "lat", json = "json.results[0].latitude" },
41 { into = "lon", json = "json.results[0].longitude" },
42 { into = "name", json = "json.results[0].name" },
43 { into = "country", json = "json.results[0].country" },
44]
45next = "found"
46
47[nodes.found]
48kind = "decide"
49on = "hit"
50branches = [
51 { when = { exists = true }, then = "forecast" },
52 { else = "unknown-place" },
53]
54
55[nodes.forecast]
56kind = "observe"
57http = { method = "GET", url = "https://api.open-meteo.com/v1/forecast?latitude={{ state.lat }}&longitude={{ state.lon }}&current=temperature_2m,relative_humidity_2m,wind_speed_10m,weather_code&temperature_unit={{ inputs.units }}" }
58into = "fc"
59extract = [
60 { into = "temp", json = "json.current.temperature_2m" },
61 { into = "humidity", json = "json.current.relative_humidity_2m" },
62 { into = "wind", json = "json.current.wind_speed_10m" },
63 { into = "code", json = "json.current.weather_code" },
64 { into = "unit", json = "json.current_units.temperature_2m" },
65]
66next = "have-temp"
67
68[nodes.have-temp]
69kind = "decide"
70on = "temp"
71branches = [
72 { when = { type = "number" }, then = "sky" },
73 { else = "no-forecast" },
74]
75
76# WMO weather codes, folded into words a person would use.
77[nodes.sky]
78kind = "decide"
79on = "code"
80branches = [
81 { when = { eq = 0 }, then = "clear" },
82 { when = { between = [1, 3] }, then = "cloudy" },
83 { when = { between = [45, 48] }, then = "fog" },
84 { when = { between = [51, 67] }, then = "rain" },
85 { when = { between = [71, 77] }, then = "snow" },
86 { when = { between = [80, 82] }, then = "rain" },
87 { when = { between = [85, 86] }, then = "snow" },
88 { when = { between = [95, 99] }, then = "storm" },
89 { else = "unknown-sky" },
90]
91
92[nodes.clear]
93kind = "set"
94set = { sky = "clear" }
95next = "reply"
96[nodes.cloudy]
97kind = "set"
98set = { sky = "cloudy" }
99next = "reply"
100[nodes.fog]
101kind = "set"
102set = { sky = "foggy" }
103next = "reply"
104[nodes.rain]
105kind = "set"
106set = { sky = "raining" }
107next = "reply"
108[nodes.snow]
109kind = "set"
110set = { sky = "snowing" }
111next = "reply"
112[nodes.storm]
113kind = "set"
114set = { sky = "stormy" }
115next = "reply"
116[nodes.unknown-sky]
117kind = "set"
118set = { sky = "weather code {{ state.code }}" }
119next = "reply"
120
121[nodes.reply]
122kind = "done"
123say = "{{ state.name }}, {{ state.country }}: {{ state.temp }}{{ state.unit }}, {{ state.sky }}, humidity {{ state.humidity }}%, wind {{ state.wind }} km/h."
124output = { place = "{{ state.name }}", country = "{{ state.country }}", temperature = "{{ state.temp }}", unit = "{{ state.unit }}", sky = "{{ state.sky }}", humidity = "{{ state.humidity }}", wind_kmh = "{{ state.wind }}" }
125
126[nodes.unknown-place]
127kind = "fail"
128reason = "unknown-place"
129say = "I could not find a place called {{ inputs.place }}."
130
131[nodes.no-forecast]
132kind = "fail"
133reason = "no-forecast"
134say = "Open-Meteo found {{ state.name }} but returned no current temperature (status {{ state.fc.status }})."