Bash is ideaal om scripts te schrijven. Het heeft niet de mogelijkheden van een programmeertaal zoals python, maar kan mits het gebruik van zijn uitgebreide mogelijkheden en flexibel denken toch gebruikt worden om te programmeren.
Vandaag schrijven we een script waarbij we uitgebreid gebruik maken van array's (verzameling variabelen) om een thermostaat op basis van een weekplanning te maken.
Het script zelf bevat uitgebreide commentaar om de volgende mogelijkheden van bash in de verf te zetten:
Dit script is een vereenvoudiging van een thermostaatregeling in ontwikkeling. Het werd dus vereenvoudigd, van uitgebreidere commentaar voorzien en daardoor niet helemaal uitgewerkt. Maar het toont wel goed hoe je met array's kunt werken en een volledig programma in bash kunt schrijven. Misschien een basis voor een eigen ontwerp.
Om het script te testen, kan je verschillende variabelen aanpassen.
dany@pindabook:~> sh timerThermostat.sh
Script draait in testmode en toont debug informatie.
Reset thermostaat
Indien niet alle radiatoren werden aangestuurd, verschijnen rm foutmeldingen.
rm: kan '/tmp/0B1100014153860280' niet verwijderen: Bestand of map bestaat niet
6 07:20 22:45
awake
07:20 08:30 22.00
11:00 13:30 22.20
16:45 17:30 22.20
22:15 22:45 22.20
10:44
Keuken niet verwarmen
Uit te voeren Tasmota opdracht: wget -q http://tasmota_a943fa-1018/cm?cmnd=Power%20Off
Kamertemp: 21.78 °C
Thermostaat temp: 22.00
Zuid aan bij 21.9 °C.
Noord aan bij 21.7 °C.
Noord uit bij 21.9 °C.
Zuid uit bij 22.1 °C.
Livingverwarming Zuid inschakelen
Stuur RF Code: 0B1100010141538601010F80
#!/bin/bash # Testmode uitschakelen door de volgende regel in commentaar te plaatsen debug=1 if [ ! -z $debug ]; then echo "Script draait in testmode en toont debug informatie." # De timer en thermostaat elke keer resetten touch timerThermostat/thermostatReset fi # Hier initialiseren we arrays # Timer array met standaard timergegevens # dag van de week - starttijd - stoptijd timerdefault[0]="0 07:20 22:45" timerdefault[1]="2 07:20 22:45" timerdefault[2]="4 07:20 22:45" timerdefault[3]="6 07:20 22:45" timerdefault[4]="1 07:20 22:45" timerdefault[5]="3 07:20 22:45" timerdefault[6]="5 07:20 22:45" # De volgende items staan niet chronologisch timerdefault[7]="0 16:30 17:00" timerdefault[8]="0 17:30 22:45" # Thermostaat array met standaard gegevens voor de keuken # Starttijd - stoptijd - temperatuur thermostatkitchendefault[0]="07:20 08:30 22.00" thermostatkitchendefault[2]="11:00 13:30 22.20" thermostatkitchendefault[1]="22:15 22:45 22.20" thermostatkitchendefault[3]="16:45 17:30 22.20" # Thermostaat array met standaard gegevens voor de woonkamer thermostatlivingdefault[0]="08:10 11:15 22.00" thermostatlivingdefault[2]="13:15 17:00 22.00" thermostatlivingdefault[1]="17:15 22:45 22.00" # Het volgende item staat niet chronologisch thermostatlivingdefault[3]="16:45 17:15 22.00" # Waar worden de arrays bewaard if [ ! -d timerThermostat ]; then mkdir timerThermostat fi timerfile="timerThermostat/timer" thermostatkitchenfile="timerThermostat/thermostatkitchen" thermostatlivingfile="timerThermostat/thermostatliving" # Luchtdruk, Vochtigheid en Temperatuur afkomstig van een sensor PresHumiTempfile="timerThermostat/PresHumiTemp" # Indien geen sensor aanwezig, zelf gegevens aanmaken als voorbeeld if [ ! -f $PresHumiTempfile ]; then echo " 998.61 hPa" > $PresHumiTempfile echo " 51.58 %" >> $PresHumiTempfile echo " 21.78 C" >> $PresHumiTempfile fi # Initialiseren Associatieve arrays (namen als index) met de verschillende radiators declare -A heater heater[LivingZuidOn]="0B1100010141538601010F80 on" heater[LivingZuidOff]="0B1100000141538601000080 off" heater[LivingNoordOn]="0B1100000141538602010F80 on" heater[LivingNoordOff]="0B1100010141538602000080 off" heater[KeukenZuidOn]="tasmota_a943fa-1018 on" heater[KeukenZuidOff]="tasmota_a943fa-1018 off" # Temperatuurschommeling tussen aan- en uitschakelen radiator hysteresis="0.1" if [ -f timerThermostat/thermostatDefault ]; then echo "Herstel standaard thermostaat" rm $timerfile rm $thermostatkitchenfile rm $thermostatlivingfile # In testmode elke keer Standaard thermostaat gebruiken if [ -z $debug ]; then rm timerThermostat/thermostatDefault fi fi if [ -f timerThermostat/thermostatReset ]; then echo "Reset thermostaat" echo "Indien niet alle radiatoren werden aangestuurd, verschijnen rm foutmeldingen." for switch in "${!heater[@]}" do if [[ "$switch" == *Off ]]; then heateritem=${heater[$switch]} if [[ "$heateritem" == tasmota* ]]; then tempfile="/tmp/${heateritem:0:19}" else tempfile="/tmp/${heateritem:0:6}${heateritem:8:10}${heateritem:22:2}" fi rm $tempfile fi done # In testmode elke keer de thermostaat resetten en Manueel ingestelde temperatuur gebruiken if [ -z $debug ]; then rm /var/www/html/data/thermostatManual rm /var/www/html/data/thermostatReset fi fi # Functie om een Radiografische KlikAanUit Radiator in- of uit te schakelen sendRF () { # Radiator eenduidig uit RF code filteren tempfile="/tmp/${1:0:6}${1:8:10}${1:22:2}" # indien nodig de status van de radiator initialiseren if [ ! -f "$tempfile" ]; then if [ "$2" == "off" ]; then echo 'on' > "$tempfile" else echo 'off' > "$tempfile" fi fi # De radiator enkel aansturen indien dit nodig is if [ $(cat "$tempfile") == "off" ] && [ "$2" == "on" ]; then if [ -f /home/pi/rfxcmd_gc-master/rfxcmd.py ]; then python /home/pi/rfxcmd_gc-master/rfxcmd.py -d /dev/ttyUSB0 -s "$1" else echo "Stuur RF Code: $1" fi # Bewaar de status van de radiator echo 'on' > "$tempfile" elif [ $(cat "$tempfile") == "on" ] && [ "$2" == "off" ]; then if [ -f /home/pi/rfxcmd_gc-master/rfxcmd.py ]; then python /home/pi/rfxcmd_gc-master/rfxcmd.py -d /dev/ttyUSB0 -s "$1" else echo "Stuur RF Code: $1" fi echo 'off' > "$tempfile" fi } # Functie om een WiFi Tasmota Radiator in- of uit te schakelen tasmota () { # indien nodig de status van de radiator initialiseren if [ ! -f /tmp/$1 ]; then echo "Uit te voeren Tasmota opdracht: wget -q http://$1/cm?cmnd=Power%20Off" echo '{"POWER":"OFF"}' > /tmp/$1 fi # De radiator enkel aansturen indien dit nodig is if [ $2 == "on" ] && [ $(cat /tmp/$1) == '{"POWER":"OFF"}' ]; then echo "Uit te voeren Tasmota opdracht: wget -q http://$1/cm?cmnd=Power%20On" # Bewaar de status van de radiator echo '{"POWER":"ON"}' > /tmp/$1 #echo $(wget -qO- http://$1/cm?cmnd=Power) > /tmp/$1 elif [ $2 == "off" ] && [ $(cat /tmp/$1) == '{"POWER":"ON"}' ]; then echo "Uit te voeren Tasmota opdracht: wget -q http://$1/cm?cmnd=Power%20Off" echo '{"POWER":"OFF"}' > /tmp/$1 #echo $(wget -qO- http://$1/cm?cmnd=Power) > /tmp/$1 fi } # Functie om alle radiators uit te schakelen function thermostatOff { # Overloop alle radiator waarden for switch in "${!heater[@]}" do # Enkel de indexnamen die eindigen op Off zijn van belang if [[ "$switch" == *Off ]]; then # Hoe wordt de radiator aangestuurd, via Tasmota of Radiografische if [[ "${heater[$switch]}" == tasmota* ]]; then tasmota ${heater[$switch]} else sendRF ${heater[$switch]} fi fi done } # Functie die de radiators aanstuurd function thermostat { temp=$(tail -1 $PresHumiTempfile) temp=${temp%% C*} # Verwijder eventuele witruimte voor de temperatuur temp="${temp#"${temp%%[![:space:]]*}"}" # Gebruik indien mogelijk de thermostaat voor de keuken opgeslagen in een bestand, anders de standaard keuken thermostaat opgeslagen in de array thermostatkitchendefault if [ ! -f $thermostatkitchenfile ]; then # Sla de standaard thermostaat op printf "%s\n" "${thermostatkitchendefault[@]}" > $thermostatkitchenfile fi # Lees de thermostaat voor de keuken in mapfile -t raw < $thermostatkitchenfile # Sorteer de thermostaatgegevens chronologisch IFS=$'\n' thermostatkitchen=($(sort <<<"${raw[*]}")) # Verwijder tijdelijke hulpvariabelen unset IFS unset raw if [ ! -z $debug ]; then printf "%s\n" "${thermostatkitchen[@]}" echo $now fi # Verwarming wordt standaard uitgeschakeld heating="off" # Controleer of nu valt tussen een thermostaat start- en stoptijd for thermostatitem in "${thermostatkitchen[@]}"; do daytime=(${thermostatitem}) if [[ "${daytime[0]}" < "$now" ]] && [[ "${daytime[1]}" > "$now" ]]; then heating="on" break fi done # De variable daytime[2] bevat de in de thermostaat ingestelde temperatuur # Controleer of er manueel een temperatuur werd ingesteld if [ -f timerThermostat/thermostatManual ]; then mapfile -t manual < timerThermostat/thermostatManual for manualitem in "${manual[@]}"; do roomtemp=(${manualitem}) if [ "${roomtemp[0]}" == "kitchen" ]; then daytime[2]=${roomtemp[1]} echo "Manual temp kichen: ${daytime[2]} °C" heating="on" break fi done fi # Enkel als de verwarming geregeld moet worden (via thermostaat of manueel) if [ "$heating" == "on" ]; then # Is de kamertemperatuur hoger of lager dan de ingestelde temperatuur, rekening houdend met de hysteresis if (( $(awk "BEGIN {print ($temp < ${daytime[2]} - $hysteresis)}") )); then echo "Keukenverwarming inschakelen" tasmota ${heater[KeukenZuidOn]} elif (( $(awk "BEGIN {print ($temp > ${daytime[2]} + $hysteresis)}") )); then echo "Keukenverwarming uitschakelen" tasmota ${heater[KeukenZuidOff]} fi else # Op dit moment wordt de keuken niet verwarmd. echo "Keuken niet verwarmen" tasmota ${heater[KeukenZuidOff]} fi # Iets complexere regeling voor de woonkamer met twee radiators # Gebruik de standaard of opgeslagen thermostaat if [ ! -f $thermostatlivingfile ]; then printf "%s\n" "${thermostatlivingdefault[@]}" > $thermostatlivingfile fi mapfile -t raw < $thermostatlivingfile IFS=$'\n' thermostatliving=($(sort <<<"${raw[*]}")) unset IFS unset raw heating="off" for thermostatitem in "${thermostatliving[@]}"; do daytime=(${thermostatitem}) if [[ "${daytime[0]}" < "$now" ]] && [[ "${daytime[1]}" > "$now" ]]; then heating="on" break fi done if [ "$heating" == "on" ]; then if [ ! -z $debug ]; then echo "Kamertemp: $temp °C" echo "Thermostaat temp: ${daytime[2]}" echo "Zuid aan bij" $(awk "BEGIN {print (${daytime[2]} - $hysteresis)}") "°C." echo "Noord aan bij" $(awk "BEGIN {print (${daytime[2]} - $hysteresis - $hysteresis * 2)}") "°C." echo "Noord uit bij" $(awk "BEGIN {print (${daytime[2]} + $hysteresis - $hysteresis * 2)}") "°C." echo "Zuid uit bij" $(awk "BEGIN {print (${daytime[2]} + $hysteresis)}") "°C." fi if (( $(awk "BEGIN {print ($temp < ${daytime[2]} - $hysteresis)}") )); then echo "Livingverwarming Zuid inschakelen" sendRF ${heater[LivingZuidOn]} # Indien de ingestelde temperatuur te laag is gezakt, wordt de tweede radiator ingeschakeld if (( $(awk "BEGIN {print ($temp < ${daytime[2]} - $hysteresis - $hysteresis * 2)}") )); then echo "Livingverwarming Noord inschakelen" sendRF ${heater[LivingNoordOn]} fi elif (( $(awk "BEGIN {print ($temp > ${daytime[2]} + $hysteresis - $hysteresis * 2)}") )); then # Als de ingestelde temperatuur bijna is bereikt, wordt de tweede radiator uitgeschakeld echo "Livingverwarming Noord uitschakelen" sendRF ${heater[LivingNoordOff]} # Bij het licht overschrijden van de ingestelde temperatuur wordt ook de eerste radiator uitgeschakeld if (( $(awk "BEGIN {print ($temp > ${daytime[2]} + $hysteresis)}") )); then echo "Livingverwarming Zuid uitschakelen" sendRF ${heater[LivingZuidOff]} fi fi else # Op dit moment wordt de woonkamer niet verwarmd. echo "Living niet verwarmen" sendRF ${heater[LivingZuidOff]} sendRF ${heater[LivingNoordOff]} fi } # Gebruik een wekenplanning om de thermostaat aan te sturen if [ ! -f $timerfile ]; then # default printf "%s\n" "${timerdefault[@]}" > $timerfile fi mapfile -t raw < $timerfile IFS=$'\n' timer=($(sort <<<"${raw[*]}")) unset IFS unset raw # Standaard werkt de thermostaat niet (slaaptoestand) state="sleep" # Bepaal de weekdag en tijdstip van het moment weekday=$(date +%w) now=$(date +%H:%M) # Doorloop de timergegevens for timeritem in "${timer[@]}"; do daytime=(${timeritem}) # Timeritem voor vandaag gevonden if [ "${daytime[0]}" == "$weekday" ]; then echo "$timeritem" # Moet de thermostaat geactiveerd worden (wakker worden) if [[ "${daytime[1]}" < "$now" ]] && [[ "${daytime[2]}" > "$now" ]]; then state="awake" break fi fi done echo $state if [ $state == "awake" ]; then # Verwarming regelen thermostat else # Verwarming uitschakelen thermostatOff fi