winds.mobi: Paraglider pilot, kitesurfer, check real-time weather conditions of your favorite spots on your smartphone, your tablet or your computer.
Python scripts that get the weather data from different providers and save it in a common format into mongodb. This project use Google Cloud APIs to compute any missing station details (altitude, name, timezone, ...). Google Cloud API results are cached with redis.
- python 3.10 and poetry
- mongodb 4.4
- redis
- Google Cloud API key
See settings.py
Create a .env
file from .env.template
which will be read by docker compose:
- fill GOOGLE_API_KEY with you own Google Cloud API key
- optionally fill the missing secrets for each provider
Then start the external services and the providers scheduler:
docker compose --profile=scheduler up --build
Some providers need winds-mobi-admin running to get stations metadata.
-
brew install openssl
-
export LDFLAGS=-L/usr/local/opt/openssl/lib
-
brew install libpq
-
export PATH=/usr/local/opt/libpq/bin:$PATH
-
brew install mysql-client
-
export PATH=/usr/local/opt/mysql-client/bin:$PATH
poetry install
poetry shell
Create a .env.localhost
file from .env.localhost.template
which will be read by dotenv
for our local commands:
- fill GOOGLE_API_KEY with you own Google Cloud API key
- optionally fill the missing secrets for each provider
Create a .env
file from .env.template
which will be read by docker compose.
Then start the external services:
docker compose up
dotenv -f .env.localhost run python run_providers.py
dotenv -f .env.localhost run python providers/ffvl.py
Format your code: poetry run black .
Run the linter: poetry run flake8 .
You know a good weather station that would be useful for many paraglider pilots or kitesurfers?
Awesome! Fork this repository and create a pull request with your new provider code. It's easy, look at the following example:
providers/my_provider.py
import arrow
import requests
from winds_mobi_provider import Provider, StationStatus, ureg, Q_, Pressure
class MyProvider(Provider):
provider_code = "my-provider"
provider_name = "my-provider.com"
def process_data(self):
self.log.info("Processing MyProvider data...")
# data = requests.get(
# "https://api.my-provider.com/stations.json", timeout=(self.connect_timeout, self.read_timeout)
# ).json()
data = [{
"id": "station-1",
"name": "Station 1",
"latitude": 46.713,
"longitude": 6.503,
"status": "ok",
"measures": [{
"time": arrow.now().format("YYYY-MM-DD HH:mm:ssZZ"),
"windDirection": 180,
"windAverage": 10.5,
"windMaximum": 20.1,
"temperature": 25.7,
"pressure": 1013,
}]
}]
for station in data:
winds_station = self.save_station(
provider_id=station["id"],
short_name=station["name"],
name=None, # Lets winds.mobi provide the full name with the help of Google Geocoding API
latitude=station["latitude"],
longitude=station["longitude"],
status=StationStatus.GREEN if station["status"] == "ok" else StationStatus.RED,
url=f"https://my-provider.com/stations/{station['id']}",
)
measure_key = arrow.get(station["measures"][0]["time"], "YYYY-MM-DD HH:mm:ssZZ").int_timestamp
measures_collection = self.measures_collection(winds_station["_id"])
if not self.has_measure(measures_collection, measure_key):
new_measure = self.create_measure(
for_station=winds_station,
_id=measure_key,
wind_direction=station["measures"][0]["windDirection"],
wind_average=Q_(station["measures"][0]["windAverage"], ureg.meter / ureg.second),
wind_maximum=Q_(station["measures"][0]["windMaximum"], ureg.meter / ureg.second),
temperature=Q_(station["measures"][0]["temperature"], ureg.degC),
pressure=Pressure(station["measures"][0]["pressure"], qnh=None, qff=None),
)
self.insert_new_measures(measures_collection, winds_station, [new_measure])
self.log.info("...Done !")
def my_provider():
MyProvider().process_data()
if __name__ == "__main__":
my_provider()
Start the external services:
docker compose up
Build a Docker image containing your new provider providers/my_provider.py
:
docker build --tag=winds.mobi/my_provider .
Then run your provider inside a container with:
docker run -it --rm --env-file=.env --network=winds-mobi-providers --entrypoint=python winds.mobi/my_provider -m providers.my_provider
To avoid building a new image on every change, you can mount your local source to the container directory /opt/project
with a Docker volume:
docker run -it --rm --env-file=.env --network=winds-mobi-providers --volume=$(pwd):/opt/project --entrypoint=python winds.mobi/my_provider -m providers.my_provider
Please see the file called LICENSE.txt