Retrieves weather information from a WeatherLink Live and publishes it to an MQTT broker.
To install the weatherlink-tools
binary, you can run cargo install weatherlink-tools
.
Docker images for amd64 are also available with tags against the git SHA, see the Dockerfile.
weatherlink-tools
combines support for either or both Prometheus and MQTT output:
--prom-listen <ip>:<port>
: Serve weather in Prometheus format at /metrics
on the specified endpoint. This is enabled by default, listening to 0.0.0.0:8080
, but can be disabled using --prom-listen ''
.--mqtt-url mqtt://<ip>:<port>?client_id=<id>
: Publish weather to a MQTT broker. A client ID may optionally be provided in the URL.For arguments, see weatherlink-tools --help
. Separately, the LOG_LEVEL
envvar defaults to info
and can be set to debug
/trace
for more logs or warn
/error
/off
for fewer logs.
I've used these tools to collect data from a Davis Vantage Vue to send into other services. The following lists the configurations that have worked for me:
TODO docs
I've used weatherlink-tools
to stream weather data to an offsite MQTT broker (Mosquitto). The MQTT topics are then consumed and processed by a WeeWX instance via the WeeWX-MQTTSubscribe
plugin. But the data sent to MQTT is a nearly as-is copy of WeatherLink output which should be usable by any consumer.
I'm running weatherlink-tools
in a docker container. The container is run with --network=host
networking so that it can receive local UDP broadcast packets from the WeatherLink Live. I'm using these arguments pointing it to the MQTT broker:
/weatherlink-tools \
--mqtt-url mqtt://<broker-ip>:1883?client_id=weatherlink-tools \
--mqtt-topic-prefix weather.json/
On the consumer side, the WeatherLink MQTT data is translated to the names/units that WeeWX expects. The following is how I've configured WeeWX-MQTTSubscribe
in my weewx.conf
to do this. This includes mapping input field names to their equivalents in WeeWX, while also e.g. converting 0.01" bucket tip counts to incremental inches of rain. Depending on your hardware, you may need to change things, or this might mostly work as-is:
[MQTTSubscribeDriver]
driver = user.MQTTSubscribe
host = <broker-ip>
port = 1883
keepalive = 60
log = true
[[message_callback]]
type = json
# use / in field names to delimit json levels
flatten_delimiter = "/"
[[topics]]
unit_system = US
[[[weather.json/txid1]]]
ignore = true
[[[[temp]]]] # most recent valid temperature (°F)
ignore = false
name = outTemp
[[[[hum]]]] # most recent valid humidity (%RH)
ignore = false
name = outHumidity
[[[[dew_point]]]] # (°F)
ignore = false
name = dewpoint
[[[[wet_bulb]]]] # (°F)
ignore = false
name = wetbulb
[[[[heat_index]]]] # (°F)
ignore = false
name = heatindex
[[[[wind_chill]]]] # (°F)
ignore = false
name = windchill
[[[[thw_index]]]] # (°F)
ignore = false
name = thw
[[[[wind_speed_avg_last_1_min]]]] # average wind speed over last 2 min **(mph)**
ignore = false
name = windSpeed
[[[[wind_dir_scalar_avg_last_10_min]]]] # scalar average wind direction over last 10 min (much smoother/cleaner than last) **(°degree)**
ignore = false
name = windDir
[[[[wind_speed_hi_last_2_min]]]] # maximum wind speed over last 2 min **(mph)**
ignore = false
name = windGust
[[[[wind_dir_at_hi_speed_last_10_min]]]] # gust wind direction over last 10 min (much smoother/cleaner than raw) **(°degree)**
ignore = false
name = windGustDir
# We want to record the DIFF in bucket tips over time to weewx.
# Sending the value as-is gets summed across EACH 2s report, leading to hugely exaggerated rain!
# Let's go with the longest timespan - the annual value. This avoids not detecting value rollover.
# The diff logic is smart enough to avoid reporting a big initial value as a big diff.
# As such we ignore rain_rate_last and rain_60_min in favor of rain_24_hr, to increase the likelihood
# of detecting a decrease when the value rolls over. The rate/1h values risk the rate staying exactly
# the same across intervals, in which case we won't detect the diff.
# Meanwhile we don't worry about the longer amounts like rainfall_monthly and rainfall_yearly because
# if weewx is restarted then those will appear to have large diffs that will get recorded all at once.
[[[[rainfall_year]]]] # total rain count since start of year (counts)
ignore = false
name = rain
# enables recording diffs, see also:
# https://github.com/bellrichm/WeeWX-MQTTSubscribe/blob/master/bin/user/MQTTSubscribe.py#L1273-L1277
contains_total = true
# if the value goes from 1234 back to 1, then report the 1 as a diff
wrap_around = true
# each count is 0.01"
conversion_func = lambda x: x / 100.
[[[[rain_60_min]]]] # total rain count over last 60 min (counts)
ignore = false
name = hourRain
# each count is 0.01"
conversion_func = lambda x: x / 100.
[[[[rain_24_hr]]]] # total rain count over last 24 hours (counts)
ignore = false
name = rain24
# each count is 0.01"
conversion_func = lambda x: x / 100.
[[[[rainfall_daily]]]] # total rain count since local midnight (counts)
ignore = false
name = dayRain
# each count is 0.01"
conversion_func = lambda x: x / 100.
[[[[solar_rad]]]] # most recent solar radiation (W/m²)
ignore = true # null in my data
name = radiation # watt_per_meter_squared
[[[[uv_index]]]] # most recent UV index (Index)
ignore = true # null in my data
name = UV
[[[[trans_battery_flag]]]] # transmitter battery status flag (no unit)
ignore = false
name = outTempBatteryStatus
[[[weather.json/local]]]
ignore = true
[[[[temp_in]]]] # most recent valid inside temp (°F)
ignore = false
name = inTemp
[[[[hum_in]]]] # most recent valid inside humidity (%RH)
ignore = false
name = inHumidity
[[[[dew_point_in]]]] # (°F)
ignore = false
name = inDewpoint
[[[[heat_index_in]]]] # (°F)
ignore = false
name = inHeatindex # maybe?
[[[[bar_sea_level]]]] # most recent bar sensor reading with elevation adjustment (inches)
ignore = false
name = barometer
[[[[bar_absolute]]]] # raw bar sensor reading (inches)
ignore = false
name = pressure
This project is licensed under the AGPLv3 (or later) and is copyright Nicholas Parker.