#! /bin/bash

MYNAME=$(basename "$0")
MYDIR=$(dirname "$0")
MYDIR=$(readlink -m "$MYDIR")
VERSION="0.0.6"
VERBOSE=""
LOGDIR="$HOME/"
LOGFILE="$LOGDIR/${MYNAME}.log"

OPTION_DEVICE=""
OPTION_PRINT_FILES=""
OPTION_PRINT_REPORT=""

PDU_SERVER=""
PDU_OUTLETS="all"
PDU_OPERATION="on"


SUM_DEVICES=0
PINGED_DEVICES=0
REPLIED_DEVICES=0

if [ -f "$MYDIR/utilityfunctions.sh" ]; then
    source $MYDIR/utilityfunctions.sh
else
    echo "File '$MYDIR/utilityfunctions.sh' was not found." >&2
    exit 5
fi

printVerbose "USER = $USER"
printVerbose "HOME = $HOME"

[ -z "$USER" ] && export USER="pipas"
[ -z "$HOME" ] && export HOME="/home/pipas"

MQTT_CONFIG_FILE="$HOME/.pip/mqtt.conf"

#
# SonOTA: 
#   source: https://github.com/mirko/SonOTA/
#     blog: https://support.itead.cc/support/discussions/topics/11000017070/page/10?url_locale=
# compatibility: https://github.com/mirko/SonOTA/wiki
#
# Hack without flash:
#   https://blog.ipsumdomus.com/sonoff-switch-complete-hack-without-firmware-upgrade-1b2d6632c01
#
# Sonoff Tasmota commands: 
#   https://github.com/arendst/Sonoff-Tasmota/wiki/Commands
#

#
# Prints the software version and exits.
#
function printVersionAndExit()
{
    echo "$MYNAME Version $VERSION on $(hostname)" >&2
}


#
# Prints the help message and exits the program.
#
function printHelpAndExit()
{
cat <<EOF
Usage:
  $MYNAME [OPTION]... [DEVICE]...

  $MYNAME - Turns on and off pdu outlets.

  -h, --help           Print this help and exit.
  -v, --version        Print version information and exit.
  --verbose            Print more messages.
  --log-file=FILE      Store all the messages in the given file too.
  --print-report       Print a detailed report at the end.

  --discover           Try to contact the device and find information about it.
  --list               List the power distribution units.
  --list-measurements  Fetch and list values the devices can measure.
  --list-outlets       List the outlets of the PDU.
  --off                Turn the socket off.
  --on                 Turn the socket on.
  --state              Check the state of the socket.
  --toggle             Toggle the socket.
  
  --long               Print the long, detailed list.
  --ping               Ping the device and print its state in the list.
  --print-files        Print file names instead of names.

  --power              Read the current power in watts.
  --voltage            Measure the voltage on the PDU.
  --current            Measure the current on the PDU.
  --temperature        Read the temperature from the PDU.
  --humidity           Read the humidity percent from the PDU.
  
  --outlet=N           The numerical ID of the outlet.
  --group=GROUP        Only PDUs in the given group.
  --all                All the PDUs.
  --with-power         Lists only the ones that have power measurements.
  --with-temperature   Lists only the ones that have temperature sensor.

EOF
    exit 0
}

ARGS=$(\
    getopt \
        -o hvs:c:l \
        -l "help,verbose,version,log-file:,print-report,"\
"list,list-outlets,list-measurements,discover,on,off,toggle,state,\
power,voltage,current,temperature,humidity,print-files,long,ping,\
outlet:,group:,all,with-power,with-temperature" \
        -- "$@")

if [ $? -ne 0 ]; then
    exit 6
fi

eval set -- "$ARGS"
while true; do
    case "$1" in
        -h|--help)
            shift
            printHelpAndExit
            ;;

        --verbose)
            shift
            VERBOSE="true"
            VERBOSE_OPTION="--verbose"
            ;;

        -v|--version)
            shift
            VERSION_OPTION="--version"
            ;;

        --log-file)
            shift
            LOGFILE=$(readlink -m "$1")
            shift
            ;;

        --print-report)
            shift
            OPTION_PRINT_REPORT="true"
            ;;

        --list)
            shift
            LIST_OPTION="true"
            ;;

        --list-outlets)
            shift
            LIST_OUTLETS_OPTION="true"
            ;;

        --list-measurements)
            shift
            LIST_MEASUREMENTS_OPTION="true"
            ;;

        --discover)
            shift
            DISCOVER_OPTION="true"
            ;;

        --on)
            shift
            PDU_OPERATION="on"
            ;;

        --power)
            shift
            PDU_OPERATION="power"
            ;;
        
        --voltage)
            shift
            PDU_OPERATION="voltage"
            ;;
        
        --current)
            shift
            PDU_OPERATION="current"
            ;;

        --off)
            shift
            PDU_OPERATION="off"
            ;;

        --toggle)
            shift
            PDU_OPERATION="toggle"
            ;;

        --state)
            shift
            PDU_OPERATION="state"
            ;;
        
        --temperature)
            shift
            PDU_OPERATION="temperature"
            ;;

        --humidity)
            shift
            PDU_OPERATION="humidity"
            ;;

        --print-files)
            shift
            OPTION_PRINT_FILES="true"
            ;;

        --long)
            shift
            OPTION_LONG="true"
            ;;
        
        --ping)
            shift
            OPTION_PING="true"
            ;;

        --group)
            shift
            OPTION_GROUP="$1"
            shift
            ;;

        --outlet)
            shift
            OPTION_OUTLET="$1"
            shift
            ;;

        --all)
            shift
            OPTION_ALL="true"
            ;;

        --with-power)
            shift
            OPTION_WITH_POWER="true"
            ;;
        
        --with-temperature)
            shift
            OPTION_WITH_TEMPERATURE="true"
            ;;

        --)
            shift
            break
            ;;

        *)
            break
            ;;
    esac
