69 lines
2.0 KiB
Python
69 lines
2.0 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
health-poller: pull vitals from consumer health devices into Inou.
|
|
Wraps Home Assistant integrations — never reimplements vendor APIs.
|
|
|
|
Usage:
|
|
python -m poller.main --config config.yaml
|
|
"""
|
|
import argparse
|
|
import asyncio
|
|
import logging
|
|
from poller.config import load_config
|
|
from poller.dedup import Dedup
|
|
from poller.sink import Sink
|
|
from poller.sources.renpho import RenphoSource
|
|
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
|
|
datefmt="%Y-%m-%d %H:%M:%S",
|
|
)
|
|
log = logging.getLogger("health-poller")
|
|
|
|
SOURCE_CLASSES = {
|
|
"renpho": RenphoSource,
|
|
}
|
|
|
|
|
|
def make_source(cfg: dict):
|
|
cls = SOURCE_CLASSES.get(cfg["type"])
|
|
if not cls:
|
|
raise ValueError(f"unknown source type: {cfg['type']}")
|
|
if cfg["type"] == "renpho":
|
|
return cls(email=cfg["email"], password=cfg["password"], user_id=cfg.get("user_id"))
|
|
raise ValueError(f"no constructor for source type: {cfg['type']}")
|
|
|
|
|
|
async def poll_source(src_cfg: dict, dedup: Dedup, sink: Sink):
|
|
source = make_source(src_cfg)
|
|
dossier_id = src_cfg.get("dossier_id", "")
|
|
readings = await source.fetch()
|
|
new = dedup.filter_new(readings)
|
|
if new:
|
|
sink.push(dossier_id, new)
|
|
dedup.mark_seen(new)
|
|
log.info(f"{src_cfg['type']}: pushed {len(new)} new readings")
|
|
else:
|
|
log.info(f"{src_cfg['type']}: no new readings")
|
|
|
|
|
|
async def main():
|
|
parser = argparse.ArgumentParser(description="Inou health data poller")
|
|
parser.add_argument("--config", default="config.yaml", help="config file path")
|
|
args = parser.parse_args()
|
|
|
|
cfg = load_config(args.config)
|
|
dedup = Dedup()
|
|
sink = Sink(cfg["inou"]["api_url"], cfg["inou"].get("api_key", ""))
|
|
|
|
for src_cfg in cfg["sources"]:
|
|
try:
|
|
await poll_source(src_cfg, dedup, sink)
|
|
except Exception:
|
|
log.exception(f"error polling {src_cfg['type']}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|