Git Product home page Git Product logo

wfudptools's Introduction

Description

These python3 utilities let you test (and simulate) a WeatherFlow station installation

These permit you to listen to UDP broadcasts from your Hub and:

  • print the received UDP broadcasts to stdout
  • print the decoded broadcasts in a more human-friendly form
  • publish derived topics to MQTT
  • publish derived topics to influxdb (v1.x and v2.x)
  • support any combination of Air/Sky/Tempest
  • support multiple instances of Air/Sky/Tempest at your site

They also provide a utility to permit you to simulate a WeatherFlow Hub with minimal test data.

NOTE - These utilities are tested using v119 of the WeatherFlow hub firmware.

IMPORTANT SUPPORT INFORMATION

These utilities are provided as-is and at your own risk.

As of September 1, 2020 - I no longer have any WeatherFlow gear and can't test 'anything' on real gear While PR for fixes/enhancements are of course accepted, I am 'very' unlikely to ever merge them. If you have PR that can enhance these utilities, it's ok to submit them here so others can find them, or optionally go appropriately wild and fork this repo.

Bottom line - be polite please.


IMPORTANT NOTE ABOUT WEATHERFLOW UDP BROADCAST DATA

It is important to understand your reasonable expectations regarding this software working via listening to the local UDP broadcasts of observations and events emitted by the Hub. The following are limitations of the WeatherFlow gear itself and how it is implemented by the vendor.

  • you can not expect 'rain' readings to be accurate. WeatherFlow adjusts the accumulated rain for a calendar day via their proprietary RainCheck algorithms, which run in a batch job on the morning of the 'next' calendar day based on midnight at your location. While you can trust that the rain start 'event' you see is truth as your station's sensors saw when rain started, you can 'not' reasonably trust rain accumulation or rain rate based on the UDP broadcasts by your Hub.

  • you can not expect 'lightning' to be accurate. WeatherFlow essentially crowd-sources and calculates an aggregated lightning reading for your station as you would see it their web page or mobile app. While the UDP lightning data from your station 'is' used by the WeatherFlow servers as part of this aggregation on their end, you are not the 'sole' source of lightning data reported for your station (as viewed on their web or mobile app).

  • all other data reported from the UDP broadcasts on your LAN 'can' be assumed to match up with what you would see if you used the WeatherFlow web page, mobile app, REST, or Websockets APIs which all connect to WeatherFlow servers on Internet.

Known limitations - multiple 'live' network interfaces

  • The listener by default binds to all active network interfaces, so if you have multiple live network interfaces on the same subnet, it is possible that you will 'hear' multiple copies of the UDP broadcasts.

  • The author has experienced this on a Intel NUC running Ubuntu 18.04LTS, although it does not seem to be the default behavior on the raspberry pi running Raspbian. The preferred workaround, of course, is to disable wifi if you have a wired computer. The listener also supports an optional --address x.x.x.x parameter where you can hard-set the ip address of the interface on the runtime host that you want to listen on.


Changelog

v5.x

  • python3 only, tested on 3.10 but earlier versions 'should' work ok
  • buildable via 'poetry'
  • installable via 'pip'
  • removed threading for supportability reasons

v4.x

  • Added ability to write directly to influxdb (thanks to user clouserw via PR)

v3.x

  • Multiple Air/Sky devices per hub is now supported. See the -M option below
  • The --weewx option has been deleted

v2.x

  • The listener now supports python3. All examples below have been updated accordingly.
  • Typical output has been significantly quieted down, with debugging output suppressed unless you use the --verbose flag

Installation from PyPi

pip3 install wfudptools [--user]

To run the simulator

wfudpsimulator

To run the listener

usage: wfudplistener [-h] [-r] [-d] [-s] [-l LIMIT] [-x EXCLUDE] [-i] [-m] [-n]
                 [-w] [-b MQTT_BROKER] [-t MQTT_TOPIC]
