Pond[CTRL] Update

Er zat een bug in de firmware waarbij uitgeschakelde sensoren een alarm konden verzoorzaken. Dit is opgelost vanaf firmwareversie 1130.

Huidige firmwareversie: 1130

Pond[CTRL] Update

De vernieuwde taakverdeling tussen microprocessor en WiFi-module blijkt een goede zet te zijn geweest. Pond[CTRL] draait vele malen stabieler en heeft de afgelopen 24 uur nog geen enkele ongeplande reset gehad (een ongeplande reset is een reset door bijvoorbeeld een hard- of softwarefout).

Vandaag heb ik de API en de firmware van de WiFi-module aangepast. De API geeft nu een json-resultaat na uploaden van data waarin de meest firmware-versie en de status vermeld staan. Indien de API-call succesvol was, dan zal de programmeerbare LED gaan branden wat vanaf nu aangeeft of er een succesvolle link is met de API. De LED brandt net zo lang tot er een negatieve of geen status ontvangen wordt. Door de versienummers van de huidige firmware en de nieuwste firmware te vergelijken, wordt er nu een automatische update in gang gezet.

Huidige firmwareversie: 1129

Pond[CTRL] Update

Omdat Pond[CTRL] nu naast de vijver hangt in een waterdichte behuizing, is het lastig om software van de microprocessor te updaten. Hierdoor heb ik de beslissing genomen om alle logica en berekeningen te verplaatsen van de software van de microprocessor naar die van de WiFi-module welke op afstand bij te werken is.

De microprocessor doet nu niets anders van sensorgegevens ophalen en luisteren naar opdrachten van de WiFi-module. De WiFi-module kan vanaf nu ook stekkers schakelen, de status-LED besturen en buzzer (de)activeren.

Daarnaast was het geven van opdrachten vanaf de online app te complex. Er moest gewerkt worden met byte-arrays die direct doorgespeeld werden naar de microprocessor. Ik heb opdracht-URLs toegevoegd aan de webserver van de WiFi-module, waarmee aan de hand van simpele opdrachten gecombineerd met POST-data alles op te halen en in te stellen is. Voor test-doeleinden is de ‘query?data=’-URL nog steeds aanwezig maar werkt alleen zodra ingelogd op de module.

De microprocessor crashte, ondanks het toevoegen van varistors, bij het schakelen van meerde apparaten gelijktijdig. Om dit te voorkomen heb ik een kleine vertraging van het schakelen van elk relais toegevoegd waarmee het probleem verholpen lijkt.

Enige probleem op dit moment is nog steeds de foute meetwaarden van de pH-sensor bij een buitentemperatuur van onder de 10 graden Celcius. Het vreemde is dat de meting niet afhankelijk is van de watertemperatuur, terwijl de sensor in het water hangt.

Pond[CTRL] Update

Pond[CTRL] is nu enige tijd actief en door uitgebreid te testen heb ik een en ander aan de hard- en software aangepast.

Software

De belangrijkste wijziging in de software is het afvlakken van de meetwaarden. Er wordt nu bij temperatuur- en pH-sensoren een gemiddelde van de laatste 120 juiste meetwaarden weergegeven, waarbij een juiste meetwaarde een waarde is die maximaal 5% afwijkt van de voorgaande. Bij schakelen van relais bleek er soms een foute meetwaarde tussen te zitten (door een dip in de stroomvoorziening) waardoor meetwaarden een alarm veroorzaakten. De 5% regel zit geprogrammeerd in de microprocessor zelf en de WiFi-module om ook storingen op de I2C-bus op te vangen.

De ingebouwde interface van de WiFi-module werkte alleen juist in standalone-modus (wanneer Pond[CTRL] als access point fungeerde). De interface werkt nu in beide modi.

Hardware

