diff --git a/README.md b/README.md index b1ea44d..460cbc2 100755 --- a/README.md +++ b/README.md @@ -11,10 +11,6 @@ It can also log to an influxdb database for easy integration into Grafana or sim ![grafana integration](https://cube.nethack.net/images/aircon_grafana.png "grafana integration") -# Requirements - -- [pymyair](https://github.com/smallsam/pymyair) - # Features @@ -25,35 +21,40 @@ It can also log to an influxdb database for easy integration into Grafana or sim - Turn the entire aircon system on/off as needed to control temperature - Turn zones on/off based on ARP table entries for specific MAC addresses (for example, if a TV is turned on or a person's phone is detected) - Log temperature measurements to a CSV file or InfluxDB +- Optional integration with [pymyair](https://github.com/smallsam/pymyair) (but not required) # Usage ```text -root@gridbug:~# scripts/aircon.sh -h -usage: scripts/aircon.sh [options] +usage: ./aircon.sh [options] - Modifies aircon based on configured parameters in /root/.airconrc. + Modifies aircon based on configured parameters in /Users/rob/.airconrc. - -h Show this text. - -c Cron mode. Only show output if actions were taken. - -i x.x.x.x Specify IP address for aircon (default is 10.99.99.1) - -D dbhost Specify influxdb hostname for -I and -A options (default: localhost) - -I db Log all zone temperatures to given influxdb database, then exit (see -o). + -a file Specify location of arping binary (default: /usr/local/sbin/arping). + -b Rule debug mode. Show processing of time rules. -A db Log actions to given influxdb database. + -c Cron mode. Only show output if actions were taken. + -D dbhost Specify influxdb hostname for -I and -A options (default: localhost) -f file Specify an alternate config file. - -k file If file exists, never change aircon settings (default: /root/.aircon_noaction). + -h Show this text. + -i x.x.x.x Specify IP address for aircon (default is 10.99.99.1) + -I db Log all zone temperatures to given influxdb database, then exit (see -o). + -k file If file exists, never change aircon settings (default: /Users/rob/.aircon_noaction). -l num Set number of too hot/cold zones at which to taking action. -L Log all zone temperatures to CSV file, then exit (see -o). - -o file Specify CSV output file. Default: /root/acstats.csv -m Generate a config file based on current aircon setup. + -o file Specify CSV output file. Default: /Users/rob/acstats.csv + -P Use pymyair instead of direct JSON API calls (also see -x). -p Profiler mode. -s Validate config file then exit. -S[H] Show configured rules in human-readable format (HTML if -H given, otherwise ANSI). - -w List which people are available then exit. - -W List zone-owning devices are available then exit. -t num Specify degrees below min temperature before taking action. -T num Specify degrees above max temperature before taking action. + -w List which people are available then exit. + -W List zone-owning devices are available then exit. + -x path Specify location of 'pymyair' (from https://github.com/smallsam/pymyair) + (only used if -P specified) -y Actually run commands/db inserts. By default, commands are just displayed. ``` @@ -92,7 +93,6 @@ adj Study Family person Rob robsphone person Beth bethsphone -person Mel melsphone person Brendan brendansphone ``` @@ -139,7 +139,7 @@ person Brendan brendansphone Media 18-22 close 20 21.6 n/a Rob&Beth 18-22 close 18 19.5 n/a Study n/a close 19 20.6 n/a - Mel&Rob 18-21 close 18 19.5 n/a + Bedroom2 18-21 close 18 19.5 n/a Nursery 17-19 open 19 18.9 set_myzone Analysis: @@ -163,7 +163,7 @@ person Brendan brendansphone Media 18-22 close 20 21.6 n/a Rob&Beth 18-22 close 18 19.5 n/a Study n/a close 19 20.6 n/a - Mel&Rob 18-21 close 18 19.5 n/a + Bedroom2 18-21 close 18 19.5 n/a Nursery 17-19 open 19 18.9 set_myzone Analysis: diff --git a/aircon.sh b/aircon.sh index 4815e6b..6c2ff78 100755 --- a/aircon.sh +++ b/aircon.sh @@ -2,7 +2,6 @@ # TODO: comparison to previous reading # TODO: gnuplot of given time range -# TODO: ditch pymyair # TODO: manual controls DEFAULT_KILLFILE="${HOME}/.aircon_noaction" @@ -72,6 +71,7 @@ function usage() { echo " -L Log all zone temperatures to CSV file, then exit (see -o)." echo " -m Generate a config file based on current aircon setup." echo " -o file Specify CSV output file. Default: $DEFAULT_CSVFILE" + echo " -P Use pymyair instead of direct JSON API calls (also see -x)." echo " -p Profiler mode." echo " -s Validate config file then exit." echo " -S[H] Show configured rules in human-readable format" @@ -80,6 +80,8 @@ function usage() { echo " -T num Specify degrees above max temperature before taking action." echo " -w List which people are available then exit." echo " -W List zone-owning devices are available then exit." + echo " -x path Specify location of 'pymyair' (from https://github.com/smallsam/pymyair)" + echo " (only used if -P specified)" echo " -y Actually run commands/db inserts. By default, commands are just displayed." echo } @@ -88,6 +90,10 @@ function action() { echo -e "$BOLD$GREEN>> Running action: $PLAIN$GREEN$*$PLAIN" } +function warn() { + echo -e "$BOLD${YELLOW}Warning: $PLAIN$YELLOW$*$PLAIN" >/dev/stderr +} + function error() { echo -e "$BOLD${RED}ERROR: $PLAIN$RED$*$PLAIN" >/dev/stderr } @@ -694,7 +700,7 @@ function gen_aircon_command() { done } -# add_aircon_command zone_idx "comment goes here" "actual pymyair command to run" +# add_aircon_command zone_idx "comment goes here" " pymyair command to run" function add_aircon_command() { local x idx otheridx comment db cmd jcmd @@ -1524,18 +1530,18 @@ function get_aircon_info() { if [[ $JSONAPI -eq 1 ]]; then url="${AIRCON_URL}/getSystemData" jsoninfo=$(curl -s "$url" 2>/dev/null) - zones=$(echo "$jsoninfo" | jq -r '.aircons.ac1.zones[] | [ .name, .state, .setTemp, .measuredTemp, .number ] | @csv' | tr -d '" ') - state=$(echo "$jsoninfo" | jq -r '.aircons.ac1.info.state' | tr -d '" ') + zones=$(echo "$jsoninfo" | $JQ -r '.aircons.ac1.zones[] | [ .name, .state, .setTemp, .measuredTemp, .number ] | @csv' | tr -d '" ') + state=$(echo "$jsoninfo" | $JQ -r '.aircons.ac1.info.state' | tr -d '" ') if [[ $state == "off" ]]; then airconmode="off" else - airconmode=$(echo "$jsoninfo" | jq -r '.aircons.ac1.info.mode' | tr -d '" ') + airconmode=$(echo "$jsoninfo" | $JQ -r '.aircons.ac1.info.mode' | tr -d '" ') fi - airconmyzoneid=$(echo "$jsoninfo" | jq -r '.aircons.ac1.info.myZone ' | tr -d '" ') + airconmyzoneid=$(echo "$jsoninfo" | $JQ -r '.aircons.ac1.info.myZone ' | tr -d '" ') else - zones=$(myair $AIRCON_IP zones | jq -r '.[] | [ .name, .state, .setTemp, .measuredTemp, .number ] | @csv' | tr -d '" ') - airconmode=$(myair $AIRCON_IP mode) - airconmyzoneid=$(myair $AIRCON_IP myzone) + zones=$($MYAIR $AIRCON_IP zones | $JQ -r '.[] | [ .name, .state, .setTemp, .measuredTemp, .number ] | @csv' | tr -d '" ') + airconmode=$($MYAIR $AIRCON_IP mode) + airconmyzoneid=$($MYAIR $AIRCON_IP myzone) fi nzones=0 for line in $zones; do @@ -1757,7 +1763,7 @@ function combine_commands() { combinejq="${combinejq} * .[$((x + 1))]" done - jcmd=$(jq -s "$combinejq" $basefile ${tfile[@]} ) + jcmd=$($JQ -s "$combinejq" $basefile ${tfile[@]} ) [[ $? -ne 0 ]] && { error "couldnt merge json command files " >&2; exit 1; } [[ -z $jcmd ]] && { error "got empty merged json command" >&2; exit 1; } jurl="${AIRCON_URL}/setAircon?json=$jcmd" @@ -1779,7 +1785,7 @@ function run_commands() { [[ -n ${airconcomment[$x]} ]] && action "${airconcomment[$x]}" if [[ $JSONAPI -eq 1 ]]; then - myair $AIRCON_IP ${airconcmd[$x]} >/dev/null 2>&1 + $MYAIR $AIRCON_IP ${airconcmd[$x]} >/dev/null 2>&1 fi influx_insert "INSERT aircon action=\"${airconcomment[$x]}\",comment=\"${airconproblem[$x]}\"" done @@ -1787,7 +1793,7 @@ function run_commands() { if [[ $JSONAPI -eq 1 ]]; then jurl=$(combine_commands) res=$(curl -s -g "$jurl" 3>/dev/null) - jqres=$(echo "$res" | jq -r '.ack' 2>/dev/null) + jqres=$(echo "$res" | $JQ -r '.ack' 2>/dev/null) if [[ $jqres != "true" ]]; then error "Myair API call failed:" echo -e "$RED curl -s -g $jurl$PLAIN" @@ -1896,7 +1902,7 @@ limit=$DEFAULTLIMIT profiler=0 csvfile="$DEFAULT_CSVFILE" sanitycheck=0 -JSONAPI=0 +JSONAPI=1 RULEFORMAT=ansi robtest=0 @@ -1916,15 +1922,21 @@ fi ALLARGS="$ALLARGS $*" -optstring="aA:bcD:f:hHi:I:jk:l:Lo:pmRsSt:T:wWx:y" +optstring="aA:bcD:f:hHi:I:k:l:Lo:pPmRsSt:T:wWx:y" while getopts "$optstring" i $ALLARGS; do case "$i" in a) ARPING=${OPTARG} ;; + A) + influxdb="$OPTARG" + ;; b) RULEDB=1 ;; + c) + enable_cronmode + ;; D) influxhost="${OPTARG}" ;; @@ -1945,51 +1957,42 @@ while getopts "$optstring" i $ALLARGS; do logmode=2 influxdb="$OPTARG" ;; - j) - JSONAPI=1 - ;; - A) - influxdb="$OPTARG" - ;; k) KILLFILE=${OPTARG} ;; l) limit=${OPTARG} ;; - o) - csvfile="$OPTARG" - ;; L) logmode=1 ;; - t) - tolerance_l=${OPTARG} + m) + makeconfig=1 ;; - T) - tolerance_h=${OPTARG} + o) + csvfile="$OPTARG" + ;; + P) + JSONAPI=0 + ;; + p) + profiler=1 + info "Profiler mode enabled" ;; R) robtest=1 ;; - c) - enable_cronmode - ;; - p) - profiler=1 - info "Profiler mode enabled" - ;; - m) - makeconfig=1 - ;; s) sanitycheck=1 ;; S) sanitycheck=2 ;; - y) - DOIT=1 + t) + tolerance_l=${OPTARG} + ;; + T) + tolerance_h=${OPTARG} ;; w) showwho=1 @@ -2000,9 +2003,12 @@ while getopts "$optstring" i $ALLARGS; do x) MYAIR="${OPTARG}" ;; + y) + DOIT=1 + ;; *) usage; - exit 1; + exit 1; ;; esac done @@ -2011,7 +2017,7 @@ shift $((OPTIND - 1)) AIRCON_URL="http://${AIRCON_IP}:2025" if [[ ! -e $ARPING ]]; then - info "Warning: arping binary '$ARPING' not found, will use ping instead" + warn "arping binary '$ARPING' not found, will use ping instead" fi if [[ -n $influxdb ]]; then @@ -2029,17 +2035,16 @@ if [[ $makeconfig -eq 1 ]]; then exit $rv fi - - - - if [[ $showwho -eq 0 ]]; then if [[ $JSONAPI -eq 1 ]]; then - JQ=$(which myair 2>/dev/null) + JQ=$(which jq 2>/dev/null) if [[ $? -ne 0 ]]; then error "Can't find jq executable in path." exit 1 fi + if [[ -n $MYAIR ]]; then + warn "-x option ignored as -P not used." + fi else if [[ -z $MYAIR ]]; then MYAIR=$(which myair 2>/dev/null) @@ -2056,8 +2061,6 @@ if [[ $showwho -eq 0 ]]; then fi fi - - # Must do this BEFORE parsing the config file, otherwise # we can't resolve zone names. get_aircon_info