Skip to content

custom data sources beyond the BACnet driver #45

@bbartling

Description

@bbartling

🎯 Goal

Add a pathway for custom data sources beyond the BACnet driver by supporting an AWS Lambda–style “handler” that a user can upload as a .zip. The handler should fetch data from an external source, sync values into the Open-FDD database/time-series layer, and attach them to the existing site data model (points).

This enables:

  • custom drivers (weather, utility APIs, vendor clouds, Modbus gateways, CSV pullers, etc.)
  • future “cloud export” or “cloud ingest” workflows using the same plugin mechanism

🛠 What To Do

  1. Define a minimal driver plugin contract (Lambda-like):
    • A Python entrypoint function (e.g., handler(event, context))
    • A structured return payload that Open-FDD can ingest
  2. Implement a basic backend endpoint + loader concept (MVP):
    • Accept a .zip upload (containing handler.py + requirements.txt optional)
    • Store it for execution (local dev is fine; sandboxing can be later)
    • Allow running it on demand OR on a simple schedule (hourly) (MVP can be manual trigger)
  3. Demonstrate the system with a weather API custom driver.

✅ “Dirt Simple” Example Handler (Weather)

Below is an example handler.py that calls a free weather API (Open-Meteo) once per run and returns values in a simple structure.

Note: this is a demo driver; it’s okay if the MVP just prints/returns values and the Open-FDD backend wires storage later.

# handler.py
import json
import urllib.request
from datetime import datetime, timezone

def handler(event, context=None):
    # event should provide lat/lon and a site_id, but defaults are fine for demo
    lat = float(event.get("lat", 43.0389))   # Madison-ish default
    lon = float(event.get("lon", -89.4012))
    site_id = event.get("site_id", "demo_site")

    url = (
        "https://api.open-meteo.com/v1/forecast"
        f"?latitude={lat}&longitude={lon}"
        "&current=temperature_2m,relative_humidity_2m,wind_speed_10m"
    )

    with urllib.request.urlopen(url, timeout=10) as resp:
        data = json.loads(resp.read().decode("utf-8"))

    current = data.get("current", {})
    now = datetime.now(timezone.utc).isoformat()

    # Return a normalized payload that Open-FDD can ingest.
    # "points" keys should match timeseries keys or model point identifiers.
    return {
        "site_id": site_id,
        "timestamp": current.get("time", now),
        "points": {
            "weather_oat_c": current.get("temperature_2m"),
            "weather_rh_pct": current.get("relative_humidity_2m"),
            "weather_wind_mps": current.get("wind_speed_10m"),
        },
        "meta": {
            "source": "open-meteo",
            "lat": lat,
            "lon": lon,
        },
    }

To zip it:

zip -r openfdd_weather_driver.zip handler.py

🔌 Expected Integration (MVP)

  • Open-FDD stores the handler zip and can execute it with an event payload like:

    • site_id, lat, lon
  • The handler returns {site_id, timestamp, points{...}}

  • Open-FDD writes:

    • points into time-series storage (Timescale or whatever is configured)
    • optional mapping metadata so these points can be attached to the data model (Brick tagging later)

📎 Deliverable

  • A short doc describing the driver plugin contract (inputs/outputs)

  • Minimal backend wiring to upload and execute a zipped driver (manual trigger is OK)

  • A working demo using the included weather handler:

    • run once and persist at least 1–3 values into the database/time-series
    • show a screenshot or query output proving the write happened

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions