diff --git a/README.md b/README.md index e519dce..0bfe40d 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,78 @@ # go-weatherstation -Starter go-weatherstationrology data pipeline: +Starter weather-station data pipeline: - MQTT ingest of WS90 payloads -> TimescaleDB -- Baseline forecast polling (Open-Meteo) -> TimescaleDB +- ECMWF (Open-Meteo) forecast polling -> TimescaleDB +- Web UI with live metrics, comparisons, and charts -## Run +## Quick start 1) Start services: - docker compose up -d + `docker compose up -d` -2) Copy config: - cp config.example.yaml config.yaml - # edit mqtt topic/broker + site lat/lon +2) Configure: + edit `config.yaml` (or `test.yaml`) with your MQTT broker, topic, and site coordinates. -3) Run: - go run ./cmd/ingestd -config config.yaml +3) Run the ingest service locally: + `go run ./cmd/ingestd -config config.yaml` -4) Web UI: - http://localhost:8080 +4) Open the UI: + `http://localhost:8080` + +## Configuration reference +`config.yaml` is the single config file for ingest, forecast polling, and the web UI. + +```yaml +mqtt: + broker: "tcp://mosquitto:1883" # MQTT broker URL + client_id: "go-weatherstation-ingestd" # Client ID + username: "" # Optional username + password: "" # Optional password + topic: "ecowitt/ws90" # Topic to subscribe to + qos: 1 # MQTT QoS (0, 1, or 2) + +db: + conn_string: "postgres://postgres:postgres@timescaledb:5432/micrometeo?sslmode=disable" + +site: + name: "home" + latitude: -33.8688 # WGS84 latitude + longitude: 151.2093 # WGS84 longitude + elevation_m: 50 # Currently informational (not used by Open-Meteo ECMWF endpoint) + +pollers: + open_meteo: + enabled: true + interval: "30m" # How often to fetch forecast data + model: "ecmwf" # Stored as metadata; endpoint is fixed to ECMWF + +web: + enabled: true + listen: ":8080" # Web UI listen address + +wunderground: + enabled: false + station_id: "YOUR_STATION_ID" + station_key: "YOUR_STATION_KEY" + interval: "60s" # Upload cadence +``` + +### Notes +- The Open-Meteo ECMWF endpoint is queried by the poller only. The UI reads forecasts from TimescaleDB. +- Web UI supports Local/UTC toggle and date-aligned ranges (6h, 24h, 72h, 7d). + +## Schema & tables +TimescaleDB schema is initialized from `db/init/001_schema.sql` and includes: + +- `observations_ws90` (hypertable): raw WS90 observations with payload metadata, plus the full JSON payload (`payload_json`). +- `forecast_openmeteo_hourly` (hypertable): hourly forecast points keyed by `(site, model, retrieved_at, ts)`. +- Continuous aggregates: + - `cagg_ws90_1m`: 1‑minute rollups (avg/min/max for temp, humidity, wind, uvi, light, rain). + - `cagg_ws90_5m`: 5‑minute rollups (same metrics as `cagg_ws90_1m`). + +Retention/compression: +- `observations_ws90` has a 90‑day retention policy and compression after 7 days. ## Publish a test WS90 payload +```sh mosquitto_pub -h localhost -t ecowitt/ws90 -m '{"model":"Fineoffset-WS90","id":70618,"battery_ok":1,"battery_mV":3180,"temperature_C":24.2,"humidity":60,"wind_dir_deg":129,"wind_avg_m_s":0,"wind_max_m_s":0,"uvi":0,"light_lux":0,"flags":130,"rain_mm":0,"rain_start":0,"supercap_V":0.5,"firmware":160,"data":"3fff000000------0000ff7ff70000","mic":"CRC","protocol":"Fine Offset Electronics WS90 weather station","rssi":-44,"duration":32996}' +``` diff --git a/cmd/ingestd/web/app.js b/cmd/ingestd/web/app.js index b753f8b..c08fd41 100644 --- a/cmd/ingestd/web/app.js +++ b/cmd/ingestd/web/app.js @@ -109,6 +109,13 @@ function computeRange(range, tz) { axisEnd = end; break; } + case "24h-last": { + end = now; + start = new Date(now.getTime() - 24 * 60 * 60 * 1000); + axisStart = start; + axisEnd = end; + break; + } case "72h": { end = startOfDay(now, tz); start = new Date(end.getTime() - 72 * 60 * 60 * 1000); @@ -249,16 +256,6 @@ function renderDashboard(data) { updateText("live-supercap", "--"); } - const forecastUrl = document.getElementById("forecast-url"); - if (forecastUrl) { - if (data.open_meteo_url) { - forecastUrl.href = data.open_meteo_url; - forecastUrl.textContent = data.open_meteo_url; - } else { - forecastUrl.textContent = "--"; - } - } - const obs = data.observations || []; const forecastAll = (data.forecast && data.forecast.points) || []; diff --git a/cmd/ingestd/web/index.html b/cmd/ingestd/web/index.html index a6bdcfe..95bc649 100644 --- a/cmd/ingestd/web/index.html +++ b/cmd/ingestd/web/index.html @@ -19,6 +19,7 @@