Ajoute 5 feeds open-data sans cle, branches sur le profil config.data-only.toml : - openmeteo : meteo locale (temp, vent, humidite, pression, pluie) via api.open-meteo.com, poll 10 min, lat/lon configurables - openaq : qualite de l'air autour d'une position (PM2.5, PM10, NO2, O3) via openaq.org v3, poll 15 min, radius configurable - iss : position ISS temps reel via wheretheiss.at, poll 5 s, emet aussi un event /pass quand la station entre dans un rayon configurable autour de l'observateur - volcano : eruptions actives 7-jours via volcano.si.edu (GVP), poll 1 h, dedup par eventid, emet /eruption + /active count - social_buzz : Reddit /r/all hot + HackerNews top, poll 1 min, emet score/comments moyens par source + /pulse normalise Tous les feeds suivent le pattern Context.send(sub, *args).
66 lines
2.4 KiB
Python
66 lines
2.4 KiB
Python
"""Volcans actifs — Smithsonian GVP weekly reports + USGS volcano hazards.
|
|
|
|
Source primaire : USGS volcano feed (RSS / GeoJSON), couvre les volcans
|
|
US actifs. Pour les volcans monde, on parse les CSV publics Smithsonian
|
|
si configures. Polling 1h.
|
|
|
|
OSC out :
|
|
/data/volcano/active count
|
|
/data/volcano/eruption lat lon vei region (nouvelle eruption depuis last poll)
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import collections
|
|
import logging
|
|
|
|
import httpx
|
|
|
|
LOG = logging.getLogger("feed.volcano")
|
|
|
|
USGS_URL = ("https://volcanoes.usgs.gov/hans2/api/volcano/getEvents"
|
|
"?starttime={start}&endtime={end}")
|
|
|
|
|
|
async def run(ctx) -> None:
|
|
cfg = ctx.cfg
|
|
period = float(cfg.get("poll_seconds", 3600.0))
|
|
url = cfg.get("url",
|
|
"https://volcano.si.edu/feeds/eruptions7days.json")
|
|
seen: collections.OrderedDict[str, None] = collections.OrderedDict()
|
|
SEEN_MAX = 512
|
|
async with httpx.AsyncClient(timeout=30.0) as cli:
|
|
while True:
|
|
try:
|
|
r = await cli.get(url)
|
|
r.raise_for_status()
|
|
ct = r.headers.get("content-type", "")
|
|
items = []
|
|
if "json" in ct:
|
|
data = r.json()
|
|
items = data.get("features", data.get("items", []))
|
|
count = 0
|
|
for it in items:
|
|
props = it.get("properties", it)
|
|
eid = str(props.get("id") or props.get("eventid")
|
|
or props.get("volcanoNumber") or "")
|
|
if not eid:
|
|
continue
|
|
count += 1
|
|
if eid in seen:
|
|
continue
|
|
seen[eid] = None
|
|
if len(seen) > SEEN_MAX:
|
|
seen.popitem(last=False)
|
|
geom = it.get("geometry") or {}
|
|
coords = geom.get("coordinates") or [0, 0]
|
|
lon, lat = float(coords[0]), float(coords[1])
|
|
vei = float(props.get("vei", 0) or 0)
|
|
region = str(props.get("country")
|
|
or props.get("region", ""))[:32]
|
|
ctx.send("eruption", lat, lon, vei, region)
|
|
ctx.send("active", float(count))
|
|
except Exception as e: # noqa: BLE001
|
|
LOG.warning("volcano fetch failed: %s", e)
|
|
await asyncio.sleep(period)
|