aircon/aircon.sh

1579 lines
41 KiB
Bash
Executable File

#!/bin/bash
# TODO: comparison to previous reading
# TODO: gnuplot of given time range
# TODO: ditch pymyair
# TODO: manual controls
DEFAULT_KILLFILE="${HOME}/.aircon_noaction"
DEFAULT_CONFIGFILE=${HOME}/.airconrc
DEFAULT_CSVFILE=${HOME}/acstats.csv
DEFAULT_AIRCON_IP=10.99.99.1
DEFAULTLIMIT=3
DEFAULT_TOLERANCE=0
VALID_MODES=" cool heat "
VALID_ZONE_STATES=" open close "
BOLD="\033[1m"
PLAIN="\033[0m"
UNDERLINE="\033[4m"
GREY="\033[1;30m"
RED="\033[31m"
GREEN="\033[32m"
YELLOW="\033[33m"
BLUE="\033[34m"
CYAN="\033[36m"
WHITE="\033[37m"
LINK="$BLUE$UNDERLINE"
function enable_cronmode() {
cronmode=1
BOLD=""
PLAIN=""
UNDERLINE=""
GREY=""
RED=""
GREEN=""
YELLOW=""
BLUE=""
CYAN=""
WHITE=""
LINK="$BLUE$UNDERLINE"
}
function usage() {
echo "usage: $0 [options]"
echo
echo " Modifies aircon based on configured parameters in $DEFAULT_CONFIGFILE."
echo
echo " -h Show this text."
echo " -c Cron mode. Only show output if actions were taken."
echo " -i x.x.x.x Specify IP address for aircon (default is $DEFAULT_AIRCON_IP)"
echo " -I db Log all zone temperatures to given influxdb database, then exit (see -o)."
echo " -A db Log actions to given influxdb database."
echo " -f file Specify an alternate config file."
echo " -k file If file exists, never change aircon settings (default: $DEFAULT_KILLFILE)."
echo " -l num Set number of too hot/cold zones at which to taking action."
echo " -L Log all zone temperatures to CSV file, then exit (see -o)."
echo " -o file Specify CSV output file. Default: $DEFAULT_CSVFILE"
echo " -m Generate a config file based on current aircon setup."
echo " -p Profiler mode."
echo " -w List which zone owners' devices are available then exit."
echo " -t num Specify degrees below min temperature before taking action."
echo " -T num Specify degrees above max temperature before taking action."
echo " -y Actually run commands/db inserts. By default, commands are just displayed."
echo
}
function action() {
echo -e "$BOLD$GREEN>> Running action: $PLAIN$GREEN$*$PLAIN"
}
function error() {
echo -e "$BOLD${RED}ERROR: $PLAIN$RED$*$PLAIN" >/dev/stderr
}
function info() {
echo -e "$BOLD${CYAN}>> $PLAIN$CYAN$*$PLAIN"
}
# get_midrange zone_idx
function get_midrange() { # outputs midpoint of temperature range for given zone
local min max
min=${zwantmin[$idx]}
max=${zwantmax[$idx]}
echo "($min + $max) / 2" | bc
}
function getoppositestate() {
if [[ $1 == "open" ]]; then
echo "close"
elif [[ $1 == "close" ]]; then
echo "open"
fi
}
function getzoneaction() { # populates zproblem[] and zaction[]
local idx problem num thisaction priority pingok fv
local donearby
idx=$1
profile "getzoneaction for idx ${zname[$idx]}"
thisaction="n/a"
if [[ ${zignore[$idx]} -eq 1 ]]; then
problem="n/a"
else
if [[ -z ${zowner[$idx]} ]]; then
pingok=1
elif canping "${zowner[$idx]}"; then
pingok=1
else
pingok=0
fi
fv=$(getforcevent ${zname[$idx]})
donearby=0
if [[ ! -z $fv && ${zstate[$idx]} == $(getoppositestate ${fv}) ]]; then
problem="force_${fv}"
priority=99
elif [[ $airconmode != "off" && ${zstate[$idx]} != "close" && $pingok -eq 0 ]]; then
problem="owner_not_home"
priority=99
elif [[ $pingok -eq 1 && $(echo "${ztemp[$idx]} > (${zwantmax[$idx]} + $tolerance_h)" | bc) -eq 1 ]]; then
problem="too_hot"
priority=$(echo "${ztemp[$idx]} - ${zwantmax[$idx]}" | bc)
if [[ $(echo "${ztemp[$idx]} > (${zwantmax[$idx]} + ($tolerance_h * 2))" | bc) -eq 1 ]]; then
donearby=1
fi
elif [[ $pingok -eq 1 && $(echo "${ztemp[$idx]} < (${zwantmin[$idx]} - $tolerance_l)" | bc) -eq 1 ]]; then
problem="too_cold"
priority=$(echo "${zwantmin[$idx]} - ${ztemp[$idx]}" | bc)
if [[ $(echo "${ztemp[$idx]} < (${zwantmin[$idx]} - ($tolerance_l * 2))" | bc) -eq 1 ]]; then
donearby=1
fi
else
problem="n/a"
priority=0
fi
#info "${zname[$idx]} priority is $priority"
zproblem[$idx]="$problem"
zpri[$idx]="$priority"
if [[ $problem == "owner_not_home" ]]; then
# turn the zone off
if [[ ${zstate[$idx]} != "close" ]]; then
thisaction="$thisaction close_vent"
fi
elif [[ $problem =~ force_ ]]; then
if [[ ${zstate[$idx]} != $fv ]]; then
thisaction="$thisaction ${fv}_vent"
fi
elif [[ $problem == "too_cold" ]]; then
if [[ $airconmode == "heat" || $airconmode == "off" ]]; then
# turn the system on, if required
if [[ $airconmode == "off" ]]; then
if [[ $modelock == "n/a" || $modelock == "heat" ]]; then
thisaction="$thisaction power_on set_mode:heat"
fi
fi
# if we couldnt turn the system on, there's nothing else to do
if [[ $airconmode == "heat" ]]; then
# adjust set temperature, if required
if [[ $(echo "${zsettemp[$idx]} > ${zwantmax[$idx]}" | bc) -eq 1 || $(echo "${zsettemp[$idx]} < ${zwantmin[$idx]}" | bc) -eq 1 ]]; then
# set temperature to midpoint of acceptable range
num=$(get_midrange $idx)
if [[ $zsettemp != $num ]]; then
thisaction="$thisaction set_temp:$num"
fi
fi
# open the vent, if required
if [[ ${zstate[$idx]} != "open" ]]; then
thisaction="$thisaction open_vent"
fi
# set myzone, even if not required (will fix later)
thisaction="$thisaction set_myzone"
fi
elif [[ $airconmode == "cool" ]]; then
# set temperature to midpoint of acceptable range
num=$(get_midrange $idx)
if [[ $zsettemp != $num ]]; then
thisaction="$thisaction set_temp:$num"
fi
# close the vent, if required
if [[ ${zstate[$idx]} != "close" ]]; then
thisaction="$thisaction close_vent"
elif [[ $donearby -eq 1 ]]; then
thisaction="close_nearby_zone"
fi
fi
elif [[ $problem == "too_hot" ]]; then
if [[ $airconmode == "cool" || $airconmode == "off" ]]; then
# turn the system on, if required
if [[ $airconmode == "off" ]]; then
if [[ $modelock == "n/a" || $modelock == "cool" ]]; then
thisaction="$thisaction power_on set_mode:cool"
fi
fi
# if we couldnt turn the system on, there's nothing else to do
if [[ $airconmode == "cool" ]]; then
# adjust set temperature, if required
if [[ $(echo "${zsettemp[$idx]} > ${zwantmax[$idx]}" | bc) -eq 1 || $(echo "${zsettemp[$idx]} < ${zwantmin[$idx]}" | bc) -eq 1 ]]; then
# set temperature to midpoint of acceptable range
num=$(get_midrange $idx)
if [[ $zsettemp != $num ]]; then
thisaction="$thisaction set_temp:$num"
fi
fi
# open the vent, if required
if [[ ${zstate[$idx]} != "open" ]]; then
thisaction="$thisaction open_vent"
fi
# set myzone, even if not required (will fix later)
thisaction="$thisaction set_myzone"
fi
elif [[ $airconmode == "heat" ]]; then
# set temperature to midpoint of acceptable range
num=$(get_midrange $idx)
if [[ $zsettemp != $num ]]; then
thisaction="$thisaction set_temp:$num"
fi
# close the vent, if required
if [[ ${zstate[$idx]} != "close" ]]; then
thisaction="$thisaction close_vent"
elif [[ $donearby -eq 1 ]]; then
thisaction="close_nearby_zone"
fi
fi
fi
fi
zaction[$idx]="$thisaction"
profile "getzoneaction for idx ${zname[$idx]}"
}
function getidxfromname() { # name
local x
for x in ${!zname[@]}; do
if [[ ${zname[$x]} == $1 ]]; then
echo "${x}"
fi
done
}
function getnamefromid() { # id
local x
for x in ${!zname[@]}; do
if [[ ${zid[$x]} == $1 ]]; then
echo "${zname[$x]}"
fi
done
}
# getforcevent zone_name
function getforcevent() {
local x retval
retval=""
for x in ${!fv_zonename[@]}; do
if [[ ${fv_zonename[$x]} == "$1" ]]; then
retval="${fv_state[$x]}"
break
fi
done
echo "$retval"
}
# getalladj zone_name
function getalladj() {
local x retval
retval=""
for x in ${!adj_zonename[@]}; do
if [[ ${adj_zonename[$x]} == "$1" ]]; then
retval="${adj_nearby[$x]}"
break
fi
done
echo "$retval"
}
function find_nearby_zones() { # zone_idx filter1 filter2 ... filter is: open|closed|noaction
local idx thiszone nearbyzones allnearbyzones thisone zi ok db conditions
idx=$1
shift
conditions="$*"
db=0
if [[ $db -eq 1 ]]; then
echo "find_nearby_zones(): idx = $idx" >/dev/stderr
echo "find_nearby_zones(): conditions = [$conditions]" >/dev/stderr
fi
thiszone=${zname[$idx]}
allnearbyzones=$(getalladj ${thiszone})
nearbyzones=""
if [[ $db -eq 1 ]]; then
echo "find_nearby_zones(): allnearbyzones = [$allnearbyzones]" >/dev/stderr
fi
for thisone in $allnearbyzones; do
ok=1
zi=$(getidxfromname $thisone)
if [[ $conditions == *open* && ${zstate[$zi]} != "open" ]]; then
ok=0
fi
if [[ $conditions == *closed* && ${zstate[$zi]} != "close" ]]; then
ok=0
fi
if [[ $conditions == *noaction* && ${zaction[$zi]} != "n/a" ]]; then
ok=0
fi
if [[ $ok -eq 1 ]]; then
nearbyzones="$nearbyzones $thisone"
fi
done
echo "$nearbyzones"
}
function generate_actions() { # populates global: nairconcommands & airconcmd[]
local x y z adj tempstr nhot ncold
local powerchange nmyzones maxpri bestidx
local globaction mzmustmove poss nomyzone
local db
db=0
nairconcommands=0
powerchange=""
for x in ${!zname[@]}; do
getzoneaction $x
done
# pass 1
profile "pass 1 start"
for x in ${!zname[@]}; do
thisaction="${zaction[$x]}"
IFS=' ' read -ra tok <<< "${zaction[$x]}"
for y in ${!tok[@]}; do
if [[ ${tok[$y]} == "close_nearby_zone" ]]; then
# is there a nearby zone that's open?
adj=$(find_nearby_zones $x open noaction)
if [[ -z $adj ]]; then
zaction[$x]="power_off"
if [[ -z $powerchange ]]; then
powerchange="power_off"
elif [[ ${zaction[$x]} != $powerchange ]]; then
zactionfail[$x]=1
fi
else
tempstr=""
for z in $adj; do
tempstr="$tempstr close:$z"
done
zaction[$x]="${zaction[$x]/close_nearby_zone/$tempstr}"
fi
fi
done
done
# pass 2 - If multiple zones are too hot/cold, turn the whole system off/on (if off, replace all actions)
nhot=0
ncold=0
for x in ${!zname[@]}; do
if [[ -z $powerchange ]]; then
if [[ ${zproblem[$x]} == "too_hot" ]]; then
nhot=$((nhot + 1))
elif [[ ${zproblem[$x]} == "too_cold" ]]; then
ncold=$((ncold + 1))
fi
fi
done
globsetmode=""
globprob=""
if [[ $airconmode == "off" ]]; then
# aircon is off
if [[ $ncold -ge $limit ]]; then
globprob="too_cold"
if [[ $modelock == "n/a" || $modelock == "heat" ]]; then
globsetmode=" set_mode:heat"
fi
elif [[ $nhot -ge $limit ]]; then
globprob="too_hot"
if [[ $modelock == "n/a" || $modelock == "cool" ]]; then
globsetmode=" set_mode:cool"
fi
fi
[[ ! -z $globsetmode ]] && globaction="power_on"
else
if [[ $airconmode == "heat" && $nhot -ge $limit ]]; then
globprob="too_hot"
elif [[ $airconmode == "cool" && $ncold -ge $limit ]]; then
globprob="too_cold"
fi
[[ ! -z $globprob ]] && globaction="power_off"
fi
if [[ ! -z $globprob ]]; then
for x in ${!zname[@]}; do
if [[ ${zproblem[$x]} == $globprob && ${zaction[$x]} != *${globaction}* ]]; then
zaction[$x]="$globaction$globsetmode ${zaction[$x]}"
zactionfail[$x]=0
elif [[ ${globaction} == "power_off" && ${zaction[$x]} != *$globaction* ]]; then
zactionfail[$x]=1
fi
done
fi
# pass xx - if we're closing the constant zone, and it's NOT the myzone,
# just turn the whole system off (since it will probably be opened.
# again anyway).
if [[ -z $powerchange && $airconmode != "off" ]]; then
for x in ${!zname[@]}; do
if [[ ${zaction[$x]} == *close_vent* && $constant == ${zname[$x]} ]]; then
if [[ $airconmyzoneid != ${zid[$x]} ]]; then
zaction[$x]="power_off"
[[ -z $powerchange ]] && powerchange="power_off"
fi
fi
done
fi
# pass 3 - If we're powering the whole system on/off, don't do anything else
if [[ ! -z $powerchange ]]; then
for x in ${!zname[@]}; do
if [[ ${powerchange} == "power_off" && ${zaction[$x]} != $powerchange ]]; then
zactionfail[$x]=1
fi
done
fi
# pass xx - if we're closing the myzone, pick a new one
[[ $db -eq 1 ]] && info "pass 3 - powerchange is '$powerchange'"
if [[ -z $powerchange ]]; then
if [[ $airconmode != "off" ]]; then
mzmustmove=0
# get list of all zones which can't be myzone
nomyzone=""
for x in ${!zname[@]}; do
if [[ ${znomyzone[$x]} -eq 1 ]]; then
nomyzone="$nomyzone ${zname[$x]} "
fi
done
for x in ${!zname[@]}; do
if [[ $airconmyzoneid == ${zid[$x]} || ${zaction[$x]} == *set_myzone* ]]; then
if [[ ${zstate[$x]} == "close" || ${zaction[$x]} == *close_vent* ]]; then
mzmustmove=1
elif [[ ${zaction[@]} == *close:${zname[$x]}* ]]; then
mzmustmove=1
elif [[ ${znomyzone[$x]} -eq 1 ]]; then
mzmustmove=1
fi
if [[ $mzmustmove -eq 1 ]]; then
[[ $db -eq 1 ]] && info "myzone must move away from ${zname[$x]}"
#info "myzone must move away from ${zname[$x]}"
zaction[$x]="${zaction[$x]/set_myzone/}"
nomyzone="$nomyzone ${zname[$x]} "
[[ $db -eq 1 ]] && info "zaction[${zname[$z]} is '${zaction[$x]}'"
[[ $db -eq 1 ]] && info "nomyzone is '$nomyzone}'"
fi
fi
done
if [[ $mzmustmove -eq 1 ]]; then
[[ $db -eq 1 ]] && info "got mzmustmove == 1"
[[ $db -eq 1 ]] && info "FINAL nomyzone is '$nomyzone}'"
# find an alternative
for x in ${!zname[@]}; do
# open or opening? set myzone. we'll resolve
# duplicates in the next step.
[[ $db -eq 1 ]] && info " checking if we can move mz to ${zname[$x]}"
poss=0
if [[ ${zaction[$x]} != *set_myzone* && $nomyzone != *\ ${zname[$x]}\ * ]]; then
[[ $db -eq 1 ]] && info " not already moving mz here + not in nomyzone list"
if [[ ${zstate[$x]} == "open" || ${zaction[$x]} == *open_vent* ]]; then
[[ $db -eq 1 ]] && info " is open or opening -> OK"
poss=1
elif [[ ${zaction[@]} == *open:${zname[$x]}* ]]; then
[[ $db -eq 1 ]] && info " is opening via adjacency-> OK"
poss=1
fi
if [[ $poss -eq 1 ]]; then
[[ $db -eq 1 ]] && info " myzone could move to ${zname[$x]}"
#info " myzone could move to ${zname[$x]}"
zaction[$x]="${zaction[$x]} set_myzone"
[[ $db -eq 1 ]] && info " new zaction[${zname[$x]}] is ${zaction[x]}"
fi
fi
done
fi
fi
fi
# pass 4 - multiple myzones?
if [[ -z $powerchange ]]; then
nmyzones=0
maxpri=-999
bestidx=-1
for x in ${!zname[@]}; do
# Multiple myzones?
if [[ ${zaction[$x]} == *set_myzone* ]]; then
#info " ${zname[$x]} wants myzone (pri=${zpri[$x]} maxpri=$maxpri)"
nmyzones=$((nmyzones + 1))
if [[ $(echo "scale=1; ${zpri[$x]} > $maxpri" | bc) == "1" ]]; then
bestidx=$x
maxpri=$(echo "scale=1; ${zpri[$x]}" | bc)
#info " new best priority: ${zname[$x]} = $maxpri"
fi
fi
done
if [[ $nmyzones -gt 1 ]]; then
for x in ${!zname[@]}; do
if [[ $x -ne $bestidx && ${zaction[$x]} == *set_myzone* ]]; then
zaction[$x]="${zaction[$x]/set_myzone/}"
fi
done
fi
fi
# final pass
for x in ${!zname[@]}; do
if [[ ${zactionfail[$x]} -eq 0 ]]; then
# Remove myzone if already set
if [[ ${zaction[$x]} == *set_myzone* && $airconmyzoneid == ${zid[x]} ]]; then
zaction[$x]="${zaction[$x]/set_myzone/}"
fi
# Remove leading spaces
zaction[$x]=$(echo ${zaction[$x]} | awk '{$1=$1; sub("n/a ",""); sub(" n/a","") };1')
# Generate actual aircon commands from zone commands
gen_aircon_command ${x} ${zaction[$x]}
fi
done
}
# gen_aircon_command zone_idx "command1 command2 etc"
function gen_aircon_command() {
local idx allactions this toadd num doneset othername otherid otheridx db donemode str
idx=$1
shift
allactions="$*"
doneset=0
donemode=0
db=0
[[ $db -eq 1 ]] && info "gen_aircon_command() for idx=${idx}: $allactions"
IFS=' ' read -ra tok <<< "${allactions}"
for this in ${tok[@]}; do
if [[ $this == *power_on* ]]; then
add_aircon_command $idx -1 "Power on system" "on"
elif [[ $this == *power_off* ]]; then
add_aircon_command $idx -1 "Power off system" "off"
elif [[ $this == *set_myzone* && $airconmyzoneid != ${zid[$idx]} ]]; then
add_aircon_command $idx -1 "Set MyZone to ${zname[$idx]}" "myzone --zone ${zid[$idx]}"
elif [[ $this == *open:* ]]; then
othername=$(echo "$this" | sed -e 's/^.*open://;s/ .*//')
otheridx=$(getidxfromname $othername)
otherid=${zid[$otheridx]}
add_aircon_command $idx $otheridx "Open vent in ${zname[$otheridx]}" "set --zone ${otherid} --state on --temp ${zsettemp[$otheridx]}"
elif [[ $this == *close:* ]]; then
othername=$(echo "$this" | sed -e 's/^.*close://;s/ .*//')
otheridx=$(getidxfromname $othername)
otherid=${zid[$otheridx]}
add_aircon_command $idx $otheridx "Close vent in ${zname[$otheridx]}" "set --zone ${otherid} --state off --temp ${zsettemp[$otheridx]}"
elif [[ $this == *set_mode:* ]]; then
str=$(echo "$this" | sed -e 's/^.*set_mode://;s/ .*//')
add_aircon_command $idx -1 "Set system mode to '$str'" "$str"
elif [[ $this == *set_temp* || $this == *open_vent* || $this == *close_vent* ]]; then
if [[ $doneset -eq 0 ]]; then
toadd=""
comm=""
if [[ $allactions == *open_vent* ]]; then
toadd="$toadd --state on"
comm="open vent"
elif [[ $allactions == *close_vent* ]]; then
toadd="$toadd --state off"
comm="close vent"
fi
if [[ $allactions == *set_temp* ]]; then
num=$(echo "$this" | sed -e 's/^.*set_temp://;s/ .*//')
toadd="$toadd --temp $num"
[[ ! -z $comm ]] && comm="${comm} and "
comm="${comm}set temperature to $num degrees"
else
toadd="$toadd --temp ${zsettemp[$idx]}"
fi
if [[ ! -z $toadd ]]; then
add_aircon_command $idx -1 "In zone ${zname[$idx]}, $comm" "set --zone ${zid[$idx]}$toadd"
doneset=1
fi
fi
fi
done
}
# add_aircon_command zone_idx "comment goes here" "actual pymyair command to run"
function add_aircon_command() {
local x idx otheridx comment db
[[ $# -le 1 ]] && return 1
idx=$1
shift
otheridx=$1
shift
comment=$1
shift
if [[ $otheridx -ne -1 ]]; then
if [[ ! -z $comment ]]; then
comment="${comment} (to fix ${zname[$idx]})"
fi
fi
db=0
[[ $db -eq 1 ]] && info " add_aircon_command() for idx=${idx} otheridx=${otheridx} comment=[$comment]: $*" >/dev/stderr
[[ -z $* ]] && return 1
for x in ${airconcmd[@]}; do
if [[ $x == "$*" ]]; then
return 1
fi
done
airconcmd[$nairconcommands]="$*"
airconcomment[$nairconcommands]="$comment"
airconcmdzone[$nairconcommands]="${zname[$idx]}"
airconcmdotherzone[$nairconcommands]="${zname[$otheridx]}"
airconproblem[$nairconcommands]="${zname[$idx]} is ${zproblem[$idx]/_/ }"
#if [[ $otheridx -ne -1 ]]; then
# airconcomment[$nairconcommands]="${airconcomment[$nairconcommands]}, update nearby zone ${zname[$otheridx]}"
#fi
nairconcommands=$((nairconcommands + 1))
return 0
}
function addnoop() {
local idx
idx=$(getidxfromname "$1")
zignore[$idx]=1
}
function addnomyzone() {
local idx
idx=$(getidxfromname "$1")
znomyzone[$idx]=1
}
function addforcevent() {
local x idx
idx=$nforcevents
for x in ${!fv_zonename[@]}; do
if [[ ${fv_zonename[$x]} == "$1" ]]; then
idx=$x
break;
fi
done
fv_zonename[$idx]="$1"
fv_state[$idx]="$2"
[[ $idx == $nforcevents ]] && nforcevents=$((nforcevents + 1))
}
function addtemprange() {
local x idx
idx=$ntempranges
for x in ${!tr_zonename[@]}; do
if [[ ${tr_zonename[$x]} == "$1" ]]; then
idx=$x
break;
fi
done
tr_zonename[$idx]="$1"
tr_range[$idx]="$2"
[[ $idx == $ntempranges ]] && ntempranges=$((ntempranges + 1))
}
function addadj() {
local name
name="$1"
shift
adj_zonename[$nadj]="$name"
adj_nearby[$nadj]="$*"
nadj=$((nadj + 1))
}
function addperson() {
local h p x idx db
db=0
p="$1"
shift
h="$*"
[[ $db -eq 1 ]] && info "addperson() person $p host(s) [$h]"
idx=$npeople
for x in ${!pname[@]}; do
if [[ ${pname[$x]} == "$p" ]]; then
[[ $db -eq 1 ]] && info " owner already exists, appending."
idx=$x
break;
fi
done
pname[$idx]="$p"
if [[ ${pdev[$idx]} != *\ $h\ * ]]; then
pdev[$idx]="${pdev[$idx]} $h "
fi
[[ $db -eq 1 ]] && info " dev val is: ${pdev[$idx]}"
[[ $idx == $npeople ]] && npeople=$((npeople + 1))
}
function addowner() {
local z h local x idx db
db=0
z="$1"
shift
h="$*"
[[ $db -eq 1 ]] && info "addowner() zone $z host(s) [$h]"
idx=$nowners
for x in ${!ownerzone[@]}; do
if [[ ${ownerzone[$x]} == "$z" ]]; then
[[ $db -eq 1 ]] && info " owner already exists, appending."
idx=$x
break;
fi
done
ownerzone[$idx]="$z"
if [[ ${ownerhost[$idx]} != *\ $h\ * ]]; then
ownerhost[$idx]="${ownerhost[$idx]} $h "
fi
[[ $db -eq 1 ]] && info " hosts val is: ${ownerhost[$idx]}"
[[ $idx == $nowners ]] && nowners=$((nowners + 1))
}
# getownerperson zone_name
# returns name of person who owns zone
function getownerperson() {
local x retval
retval=""
for x in ${!ownerzone[@]}; do
if [[ ${ownerzone[$x]} == "$1" ]]; then
retval="${ownerperson[$x]}"
break
fi
done
echo "$retval"
}
# getowner zone_name
# returns devices associated woth zone
function getowner() {
local x retval
retval=""
for x in ${!ownerzone[@]}; do
if [[ ${ownerzone[$x]} == "$1" ]]; then
retval="${ownerhost[$x]}"
break
fi
done
echo "$retval"
}
# gettemprange zone_name
function gettemprange() {
local x retval
retval="n/a"
for x in ${!tr_zonename[@]}; do
if [[ ${tr_zonename[$x]} == "$1" ]]; then
retval="${tr_range[$x]}"
break
fi
done
echo "$retval"
}
function getstrtype() {
local strtype
shopt -s nocasematch
strtype=unknown
if [[ "$*" =~ ^[0-9]{4}$ ]]; then
strtype="24h"
elif [[ "$*" =~ ^(mon|tue|wed|thu|fri|sat|sun)$ ]]; then
strtype="weekday"
fi
shopt -u nocasematch
echo "$strtype"
}
function numtoweekday() {
local num
shopt -s nocasematch
num=-1
case "$1" in
"mon") num=1;;
"tue") num=2;;
"wed") num=3;;
"thu") num=4;;
"fri") num=5;;
"sat") num=6;;
"sun") num=7;;
esac
shopt -u nocasematch
echo "$num"
}
function conderror() {
error "Invalid condition: $1"
echo -e "${RED} Start: $2"
echo -e "${RED} End: $3"
}
function load_config() {
local line rv timestr ttok stype ok starth endh nowh
local cond allconds x
local db
db=0
rv=0
while read line; do
line=${line%%#*} # remove comments
if [[ ! -z ${line// } ]]; then
# time based options
if [[ ${line:0:1} == "@" ]]; then
timestr=${line%% *}
timestr=${timestr:1}
IFS=';' read -ra allconds <<< "$timestr"
for cond in ${allconds[@]} ; do
[[ $db -eq 1 ]] && info "Processing condition: '$cond'"
IFS='-' read -ra ttok <<< "$cond"
if [[ ${#ttok[@]} == 1 ]]; then
stype[0]=$(getstrtype ${ttok[0]})
if [[ ${stype[0]} != "weekday" ]]; then
error "Invalid condition: '${cond}'"
return 1
fi
starth=$(numtoweekday ${ttok[0]})
if [[ $starth -eq -1 ]]; then
error "'${ttok[0]}' is not a valid type here (type is ${stype[0]})"
return 1
fi
endh="$starth"
nowh=$(date +%u)
else
stype[0]=$(getstrtype ${ttok[0]})
stype[1]=$(getstrtype ${ttok[1]})
starth=""
endh=""
nowh=""
if [[ ${stype[0]} != ${stype[1]} ]]; then
error "start and end condition types don't match:"
echo -e "${RED} Start: ${ttok[0]} (${stype[0]})${PLAIN}"
echo -e "${RED} End: ${ttok[1]} (${stype[1]})${PLAIN}"
return 1
elif [[ ${stype[0]} == "24h" ]]; then
starth=${ttok[0]}
endh=${ttok[1]}
nowh=$(date +%H%M)
elif [[ ${stype[0]} == "weekday" ]]; then
starth=$(numtoweekday ${ttok[0]})
endh=$(numtoweekday ${ttok[1]})
if [[ $starth -eq -1 || $endh -eq -1 ]]; then
conderror "${cond}" "'${ttok[0]}' (${stype[0]})" "'${ttok[1]}' (${stype[1]})"
return 1
fi
nowh=$(date +%u)
else
conderror "${cond}" "'${ttok[0]}' (${stype[0]})" "'${ttok[1]}' (${stype[1]})"
return 1
fi
fi
if [[ ! -z $starth ]]; then
[[ $db -eq 1 ]] && info " check if $nowh is between $starth and $endh"
ok=0
if [[ $(echo "$starth < $endh" | bc) == "1" ]]; then
if [[ $(echo "$nowh >= $starth" | bc) == "1" && $(echo "$nowh <= $endh" | bc) == "1" ]]; then
ok=1
fi
elif [[ $(echo "$starth > $endh" | bc) == "1" ]]; then
if [[ $(echo "$nowh <= $endh" | bc) == "1" || $(echo "$nowh >= $starth" | bc) == "1" ]]; then
ok=1
fi
else
if [[ $nowh == $endh || $nowh == $starth ]]; then
ok=1
fi
fi
[[ $db -eq 1 && $ok -eq 0 ]] && info " $nowh isn't in range $cond"
[[ $db -eq 1 && $ok -eq 1 ]] && info " MATCH: $nowh within range $cond"
fi
if [[ $ok -eq 0 ]]; then
break
fi
done
if [[ $ok -eq 1 ]]; then
# strip condition off the front
line=${line#* }
fi
else
ok=1
fi
if [[ $ok -eq 1 ]]; then
IFS=' ' read -ra tok <<< "$line"
if [[ ${tok[0]} == "temp" ]]; then
addtemprange ${tok[1]} ${tok[2]}
elif [[ ${tok[0]} == "constant" ]]; then
if [[ -z ${tok[1]} ]]; then
error "Missing zone name in constant command."
exit 1
elif [[ -z $constant ]]; then
constant=${tok[1]}
else
error "Constant zone defined more than once."
exit 1
fi
elif [[ ${tok[0]} == "adj" ]]; then
addadj ${tok[1]} ${tok[@]:2}
elif [[ ${tok[0]} == "nomyzone" || ${tok[0]} == "nomy" ]]; then
addnomyzone ${tok[1]}
elif [[ ${tok[0]} == "noop" || ${tok[0]} == "ignore" ]]; then
addnoop ${tok[1]}
elif [[ ${tok[0]} == "force" ]]; then
if [[ $VALID_ZONE_STATES == *\ ${tok[2]}\ * ]]; then
addforcevent ${tok[1]} ${tok[2]}
else
error "Invalid zone state '${tok[2]}'. Valid options are: $VALID_ZONE_STATES"
exit 1
fi
elif [[ ${tok[0]} == "owner" ]]; then
addowner ${tok[1]} ${tok[@]:2}
elif [[ ${tok[0]} == "person" ]]; then
addperson ${tok[1]} ${tok[@]:2}
elif [[ ${tok[0]} == "modelock" ]]; then
if [[ $VALID_MODES == *\ ${tok[1]}\ * ]]; then
modelock="${tok[1]}"
else
error "Invalid modelock '${tok[1]}'. Valid options are: $VALID_MODES"
exit 1
fi
elif [[ ${tok[0]} == "test" ]]; then
info "Got test option: '${tok[@]}'"
rv=1
fi
fi
fi
done < "$CONFIGFILE"
return $rv
}
# if we can ping any of the args, return ok
function canping() {
local ip db host thisrv mac
db=0
for host in $*; do
ip=$(getent hosts $host | grep -v :)
ip=${ip%% *}
if [[ -z $ip ]]; then
thisrv=1
else
#arp -d $ip >/dev/null 2>&1
#ping -c1 -w1 -n -q $ip >/dev/null 2>&1 &
#sleep 0.3
mac=$(arp -n $ip)
mac=$(echo "$mac" | egrep -v "Host|xpired" | awk '{print $2}')
[[ $mac == *:* ]] && thisrv=0 || thisrv=1
fi
[[ $db -eq 1 ]] && info "canping() $host ($ip) is $thisrv - mac=$mac"
if [[ $thisrv -eq 0 ]]; then
return 0
fi
done
return 1
}
function addzone() {
local name state settemp temp wanttemp wanttempmin wanttempmax id
name="$1"
state="$2"
settemp="$3"
temp="$4"
id="$5"
zname[$nzones]="$name"
zstate[$nzones]="$state"
zsettemp[$nzones]="$settemp"
ztemp[$nzones]="$temp"
zid[$nzones]="$id"
zwanttemp[$nzones]="n/a"
zproblem[$nzones]="n/a"
zaction[$nzones]="n/a"
zactionfail[$nzones]=0
zowner[$nzones]=$(getowner "$name")
zownerperson[$nzones]=$(getownerperson "$name")
#info "zone $name owner is [${zowner[$nzones]}]"
zignore[$nzones]=0
znomyzone[$nzones]=0
zpri[$nzones]=0
wanttemp=$(gettemprange "$name")
if [[ $wanttemp == "n/a" ]]; then
zwanttemp[$nzones]="n/a"
zwantmin[$nzones]="-99"
zwantmax[$nzones]="99"
else
zwanttemp[$nzones]="$wanttemp"
zwantmin[$nzones]="${wanttemp%-*}"
zwantmax[$nzones]="${wanttemp#*-}"
fi
nzones=$((nzones + 1))
}
function getcol() {
local col
case $1 in
"off") col="$GREY";;
"cool") col="$CYAN";;
"heat") col="$RED";;
*) col="$PLAIN";;
esac
echo $col
}
function gen_config() {
local x
if [[ -e $CONFIGFILE ]]; then
error "$CONFIGFILE already exists"
return 1
fi
get_aircon_info
cp /dev/null $CONFIGFILE
echo "# Specify commandline args here" >> $CONFIGFILE
echo "# options -p -t 0.3 ... " >> $CONFIGFILE
echo >> $CONFIGFILE
echo "# Don't switch to modes other than this" >> $CONFIGFILE
echo "#modelock heat|cool" >> $CONFIGFILE
echo >> $CONFIGFILE
echo "# Set temperature range to enforce" >> $CONFIGFILE
for x in ${!zname[@]}; do
echo "#temp ${zname[$x]} min_temp-max_temp" >> $CONFIGFILE
done
echo >> $CONFIGFILE
echo "# Turn off zone if we can't ARP for given hostname's IP" >> $CONFIGFILE
for x in ${!zname[@]}; do
echo "#owner ${zname[$x]} hostname.domain" >> $CONFIGFILE
done
echo >> $CONFIGFILE
echo "# Define which zones are adjacent to each other (used to close nearby" >> $CONFIGFILE
echo "# zones to help control temperature)" >> $CONFIGFILE
for x in ${!zname[@]}; do
echo "#adj ${zname[$x]} adjacent_zone1 [adjacent_zone2] ... [adjacent_zoneX]" >> $CONFIGFILE
done
echo >> $CONFIGFILE
echo "# Force a zone into a certain state" >> $CONFIGFILE
echo "#force ${zname[0]} open" >> $CONFIGFILE
echo "#force ${zname[1]} close" >> $CONFIGFILE
echo >> $CONFIGFILE
echo "# All commands except options can be restricted to certain times with:" >> $CONFIGFILE
echo "# @hhmm-hhmm Restrict between certain 24 hour times" >> $CONFIGFILE
echo "# @Mon-Wed Restrict between certain weekdays" >> $CONFIGFILE
echo "# @Mon Restrict to one weekdays only" >> $CONFIGFILE
echo "# @Mon;hhmm-hhmm Restrict both day and time" >> $CONFIGFILE
echo "# For example:" >> $CONFIGFILE
echo "# Keep zone closed on Tuesdays and Wednesdays" >> $CONFIGFILE
echo "#@Tue-Wed force ${zname[0]} close" >> $CONFIGFILE
echo "# Override temperature range between 11pm and 5am" >> $CONFIGFILE
echo "#@2300-0500 temp ${zname[0]} 20-21" >> $CONFIGFILE
echo "# Keep zone open between 10am and 11am on Mondays" >> $CONFIGFILE
echo "#@Mon;1000-1100 force ${zname[0]} open" >> $CONFIGFILE
}
function get_aircon_info() {
profile "query aircon"
[[ $cronmode -eq 0 && $logmode -eq 0 ]] && echo -en "${GREEN}${BOLD}>> ${PLAIN}${GREEN}Querying aircon... ${PLAIN}"
zones=$(myair $AIRCON_IP zones | jq -r '.[] | [ .name, .state, .setTemp, .measuredTemp, .number ] | @csv' | tr -d '" ')
airconmode=$(myair $AIRCON_IP mode)
nzones=0
for line in $zones; do
IFS=',' read -ra tok <<< "$line"
addzone "${tok[0]}" "${tok[1]}" "${tok[2]}" "${tok[3]}" "${tok[4]}"
done
airconmyzoneid=$(myair $AIRCON_IP myzone)
airconmyzone=$(getnamefromid $airconmyzoneid)
[[ $cronmode -eq 0 && $logmode -eq 0 ]] && echo -e "${GREEN}${BOLD}ok${PLAIN}"
profile "query aircon"
}
function show_aircon_status() {
local zonestr zonecol settempcol actioncol thiswantcol thisstate thisstateform
local thisformat x powercol lockcol
local FORMAT1 FORMAT2 FORMATHOT FORMATCOLD FORMATOK CLOSEDFORM OPENFORM
FORMAT1="${BOLD}%-16s%-7s%-8s%-7s%-7s%s${PLAIN}"
FORMAT2="${UNDERLINE}${FORMAT1}${PLAIN}"
FORMATHOT="${RED}%-7s${PLAIN}"
FORMATCOLD="${CYAN}%-7s${PLAIN}"
FORMATOK="${WHITE}%-7s${PLAIN}"
CLOSEDFORM="$GREY%-8s$PLAIN"
OPENFORM="$PLAIN%-8s$PLAIN"
powercol=$(getcol $airconmode)
lockcol=$(getcol $modelock)
printf "${BOLD}Aircon mode:${PLAIN} $powercol%s$PLAIN" $airconmode
if [[ $modelock != "n/a" ]]; then
echo -e " (locked to $lockcol$modelock$PLAIN)"
else
echo
fi
printf "${BOLD}Current myzone:${PLAIN} %s (zone %s)\n" $airconmyzone $airconmyzoneid
printf "${BOLD}Tolerance:${PLAIN} %s degrees low, %s degrees high" $tolerance_l $tolerance_h
echo
printf "$FORMAT1\n" "" "Valid" "" "Set" "Actual" "Proposed "
printf "$FORMAT2\n" "Zone" "Temp" "State" "Temp" "Temp" "Action "
for x in ${!zname[@]}; do
if [[ ${zproblem[$x]} == "too_hot" ]]; then
thisformat="${FORMATHOT}"
elif [[ ${zproblem[$x]} == "too_cold" ]]; then
thisformat="${FORMATCOLD}"
else
thisformat="${FORMATOK}"
fi
if [[ ${airconmode} == "off" ]]; then
thisstateform="${CLOSEDFORM}"
thisstate="<off>"
elif [[ ${zstate[$x]} == "close" ]]; then
thisstateform="${CLOSEDFORM}"
thisstate=${zstate[$x]}
else
thisstateform="${OPENFORM}"
thisstate=${zstate[$x]}
fi
thiswant="${zwanttemp[$x]}"
if [[ $thiswant == "n/a" ]]; then
thiswantcol="$GREY"
else
thiswantcol="$PLAIN"
fi
if [[ ${zactionfail[$x]} -eq 1 || ${zaction[$x]} == "n/a" ]]; then
actioncol="$GREY"
else
actioncol="$PLAIN"
fi
if [[ ${airconmode} == "off" ]]; then
settempcol="$GREY"
else
settempcol="$PLAIN"
fi
if [[ $airconmyzoneid == ${zid[$x]} ]]; then
zonecol="$WHITE$BOLD"
zonestr="${zname[$x]} (M)"
else
zonecol="$PLAIN"
zonestr="${zname[$x]}"
fi
if [[ $constant == ${zname[$x]} ]]; then
zonestr="${zname[$x]} (C)"
fi
printf "$zonecol%-16s$PLAIN$thiswantcol%-7s$PLAIN$thisstateform$settempcol%-7s$PLAIN$thisformat$actioncol%s$PLAIN\n" "${zonestr}" "${thiswant}" "${thisstate}" "${zsettemp[$x]}" "${ztemp[$x]}" "${zaction[$x]}"
done
}
function show_analysis() {
local count x ostr
echo -e "${UNDERLINE}Analysis:${PLAIN}"
count=0
for x in ${!zproblem[@]}; do
if [[ ${zproblem[$x]} != "n/a" ]]; then
if [[ ${zproblem[$x]} == "owner_not_home" ]]; then
if [[ ${zowner[$x]} == *\ * ]]; then
ostr="owners are"
else
ostr="owner is"
fi
echo "- ${zname[$x]} open but $ostr not detected (${zowner[$x]})."
elif [[ ${zproblem[$x]} =~ force_ ]]; then
what=${zproblem[$x]##*_}
[[ $what == "close" ]] && what="${what}d"
echo "- ${zname[$x]} is forced $what"
else
if [[ $constant == ${zname[$x]} ]]; then
append=" (constant zone)"
else
append=""
fi
echo "- ${zname[$x]} is ${zproblem[$x]/_/ }${append}"
fi
count=$((count + 1))
fi
done
if [[ $globprob == too_* ]]; then
echo "- $limit or more zones are ${globprob/_/ }"
fi
[[ $count -eq 0 ]] && echo -e "${GREEN}All is good!${PLAIN}"
}
function show_proposed_commands() {
local str x maxlen count cmdformat fullcmd
str="Proposed commands"
echo -e "${UNDERLINE}${str}:${PLAIN}"
if [[ $nairconcommands -gt 0 ]]; then
if [[ -f ${KILLFILE} ]]; then
echo -e "${RED}Not running commands because killfile exists ($KILLFILE)${PLAIN}"
elif [[ $DOIT -ne 1 ]]; then
echo -e "${RED}Not running commands because -y not specified${PLAIN}"
fi
fi
maxlen=-99
for x in ${!airconcmd[@]}; do
fullcmd="myair $AIRCON_IP ${airconcmd[$x]}"
thislen=${#fullcmd}
[[ $thislen -gt $maxlen ]] && maxlen=$thislen
done
cmdformat="%-$((maxlen + 3))s"
count=0
for x in ${!airconcmd[@]}; do
fullcmd="myair $AIRCON_IP ${airconcmd[$x]}"
printf -- "$cmdformat" "$fullcmd"
[[ ! -z ${airconcomment[$x]} ]] && printf " $GREEN$BOLD# $PLAIN$GREEN%s$PLAIN\n" "${airconcomment[$x]}" || echo
count=$((count + 1))
done
[[ $count -eq 0 ]] && echo -e "${GREY}n/a${PLAIN}"
}
function run_commands() {
local x
for x in ${!airconcmd[@]}; do
[[ ! -z ${airconcomment[$x]} ]] && action "${airconcomment[$x]}"
echo RUNNING myair $AIRCON_IP ${airconcmd[$x]}
myair $AIRCON_IP ${airconcmd[$x]} >/dev/null 2>&1
influx_insert "INSERT aircon action=\"${airconcomment[$x]}\",comment=\"${airconproblem[$x]}\""
done
}
function profile() {
local diff
[[ $profiler != 1 ]] && return
p_last=$p_now
p_now=$(date +%s)
diff=$(( p_now - $p_last))
if [[ -z $p_str ]]; then
p_str="$*"
else
info "+${diff} secs [$*]" >/dev/stderr
p_str=""
fi
}
# poplulates glpbals:
# failedcmds
# influxrv
function influx_clear() {
failedcmds=""
influxrv=0
}
# poplulates glpbals:
# failedcmds
# influxrv
function influx_insert() { # $1 == cmd
local cmd thisrv
if [[ -z ${influx} ]]; then
return 1
fi
cmd="$1"
if [[ $DOIT -eq 1 ]]; then
${influx} -database "$influxdb" -execute "$cmd"
else
echo "${influx} -database $influxdb -execute $cmd"
thisrv=0
fi
thisrv=$?
influxrv=$((influxrv + $thisrv))
if [[ $thisrv -ne 0 ]]; then
failedcmds="$failedcmds$cmd\n"
fi
}
# poplulates glpbals:
# influx
# influxdb
function influx_init() { # $1 == dbname
local dbfound
influx=$(which influx 2>/dev/null)
influxdb="$1"
if [[ $? -ne 0 ]]; then
error "influx executable not found in path."
return 1
fi
dbfound=$(${influx} -database "$influxdb" -execute "SHOW DATABASES" | grep -w ^${influxdb})
if [[ -z $dbfound ]]; then
error "Couldn't find influx database '$influxdb'"
return 1
fi
influx_clear
return 0
}
p_now=$(date +%s)
p_last=$p_now
p_str=""
CONFIGFILE=${DEFAULT_CONFIGFILE}
KILLFILE=${DEFAULT_KILLFILE}
AIRCON_IP=${DEFAULT_AIRCON_IP}
airconmode="unknown"
airconmyzoneid="n/a"
airconmyzone="n/a"
globprob=""
nairconcommands=0
ntempranges=0
nadj=0
nowners=0
npeople=0
tolerance_l=${DEFAULT_TOLERANCE}
tolerance_h=${DEFAULT_TOLERANCE}
constant=""
DOIT=0
makeconfig=0
modelock="n/a"
cronmode=0
logmode=0 #0=none, 1=csv_file, 2=local influxdb
influxdb=""
showwho=0
limit=$DEFAULTLIMIT
profiler=0
csvfile="$DEFAULT_CSVFILE"
# check for config file option first
if [[ $* == *-f\ * ]]; then
cf=$(echo "$*" | sed -e 's/.*-f //;s/ .*//')
if [[ ! -z $cf ]]; then
CONFIGFILE="$cf"
fi
fi
ALLARGS=""
if [[ -e $CONFIGFILE ]]; then
# load options from file...
ALLARGS=$(egrep "^options " $CONFIGFILE | sed -e 's/^options //' | tr '\n' ' ')
fi
ALLARGS="$ALLARGS $*"
optstring="A:cf:hi:I:k:l:Lo:pymwt:T:x:"
while getopts "$optstring" i $ALLARGS; do
case "$i" in
h)
usage;
exit 1;
;;
f)
CONFIGFILE=${OPTARG}
;;
i)
AIRCON_IP=${OPTARG}
;;
I)
logmode=2
influx_init "$OPTARG" || exit 1
;;
A)
influx_init "$OPTARG" || exit 1
;;
k)
KILLFILE=${OPTARG}
;;
l)
limit=${OPTARG}
;;
o)
csvfile="$OPTARG"
;;
L)
logmode=1
;;
t)
tolerance_l=${OPTARG}
;;
T)
tolerance_h=${OPTARG}
;;
c)
enable_cronmode
;;
p)
profiler=1
info "Profiler mode enabled"
;;
m)
makeconfig=1
;;
y)
DOIT=1
;;
w)
showwho=1
;;
x)
MYAIR="${OPTARG}"
;;
*)
usage;
exit 1;
;;
esac
done
shift $((OPTIND - 1))
if [[ -z $MYAIR ]]; then
MYAIR=$(which myair)
if [[ $? -ne 0 ]]; then
error "Can't find pymyair executable 'myair' in path. Install it from here: https://github.com/smallsam/pymyair"
exit 1
fi
else
if [[ ! -x "$MYAIR" ]]; then
error "Specified pymyair executable '$MYAIR' not found."
exit 1
fi
fi
if [[ $makeconfig -eq 1 ]]; then
gen_config
rv=$?
if [[ $rv -eq 0 ]]; then
info "A config file for your current aircon setup has been generated here:"
info " $CONFIGFILE"
info "Please review this and update as required."
fi
exit $rv
fi
if [[ -e $CONFIGFILE ]]; then
if ! load_config; then
error "Config load failed"
exit 1
fi
else
error "Config file $CONFIGFILE doesn't exist."
exit 1
fi
# ping all hosts in background to populate arp table
for x in ${devices}; do
ping -c1 -w1 -n -q $ip >/dev/null 2>&1 &
done
wait
if [[ $showwho -eq 1 ]]; then
# get max phone name length
maxlen=1
for x in ${pname[@]}; do
[[ ${#x} -gt $maxlen ]] && maxlen=$((${#x} + 3))
done
# get a list of all phones
TFORMAT="${BOLD}${UNDERLINE}%-${maxlen}s%-16s${PLAIN}\n"
HFORMAT="%-${maxlen}s${GREEN}%-16s${PLAIN}\n"
AFORMAT="%-${maxlen}s${RED}%-16s${PLAIN}\n"
echo
printf "$TFORMAT" "Person" "Availability"
for x in ${!pname[@]}; do
if canping "${pdev[$x]}"; then
thisform="$HFORMAT"
str="At home"
else
thisform="$AFORMAT"
str="Out of the house"
fi
printf "$thisform" "${pname[$x]}" "$str"
done
echo
exit 0
fi
get_aircon_info
if [[ $logmode -eq 1 ]]; then
now=$(date +'%d/%m/%Y %H:%M:%S')
if [[ ! -e $csvfile ]]; then
cp /dev/null $csvfile
echo -n "Date" >>$csvfile
for x in ${!zname[@]}; do
echo -n ",${zname[$x]}" >> $csvfile
done
echo >> $csvfile
fi
echo -n "${now}" >>$csvfile
for x in ${!zname[@]}; do
echo -n ",${ztemp[$x]}" >> $csvfile
done
echo >> $csvfile
exit 0
elif [[ $logmode -eq 2 ]]; then
now=$(date +'%d/%m/%Y %H:%M:%S')
rv=0
failedcmds=""
influx_clear
for x in ${!zname[@]}; do
influx_insert "INSERT temperature,room=${zname[$x]} value=${ztemp[$x]}"
done
for x in ${!pname[@]}; do
canping "${pdev[$x]}" && ishome=1 || ishome=0
influx_insert "INSERT attendance,person=${pname[$x]} value=$ishome"
done
[[ $airconmode == "off" ]] && pw=0 || pw=1
influx_insert "INSERT aircon running=$pw"
# 0= off
# 1=cool
# 2=heat
# 3=dry
# 4=fan/other
case "$airconmode" in
"off") modenum=0;;
"cool") modenum=1;;
"heat") modenum=2;;
"dry") modenum=3;;
*) modenum=4;;
esac
influx_insert "INSERT aircon mode=$modenum"
if [[ $influxrv -gt 0 ]]; then
echo -e "${RED}$failedcmds${PLAIN}" | sed -e 's/^/ /'
fi
exit ${rv}
fi
generate_actions
if [[ $cronmode -eq 1 ]]; then
# only show output if we are doing something
[[ $nairconcommands -ge 1 ]] && showoutput=1 || showoutput=0
else
showoutput=1
fi
if [[ $showoutput -eq 1 ]]; then
echo
show_aircon_status
echo
show_analysis
echo
show_proposed_commands
echo
fi
# Actually run the commands
if [[ $DOIT -eq 1 ]]; then
if [[ ! -f ${KILLFILE} ]]; then
run_commands
[[ $cronmode -ne 1 ]] && echo
fi
fi