#!/usr/local/bin/bash
# -*- sh -*-

: << =cut

=head1 NAME

  switchbotmeter_multi - Munin plugin to monitor temperature and humidity with SwitchBot Meter

=head1 CONFIGURATION

  Obtain a token, secret and deviceid for SwitchBot API. See the following website.
  https://github.com/OpenWonderLabs/SwitchBotAPI

=head1 ENVIRONMENT VARIABLES

  env.token    : Token to access SwitchBot API. (Required)
  env.secret   : Secret to access SwitchBot API. (Required)
  env.deviceid : Device ID(s) of SwitchBot Meter separated by white space. (Required)
  env.interval : Interval in seconds for API access. (Optional, default is 0)

=head1 NOTES

  The amount of SwitchBot API calls per day is limited to 10000 times.
  If munin-node executes this plugin every 5 minutes, the API will be called 288 times a day.
  You can use env.interval parameter to prevent frequent API access. For example, if you set
  env.interval 900, the API response will be cached to local file for 900 seconds(15 minutes).

=head1 AUTHOR

  K.Cima https://github.com/shakemid

=head1 LICENSE

  GPLv2
  SPDX-License-Identifier: GPL-2.0-only

=head1 Magic markers

  #%# family=contrib
  #%# capabilities=

=cut

. "${MUNIN_LIBDIR}/plugins/plugin.sh"

set -o nounset
set -o pipefail

# Token to access SwitchBot API
token=${token:?}

# Secret to access SwitchBot API
secret=${secret:?}

# Device ID of SwitchBot Meter
deviceids=${deviceid:?}

# Interval for API access (second)
interval=${interval:-0}

# Plugin Name
pluginname=$( basename "$0" )

config() {
    local deviceid

    cat <<EOF
multigraph ${pluginname}_temperature
graph_title SwitchBot Meter Temperature
graph_category sensors
graph_scale no
graph_vlabel Temperature C
graph_args --base 1000
EOF
    for deviceid in ${deviceids}
    do
        echo "${deviceid}.label" "${deviceid}"
    done

    cat <<EOF
multigraph ${pluginname}_humidity
graph_title SwitchBot Meter Humidity
graph_category sensors
graph_scale no
graph_vlabel Humidity %
graph_args --base 1000 --lower-limit 0 --upper-limit 100
EOF
    for deviceid in ${deviceids}
    do
        echo "${deviceid}.label" "${deviceid}"
    done

    cat <<EOF
multigraph ${pluginname}_battery
graph_title SwitchBot Meter Battery
graph_category sensors
graph_scale no
graph_vlabel Battery %
graph_args --base 1000 --lower-limit 0 --upper-limit 100
EOF
    for deviceid in ${deviceids}
    do
        echo "${deviceid}.label" "${deviceid}"
    done
}

fetch() {
    local deviceid func ref

    for deviceid in ${deviceids}
    do
        do_fetch "${deviceid}" &
    done
    wait

    for deviceid in ${deviceids}
    do
        local padding=1 $( cat "${MUNIN_STATEFILE}_${deviceid}" )
    done

    for func in temperature humidity battery
    do
        echo multigraph "${pluginname}_${func}"

        for deviceid in ${deviceids}
        do
           ref=${func}_${deviceid}
           echo "${deviceid}.value" "${!ref:-U}"
        done
    done
}

do_fetch() {
    local deviceid time_now time_modified
    deviceid=$1

    if [ "${interval}" -gt 0 ]; then
        time_now=$( date +%s )

        if [ -f "${MUNIN_STATEFILE}_${deviceid}" ]; then
            time_modified=$( stat --format=%Y "${MUNIN_STATEFILE}_${deviceid}" )
        else
            # initial run
            time_modified=0
        fi

        if [ $(( time_now - time_modified )) -lt "${interval}" ]; then
            # do nothing
            return
        fi
    fi

    fetch_api "${deviceid}" > "${MUNIN_STATEFILE}_${deviceid}"
}

fetch_api() {
    local deviceid temperature humidity battery t sign nonce http_response http_status api_response api_status
    deviceid=$1

    t=$( date +'%s%3N' )
    nonce=$( openssl rand -base64 32 )
    sign=$( echo -n "${token}${t}${nonce}" | openssl dgst -sha256 -hmac "${secret}" -binary | openssl base64 )

    http_response=$( curl -s --fail --retry 3 \
        -w '\n%{http_code}\n' \
        -H "Content-Type:application/json;charset=utf8" \
        -H "Authorization:${token}" -H "t:${t}" -H "nonce:${nonce}" -H "sign:${sign}" \
        "https://api.switch-bot.com/v1.1/devices/${deviceid}/status" )

    http_status=$( echo "${http_response}" | tail -n 1 )
    if [ "${http_status}" != "200" ]; then
        echo Error with HTTP status = "${http_status}" 1>&2
        return 1
    fi

    api_response=$( echo "${http_response}" | sed '$d' )
    api_status=$( echo "${api_response}" | jq '.statusCode' )
    if [ "${api_status}" != "100" ]; then
        echo Error with API status = "${api_status}" 1>&2
        return 1
    fi

    temperature=$( echo "${api_response}" | jq '.body.temperature' )
    humidity=$( echo "${api_response}" | jq '.body.humidity' )
    battery=$( echo "${api_response}" | jq '.body.battery' )

    echo "temperature_${deviceid}=${temperature}" "humidity_${deviceid}=${humidity}" "battery_${deviceid}=${battery}"
}

# Main
case ${1:-} in
config)
    config
    if [ "${MUNIN_CAP_DIRTYCONFIG:-}" = "1" ]; then
        fetch
    fi
    ;;
*)
    fetch
    ;;
esac

exit 0
