Ecowitt exporter for Prometheus & InfluxDB
The WiFi-enabled Ecowitt weather stations can export metrics in a number of protocols to various online weather services, as a push operation. They also support pushing to a custom endpoint in a choice of two protocols, Ecowitt or Wunderground. Blogger Ernest Neijenhuis has checked out the protocols and found that the Ecowitt protocol is more comprehensive. He then wrote a Python app called Ecowither to gateway the Ecowitt metrics into InfluxDB.
Here I am building on his work to present the Ecowitt metrics as an exporter for Prometheus.
This exporter runs on a single HTTP port (default 8088
) and provides two endpoints:
/report
where the Ecowitt weather station should POST its data/metrics
where Prometheus can scrape metrics with a GET request
Most Ecowitt weather stations and sensors should work fine with this exporter. The following hardware has been explicitly tested. If you have info about any hardware that does or doesn't work, please raise an issue so I can update the list.
- WS2910 Weather Station
- GW1100 Wi-Fi Gateway
- WS69 Wireless 7-in-1 Outdoor Sensor Array
- WS90 7-in-1 Outdoor Anti-vibration Haptic Sensor Array
- WH41/WH43 PM2.5 Air Quality Sensor
- WH57 Outdoor Lightning Sensor
- WN31/WH31 Multi-Channel Temperature & Humidity Sensor
- WN32/WH32 Single-Channel Temperature & Humidity Sensor
- WN36 Floating Pool Temperature Sensor
It seems that an Ecowitt WS2910 with firmware v5.1.1 has a bug when writing out HTTP headers, which causes it to not work with Flask. This has been reported to Flask, but closed as WONTFIX because the underlying problem is with the Ecowitt firmware.
It is possible to work around this issue by fronting this exporter with an NGINX reverse proxy (such as a Kubernetes Ingress), because NGINX magically fixes the headers on the fly.
Set the units for the export of each metric. Ecowitt weather stations always take measurements in Imperial. This exporter converts them on the fly if necessary, to present them to Prometheus in your desired format. Metric/SI is always the default. People in the USA will probably want to set everything to Imperial alternatives, while Brits will likely want a mixture of both!
All units are expressed in lower case and without slashes, for simplicity. Apologies to scientists, for whom this will be a difficult time.
Variable | Default | Choices | Meaning |
---|---|---|---|
DEBUG |
no |
no , yes |
Enable extra output for debugging |
PROMETHEUS |
yes |
no , yes |
Enable Prometheus exporter |
INFLUXDB |
no |
no , yes |
Enable InfluxDB support |
TEMPERATURE_UNIT |
c |
c , f , k |
Temperature in Celsius, Fahrenheit or Kelvin |
PRESSURE_UNIT |
hpa |
hpa , in , mmhg |
Pressure in hectopascals (millibars), inches of mercury or mm of mercury |
WIND_UNIT |
kmh |
kmh , mph , ms , knots , fps |
Speed in km/hour, miles/hour, metres/second, knots or feet/second |
RAIN_UNIT |
mm |
mm , in |
Rainfall in millimetres or inches |
IRRADIANCE_UNIT |
wm2 |
wm2 , lx , fc |
Solar irradiance in Watts/m^2 |
DISTANCE_UNIT |
km |
km , mi |
Distance from the last lightning in kilometers |
AQI_STANDARD |
uk |
uk , epa , mep , nepm |
Air Quality Index standard in UK DAQI, US EPA, China MEP, Australia NEPM |
INFLUXDB_TOKEN |
InfluxDB token | ||
INFLUXDB_URL |
http://localhost:8086/ |
InfluxDB endpoint | |
INFLUXDB_ORG |
influxdata |
InfluxDB organisation | |
INFLUXDB_BUCKET |
default |
InfluxDB bucket |
If you want to use one of the units that is not yet supported, please open an issue and request it. I can add the code to convert and display other units if there is demand.
This project is available as a Docker image djjudas21/ecowitt-exporter which can be run as a standalone container, but the recommended way to run it is in Kubernetes via the Helm chart. The Helm chart also supports integration with the Prometheus Operator and will create ServiceMonitor resources to enable automatic scraping.
helm repo add djjudas21 https://djjudas21.github.io/charts/
helm repo update djjudas21
helm install -n monitoring ecowitt-exporter djjudas21/ecowitt-exporter
After deploying via Helm, it will print some output to explain how to find the IP and/or hostname of the exporter running in Kubernetes.
Use the WSView Plus all to configure the integration. Go into the device, scroll across until Customized and set the following:
- Customized: Enable
- Protocol: Ecowitt
- Server IP / Hostname: the IP or hostname that Helm gave you
- Path:
/report
- Port:
8088
unless you are using an Ingress, in which case probably80
- Upload interval:
60
Then hit Save. It seems to take a couple of minutes for the weather station to submit its first reading.
Real data captured from the Ecowitt weather station with http-webhook to be used as a test:
{
"path": "/report",
"headers": {
"host": "192.168.0.65",
"connection": "Close",
"content-type": "application/x-www-form-urlencoded",
"content-length": "493"
},
"method": "POST",
"body": "PASSKEY=573AF40DB42C66057D20631F706CD585&stationtype=EasyWeatherPro_V5.1.1&runtime=0&dateutc=2023-10-20+11:24:35&tempinf=73.4&humidityin=57&baromrelin=28.984&baromabsin=28.603&tempf=59.2&humidity=90&winddir=256&windspeedmph=2.91&windgustmph=4.47&maxdailygust=9.17&solarradiation=96.86&uv=0&rainratein=0.000&eventrainin=1.472&hourlyrainin=0.000&dailyrainin=0.154&weeklyrainin=1.480&monthlyrainin=3.720&yearlyrainin=15.642&totalrainin=15.642&temp1f=59.5&humidity1=79&pm25_ch1=3.0&pm25_avg_24h_ch1=6.8&wh65batt=0&batt1=0&pm25batt1=5&freq=868M&model=WS2900_V2.01.18&interval=60&lightning_num=22&lightning=20&lightning_time=1691007186",
"fresh": false,
"hostname": "192.168.0.65",
"ip": "::ffff:10.1.199.64",
"ips": [],
"protocol": "http",
"query": {},
"subdomains": [],
"xhr": false,
"os": {
"hostname": "http-webhook-6675856576-j2jzb"
},
"connection": {}
}
This POST request can be simulated with curl:
curl -d "PASSKEY=573AF40DB42C66057D20631F706CD585&stationtype=EasyWeatherPro_V5.1.1&runtime=0&dateutc=2023-10-20+11:24:35&tempinf=73.4&humidityin=57&baromrelin=28.984&baromabsin=28.603&tempf=59.2&humidity=90&winddir=256&windspeedmph=2.91&windgustmph=4.47&maxdailygust=9.17&solarradiation=96.86&uv=0&rainratein=0.000&eventrainin=1.472&hourlyrainin=0.000&dailyrainin=0.154&weeklyrainin=1.480&monthlyrainin=3.720&yearlyrainin=15.642&totalrainin=15.642&temp1f=59.5&humidity1=79&pm25_ch1=3.0&pm25_avg_24h_ch1=6.8&wh65batt=0&batt1=0&pm25batt1=5&freq=868M&model=WS2900_V2.01.18&interval=60&lightning_num=22&lightning=20&lightning_time=1691007186" -X POST http://192.168.0.65:8080/report
We can then view the corresponding Prometheus metrics with a simple GET request (output has been truncated because it is very long):
curl http://localhost:8088/metrics
# HELP tempin_c Indoor temperature
# TYPE tempin_c gauge
tempin_c 18.89
# HELP temp_c Outdoor temperature
# TYPE temp_c gauge
temp_c 8.78
# HELP humidity_percent Outdoor humidity
# TYPE humidity_percent gauge
humidity_percent 91.0
# HELP humidityin_percent Indoor humidity
# TYPE humidityin_percent gauge
humidityin_percent 51.0
# HELP winddir_degree Wind direction
# TYPE winddir_degree gauge
winddir_degree 261.0
# HELP uv UV index
# TYPE uv gauge
uv 0.0
# HELP solarradiation_wm2 Solar irradiance
# TYPE solarradiation_wm2 gauge
solarradiation_wm2 57.25
# HELP baromrel_hpa Relative barometer
# TYPE baromrel_hpa gauge
baromrel_hpa 995.11
# HELP baromabs_hpa Absolute barometer
# TYPE baromabs_hpa gauge
baromabs_hpa 995.11
# HELP windspeed_kmh Wind speed
# TYPE windspeed_kmh gauge
windspeed_kmh 14.4
# HELP windgust_kmh Wind gust
# TYPE windgust_kmh gauge
windgust_kmh 23.75
# HELP maxdailygust_kmh Max daily gust
# TYPE maxdailygust_kmh gauge
maxdailygust_kmh 34.92
# HELP rainrate_mm Rainfall rate
# TYPE rainrate_mm gauge
rainrate_mm 0.0
# HELP eventrain_mm Event rainfall
# TYPE eventrain_mm gauge
eventrain_mm 1.8
# HELP hourlyrain_mm Hourly rainfall
# TYPE hourlyrain_mm gauge
hourlyrain_mm 0.0
# HELP dailyrain_mm Daily rainfall
# TYPE dailyrain_mm gauge
dailyrain_mm 0.3
# HELP weeklyrain_mm Weekly rainfall
# TYPE weeklyrain_mm gauge
weeklyrain_mm 2.8
# HELP monthlyrain_mm Monthly rainfall
# TYPE monthlyrain_mm gauge
monthlyrain_mm 36.1
# HELP yearlyrain_mm Yearly rainfall
# TYPE yearlyrain_mm gauge
yearlyrain_mm 616.1
# HELP totalrain_mm Total rainfall
# TYPE totalrain_mm gauge
totalrain_mm 616.1
# HELP lightning_km Lightning distance
# TYPE lightning_km gauge
lightning_km 20.0
# HELP lightning_num Lightning daily count
# TYPE lightning_num gauge
lightning_num 22.0
docker build -t ecowitt-exporter .
podman run -d --rm -p 8088:8088 -e DEBUG=yes ecowitt-exporter