De hardware bleek helaas ook niet helemaal te functioneren zoals gewenst. Veel apparaten kunnen geschakeld worden zonder problemen, echter bij het uitschakelen van een zwaardere pomp ontstond er storing in de stroomvoorziening waardoor de microprocessor zichzelf resette. Dit probleem is gemakkelijk op te lossen door een voldoende zware varistor op de fase en nul van elk relais te zetten. De bedoeling is om deze varistor in het standaardontwerp van de print op te nemen.

Online Dashboard

Het online Dashboard toont nu alleen het bereik van de meetwaarden en niet langer het gehele bereik tussen laag en hoog alarm.

Problemen

Het enige probleem wat ik nog niet opgelost is, is de instabiliteit van de pH sensor bij temperaturen onder de 10 graden Celsius. Omdat ik niet weet of dit aan de sensor of het hardware-ontwerp van Pond[CTRL] ligt, wil ik eerst een tweede sensor testen.

What’s next

De komende tijd zal vooral bestaan uit een duurtest en (tromgeroffel!) het ontwerpen van een iOS-app. De app zal het online dashboard en app combineren in één.

Afwezigheidsdetectie: Eureka!

Na wat finetunen werkt alles zoals in eerste instantie bedoeld: er wordt een reeks IP-adressen gescand en indien online wordt het MAC-adres vergeleken met bekende apparaten. Is er een bekend apparaat online, dan zal de thermostaat (geroote Toon) zijn programma vervolgen. Zijn er geen apparaten online, dan wordt de thermostaat ingesteld op programma ‘Weg’ en gaan de opgegeven lampen (Philips Hue) automatisch uit! Naast lampen kan door middel van slimme stekkers natuurlijk ook sluipverbruik (standby apparaten) voorkomen worden.

Toekomstwensen? Mogelijkheid tot opnieuw inschakelen van bijvoorbeeld nachtlampjes op de kinderkamers. Zijn we weg, dan blijft alles uit. Komen we weer thuis, dan zou er bijvoorbeeld ingeschakeld kunnen worden als de zon onder is. De voorbereidingen voor zoninformatie is al in de laatste versie van mijn script verwerkt.

Het script

Je dient variabelen ‘toonIP’, ‘hueIP’, ‘locationLat’, ‘locationLon’, ‘ipRange’, ‘ipStart’, ‘ipEnd’, ‘hueAPIKey’ en ‘path’ in te stellen.

#!/bin/bash
# Script door Dennis Bor

# IP van Toon
toonIP="192.168.0.X"

# IP van Hue
hueIP="192.168.0.X"

# jouw locatie
locationLat="XX.XXXXXX"
locationLon="X.XXXXXX"

# te scannen IP range (opgegeven IP .1 - .255
ipRange="192.168.0."
ipStart=130
ipEnd=140

# API-key van Hue
hueAPIKey="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

# pad naar het script
path="/home/dennis/home_automation/"

# pad naar arp
arpPath="/usr/sbin/"

# toon informatie
date

# zonsopkomst en ondergang ophalen aan de hand van de opgegeven longitude en latitude
sunrise=`curl -s "https://api.sunrise-sunset.org/json?lat=$locationLat&lng=$locationLon"  | jq -r '.results.sunrise'`
sunset=`curl -s "https://api.sunrise-sunset.org/json?lat=$locationLat&lng=$locationLon"  | jq -r '.results.sunset'`

# tijdverschil ten opzichte van UTC ophalen
timeDifferenceUTC=`date +%z`

# substring vanaf index 2 (+0100 wordt bijvoorbeeld 100; 1 uur)
timeOffset=${timeDifferenceUTC:2}

# offset delen door 100 voor het aantal hele uren
timeOffset="$(($timeOffset / 100))"

# timestamps genereren voor zonsopkomst, zonsondergang en huidige tijd, inclusief UTC offset
# voor gegevens van api.sunrise-sunset.org
sunriseTimestamp=`date -d "$sunrise + $timeOffset hours" +%s`
sunsetTimestamp=`date -d "$sunset + $timeOffset hours" +%s`
currentTimestamp=`date +%s`