optional arguments:
  -h, --help            show this help message and exit
  -r, --raw             print raw data to stddout
  -d, --decoded         print decoded data to stdout
  -s, --syslog          syslog unexpected data received
  -l LIMIT, --limit LIMIT
                        limit obs type(s) processed
  -x EXCLUDE, --exclude EXCLUDE
                        exclude obs type(s) from being processed
  -i, --indent          indent raw data to stdout (requires -d)
  -m, --mqtt            publish to MQTT
  -M, --multi-mqtt      specify there are multiple air/sky/tempest present
  -n, --no_pub          report but do not publish to MQTT
  -b MQTT_BROKER, --mqtt_broker MQTT_BROKER
                        MQTT broker hostname
  -t MQTT_TOPIC, --mqtt_topic MQTT_TOPIC
                        MQTT topic to post to
  -a ADDRESS, --address ADDRESS
                        address to listen on
  --influxdb            publish to influxdb
  --influxdb_host INFLUXDB_HOST
                        hostname or ip of InfluxDb HTTP API
  --influxdb_port INFLUXDB_PORT
                        port of InfluxDb HTTP API
  --influxdb_user INFLUXDB_USER
                        InfluxDb username
  --influxdb_pass INFLUXDB_PASS
                        InfluxDb password
  --influxdb_db INFLUXDB_DB
                        InfluxDb database name
  --influxdb2           publish to InfluxDB v2
  --influxdb2_url INFLUXDB2_URL
                        InfluxDB v2 HTTP API root URL
  --influxdb2_org INFLUXDB2_ORG
                        InfluxDB v2 Organization
  --influxdb2_bucket INFLUXDB2_BUCKET
                        InfluxDB v2 Bucket
  --influxdb2_token INFLUXDB2_TOKEN
                        InfluxDB v2 Token
  --influxdb2_debug     Debug InfluxDB v2 publisher
  -v, --verbose         verbose output to watch the threads

for --limit, possibilities are:
   rapid_wind, obs_sky, obs_air,
   hub_status, device_status, evt_precip, evt_strike
   wind_debug, light_debug, rain_rebug


Example usage


Typical Usage Scenarios

Typically it is expected that this script would be used to generate MQTT publish messages for a MQTT broker to make available for consuming devices. You can do so as follows:

# publish to mqtt, exclude one observation type, syslog unknown data received
nohup wfudplistener --mqtt \
   --exclude "rapid_wind" \
   --syslog >/dev/null 2>&1 &

# or publish to mqtt, and limit to only two observations
nohup wfudplistener --mqtt \
   --limit "obs_sky obs_air" >/dev/null 2>&1 &


# or publish to mqtt, and 'exclude' a particular observation type
nohup wfudplistener --mqtt  \
   --exclude "rapid_wind" >/dev/null 2>&1 &

FWIW, the author typically runs the first variant above by simply putting the command into /etc/rc.local so it starts up on bootup.


Debugging your WeatherFlow hub, sky, and air

Running the listener interactively can help debug the health and realtime observations of your WeatherFlow station.

Printing out unaltered data received from the station broadcasts

The --raw option prints out decoded UDP broadcasts in JSON format to stdout...

pi@zero:~ $ wfudplistener --raw

{"hub_sn": "HB-00010412", "ob": [1535684062, 0.63, 272], "serial_number": "SK-00013695", "type": "rapid_wind"}
Decoding the station data into a more human-friendly format

The --decoded option prints a more human-friendly output of the decoded UDP broadcast...

pi@zero:~ $ wfudplistener --decoded

hub_status     =>  ts  = 1535819472 firmware_revision  = 91 uptime  = 333458 rssi  = -35
rapid_wind     =>  ts  = 1535819474 mps = 1.34 dir = 190
rapid_wind     =>  ts  = 1535819477 mps = 1.48 dir = 211
Limiting the output to certain observation/status/event type(s)

The '--limit type' option limits the event/status/observation(s) being processed.

pi@zero:~ $ wfudplistener --raw --limit hub_status

{"firmware_revision": "91", "fs": "1,0", "mqtt_stats": [53], "radio_stats": [5, 3], "reset_flags": "BOR,PIN,POR", "rssi": -35, "seq": 33368, "serial_number": "HB-00010412", "timestamp": 1535819752, "type": "hub_status", "uptime": 333738}
{"firmware_revision": "91", "fs": "1,0", "mqtt_stats": [53], "radio_stats": [5, 3], "reset_flags": "BOR,PIN,POR", "rssi": -35, "seq": 33370, "serial_number": "HB-00010412", "timestamp": 1535819772, "type": "hub_status", "uptime": 333758}

Note: you may supply multiple limited observations ala:

# comma-delimited
wfudplistener --limit obs_sky,obs_air

# quoted and space-delimited
wfudplistener --limit "obs_sky obs_air"

You can also exclude observations, processing everything else, ala:

