--- /dev/null
+#!/bin/sh
+# Checks inverter state as repoted by a metecontrol weblog
+# device HTML interface, reports issues via Atlassian
+# Opsgenie. Written with the intention to run via cron on
+# a LTE Router with either OpenWRT or Teltonika RutOS.
+# Requires curl for the Opsgenie part, otherwise
+# sh/cut/cat/wget/test are enough.
+
+# createAlert <alias> <message> <description>
+createAlert() {
+ local alias="${1}"
+ local message="${2}"
+ local description="${3}"
+
+ if [ -z "${alias}" ]; then
+ echo "ERROR: Empty alert alias"
+ return
+ fi
+
+ if [ -z "${message}" ]; then
+ echo "ERROR: Empty alert message"
+ return
+ fi
+
+ curl -o /dev/null -s \
+ -X POST https://${API_HOST}/v2/alerts \
+ -H "Content-Type: application/json" \
+ -H "Authorization: GenieKey ${API_KEY}" \
+ -d \
+ "{
+ \"message\":\"${message}\",
+ \"alias\":\"${alias}\",
+ \"description\":\"${description}\"
+ }"
+}
+
+# closeAlert <alias>
+closeAlert() {
+ local alias=${1}
+
+ if [ -z "${alias}" ]; then
+ echo "ERROR: Empty alert alias"
+ return
+ fi
+
+ curl -o /dev/null -s \
+ -X POST https://${API_HOST}/v2/alerts/${alias}/close?identifierType=alias \
+ -H "Authorization: GenieKey ${API_KEY}" \
+ -H "Content-Type: application/json" \
+ -d '{}'
+
+}
+
+# parseMcLine <meteocontrol html page output line>
+parseMcLine() {
+ local input="${@}"
+
+ local wrleistung="$(echo ${input} | cut -d'>' -f 13 | cut -d'<' -f 1)"
+ local wradresse="$(echo ${input} | cut -d'>' -f 4 | cut -d'<' -f 1)"
+ local wrserial="$(echo ${input} | cut -d'>' -f 7 | cut -d'<' -f 1)"
+
+ # known bad case e.g. during the night
+ if [ "${wrleistung}" = "---" ]; then
+ echo "No Power Value at WR${wradresse} Serial:${wrserial}"
+ return 3
+ fi
+
+ # known good case
+ if [ "${wrleistung}" -gt 0 ]; then
+ echo "OK"
+ return
+ fi
+
+ echo "ERROR: WR${wradresse} ${wrleistung}W Serial:${wrserial}"
+ return 1
+}
+
+# parseMcOutput <meteocontrol status html output page>
+parseMcOutput() {
+ local file="${1}"
+ local result=""
+ if ! [ -f "${file}" ]; then
+ echo "ERROR: Supplied input ${file} is not a regular file"
+ return
+ fi
+
+ grep "cLink" "${file}" | while read line; do
+ result="$(parseMcLine \"${line}\")"
+ # good case
+ if [ "${result}" = "OK" ]; then
+ continue
+ fi
+ # return failure data
+ echo -n "${result} -- "
+ done
+}
+
+# getMCstates <outfile> <status-url>
+getMCstates() {
+ local outfile="${1}"
+ local url="${2}"
+
+ if [ -z "${outfile}" ]; then
+ echo "ERROR: no meteocontrol tmp file provided"
+ exit 1
+ fi
+
+ if [ -z "${url}" ]; then
+ echo "ERROR: no meteocontrol URL provided"
+ exit 1
+ fi
+
+ # remove eventual remains of prior runs
+ rm -f "${outfile}"
+
+ # ready to try to download the data from meteocontrol
+ if ! wget -q -O "${outfile}" "${url}"; then
+ echo "ERROR: could not download ${url} into ${outfile}"
+ exit 1
+ fi
+
+ # verify we have content to parse in our downloaded file
+ if ! grep -c -q cLink "${outfile}"; then
+ echo "ERROR: no matching cLink lines found in output file ${outfile}"
+ exit 1
+ fi
+}
+
+# checkAlertState <statefile> <new-status>
+# return 0 on alert state change
+# return 1 if nothing changed
+checkAlertState() {
+ local statefile="${1}"
+ local newstatus="${2}"
+ local oldstatus="null"
+
+ test -f "${statefile}" && oldstatus="$(cat ${statefile})"
+ if [ "${oldstatus}" = "${newstatus}" ]; then
+ return 1
+ fi
+
+ # update state file on state change
+ echo "${newstatus}" > "${statefile}"
+ return 0
+}
+
+### main configuration
+API_HOST="api.eu.opsgenie.com"
+API_KEY=""
+
+# adjust meteocontrol default password and IPs
+mc1="http://admin:ist02@192.168.1.2:80/html/de/onlineOverWr.html"
+mc2="http://admin:ist02@192.168.1.3:80/html/de/onlineOverWr.html"
+
+### main loop
+for dev in mc1 mc2; do
+ outfile="/tmp/${dev}.html"
+ statefile="/tmp/pvstate-${dev}"
+ url=$(eval echo \${$dev})
+
+ # download the html overview page from meteocontrol device
+ getMCstates "${outfile}" "${url}"
+
+ # parse the overview page and collect failures
+ failures=$(parseMcOutput "${outfile}")
+
+ # handle failures and alerting
+ if ! [ -z "${failures}" ]; then
+ checkAlertState "${statefile}" "FAILED" && \
+ createAlert "${dev}" "PV ${dev}" "${failures}"
+ continue
+ fi
+
+ # update alert state close alert on state change
+ checkAlertState "${statefile}" "OK" && \
+ closeAlert "${dev}"
+done