# huidige tijd tussen zonsopkomst en ondergang
if [ $sunriseTimestamp -lt $currentTimestamp ] && [ $sunsetTimestamp -gt $currentTimestamp ]; then
	
	# ja
	
	# toon informatie
	echo "De zon is momenteel op."
	
	# stel de boolean in op true
	sunUp=true

# huidige tijd voor zonsopkomst of na zonsondergang	
else

	# ja
	
	# toon informatie
	echo "De zon is momenteel onder."
	
	# stel de boolean in op true
	sunUp=false
fi

# Array met mac adressen
# Als geen van deze apparaten gevonden wordt in het lokale
# netwerk, dan zal er geschakeld worden.
deviceArray=()

# itereer door bestand 'devices' waarin
# per regel een mac-adres van een apparaat opgegeven
# kan worden
while read line; do

	# geen lege regel?
	if [[ $line != "" ]]; then

		# voeg het apparaat toe aan de array
		deviceArray+=("$line")
	fi

done < "$path"devices

# Boolean waarin opgeslagen wordt of er tenminste een
# van de gespecificeerde apparaten online is. Standaard false
deviceFound=false

# Toon informatie
echo "Zoeken naar apparaten, dit duurt een paar minuutjes..."

# itereer door het opgegeven ip bereik
for iterator in $(seq $ipStart $ipEnd)
do

	# apparaat gevonden (boolean true)?
	if [[ $deviceFound == true ]]; then
	
		# ja, escape deze for-loop
		break
	fi

	# toon informatie
	printf "Scannen van $ipRange$iterator"

	# itereer van 1 t/m 50 (maximaal aantal pogingen). Het grote aantal iteraties bleek
	# nodig omdat de enige open WiFi poort op de iphone tijdens standy (62078) maar
	# op bepaalde momenten reageert. 99% van de keren is dit binnen 25 pogingen, maar
	# enkele uitschieters tussen de 30 en 40 kwamen voor. 50 is dus echt met een 
	# ingebouwd veilig marge
	for subIterator in {1..50}}
	do

		# apparaat nog (steeds) niet gevonden?
		if [[ $deviceFound == false ]]; then

			# toon informatie (puntje, soort progress indicator)
			printf "."

			# scan poort 62078 met een delay van 250ms op het huidige ip adres
			nmapResult=`nmap --scan-delay 250ms -p 62078 "$ipRange$iterator"`

			# komt de tekst '1 host up' voor in de uitvoer van nmap? dat
			# betekent dat het apparaat gevonden is
			if [[ $nmapResult == *"1 host up"* ]]; then

				# itereer door alle apparaten in de array
				for macIterator in "${deviceArray[@]}"
				do
					# komt het huidige mac adres voor in de uitvoer van nmap?
					if [[ $nmapResult == *"$macIterator"* ]]; then

						# ja
						
						# toon informatie (eerste echo zorgt voor een linebreak na de progress
						# indicator puntjes)
						echo ""
						printf "Apparaat met adres $macIterator gevonden"
						
						# stel de boolean in op true (apparaat gevonden!)
						deviceFound=true
						
						# verlaat de for-loop
						break
					fi
				done
			fi
		fi
	done
	
	# apparaat nog (steeds) niet gevonden
	if [[ $deviceFound == false ]]; then

		# nee
		
		# toon progress indicator
		printf "."
	
		# als laatste poging pingen we, waarop Android apparaten wel altijd reageren.
		# lukt de ping ook niet, dan kunnen we er wel van uit gaan dat het apparaat echot
		# niet binnen het bereik van het WiFi-netwerk is.
		pingResult=`ping -c 4 "$ipRange$iterator"`

		# komt de tekst '100% packet loss' niet voor in de uitvoer van ping?
		if [[ $pingResult != *"100% packet loss"* ]]; then

			# nee! dat betekent dat het apparaat tenmiste eenmaal gereageerd
			# heeft op een ping
			
			# arp informatie ophalen van het apparaat, zodat we het mac adres	
			# kunnen vergelijken met onze bekende apparaten
			arpResult=`"$arpPath"arp -a "$ipRange$iterator"`

			# itereer door alle mac adressen in de array
			for macIterator in "${deviceArray[@]}"
            do
				# komt het huidige mac adres voor in de uitvoer van arp?	
				if [[ ${arpResult^^} == *"$macIterator"* ]]; then

					# yep! bekend apparaat gevonden!
			
					# toon informatie (eerste echo is een linebreak na de progress
					# indicator
					echo ""
					printf "Apparaat met adres $macIterator gevonden"
					
					# stel de boolean in op true (apparaat gevonden)
					deviceFound=true
					
					# verlaat de for loop
					break
				fi
			done
		fi
	fi	
	
	# toon informatie (linebreak)
	echo ""