done

OPTION_DEVICES="$@"

#
# Preparing the lo file.
#
rm -f $HOME/.pip/*.log

[ ! -d "$LOGDIR" ] && mkdir -p "$LOGDIR"
touch "$LOGFILE"

function print_value()
{
    local value="$1"
    local default="$2"

    if [ -n "$value" ]; then
        echo "$value"
    else
        echo "$default"
    fi
}

function device_requested()
{
    local this_device
    local device="$1"

    if [ -z "$OPTION_DEVICES" ]; then
        return 0
    fi

    for this_device in $OPTION_DEVICES; do
        if [ "$device" == "$this_device" ]; then
            return 0
        fi
    done

    return 1
}

function outlet_requested()
{
    local outlet="$1"
    local outlets="$2"

    if [ -z "$outlets" -o "$outlets" == "all" ]; then
        return 0;
    fi

    for this_outlet in $(echo $outlets | tr ',' ' '); do
        if [ "$this_outlet" == "$outlet" ]; then
            return 0
        fi
    done

    return 1
}

# https://tobinsramblings.wordpress.com/2011/05/03/snmp-tutorial-apc-pdus/
# sudo apt-get install snmp
# sudo apt-get install libsnmp-base
# sudo apt-get install snmp-mibs-downloader
#
# Check the status of one outlet:
# snmpget -v 1 -c private pdu01 PowerNet-MIB::sPDUOutletCtl.6
# 
# Turn on/off:
# snmpset -v 1 -c private pdu01 PowerNet-MIB::sPDUOutletCtl.5 integer 1
# snmpset -v 1 -c private pdu01 PowerNet-MIB::sPDUOutletCtl.5 integer 2
#
# Get help of an OID:
# snmptranslate -Td PowerNet-MIB::sPDUOutletCtl.1
# 
# List the OIDs:
# snmpwalk -v 1 -c public pdu01 enterprises.318
function switch_apc()
{
    local lock_file="/tmp/${CONF_PDU_SERVER}.lock"
    local outlets
    local tmp
    local retval

    printVerbose "switch_apc(): $PDU_OPERATION"
    if [ -n "$OPTION_OUTLET" ]; then
        outlets="$(echo "$OPTION_OUTLET" | tr ',' ' ')"
    elif [ "$PDU_OUTLETS" == "all" ]; then
        outlets=$(seq $CONF_PDU_FIRST_OUTLET $CONF_PDU_LAST_OUTLET)
    else
        outlets=$(echo $PDU_OUTLETS | tr ',' ' ')
    fi

    case "$PDU_OPERATION" in
        on|off)
            for outlet in $outlets; do
                printVerbose "Switching outlet $outlet of ${CONF_PDU_SERVER} ${PDU_OPERATION}."
                [ "$PDU_OPERATION" == "on" ]  && value=1
                [ "$PDU_OPERATION" == "off" ] && value=2
                
                snmpset \
                    -v 1 \
                    -c private "$CONF_PDU_SERVER" \
                    PowerNet-MIB::sPDUOutletCtl.$outlet integer $value \
                    >/dev/null 2>/dev/null    
                
                printVerbose "mosquitto_pub -u \"$MQTT_USER\" -P \"$MQTT_PASSWORD\" -h \"$MQTT_SERVER\" -t \"stat/${CONF_PDU_SERVER}_$outlet/POWER\" -m \"$PDU_OPERATION\""
                mosquitto_pub \
                    -u "$MQTT_USER" \
                    -P "$MQTT_PASSWORD" \
                    -h "$MQTT_SERVER" \
                    -t "stat/${CONF_PDU_SERVER}_$outlet/POWER" \
                    -m "$PDU_OPERATION"
                
                #pip-pdu-expect \
                #    "$CONF_PDU_SERVER" "$PDU_OPERATION" "$outlet" \
                #    >/dev/null 2>/dev/null
            done
            ;;

        state)
                has_off=""
                has_on=""

                for outlet in $outlets; do
                    line=$(snmpget \
                        -v 1 \
                        -c private "$CONF_PDU_SERVER" \
                        PowerNet-MIB::sPDUOutletCtl.$outlet)

                    printVerbose "line: $line"
                    if echo "$line" | grep -q outletOff; then
                        status="off"
                    elif echo "$line" | grep -q outletOn; then
                        status="on"
                    else
                        status="?"
                    fi
               
                    printVerbose "stat/${CONF_PDU_SERVER}_$outlet/POWER -> $status"
                    mosquitto_pub \
                        -u "$MQTT_USER" \
                        -P "$MQTT_PASSWORD" \
                        -h "$MQTT_SERVER" \
                        -t "stat/${CONF_PDU_SERVER}_$outlet/POWER" \
                        -m "$status"

                    [ "$status" == "on" ]  && has_on="true"
                    [ "$status" == "off" ] && has_off="true"
                done

                [ -n "$has_on" -a -z "$has_off" ] && echo "on"
                [ -z "$has_on" -a -n "$has_off" ] && echo "off"
            ;;

        voltage)
            #string=$(pip-pdu-expect "$CONF_PDU_SERVER" "help" "")
            #printVerbose "*** string: '$string'" 
            echo "0";
            ;;

        current)
            line=$(snmpget \
                -v 1 \
                -c private "$CONF_PDU_SERVER" \
                PowerNet-MIB::rPDULoadStatusLoad.3)
            
            line=$(echo "$line" | awk '{print $4}')
            line=$(bc <<< "scale = 2; $line / 10")
            print_value "$line"
            ;;

        power)

            line=$(snmpget \
                -v 1 \
                -c private "$CONF_PDU_SERVER" \
                PowerNet-MIB::rPDUIdentDevicePowerWatts.0)

            line=$(echo "$line" | awk '{print $4}')
            print_value "$line" "0"
            ;;

    esac

    rm -f "$lock_file"
}

function switch_orvibo()
{
    local retcode

    if [ -z "$CONF_PDU_MAC" ]; then
        printError "MAC address needed to switch Orvibo devices."
        return 1
    fi

    #
    # The S20control.py single script can be downloaded from git at
    # git@github.com:glenpp/OrviboS20.git
    #
    case "$PDU_OPERATION" in 
        on)
            S20control.py poweron $CONF_PDU_SERVER $CONF_PDU_MAC \
                2>/dev/null >/dev/null
            ;;

        off)
            S20control.py poweroff $CONF_PDU_SERVER $CONF_PDU_MAC \
                2>/dev/null >/dev/null
            ;;

        state)
            printVerbose "S20control.py getstate $CONF_PDU_SERVER $CONF_PDU_MAC"
            S20control.py getstate $CONF_PDU_SERVER $CONF_PDU_MAC \
                2>/dev/null >/dev/null
            
            retcode=$?
            printVerbose "retcode: $retcode"
            if [ "$retcode" -eq 0 ]; then
                echo "on"
            else
                echo "off"
            fi
            ;;

    esac
}

function outlet_name()
{
    local ip_address="$1"
    local id="$2"
    local url=""
    local retval

    if [ "$CONF_PDU_STANDARD" == "tasmota" ]; then
        let id-=1
        url="http://$ip_address/cm?cmnd=Status%200"
        printVerbose "*** url='$url'"

        retval=$(curl $url 2>/dev/null | jq ".Status.FriendlyName[$id]")
        retval=$(echo "$retval" | tr -d '"')
    fi

    if [ -z "$retval" ]; then
        retval="Outlet $id"
    fi

    echo "$retval"
    return 0
}

#
# pipas@t7500:~$ curl http://192.168.2.5/cm?cmnd=Status%2010 2>/dev/null | grep -o '"Temperature":[^,]\+' | awk -F: '{print $2}'
# 24.4
#
# curl http://192.168.2.9/cm?cmnd=Status%208 | grep -o '"Power":[^,]\+' | awk -F: '{print $2}'
#
# TOGGLE:
# curl "http://192.168.2.30/cm?cmnd=Power%20TOGGLE"
#
# STATUS DETAILS:
# curl "http://192.168.2.30/cm?cmnd=Status%200" 2>/dev/null | jq .
#
# Get the friendly name from a tasmota device:
# curl "http://192.168.2.30/cm?cmnd=Status%209" 2>/dev/null | jq '.Status.FriendlyName[0]' | tr -d '"'
# curl "http://192.168.2.30/cm?cmnd=Status%209" 2>/dev/null | jq '.Status.Topic' | tr -d '"'
#
# Number of outlets:
# curl "http://192.168.2.18/cm?cmnd=Status%200" 2>/dev/null | jq '.Status.FriendlyName | length'
#
function switch_tasmota()
{
    local output
    local url="http://$CONF_PDU_SERVER/cm?cmnd="
    local logfile="$LOGFILE"
    local topic=""
    local string
    local outlet

    if [ -z "$logfile" ]; then
        logfile="/dev/null"
    fi

    outlet="$CONF_PDU_OUTLET"
    if [ -n "$OPTION_OUTLET" ]; then
        outlet="$OPTION_OUTLET"
    fi

    #
    #
    #
    case "$PDU_OPERATION" in 
        on)
            if [ -n "$CONF_PDU_MQTT" -a -n "$MQTT_SERVER" ]; then
                #
                # Switching with mqtt.
                # https://github.com/arendst/Sonoff-Tasmota/wiki/MQTT-Features
                #
                printVerbose "Switching '$CONF_PDU_DEVICE' on with MQTT..."
                
                topic="cmnd/$CONF_PDU_DEVICE/power"
                [ -n "$outlet" ] && topic+=$outlet

                mosquitto_pub \
                    -u "$MQTT_USER" \
                    -P "$MQTT_PASSWORD" \
                    -h "$MQTT_SERVER" \
                    -t "$topic" \
                    -m 1
            elif [ -n "$outlet" ]; then
                curl ${url}Power${outlet}%20on \
                    2>/dev/null >/dev/null
            else
                # Switching a specific outlet.
                curl ${url}Power%20on \
                    2>/dev/null >/dev/null
            fi
            ;;

        off)
            if [ -n "$CONF_PDU_MQTT" -a -n "$MQTT_SERVER" ]; then
                #
                # Switching with mqtt.
                # https://github.com/arendst/Sonoff-Tasmota/wiki/MQTT-Features
                #
                printVerbose "Switching '$CONF_PDU_DEVICE' off with MQTT..."
                
                topic="cmnd/$CONF_PDU_DEVICE/power"
                [ -n "$outlet" ] && topic+=$outlet

                mosquitto_pub \
                    -u "$MQTT_USER" \
                    -P "$MQTT_PASSWORD" \
                    -h "$MQTT_SERVER" \
                    -t "$topic" \
                    -m 0
            elif [ -n "$oulet" ]; then            
                curl ${url}Power${outlet}%20off \
                    2>/dev/null >/dev/null
            else
                curl ${url}Power%20off \
                    2>/dev/null >/dev/null
            fi
            ;;

        state)
            printVerbose "Checking switch state."
            printVerbose "*** outlet='$outlet'"
            if [ -z "$outlet" ]; then
                output=$(curl \
                    ${url}Power%20status \
                    2>$logfile)
            else
                output=$(curl \
                    ${url}Power${outlet}%20status \
                    2>$logfile)
            fi

            printVerbose "output: $output"
            if echo "$output" | grep -q '"ON"'; then
                printVerbose "Result: 'on'"
                echo "on"
            else
                echo "off"
                printVerbose "Result: 'off'"
            fi
            ;;

        power)
            # 
            # Reading the power from tasmota.
            #
            string="$(curl ${url}Status%2010 2>/dev/null)"
            printVerbose " *** string: $string"
            
            string="$(echo "$string" | grep -o '"Power":[^,}]\+')"
            string="$(echo "$string" | awk -F: '{print $2}')"
            printVerbose " *** string: $string"
           
            print_value "$string" "0"
            ;;
        
        voltage)
            # 
            # Reading the voltage from tasmota.
            #
            string="$(curl ${url}Status%2010 2>/dev/null)"
            printVerbose " *** string: $string"
            
            string="$(echo "$string" | grep -o '"Voltage":[^,}]\+')"
            string="$(echo "$string" | awk -F: '{print $2}')"
            printVerbose " *** string: $string"
           
            print_value "$string" "0"
            ;;
        
        current)
            # 
            # Reading the current from tasmota.
            #
            string="$(curl ${url}Status%2010 2>/dev/null)"
            printVerbose " *** string: $string"
            
            string="$(echo "$string" | grep -o '"Current":[^,}]\+')"
            string="$(echo "$string" | awk -F: '{print $2}')"
            printVerbose " *** string: $string"
           
            print_value "$string" "0"
            ;;

        temperature)
            string="$(curl ${url}Status%2010 2>/dev/null)"
            printVerbose " *** string: $string"

            string="$(echo "$string" | grep -o '"Temperature":[^,}]\+')"
            string="$(echo "$string" | awk -F: '{print $2}')"
            printVerbose " *** string: $string"

            print_value "$string" "0"
            ;;

        humidity)
            # 
            # Reading the humidity from tasmota.
            #
            string="$(curl ${url}Status%2010 2>/dev/null)"
            printVerbose " *** string: $string"

            string="$(echo "$string" | grep -o '"Humidity":[^,}]\+')"
            string="$(echo "$string" | awk -F: '{print $2}')"
            printVerbose " *** string: $string"

            print_value "$string" "0"
            ;;
    esac
}

function print_header()
{
    echo -en $TERM_BOLD

    cat <<EOF
DEVICE           MODE  #O GROUP         SERVER           NAME
--------------------------------------------------------------------------------
EOF

    echo -en $TERM_NORMAL


}

# https://tobinsramblings.wordpress.com/2011/05/03/snmp-tutorial-apc-pdus/
#
# Check the status of one outlet:
# snmpget -v 1 -c private pdu01 PowerNet-MIB::sPDUOutletCtl.6
# 
# Turn on/off:
# snmpset -v 1 -c private pdu01 PowerNet-MIB::sPDUOutletCtl.5 integer 1
# snmpset -v 1 -c private pdu01 PowerNet-MIB::sPDUOutletCtl.5 integer 2
#
# Get help of an OID:
# snmptranslate -Td PowerNet-MIB::sPDUOutletCtl.1
# 
# List the OIDs:
# snmpwalk -v 1 -c public pdu01 enterprises.318
function print_outlets_long_for_device()
{
    local device="$1"
    local outlet
    local outlet_name
    local status

    CONFIG_FILE_PATH="$HOME/.pip/${device}.pdu"
    CONF_PDU_NAME=""
    CONF_PDU_SERVER=""
    CONF_PDU_STANDARD=""
    CONF_PDU_FIRST_OUTLET=""
    CONF_PDU_LAST_OUTLET=""
    
    if [ ! -f "$CONFIG_FILE_PATH" ]; then
        printError "File '$CONFIG_FILE_PATH' was not found."
    fi

    printVerbose "Loading file '$CONFIG_FILE_PATH'..."
    source "$CONFIG_FILE_PATH"

    [ -z "$CONF_PDU_FIRST_OUTLET" ] && CONF_PDU_FIRST_OUTLET=1
    [ -z "$CONF_PDU_LAST_OUTLET" ] && CONF_PDU_LAST_OUTLET=1
    for outlet in $(seq "$CONF_PDU_FIRST_OUTLET" "$CONF_PDU_LAST_OUTLET"); do
        if [ "$CONF_PDU_STANDARD" == "apc" ]; then
            printVerbose "Getting status with snmp."
            line=$(snmpget \
                -v 1 \
                -c private $device \
                PowerNet-MIB::sPDUOutletCtl.$outlet)
        
            if echo "$line" | grep -q outletOff; then
                status="off"
            elif echo "$line" | grep -q outletOn; then
                status="on"
            else
                status="?"
            fi
        
            printVerbose "stat/${CONF_PDU_SERVER}_$outlet/POWER -> $status"
            mosquitto_pub \
                -u "$MQTT_USER" \
                -P "$MQTT_PASSWORD" \
                -h "$MQTT_SERVER" \
                -t "stat/${CONF_PDU_SERVER}_$outlet/POWER" \
                -m "$status"

            outlet_name=$(snmpget \
                -v 1 \
                -c private $device \
                PowerNet-MIB::sPDUOutletCtlName.$outlet)
        
            outlet_name=$(echo "$outlet_name" | cut -d'"' -f2)
        else
            status=$(pip-pdu-control --state $device --outlet="$outlet")
            outlet_name=$(outlet_name "$CONF_PDU_SERVER" "$outlet")
        fi

        printf "%2d " "$outlet"
        if [ "$status" == "on" ]; then
            printf "$XTERM_COLOR_GREEN%-4s$TERM_NORMAL " "$status"
        elif [ "$status" == "off" ]; then
            printf "$XTERM_COLOR_RED%-4s$TERM_NORMAL " "$status"
        else
            printf "%-4s " "$status"
        fi

        printf "$DEVICE_COLOR%-16s$TERM_NORMAL "       "$device"
        printf "$XTERM_COLOR_PURPLE%-12s$TERM_NORMAL " "$outlet_name"
        printf "\n"
    done
}

function print_outlets_long_header()
{
    echo -en $TERM_BOLD
    cat <<EOF
 # STAT DEVICE           OUTLET_NAME
--------------------------------------------------------------------------------
EOF

    echo -en $TERM_NORMAL

}


function print_outlets_long()
{
    print_outlets_long_header

    for OPTION_DEVICE in $(echo $OPTION_DEVICES | tr ',' ' '); do
        print_outlets_long_for_device $OPTION_DEVICE
    done
}

function print_summary()
{
    if [ -z "$OPTION_PRINT_REPORT" ]; then
        return 0
    fi

    printf "\n"
    printf "Summary:\n"
    printf "         Total: %'6d devices\n" "$SUM_DEVICES" 
    printf "        Pinged: %'6d devices\n" "$PINGED_DEVICES" 
    printf "       Replied: %'6d devices\n" "$REPLIED_DEVICES" 
    printf "\n"
}

function print_measurements_long()
{
    for config_file in $HOME/.pip/*.pdu; do
        if [ ! -f "$config_file" ]; then
            continue
        fi

        device_name=$(basename "$config_file" .pdu)
        
        if ! device_requested "$device_name"; then
            continue
        fi
        
        CONF_PDU_NAME=""
        CONF_PDU_SERVER=""
        CONF_PDU_URL=""
        CONF_PDU_STANDARD=""
        CONF_PDU_CATEGORY=""
        CONF_PDU_MQTT=""
        CONF_PDU_MEASURES_POWER=""
        CONF_PDU_MEASURES_VOLTAGE=""
        CONF_PDU_MEASURES_CURRENT=""
        CONF_PDU_MEASURES_TEMPERATURE=""
        CONF_PDU_MEASURES_HUMIDITY=""
        CONF_PDU_FIRST_OUTLET=""
        CONF_PDU_LAST_OUTLET=""
        source "$config_file"

        has_value=""
        temperature="-"
        humidity="-"
        power="-"
        voltage="-"
        current="-"
        if [ "$CONF_PDU_MEASURES_TEMPERATURE" ]; then
            temperature=$(pip-pdu-control --temperature "$device_name")
            temperature+="C"
            has_value="true"
        fi

        if [ "$CONF_PDU_MEASURES_HUMIDITY" ]; then
            humidity=$(pip-pdu-control --humidity "$device_name")
            humidity+="%"
            has_value="true"
        fi

        if [ "$CONF_PDU_MEASURES_POWER" ]; then
            power=$(pip-pdu-control --power "$device_name")
            power+="W"
            has_value="true"
        fi

        if [ "$CONF_PDU_MEASURES_VOLTAGE" ]; then
            voltage=$(pip-pdu-control --voltage "$device_name")
            voltage+="V"
            has_value="true"
        fi

        if [ "$CONF_PDU_MEASURES_CURRENT" ]; then
            current=$(pip-pdu-control --current "$device_name")
            current+="A"
            has_value="true"
        fi
        
        if [ -z "$has_value" ]; then
            continue
        fi

        printf "$DEVICE_COLOR%-16s$TERM_NORMAL "  "$device_name"
        printf "%7s " "$voltage"
        printf "%7s " "$current"
        printf "%7s " "$power"
        printf "%7s " "$temperature"
        printf "%7s " "$humidity"
        printf "$COMMENT_COLOR%s$TERM_NORMAL"  "$CONF_PDU_NAME"

        printf "\n"
    done
}

function ping_device()
{
    ping -q -W 1 -c 1 "$1" >/dev/null 2>/dev/null
}

#
#
#    payload.title = payload.hostName + " (" + payload.name + ")";
#    payload.description = payload.ipAddress;
#    payload.icon_name = "signal_wifi_2_bar";
#
function device_mqtt_string()
{
    local device
    local ip
    local long_name
    local title=""
    local standard=""
    local icon_name="signal_wifi_2_bar"
    local description

    while [ -n "$1" ]; do
        case "$1" in
            --device)
                device="$2"
                shift 2
                ;;

            --ip)
                ip="$2"
                shift 2
                ;;

            --long-name)
                long_name="$2"
                shift 2
                ;;

            --standard)
                standard="$2"
                shift 2
                [ "$standard" == "tasmota" ] && icon_name="signal_wifi_2_bar"
                [ "$standard" == "apc" ]     && icon_name="power"
                ;;

            *)
                break
                ;;
        esac
    done

    title="$long_name"
    description="$device ($ip)"
    echo -n "{"
    echo -n "\"name\": \"$device\", "
    echo -n "\"icon_name\": \"$icon_name\", "
    echo -n "\"title\": \"$title\", "
    echo -n "\"description\": \"$description\", "
    echo -n "\"ip\": \"$ip\""
    echo -n "}"
    echo 

}

function device_mqtt_topic()
{
    local device_name=$1

    echo "pip/pdu-control/discover/$device_name"
}

# Prints the list of PDU devices in long format.
#
function print_device_list_long()
{
    local group
    local name
    local mode
    local n_outlets
    local topic
    local message

    printVerbose "print_device_list_long()"
    print_header

    for config_file in $HOME/.pip/*.pdu; do
        if [ ! -f "$config_file" ]; then
            continue
        fi

        device_name=$(basename "$config_file" .pdu)

        if ! device_requested "$device_name"; then
            continue
        fi
        
        CONF_PDU_NAME=""
        CONF_PDU_SERVER=""
        CONF_PDU_URL=""
        CONF_PDU_STANDARD=""
        CONF_PDU_CATEGORY=""
        CONF_PDU_MQTT=""
        CONF_PDU_MEASURES_POWER=""
        CONF_PDU_MEASURES_VOLTAGE=""
        CONF_PDU_MEASURES_CURRENT=""
        CONF_PDU_MEASURES_TEMPERATURE=""
        CONF_PDU_MEASURES_HUMIDITY=""
        CONF_PDU_FIRST_OUTLET=""
        CONF_PDU_LAST_OUTLET=""
        source "$config_file"
    
        group="$CONF_PDU_CATEGORY"
        if [ -z "$group" ]; then
            group="-"
        fi

        if [ -n "$OPTION_GROUP" -a "$OPTION_GROUP" != "$group" ]; then
            continue
        elif [ -n "$OPTION_WITH_POWER" -a -z "$CONF_PDU_MEASURES_POWER" ]; then
            continue
        elif [ -n "$OPTION_WITH_TEMPERATURE" -a \
               -z "$CONF_PDU_MEASURES_TEMPERATURE" ]; then
            continue
        fi

        n_outlets="$CONF_PDU_LAST_OUTLET"
        [ -z "$n_outlets" ] && n_outlets="1"

        #
        # The mode string.
        #
        mode=""
        if [ -n "$CONF_PDU_MEASURES_POWER" ]; then
            mode+="W"
        else
            mode+="-"
        fi
        
        if [ -n "$CONF_PDU_MEASURES_VOLTAGE" ]; then
            mode+="V"
        else
            mode+="-"
        fi
        
        if [ -n "$CONF_PDU_MEASURES_CURRENT" ]; then
            mode+="A"
        else
            mode+="-"
        fi

        if [ -n "$CONF_PDU_MEASURES_TEMPERATURE" ]; then
            mode+="C"
        else
            mode+="-"
        fi
        
        if [ -n "$CONF_PDU_MEASURES_HUMIDITY" ]; then
            mode+="%"
        else
            mode+="-"
        fi

        # Printing the device name, pinging it if requested.
        if [ -z "$OPTION_PING" ]; then 
            printf "$DEVICE_COLOR%-16s$TERM_NORMAL "  "$device_name"
        else
            let PINGED_DEVICES+=1
            if ping_device "$CONF_PDU_SERVER"; then
                printf "$OK_COLOR%-16s$TERM_NORMAL "  "$device_name"
                let REPLIED_DEVICES+=1
            else
                printf "$ERR_COLOR%-16s$TERM_NORMAL "  "$device_name"
            fi
        fi

        printf "$TERM_NORMAL%-5s$TERM_NORMAL "    "$mode"
        printf "$NUMBER_COLOR%2d$TERM_NORMAL "     "$n_outlets"

        #printf "$GROUP_COLOR%-8s$TERM_NORMAL "    "$CONF_PDU_STANDARD"
        printf "$OWNER_COLOR%-13s$TERM_NORMAL "   "$group"
        printf "$IP_COLOR%-16s$TERM_NORMAL "      "$CONF_PDU_SERVER"

        if [ -z "$OPTION_PRINT_FILES" ]; then
            printf "$COMMENT_COLOR%s$TERM_NORMAL"  "$CONF_PDU_NAME"
        else
            printf "$FILE_COLOR%s$TERM_NORMAL"     "$config_file"
        fi

        printf "\n"

        let SUM_DEVICES+=1

        topic=$(device_mqtt_topic "$device_name")
        message=$(device_mqtt_string \
            --device    "$device_name" \
            --ip        "$CONF_PDU_SERVER" \
            --long-name "$CONF_PDU_NAME" \
            --standard  "$CONF_PDU_STANDARD")

        #echo "$topic: $message"
        #echo "$CONF_PDU_STANDARD"
        mosquitto_pub \
            -u "$MQTT_USER" \
            -P "$MQTT_PASSWORD" \
            -h "$MQTT_SERVER" \
            -t "$topic" \
            -m "$message"
    done

    print_summary
}

#
# Lists the devices in a brief format (only the name or only the files).
#
function print_device_list_brief()
{
    local group
    local name
    local config_file

    for config_file in $HOME/.pip/*.pdu; do
        if [ ! -f "$config_file" ]; then
            continue
        fi

        device_name=$(basename "$config_file" .pdu)
        
        if ! device_requested "$device_name"; then
            continue
        fi
        
        CONF_PDU_NAME=""
        CONF_PDU_SERVER=""
        CONF_PDU_URL=""
        CONF_PDU_STANDARD=""
        CONF_PDU_CATEGORY=""
        CONF_PDU_MEASURES_POWER=""
        CONF_PDU_MEASURES_VOLTAGE=""
        CONF_PDU_MEASURES_CURRENT=""
        CONF_PDU_MEASURES_TEMPERATURE=""
        CONF_PDU_MEASURES_HUMIDITY=""
        source "$config_file"
    
        group="$CONF_PDU_CATEGORY"
        if [ -z "$group" ]; then
            group="-"
        fi

        if [ -n "$OPTION_GROUP" -a "$OPTION_GROUP" != "$group" ]; then
            continue
        elif [ -n "$OPTION_WITH_POWER" -a -z "$CONF_PDU_MEASURES_POWER" ]; then
            continue
        elif [ -n "$OPTION_WITH_TEMPERATURE" -a \
               -z "$CONF_PDU_MEASURES_TEMPERATURE" ]; then
            continue
        fi

        if [ -n "$OPTION_PRINT_FILES" ]; then
            printf "%s " "$config_file"
        else
            printf "%s "  "$device_name"
        fi

        printf "\n"
    done
}

function discover_device_at()
{
    local device="$1"
    local url="http://$device/cm?cmnd=Status%200"
    local name
    local ip_address
    local mac
    local last_outlet
    local config_file

    printVerbose "Accessing '$url'..."
    name=$(curl $url 2>/dev/null | jq '.Status.Topic' | tr -d '"')
    if [ -z "$name" ]; then
        printError "Failed to discover $url..."
        return 0
    fi

    config_file="$HOME/.pip/$name.pdu"
    if [ -f "$config_file" ]; then
        printError "File '$config_file' already exists."
        return 0
    fi

    ip_address=$(curl $url 2>/dev/null | jq '.StatusNET.IPAddress' | tr -d '"')
    mac=$(curl $url 2>/dev/null | jq '.StatusNET.Mac' | tr -d '"')
    last_outlet=$(curl $url 2>/dev/null | jq '.Status.FriendlyName | length')
    energy=$(curl $url 2>/dev/null | jq '.StatusSNS.ENERGY.Total')
    temperature=$(curl $url 2>/dev/null | jq '.StatusSNS.AM2301.Temperature')

    if [ "$energy" == "null" ]; then
        energy=""
    else
        energy="true"
    fi
    
    if [ "$temperature" == "null" ]; then
        temperature=""
    else
        temperature="true"
    fi

    cat >"$config_file" <<EOF 
#
# Device $name
#
CONF_PDU_NAME="$name"
CONF_PDU_SERVER="$device"
CONF_PDU_URL="http://\$CONF_PDU_SERVER"
CONF_PDU_STANDARD="tasmota"
CONF_PDU_FIRST_OUTLET="1"
CONF_PDU_LAST_OUTLET="$last_outlet"

CONF_PDU_CATEGORY="pending"

CONF_PDU_IP_ADDRESS="$ip_address"
CONF_PDU_MAC="$mac"

CONF_PDU_MEASURES_POWER="$energy"
CONF_PDU_MEASURES_VOLTAGE="$energy"
CONF_PDU_MEASURES_CURRENT="$energy"
CONF_PDU_MEASURES_TEMPERATURE="$temperature"
CONF_PDU_MEASURES_HUMIDITY="$temperature"
EOF

    cat "$config_file"
}

if [ -f "$MQTT_CONFIG_FILE" ]; then
    #printVerbose "Loading '$MQTT_CONFIG_FILE'..."
    source "$MQTT_CONFIG_FILE"
fi

#
# Checking the command line options.
#
if [ -n "$DISCOVER_OPTION" ]; then
    for device in $@; do
        discover_device_at $device
    done
    exit 1
elif [ -n "$LIST_MEASUREMENTS_OPTION" ]; then
    print_measurements_long
    exit 0
elif [ -n "$LIST_OPTION" ]; then
    if [ "$OPTION_LONG" ]; then
        print_device_list_long
    else
        print_device_list_brief | column -s' '
    fi

    exit 0
elif [ -n "$LIST_OUTLETS_OPTION" ]; then
    print_outlets_long
    exit 0
fi

if [ -z "$OPTION_DEVICES" ]; then
    if [ -n "$OPTION_GROUP" -o -n "$OPTION_ALL" ]; then
        OPTION_DEVICES=$(print_device_list_brief)
    fi
fi

if [ -z "$OPTION_DEVICES" ]; then
    printError "Device name is not provided."
    exit 1
fi

for OPTION_DEVICE in $(echo $OPTION_DEVICES | tr ',' ' '); do
    printMessage "Processing operation '$PDU_OPERATION' on '$OPTION_DEVICE'."
    CONFIG_FILE_PATH="$HOME/.pip/${OPTION_DEVICE}.pdu"

    if [ "$PDU_OPERATION" == "toggle" ]; then
        state=$(pip-pdu-control --state $OPTION_DEVICE)
        if [ "$state" == "on" ]; then
            pip-pdu-control --off "$OPTION_DEVICE"
        else
            pip-pdu-control --on "$OPTION_DEVICE"
        fi

        continue
    fi

    #
    # If we are requested for state and we already received the state from MQTT
    # we don't need to waste time checking it on the device.
    #
    if [ "$PDU_OPERATION" == "state" ]; then
        STATE_DIR="$HOME/.pip.measurements"
        if [ -f "$STATE_DIR/${OPTION_DEVICE}.state" ]; then
            cat "$STATE_DIR/${OPTION_DEVICE}.state" | tr '[A-Z]' '[a-z]'
            continue
        fi
    fi

    if [ ! -f "$CONFIG_FILE_PATH" ]; then
        printError "Config file '$CONFIG_FILE_PATH' does not exist."
        continue
    fi

    #
    # Loading the config file.
    #
    CONF_PDU_OUTLET=""
    CONF_PDU_MQTT=""
    CONF_PDU_NAME=""
    CONF_PDU_SERVER=""
    CONF_PDU_URL=""
    CONF_PDU_STANDARD=""
    CONF_PDU_CATEGORY=""
    CONF_PDU_DEVICE="$OPTION_DEVICE"
    CONF_PDU_MEASURES_POWER=""
    CONF_PDU_MEASURES_CURRENT=""
    CONF_PDU_MEASURES_TEMPERATURE=""
    CONF_PDU_MEASURES_HUMIDITY=""

    source "$CONFIG_FILE_PATH"

    if [ "$CONF_PDU_OUTLET" ]; then
        PDU_OUTLETS="$CONF_PDU_OUTLET"
    fi
    
    #printVerbose "      device : '$OPTION_DEVICE'"
    #printVerbose " conf_device : '$CONF_PDU_DEVICE'"
    #printVerbose "      server : '$CONF_PDU_SERVER'"
    #printVerbose "    standard : '$CONF_PDU_STANDARD'"
    #printVerbose "       isMqtt: '$CONF_PDU_MQTT'"
    #printVerbose "   operation : '$PDU_OPERATION'"
    #printVerbose ""

    case "$CONF_PDU_STANDARD" in 
        apc)
            switch_apc
            ;;

        orvibo)
            switch_orvibo
            ;;

        tasmota)
            switch_tasmota
            ;;

        *)
            printError "The CONF_PDU_STANDARD is invalid in '$CONFIG_FILE_PATH'"
    esac

    #sleep 0.5
done


