Weather Watchdog
This example application compares weather forecast information from Open-Meteo with meteorological airport reports (METAR) from AVWX in real time. It will retrieve the forecast every day for the next 24h and for a number of airports using their geographical coordinates. Then every time some new METAR data is available, it will compare it with the forecast.
This showcases how Renelick can orchestrate tasks, events and data all together in a single application with several services interfacing with third-party APIs. The produced output is real data and might happen to be interesting to look at, however it's primarily a by-product for illustrative purposes.
Specifications
Input sources
- Weather stations: METAR from airports around the world
- Forecast services: Open-Meteo
Tasks
- Collecting data as Node objects
- Processing data: averages, comparisons, correlations etc.
- Generating public output
Output data
- Charts, maps
- Notifications by email and pub/sub events
Flow
A timer service sends pub/sub events at regular intervals with the name of an
airport (ICAO code). This triggers some tasks to go and fetch the
corresponding METAR data, and if it's different than the last sample then it
gets added to the database as a metar node. Only temperature, humidity and
wind measurements are being stored. The app keeps a list of airports to
monitor; a separate event is sent for each of them every 10 minutes.
Meanwhile, another timer service sends hourly events to request new weather
forecast data for the current day. This in turn triggers a task to go and
retrieve it from Open-Meteo and store it as a forecast node.
Every time a new METAR entry is added, a pub/sub event is generated which triggers a task to look at the data, compare it against the forecast and decide what to do with it - not implemented yet. Typically, it will store a sub-tree of data nodes in the database with the result of the comparison, using the METAR entry as the parent node.
Instructions
Renelick weather user
A user account called weather is needed to run this application. To create
one, run this command with an arbitrary email address:
. scripts/devenv
rki register weather@renelick.io weather
rki login --method=key weather
uid=$(rk get-user --user=weather | jq -r .id)
docker-compose exec api renelick-admin $uid set verified
Note: Setting the full name of a user can currently only be done via the web dashboard. For example, in this case it could be set to "Weather Watchdog".
Then to confirm it worked as expected:
$ rk whoami --user=weather
Authenticating with persistent API key
Connecting to http://172.17.0.1:8000
User profile:
id 66d9a3245ab47a1fb6aa0433
username weather
email weather@renelick.io
full_name Weather Watchdog
is_superuser False
is_verified True
External APIs
The METAR data is retrieved from AVWX which requires an API token with a free account. Please follow the Getting Started steps to create a token.
It needs to be stored in the weather user's credentials, which can be done
interactively with this command:
The app will then be able to find it and load it from the stored credentials to access the AVWX API.
No key is needed for non-commercial Open-Meteo clients such as this app, so nothing special needs to be done for it here.
Docker Compose
Like all sample applications provided in this repository, it can be started
using docker-compose. Typically, you may want to leave it running in the
background to let it gather data over some period of time. This can be done
easily by starting the services in detached mode and then monitor the logs:
docker-compose -f apps/weather/docker-compose.yaml up -d
docker-compose -f apps/weather/docker-compose.yaml ps
docker-compose -f apps/weather/docker-compose.yaml logs -f orchestrator
The output should look like this:
Name Command State Ports
----------------------------------------------------------------------------
weather_orchestrator_1 /usr/bin/env python3 /home ... Up 8000/tcp
weather_scheduler_1 /usr/bin/env python3 /home ... Up 8000/tcp
weather_timer-forecast_1 /usr/bin/env python3 /home ... Up 8000/tcp
weather_timer-metar_1 /usr/bin/env python3 /home ... Up 8000/tcp
Attaching to weather_orchestrator_1
orchestrator_1 | Starting orchestrator
orchestrator_1 | User: weather
orchestrator_1 | API: http://172.17.0.1:8000
orchestrator_1 | Time METAR Airport Temp Humid Wind
orchestrator_1 | forecast-LFPG-2024-09-11
orchestrator_1 | forecast-KJFK-2024-09-11
orchestrator_1 | forecast-RJTT-2024-09-11
orchestrator_1 | forecast-SBGR-2024-09-11
orchestrator_1 | 2024-09-11T08:30:00Z LFPG Paris (FR) 13.0ºC 82.0% 9.3 km/h
orchestrator_1 | 2024-09-11T07:51:00Z KJFK New York (US) 16.0ºC 67.1% 5.6 km/h
orchestrator_1 | forecast-FACT-2024-09-11
orchestrator_1 | 2024-09-11T08:00:00Z RJTT Tokyo (JP) 31.0ºC 74.8% 27.8 km/h
orchestrator_1 | 2024-09-11T08:00:00Z SBGR São Paulo (BR) 15.0ºC 77.0%
orchestrator_1 | 2024-09-11T08:00:00Z FACT Cape Town (ZA) 18.0ºC 45.3% 5.6 km/h
orchestrator_1 | forecast-YPPH-2024-09-11
orchestrator_1 | 2024-09-11T08:30:00Z YPPH Perth (AU) 24.0ºC 41.1% 20.4 km/h
Summary
The application currently only creates two kinds of nodes: weather.metar and
weather.forecast. The next things to implement to make it more interesting
would be to compare the METAR results with the forecasts and add other kinds of
nodes with the result.
For now, let's take a look at how we can access the existing data on the command line. For example, to get the latest METAR report for Cape Town:
rk find-nodes \
kind=weather.metar \
data.airport="Cape Town (ZA)" \
created__sort=down \
--limit=1 \
--indent=2
{
"id": "66e1562df1292abef84fbead",
"name": "metar-FACT-2024-09-11T08:00:00Z",
"parent": null,
"artifacts": {},
"kind": "weather.metar",
"data": {
"icao": "FACT",
"airport": "Cape Town (ZA)",
"coordinates": {
"latitude": -33.9648017883,
"longitude": 18.6016998291
},
"time": "2024-09-11T08:00:00Z",
"humidity": [
0.45322763633169727,
null
],
"temperature": [
18.0,
"C"
],
"wind_speed": [
3.0,
"kt"
]
},
"task": {
"attributes": {
"name": "metar",
"icao": "FACT"
},
"scheduler": "weather-inline",
"id": "d67708ec-a22f-4c1c-9fef-82b0852e5cfa",
"timeout": "2024-09-11T08:35:52.414000"
},
"path": [
"metar-FACT-2024-09-11T08:00:00Z"
],
"created": "2024-09-11T08:34:53.792000",
"owner": "weather"
}
Any field can be used, for example to look for entries with a temperature above
15°C in the southern hemisphere, and using jq to filter the output:
rk find-nodes \
kind=weather.metar \
data.coordinates.latitude__lt=0 \
data.temperature__gt=15 \
--limit=3 \
| jq '.data.airport, .data.time, .data.temperature'
As a first case study, this is already making use of many Renelick features:
- trigger events
- orchestration logic
- task scheduler
- data nodes
- user authentication
- command-line tool
- containerised deployments
One particular aspect that's not covered here is the tree structure of the data, as the current nodes don't have any hierarchy. This will start to make more sense with post-processing of the collected data.
That's all, Folks! To be continued...