wfudplistener --exclude "rapid_wind"
wfudplistener --exclude "device_status rapid_wind"
wfudplistener --exclude device_status,rapid_wind
Reformatting the JSON data for easier interpretation

The --indent option reformats the output to be a little more readable...

@zero:~ $ wfudplistener --raw --limit rapid_wind --indent

{
  "hub_sn": "HB-00010412",
  "ob": [
    1535819864,
    0.0,
    0
  ],
  "serial_number": "SK-00013695",
  "type": "rapid_wind"
}

{
  "hub_sn": "HB-00010412",
  "ob": [
    1535819867,
    0.0,
    0
  ],
  "serial_number": "SK-00013695",
  "type": "rapid_wind"
}
Syslogging unexpected JSON received

The --syslog option will syslog any received JSON data that has an unknown or missing device["type"]. You almost certainly do 'not' want to use this option and also specify the --indent option, as the default JSON dumped will be a (long) one-liner suitable for syslog.

Writing to an output directory

Add the --raw --quiet --output DIRNAME to write the JSON to DIRNAME.

By default, this will log all sensors and hubs detected. You may limit the observations via the --limit option.

# for serial number ST-000001, this will write to /var/tmp/ST-000001.rapid_wind
@zero:~ wfudplistener --raw --quiet --output /var/tmp --limit rapid_wind

# omitting the --limit switch will log all observations

Publishing to MQTT

The --mqtt option publishes JSON to MQTT.

pi@zero:~ $ wfudplistener --mqtt

publishing to mqtt://mqtt/wf/status/sky
publishing to mqtt://mqtt/wf/obs/sky
publishing to mqtt://mqtt/wf/rapid_wind
publishing to mqtt://mqtt/wf/rapid_wind

Adding the --decoded option shows decoded data from the broadcast as well

pi@zero:~ $ wfudplistener --mqtt --decoded

rapid_wind     =>  ts  = 1535821622 mps = 1.07 dir = 316
publishing to mqtt://mqtt/wf/rapid_wind
hub_status     =>  ts  = 1535821622 firmware_revision  = 91 uptime  = 335608 rssi  = -35
publishing to mqtt://mqtt/wf/status/hub

Adding the --raw option shows the data that would be published as well as the raw UDP data

pi@zero:~ $ wfudplistener --mqtt --raw

    raw data:  {"hub_sn": "HB-00010412", "ob": [1535821559, 0.94, 272], "serial_number": "SK-00013695", "type": "rapid_wind"}
publishing to mqtt://mqtt/wf/rapid_wind
     {"direction": 272, "speed": 0.94, "timestamp": 1535821559}

The listener defaults to publishing MQTT topics to a host named 'mqtt' on your local network. You may supersede this at runtime via the --broker <broker_hostname_or_ip_here> option. See the usage instructions above for details.


Support for multiple sensors

Supporting multiple sensors means changing the published topic to something that permits the consumer to tell Sky-A apart from Sky-B, for example. This can be done by adding the -M or --multi-mqtt option to your command invocation. This changes the published topic to /sensors/<SERIAL_NUMBER>/<TOPIC> ala:

In this example, note the -n option has been added for illustrative purposes only:


# typical invocation for a one-Sky one-Air system

$ wfudplistener -m -n --limit "rapid_wind"
publishing to mqtt://mqtt/wf/rapid_wind


# add -M if you have a multi-Sky and/or multi-Air system
# note the different MQTT topic it publishes to

$ wfudplistener -m -n -M --limit "rapid_wind"
publishing to mqtt://mqtt/sensors/SK-00013695/wf/rapid_wind

Mosquitto MQTT client primer

While documenting mosquitto-mqtt, or any other MQTT client/broker, is out of scope for this document in the general sense, the following are some examples of how you might do so using mosquitto-mqtt assuming you have published topics to MQTT using the --mqtt option to this listener.

WeatherFlow MQTT topics

This gives an example of how to use the mosquitto_sub MQTT client to subscribe to a published topic. See the mosquito_sub man page for detailed usage of that client.


pi@zero$ mosquitto_sub -t "wf/obs/#" -h mqtt

{"firmware_revision": 20, "hub_sn": "HB-00010412", "obs": [[1535685379, 1006.8, 18.51, 76, 0, 0, 3.51, 1]], "serial_number": "AR-00013349", "type": "obs_air"}

{"firmware_revision": 43, "hub_sn": "HB-00010412", "obs": [[1535685389, 18, 0.0, 0.0, 0.0, 1.08, 2.06, 258, 3.45, 1, 0, null, 0, 3]], "serial_number": "SK-00013695", "type": "obs_sky"}


