Prometheus Ecowitt Exporter on NixOS
6 min readEcowitt weather stations are solid hardware. The sensors are reliable, the gateways are cheap, and the ecosystem covers everything from soil moisture to lightning detection. The cloud platform, though — that’s where it falls apart. Limited retention, clunky dashboards, no alerting worth mentioning, and your data sitting on someone else’s servers. You already run Prometheus and Grafana for everything else. You just need a bridge.
prometheus-ecowitt-exporter is that bridge. It’s a Rust service that receives HTTP POSTs from your Ecowitt gateway, converts the readings into Prometheus metrics, and exposes them on a /metrics endpoint. It ships with a NixOS module that wires everything up — including optional Prometheus scrape config and a Grafana dashboard.
How the data flows
The path from sensor to graph looks like this:
Ecowitt sensors
↓ RF 433/868MHz
Gateway (GW1100, etc.)
↓ HTTP POST
prometheus-ecowitt-exporter
↓ /metrics
Prometheus
↓ query
Grafana
Your sensors transmit over RF to the gateway. The gateway — typically a GW1100 or similar — supports “customized” weather services, which is really just an HTTP POST to an arbitrary endpoint on an interval you choose. The exporter receives those posts, parses the Ecowitt protocol, applies unit conversions, and serves the results as Prometheus metrics. Standard scrape-and-graph from there.
No polling. No API keys. No cloud dependency. The gateway pushes directly to your host.
Getting the module
You have two options for pulling this into your NixOS configuration: directly from the flake, or through NUR.
Direct flake input
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
prometheus-ecowitt-exporter.url = "github:ijohanne/prometheus-ecowitt-exporter";
};
outputs = { nixpkgs, prometheus-ecowitt-exporter, ... }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
modules = [
prometheus-ecowitt-exporter.nixosModules.default
./configuration.nix
];
};
};
}
This pins you to a specific revision of the exporter. You control when you update. Straightforward.
Via NUR
{
inputs.nur.url = "github:nix-community/NUR";
outputs = { self, nixpkgs, nur, ... }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
modules = [
nur.modules.nixos.repos.ijohanne.prometheus-ecowitt-exporter
./configuration.nix
];
};
};
}
Same module, different delivery mechanism. If you already use NUR for other packages, this keeps your inputs list shorter.
NixOS configuration
Here’s a full configuration with all the knobs visible:
{
services.prometheus-ecowitt-exporter = {
enable = true;
port = 8088;
temperatureUnit = "c";
pressureUnit = "hpa";
windUnit = "kmh";
rainUnit = "mm";
distanceUnit = "km";
irradianceUnit = "wm2";
aqiStandard = "epa";
outdoorLocation = "garden";
indoorLocation = "living-room";
enableLocalScraping = true;
enableGrafanaDashboard = true;
};
}
Two options deserve special attention. enableLocalScraping automatically adds a Prometheus scrape target for the exporter — no need to manually edit your scrapeConfigs. enableGrafanaDashboard provisions a Grafana dashboard that covers all the metric types the exporter produces. Both save you the tedium of wiring things together by hand.
The service runs as a dedicated ecowitt-exporter user by default. You can override user and group if you have opinions about that. The listenAddress defaults to 0.0.0.0 — change it if you want to bind to a specific interface.
Unit conversions
The exporter handles all unit conversion server-side. You configure it once and every metric comes out in the units you actually want.
| Measurement | Default | Options |
|---|---|---|
| Temperature | c | c, f, k |
| Pressure | hpa | hpa, inhg, mmhg |
| Wind | kmh | kmh, mph, ms, knots, fps |
| Rain | mm | mm, in |
| Distance | km | km, mi |
| Irradiance | wm2 | wm2, lx, fc |
| AQI Standard | epa | uk, epa, mep, nepm |
The AQI standard option is worth noting — it changes the algorithm used to calculate the air quality index from PM2.5 readings. UK, EPA, Chinese MEP, and Australian NEPM standards are all available. Pick whichever one your local regulatory body uses.
Multi-station support
Each Ecowitt gateway posts to a URL path you configure, and that path segment becomes the station label on every metric:
POST /report/garden → station="garden"
POST /report/rooftop → station="rooftop"
One exporter instance handles multiple stations. No duplicate deployments, no port juggling, no metric collisions. You just point each gateway at a different path.
Sensor location labels
Multi-channel temperature and humidity sensors — the WN31, WH31, and similar — show up as numbered channels. Channel numbers are not particularly descriptive in a dashboard. The tempLocations option maps them to human-readable labels:
services.prometheus-ecowitt-exporter = {
tempLocations = {
"1" = "greenhouse";
"2" = "garage";
"3" = "pool";
};
};
These strings become the location label on ecowitt_temp and ecowitt_humidity metrics. Your Grafana panels say “greenhouse” instead of “channel 1.”
Configuring the weather station gateway
The gateway needs to know where to send its data. You configure this through the WSView Plus app on your phone:
- Open WSView Plus and go to Device List
- Select your gateway
- Navigate to Weather Services, then Customized
- Set Enable to On
- Set Protocol Type to Ecowitt
- Enter your NixOS host’s IP address as the Server IP/Hostname
- Set the Path to
/report/mystationname— pick a name that makes sense as a Prometheus label - Set Port to
8088(or whatever you configured) - Set Upload Interval to 60 seconds
That last setting controls how often you get new data points. Sixty seconds is a reasonable default. You can go lower, but your weather doesn’t change that fast.
What metrics you get
The exporter produces a comprehensive set of metrics. Here’s what each family covers:
ecowitt_temp— temperature readings from outdoor, indoor, and up to eight multi-channel sensors, withstation,sensor,unit, andlocationlabelsecowitt_humidity— outdoor, indoor, and eight channelsecowitt_pressure— barometric pressure in both absolute and relative variants, plus vapor pressure deficitecowitt_windspeedandecowitt_windspeed_beaufort— current speed, gust, max daily gust, and wind directionecowitt_rain— hourly, daily, weekly, monthly, yearly, and event totals for both standard and WS90 piezo rain sensorsecowitt_solar_radiationandecowitt_uv_index— solar metricsecowitt_lightning_distanceandecowitt_lightning_count— lightning detection dataecowitt_pm25andecowitt_aqi— PM2.5 concentration with 24-hour averages, plus calculated AQI under your chosen standardecowitt_soil_moisture— multiple channels with raw voltage readingsecowitt_battery— status, voltage, and level for every sensor type
Every metric carries appropriate labels so you can filter and aggregate in PromQL without ambiguity.
Grafana dashboard
When you set enableGrafanaDashboard = true, the module provisions a Grafana dashboard automatically through Grafana’s provisioning system. It covers all the metric families listed above with sensible panel layouts. You get graphs, gauges, and stat panels — the kind of thing that would take an afternoon to build by hand.
If you want to customize it, the provisioned dashboard is a starting point. Clone it in Grafana and edit from there.
Docker alternative
Not on NixOS? The exporter builds as a standard Docker image:
docker build -t prometheus-ecowitt-exporter .
docker run -p 8088:8088 prometheus-ecowitt-exporter \
--temperature-unit c --pressure-unit hpa
You lose the automatic Prometheus and Grafana integration, but the exporter itself works the same way. Point your gateway at the container’s IP and port, configure your Prometheus scrape target manually, and import a dashboard.
Testing with curl
You don’t need a weather station connected to verify the exporter is working. Fake a gateway POST:
curl -X POST http://localhost:8088/report/test \
-d "tempf=72.5&humidity=45&baromrelin=29.92&windspeedmph=5.6&dailyrainin=0.02"
Then check the metrics endpoint:
curl http://localhost:8088/metrics
You should see Prometheus-formatted metrics with the values you posted — converted to whatever units you configured. This is also useful for testing alerting rules before the next thunderstorm.
Supported hardware
The exporter handles data from the full Ecowitt ecosystem:
- WS2910 — weather station
- GW1100 — Wi-Fi gateway
- WS69 and WS90 — sensor arrays
- WH41 and WH43 — PM2.5 air quality sensors
- WH57 — lightning detection sensor
- WN31, WH31, WN32, WH32 — temperature and humidity sensors
- WN36 — pool temperature sensor
- WH51 — soil moisture meter
If your sensor transmits to an Ecowitt gateway and the gateway can do a customized HTTP POST, the exporter will handle it.
Wrapping up
The whole setup is a flake input, a module configuration, and a phone app setting. Your weather data stays local, updates every minute, and sits in the same Prometheus instance as the rest of your infrastructure metrics. You can alert on freezing temperatures the same way you alert on disk space. The project is on GitHub under MIT — contributions welcome.