done

# Geen apparaten online?
if [[ $deviceFound == false ]]; then

	# nee

	# toon informatie
	echo "Geen apparaten online, actie ondernemen..."

	# schakel programma Toon in op Weg
	# gebruik curl, parse de uitvoer als JSON en sla de waarde van 'result' op
	# de waarde wordt bij de tweede call overschreven; als de laatste faalt,
	# dan moet de eerste ook gefaald zijn en andersom
	result=`curl -s "http://$toonIP/happ_thermstat?action=changeSchemeState&state=0"`
	result=`curl -s "http://$toonIP/happ_thermstat?action=changeSchemeState&state=2&temperatureState=3" | jq -r '.result'`

	# waarde van result 'ok'?
	# dat betekent dat Toon het gekozen programma geacvtiveerd heeft
	if [[ $result == "ok" ]]; then

		# toon informatie
		echo "Toon ingesteld op 'Weg'"

	# waarde van result ongelijk aan 'ok'? dit kan alles zijn, van een ander resultaat tot
	# verbindingsproblemen, etc.
	else

		# toon informatie
		echo "Kan toon niet instellen op 'Weg'"
	fi

	# toon informatie
	echo "Hue instellen..."

	# haal alle lampen op
	result=`curl -s "http://$hueIP/api/$hueAPIKey/lights"`

	# array keys uit de json array van Hue halen. helaas gebruikt Hue geen
	# array met indices, maar met keys. van lampen die verwijderd worden
	# vervalt het id.
	arrayKeys=`echo "$result" | jq -r 'keys | .[]'`
	
	# nieuwe array om alle keys in op te slaan
	arrayIDs=()

	# itereer door alle array keys
	for key in ${arrayKeys//\\n/ }
	do
		# voeg de key toe aan de array
		arrayIDs+=("$key")

	done

	# inhoud van bestand 'lights' opslaan in de variabele. Bij alle lampen van Hue
	# wordt gecontroleerd of ze in deze lijst voorkomen, voordat ze uitgeschakeld worden.
	# hierdoor blijft het mogelijk om bepaalde verlichting te laten branden (bijvoorbeeld
	# tuinverlichting)
	lightsToSwitch=`cat "$path"lights`

	# itereer door alle IDs
	for lightIterator in "${arrayIDs[@]}"
	do

		# unieke id van de lamp ophalen. in het betand 'lights' kun je een lijst aanleggen
		# van unieke ids van lampen die uitgeschakeld moeten worden
		currentUniqueId=`echo "$result" | jq --arg value $lightIterator -r '.[$value].uniqueid'`
		
		# komt het huidige unieke id voor in de lijst met ids?
		if [[ $lightsToSwitch == *"$currentUniqueId"* ]]; then

			# ja
			
			# lamp naam ophalen zodat deze getoond kan worden
			currentName=`echo "$result" | jq --arg value $lightIterator -r  '.[$value].name'`
			
			# toon informatie
			echo "Uitschakelen: $currentName ($currentUniqueId)"
			
			# schakel de lamp uit
			toonResult=`curl -s -X PUT -H "Content-Type: application/json" -d '{"on":false}' "http://$hueIP/api/$hueAPIKey/lights/$lightIterator/state"`

		fi
	done

# Tenminste een apparaat online?
else

	# ja

	 # toon informatie
     echo "Een of meer apparaten online, thermostaat herstellen..."

	# hervat het programma van toon
	# gebruik curl, parse de uitvoer als JSON en sla de waarde van 'result' op
	result=`curl -s "http://$toonIP/happ_thermstat?action=changeSchemeState&state=1" | jq -r '.result'`

	# waarde van result 'ok'?
        # dat betekent dat Toon het programma hersteld heeft
        if [[ $result == "ok" ]]; then

                # toon informatie
                echo "Toon hervat programma"

        # waarde van result ongelijk aan 'ok'? dit kan alles zijn, van een ander resultaat tot
        # verbindingsproblemen, etc.
        else

                # toon informatie
                echo "Kan het programma op Toon niet hervatten"
        fi

	# toon informatie
	echo "Klaar..."

fi

Benodigde bestanden

Bestandsnaam ‘devices’

# lijst met MAC adressen van bekende apparaten. 
# zet elk apparaat op een aparte regel 
XX:XX:XX:XX:XX:XX
XX:XX:XX:XX:XX:XX

Bestandsnaam ‘lights’

# lijst met uit te schakelen Hue lampen bij afwezigheid
# zet elke lamp (of slimme stekker) op een aparte regel
# gebruik de 'uniqueid' parameter
XX:XX:XX:XX:XX:XX-XX:XX
XX:XX:XX:XX:XX:XX-XX:XX

Afwezigheidsdetectie: testresultaten

Update na 48 uur

Het veranderen van DHCP leases had geen invloed op de betrouwbaarheid van de arp-cache. Ik moest dus een andere manier verzinnen om aanwezigheid te detecteren. Nu blijkt dat iPhones, zodra het scherm uit gaat, in een soort slaapmodus terecht komen waarin WiFi niet beschikbaar is, BEHALVE op poort 62078. Deze poort blijkt helaas niet altijd bereikbaar. Het script werkt nu als volgt voor ieder te controleren IP-adres:

  • Itereer maximaal 20 keer om via nmap verbinding te maken op poort 62078 met een interval van 250ms. Ik heb diverse intervals geprobeerd, maar met deze interval werd mijn iPhone (iPhone 7) tot nu toe altijd binnen 20 pogingen gedetecteerd.
  • Na 20 iteraties wordt er een ping uitgevoerd, zodat andere apparaten dan iPhones gedetecteerd worden. Dit lijkt betrouwbaar te werken bij Android telefoons.

Testresultaten na 24 uur

E3 arp-cache uitlezen bleek niet betrouwbaar. Vooral iPhones bleven onnodig lang in de cache staan. Ik heb geprobeerd om de maximale DHCP-lease te verkorten naar 10 minuten, zodat de apparaten hopelijk ook na maximaal 10 minuten inactiviteit uit de cache verdwijnen.

Een toiletbezoek zonder verlichting is lastig ?. Ik dacht nog even te testen en WiFi uit te schakelen op mijn mobiel. Vrouwlief was uit huis en de mobiel van m’n zoon uitgeschakeld. Het script werkte prima! In het toilet, hallen en badkamer heb ik bewegingssensoren. Het script werkte in dit geval prima, echter was de configuratie niet juist. Deze lampen verwijderd uit het ‘lights’ bestand zodat de bewegingssensoren niet overruled worden.

Nu wederom testen. To be continued!

Update: afwezigheidsdetectie

Sinds vandaag werkt het script voor afwezigheidsdetectie met zowel (geroote)Toon als Hue. Er zijn een ook aantal aanpassingen gemaakt zoals het toevoegen van absolute paden naar bestanden voor gebruik met cron.

Het script bestaat uit drie bestanden:

  • daemon.sh – het script zelf
  • devices – lijst met mac-adressen van telefoons
  • lights – lijst met unieke ids van Hue-lampen

Belangrijk is om voor Hue een unieke API-key aan te maken. Dit kan via de clip API debugger (check voor meer informatie de site van Philips Hue).

Bij mij draait het bestand elke vijf minuten via Cron en wordt uitvoer opgeslagen naar een log-bestand, waarvan de uitvoer als volgt is:

Zoeken naar apparaten, dit duurt een paar minuutjes...
Apparaat offline: xx:xx:xx:xx:xx:xx
Apparaat offline: xx:xx:xx:xx:xx:xx
Apparaat offline: xx:xx:xx:xx:xx:xx
Apparaat offline: xx:xx:xx:xx:xx:xx
Geen apparaten online, actie ondernemen...
Toon ingesteld op 'Weg'
Hue instellen...
Uitschakelen: Badkamer spot 1 (xx:xx:xx:xx:xx:xx:xx:xx-xx)
Uitschakelen: Badkamer spot 2 (xx:xx:xx:xx:xx:xx:xx:xx-xx)
Uitschakelen: Badkamer spot 3 (xx:xx:xx:xx:xx:xx:xx:xx-xx)
Uitschakelen: Hal boven spot 1 (xx:xx:xx:xx:xx:xx:xx:xx-xx)
Uitschakelen: Hal boven spot 2 (xx:xx:xx:xx:xx:xx:xx:xx-xx)
Uitschakelen: Hal boven spot 3 (xx:xx:xx:xx:xx:xx:xx:xx-xx)
Uitschakelen: Plafonniere (xx:xx:xx:xx:xx:xx:xx:xx-xx)
Uitschakelen: Plafonniere hal (xx:xx:xx:xx:xx:xx:xx:xx-xx)
Uitschakelen: Plafonnière (xx:xx:xx:xx:xx:xx:xx:xx-xx)
Uitschakelen: Keuken (xx:xx:xx:xx:xx:xx:xx:xx-xx)
Uitschakelen: Keuken spot 1 (xx:xx:xx:xx:xx:xx:xx:xx-xx)
Uitschakelen: Keuken spot 2 (xx:xx:xx:xx:xx:xx:xx:xx-xx)
Uitschakelen: Tv hoek (xx:xx:xx:xx:xx:xx:xx:xx-xx)
Uitschakelen: Zithoek (xx:xx:xx:xx:xx:xx:xx:xx-xx)
Uitschakelen: Plafonnière (xx:xx:xx:xx:xx:xx:xx:xx-xx)

Het script ziet er nu als volgt uit:

#!/bin/bash
# Script door Dennis Bor

# IP van Toon
toonIP="192.168.0.58"

# IP van Hue
hueIP="192.168.0.87"

# API-key van Hue
hueAPIKey="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# pad naar het script
path="/home/dennis/home_automation/"

# pad naar arp
arpPath="/usr/sbin/"

# Array met mac adressen
# Als geen van deze apparaten gevonden wordt in het lokale
# netwerk, dan zal er geschakeld worden.
deviceArray=()

# itereer door bestand 'devices' waarin
# per regel een mac-adres van een apparaat opgegeven
# kan worden
while read line; do

	# geen lege regel?
	if [[ $line != "" ]]; then

		# voeg het apparaat toe aan de array
		deviceArray+=("$line")
	fi

done < "$path"devices


# Boolean waarin opgeslagen wordt of er tenminste een
# van de gespecificeerde apparaten online is. Standaard false
deviceFound=false

# Toon informatie
echo "Zoeken naar apparaten, dit duurt een paar minuutjes..."

# Leeg de arp cache
ipResult=`ip -s -s neigh flush all`

# Wacht drie minuten
sleep 3m

# Haal de arp table op
arpResult=`"$arpPath"arp -a`

# Itereer door alle gespecificeerde mac adressen
for macIterator in "${deviceArray[@]}"
do

	# huidige adres in de arp tabel?
	if [[ $arpResult == *"$macIterator"* ]]; then

		# ja

		# toon informatie
		echo "Apparaat online: $macIterator"

		# stel de boolean in op true
		deviceFound=true

	# huidige adres niet in de arp tabel
	else

		# nee

		# toon informatie
		echo "Apparaat offline: $macIterator"

	fi
done

# Geen apparaten online?
if [[ $deviceFound == false ]]; then

	# nee

	# toon informatie
	echo "Geen apparaten online, actie ondernemen..."

	# schakel programma Toon in op Weg
	# gebruik curl, parse de uitvoer als JSON en sla de waarde van 'result' op
	# de waarde wordt bij de tweede call overschreven; als de laatste faalt,
	# dan moet de eerste ook gefaald zijn en andersom
	result=`curl -s "http://$toonIP/happ_thermstat?action=changeSchemeState&state=0"`
	result=`curl -s "http://$toonIP/happ_thermstat?action=changeSchemeState&state=2&temperatureState=3" | jq -r '.result'`

	# waarde van result 'ok'?
	# dat betekent dat Toon het gekozen programma geacvtiveerd heeft
	if [[ $result == "ok" ]]; then

		# toon informatie
		echo "Toon ingesteld op 'Weg'"

	# waarde van result ongelijk aan 'ok'? dit kan alles zijn, van een ander resultaat tot
	# verbindingsproblemen, etc.
	else

		# toon informatie
		echo "Kan toon niet instellen op 'Weg'"
	fi

	# toon informatie
	echo "Hue instellen..."

	# haal alle lampen op
	result=`curl -s "http://$hueIP/api/$hueAPIKey/lights"`

	# array keys uit de json array van Hue halen. helaas gebruikt Hue geen
	# array met indices, maar met keys. van lampen die verwijderd worden
	# vervalt het id.
	arrayKeys=`echo "$result" | jq -r 'keys | .[]'`
	
	# nieuwe array om alle keys in op te slaan
	arrayIDs=()

	# itereer door alle array keys
	for key in ${arrayKeys//\\n/ }
	do
		# voeg de key toe aan de array
		arrayIDs+=("$key")

	done

	# inhoud van bestand 'lights' opslaan in de variabele. Bij alle lampen van Hue
	# wordt gecontroleerd of ze in deze lijst voorkomen, voordat ze uitgeschakeld worden.
	# hierdoor blijft het mogelijk om bepaalde verlichting te laten branden (bijvoorbeeld
	# tuinverlichting)
	lightsToSwitchOff=`cat "$path"lights`

	# itereer door alle IDs
	for lightIterator in "${arrayIDs[@]}"
	do

		# unieke id van de lamp ophalen. in het betand 'lights' kun je een lijst aanleggen
		# van unieke ids van lampen die uitgeschakeld moeten worden
		currentUniqueId=`echo "$result" | jq --arg value $lightIterator -r '.[$value].uniqueid'`
		
		# komt het huidige unieke id voor in de lijst met ids?
		if [[ $lightsToSwitchOff == *"$currentUniqueId"* ]]; then

			# ja
			
			# lamp naam ophalen zodat deze getoond kan worden
			currentName=`echo "$result" | jq --arg value $lightIterator -r  '.[$value].name'`
			
			# toon informatie
			echo "Uitschakelen: $currentName ($currentUniqueId)"
			
			# schakel de lamp uit
			toonResult=`curl -s -X PUT -H "Content-Type: application/json" -d '{"on":false}' "http://$hueIP/api/$hueAPIKey/lights/$lightIterator/state"`

		fi
	done

# Tenminste een apparaat online?
else

	# ja

	 # toon informatie
     echo "Een of meer apparaten online, thermostaat herstellen..."

	# hervat het programma van toon
	# gebruik curl, parse de uitvoer als JSON en sla de waarde van 'result' op
	result=`curl -s "http://$toonIP/happ_thermstat?action=changeSchemeState&state=1" | jq -r '.result'`

	# waarde van result 'ok'?
        # dat betekent dat Toon het programma hersteld heeft
        if [[ $result == "ok" ]]; then

                # toon informatie
                echo "Toon hervat programma"

        # waarde van result ongelijk aan 'ok'? dit kan alles zijn, van een ander resultaat tot
        # verbindingsproblemen, etc.
        else

                # toon informatie
                echo "Kan het programma op Toon niet hervatten"
        fi

	# toon informatie
	echo "Klaar..."

fi

En nu maar testen. Vooral het detecteren van iPhones lijkt (nog) niet helemaal vlekkeloos te verlopen: ofwel ze blijven te lang in de cache staan, of ze worden niet verwijderd.

Update: Afwezigheidsdetectie

Na het rooten van Toon kun je door het aanroepen van een bepaalde URL veel, zoals programma’s, instellen. Ik heb het script voor afwezigheidsdetectie aangepast zodat Toon bij afwezigheid het programma ‘Weg’ activeert.

Een andere kleine update aan het script is dat MAC adressen van apparaten nu in een apart bestand ‘devices’ opgeslagen moeten worden. Het script leest dit bestand uit.

Omdat er gebruik wordt gemaakt van JSON output, dient ‘jq’ geïnstalleerd te zijn op de server.

#!/bin/bash
# Script door Dennis Bor

# IP van Toon
toonIP="192.168.0.58"

# Array met mac adressen
# Als geen van deze apparaten gevonden wordt in het lokale
# netwerk, dan zal er geschakeld worden.
deviceArray=()

# itereer door bestand 'devices' waarin
# per regel een mac-adres van een apparaat opgegeven
# kan worden
while read line; do

        # geen lege regel?
        if [[ $line != "" ]]; then

                # voeg het apparaat toe aan de array
                deviceArray+=("$line")
        fi

done < devices

# Boolean waarin opgeslagen wordt of er tenminste een
# van de gespecificeerde apparaten online is. Standaard false
deviceFound=false

# Toon informatie
echo "Zoeken naar apparaten, momentje..."

# Haal de arp table op
arpResult=`arp -a`

# Itereer door alle gespecificeerde mac adressen
for macIterator in "${deviceArray[@]}"
do

        # huidige adres in de arp tabel?
        if [[ $arpResult == *"$macIterator"* ]]; then

                # ja

                # toon informatie
                echo "Apparaat online: $macIterator"

                # stel de boolean in op true
                deviceFound=true

        # huidige adres niet in de arp tabel
        else

                # nee

                # toon informatie
                echo "Apparaat offline: $macIterator"

        fi
done

# Geen apparaten online?
if [[ $deviceFound == false ]]; then

        # nee

        # toon informatie
        echo "Geen apparaten online, actie ondernemen..."

        # schakel programma Toon in op Weg
        # gebruik curl, parse de uitvoer als JSON en sla de waarde van 'result' op
        result=`curl -s "http://$toonIP/happ_thermstat?action=changeSchemeState&state=2&temperatureState=3" | jq -r '.result'`


        # waarde van result 'ok'?
        # dat betekent dat Toon het gekozen programma geacvtiveerd heeft
        if [[ $result == "ok" ]]; then

                # toon informatie
                echo "Toon ingesteld op 'Weg'"

        # waarde van result ongelijk aan 'ok'? dit kan alles zijn, van een ander resultaat tot
        # verbindingsproblemen, etc.
        else

                # toon informatie
                echo "Kan toon niet instellen op 'Weg'"
        fi

# Tenminste een apparaat online?
else

        # ja

        # toon informatie
        echo "Een of meer apparaten online, afsluiten..."

fi