Publishing to InfluxDB

It is also possible to publish directly to a influxdb database

Example usage:

This will print to InfluxDB v1.x on a remote host 'influxdb' using a specified port and database, and also enable the multisensor syntax for more searchable generic topic names in the database. This particular example requires no influxdb username/password, so those options have been omitted.

# this is typical usage for calling the program via /etc/rc.local or from a shell
# - it backgrounds the command, and stay alive after the calling shell exits

nohup wfudplistener --influxdb --influxdb_host=influxdb --influxdb_port=8086  --influxdb_db=testdb -M &

An equivalent example for InfluxDB v2.x:

nohup wfudplistener --influxdb --influxdb2_url="http://influxdb:8086"  --influxdb2_org=testorg --influxdb2_token=longsecret --influxdb2_bucket=weather -M &

Initially it might make sense to add the -n (no_pub) flag to see is being published, to aid in you writing queries or grafana dashboards versus your influxdb data. Remember, however, that the -n flag writes to stdout, so you'd want to run the command in the foreground.

# see with it 'would' publish, but do not actually publish anything
# (this will need two control-C to kill the process)

# you might want to also add -v to see the reporter and listener threads

wfudplistener --influxdb --influxdb_host=influxdb --influxdb_port=8086  --influxdb_db=testdb -M -n

wfudptools's People

Contributors

vinceskahan avatar koleson avatar

Stargazers

Nick Miethe avatar shaunwbell avatar Julian Missig avatar  avatar Kris Payne avatar Sean Ostermann avatar  avatar Wim Leers avatar  avatar Ryan Phillips avatar  avatar

Watchers

 avatar

wfudptools's Issues

add CREDITS

Add a CREDITS file to reflect the folks who've helped along the way...

Error when publishing to influx DB

First off - thank you for the tool. I am coming from the old deprecated repo.

I'm getting the following error when testing with my influxdb:

wfudplistener --influxdb --influxdb_host=192.168.1.105 --influxdb_port=8086 --influxdb_db=weatherflow -M -n

setting up socket - done
listening for broadcasts..
Failed to connect to InfluxDB: name 'args' is not defined
Traceback (most recent call last):
File "/usr/local/lib/python3.9/dist-packages/wfudptools/listener.py", line 537, in influxdb_publish
client = InfluxDBClient(host=args.influxdb_host,
NameError: name 'args' is not defined

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/usr/local/bin/wfudplistener", line 8, in
sys.exit(main())
File "/usr/local/lib/python3.9/dist-packages/wfudptools/listener.py", line 746, in main
elif data["type"] == "rapid_wind": process_rapid_wind(data,args)
File "/usr/local/lib/python3.9/dist-packages/wfudptools/listener.py", line 227, in process_rapid_wind
influxdb_publish(topic, rapid_wind)
File "/usr/local/lib/python3.9/dist-packages/wfudptools/listener.py", line 556, in influxdb_publish
print(" Payload was: %s" % payload)
UnboundLocalError: local variable 'payload' referenced before assignment
root@WeatherFlowInflux:~# wfudplistener --influxdb --influxdb_host=192.168.1.105 --influxdb_port=8086 --influxdb_db=weatherflow -M -n -v
setting up socket - done
listening for broadcasts..
Failed to connect to InfluxDB: name 'args' is not defined
Traceback (most recent call last):
File "/usr/local/lib/python3.9/dist-packages/wfudptools/listener.py", line 537, in influxdb_publish
client = InfluxDBClient(host=args.influxdb_host,
NameError: name 'args' is not defined

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/usr/local/bin/wfudplistener", line 8, in
sys.exit(main())
File "/usr/local/lib/python3.9/dist-packages/wfudptools/listener.py", line 751, in main
elif data["type"] == "hub_status": process_hub_status(data,args)
File "/usr/local/lib/python3.9/dist-packages/wfudptools/listener.py", line 527, in process_hub_status
influxdb_publish(topic, hub_status) # careful here, might need to hub_status.pop("foo", None) for arrays
File "/usr/local/lib/python3.9/dist-packages/wfudptools/listener.py", line 556, in influxdb_publish
print(" Payload was: %s" % payload)
UnboundLocalError: local variable 'payload' referenced before assignment

Thank you for any insights you can provide!

add Changelog

add a quick Changelog file tracking what changed when version by version

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.