#!/bin/bash : << =cut =head1 NAME tasmota_ - logs voltage, energy usage, power, power factor and current logged from tasmota power switches To install the plugin, copy or move the plugin to /usr/share/munin/plugins/ set the chmod to 755 and create a symbolic link: ln -s /usr/share/munin/plugins/tasmota_ /etc/munin/plugins/tasmota_hostname_voltage ln -s /usr/share/munin/plugins/tasmota_ /etc/munin/plugins/tasmota_hostname_energy ln -s /usr/share/munin/plugins/tasmota_ /etc/munin/plugins/tasmota_hostname_power ln -s /usr/share/munin/plugins/tasmota_ /etc/munin/plugins/tasmota_hostname_powerfactor ln -s /usr/share/munin/plugins/tasmota_ /etc/munin/plugins/tasmota_hostname_current Plugin also implements suggests, so if you have nodeattr installed and /etc/genders populated with "tasmota" as well as "powermon", "DS18B20", "temperature", "SCD40", "PMS5003" or "millivolts" flags for those tasmota devices that implement energy or temperature monitoring, you can run an ansible play like this to set up your links: https://github.com/spacelama/ansible-initial-server-setup/tree/master/roles/monitoring/tasks =head1 APPLICABLE SYSTEMS Any host that can access tasmota systems and has "jq" installed. Can auto suggest values if nodeattr genders (debian: apt install genders) installed and configured with tasmota, powermon, DS18B20, SCD40, PMS5003, millivolts flags. =head1 AUTHOR Tim Connors =head1 LICENSE GPLv2 or later =head1 MAGICK MARKERS #%# family=auto #%# capabilities=autoconf =cut cached_curl=none # runs curl on "Status $1", and parses through jq -c "$2", setting the value in "$res" # Caches the curl results for when we're plotting multiple (power) values # -- don't forget to make sure we're not being called in a subshell! get_status() { if [ "$cached_curl" = none ] ; then # run curl, cache the result, and if there was an error, set value to "U" if ! cached_curl=$( curl --max-time 5 -fsS --data-urlencode "cmnd=Status $1" "http://$DEVICE/cm" ) ; then cached_curl='{}' fi fi res=$( echo "$cached_curl" | jq -c -r "$2" ) if [ "$res" = null ] ; then res=U fi } DEVICE=$(basename "$0" | cut -d_ -f2) FUNCTION=$(basename "$0" | cut -d_ -f3) . "$MUNIN_LIBDIR/plugins/plugin.sh" if [ "$1" = "autoconf" ]; then if ! which curl > /dev/null 2>&1 ; then echo "no (no curl)" exit 0 fi if ! which jq > /dev/null 2>&1 ; then echo "no (no jq)" exit 0 fi echo yes && exit 0 fi if [ "$1" = "suggest" ]; then nodeattr -n '(tasmota || beken) && powermon' | while read -r device ; do for i in voltage power powerfactor current energy ; do echo "${device}_${i}" done done for i in temperature DS18B20 SCD40 PMS5003 millivolts switch dimmer ; do nodeattr -n "(tasmota || beken) && $i" | while read -r device ; do echo "${device}_${i}" done done exit fi voltage() { axis=Volts if [ "$1" = "config" ]; then echo "graph_title Tasmota Voltage: $DEVICE" echo "graph_vlabel Volts" echo "graph_args --base 1000 -l 0" echo "$axis.label Voltage" else get_status 8 '.StatusSNS["ENERGY"]'.Voltage echo "$axis.value $res" fi } current() { axis=Current if [ "$1" = "config" ]; then echo "graph_title Tasmota Current: $DEVICE" echo "graph_vlabel Amps" echo "graph_args --base 1000 -l 0" echo "$axis.label Current" else get_status 8 '.StatusSNS["ENERGY"]'.Current echo "$axis.value $res" fi } power() { if [ "$1" = "config" ]; then echo "graph_title Tasmota Power: $DEVICE" echo "graph_vlabel Watts" echo "graph_args --base 1000 -l 0" for axis in ApparentPower ReactivePower Power ; do echo "$axis.label $axis" echo "$axis.type GAUGE" echo "$axis.min 0" print_thresholds "$axis" done else for axis in ApparentPower ReactivePower Power ; do get_status 8 '.StatusSNS["ENERGY"]'.$axis echo "$axis.value $res" done fi } powerfactor() { axis=PowerFactor if [ "$1" = "config" ]; then echo "graph_title Tasmota Power factor: $DEVICE" echo "graph_vlabel Power Factor" echo "graph_args --base 1000 -l 0" echo "$axis.min 0" echo "$axis.max 1" echo "$axis.label Power Factor" else get_status 8 '.StatusSNS["ENERGY"]'.Factor echo "$axis.value $res" fi } energy() { axis=Energy if [ "$1" = "config" ]; then echo "graph_title Tasmota Energy: $DEVICE" echo "graph_args --base 1000" echo "graph_vlabel kWh" echo "$axis.label Energy" echo "$axis.draw AREA" else get_status 8 '.StatusSNS["ENERGY"]'.Total echo "$axis.value $res" fi } temperature() { axis=Temperature if [ "$1" = "config" ]; then echo "graph_title Tasmota Temperature: $DEVICE" echo "graph_args --base 1000" echo "graph_vlabel °C" echo "$axis.label Temperature" echo "$axis.type GAUGE" #echo "$axis.min 0" else get_status 10 '.StatusSNS["ANALOG"]'.Temperature1 echo "$axis.value $res" fi } DS18B20() { if [ "$1" = "config" ]; then echo "graph_title Tasmota Temperature: $DEVICE" echo "graph_args --base 1000" echo "graph_vlabel °C" fi for i in '' `seq 1 9` ; do axis=Temperature$i if [ "$1" = "config" ]; then get_status 10 ".StatusSNS[\"DS18B20${i:+-$i}\"]".Temperature if [ "$res" != U ] ; then echo "$axis.label Temperature${i:+ $i}" echo "$axis.type GAUGE" #echo "$axis.min 0" fi else get_status 10 ".StatusSNS[\"DS18B20${i:+-$i}\"]".Temperature if [ "$res" != U ] ; then # should only echo U if there's # not a valid temperature where # there once was, but we don't # immediately know which ones # are intended to be valid echo "$axis.value $res" fi fi done } SCD40() { axis=CO2 if [ "$1" = "config" ]; then echo "graph_title Tasmota Carbon Dioxide: $DEVICE" echo "graph_args --base 1000" echo "graph_vlabel PPM" echo "$axis.label Carbon Dioxide" echo "$axis.type GAUGE" else get_status 8 '.StatusSNS["SCD40"]'.CarbonDioxide echo "$axis.value $res" fi } PMS5003() { if [ "$1" = "config" ]; then echo "graph_title Tasmota Particulate Pollution: $DEVICE" echo "graph_args --base 1000" for v in 'PB1=<1μm PPD' 'PB2_5=<2.5μm PPD' 'PB10=<10μm PPD' ; do axis=$( echo "$v" | cut -d= -f 1 ) lab=$( echo "$v" | cut -d= -f 2 ) echo "$axis.label $lab" echo "$axis.type GAUGE" echo "$axis.min 0" done else for v in 'PB1="PB1"' 'PB2_5="PB2.5"' 'PB10="PB10"' ; do axis=$( echo "$v" | cut -d= -f 1 ) field=$( echo "$v" | cut -d= -f 2 ) get_status 8 '.StatusSNS["PMS5003"]'.$field echo "$axis.value $res" done fi } millivolts() { axis=Volts if [ "$1" = "config" ]; then limits= # # man rrdgraph for relevant --limit args # if [ -n "$lower" ] ; then # limits="$limits --lower-limit $lower --rigid --allow-shrink" # limits="$limits --lower-limit $lower --rigid --alt-autoscale" # fi # if [ -n "$upper" ] ; then # limits="$limits --upper-limit $upper --rigid" # fi echo "graph_title Tasmota Volts: $DEVICE" # echo "graph_args --base 1000$limits" echo "graph_args --base 1000 --alt-autoscale --alt-y-grid" echo "graph_vlabel Volts" echo "$axis.label V" echo "$axis.type GAUGE" else get_status 8 '.StatusSNS["ANALOG"]'.Range if [ "$res" != U ] ; then res=$( echo "$res" | awk '{printf "%0.3f", $1/1000}' ) fi echo "$axis.value $res" fi } switch() { if [ "$1" = "config" ]; then echo "graph_title Tasmota Switch: $DEVICE" echo "graph_args --base 1000 -l 0" fi for v in POWER POWER{1,2,3,4,5,6,7,8,9} ; do axis=$( echo "$v" | cut -d= -f 1 ) lab=$( echo "$v" | cut -d= -f 2 ) get_status 11 ".StatusSTS[\"$v\"]" if [ "$res" != U ] || [ -e $MUNIN_PLUGSTATE/tasmota/$DEVICE.$FUNCTION.$v.seen ] ; then # if we've ever seen an axis, then we want to keep # telling munin we are capable of reading that axis, # even if the device is currently unpowered mkdir -p $MUNIN_PLUGSTATE/tasmota touch $MUNIN_PLUGSTATE/tasmota/$DEVICE.$FUNCTION.$v.seen if [ "$1" = "config" ]; then echo "$axis.label $lab" echo "$axis.type GAUGE" echo "$axis.min 0" else case "$res" in ON) res=1 ;; OFF) res=0 ;; U) # assume off, since if it doesn't have power to get wifi, then it's not going to have power output res=0 ;; esac echo "$axis.value $res" fi fi done } dimmer() { if [ "$1" = "config" ]; then echo "graph_title Tasmota Dimmer: $DEVICE" echo "graph_args --base 1000 -l 0" fi for v in Channel{1,2,3,4} ; do axis=$( echo "$v" | cut -d= -f 1 ) lab=$( echo "$v" | cut -d= -f 2 ) get_status 11 ".StatusSTS[\"$v\"]" if [ "$res" != U ] || [ -e $MUNIN_PLUGSTATE/tasmota/$DEVICE.$FUNCTION.$v.seen ] ; then # if we've ever seen an axis, then we want to keep # telling munin we are capable of reading that axis, # even if the device is currently unpowered mkdir -p $MUNIN_PLUGSTATE/tasmota touch $MUNIN_PLUGSTATE/tasmota/$DEVICE.$FUNCTION.$v.seen if [ "$1" = "config" ]; then echo "$axis.label $lab" echo "$axis.type GAUGE" echo "$axis.min 0" else if [ "$res" = U ] ; then res=0 # assume 0 power, since if it doesn't have power to get wifi, then it's not going to have power output fi echo "$axis.value $res" fi fi done } case "$FUNCTION" in voltage|power|powerfactor|current|energy|temperature|DS18B20|SCD40|PMS5003|millivolts|switch|dimmer) $FUNCTION "$1" ;; *) echo "Unknown Function" ;; esac if [ "$1" = "config" ] ; then echo "graph_category sensors" print_thresholds "$axis" fi