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

Toon rooten

Als Toon geroot is kun je het prijzige abonnement bij Eneco opzeggen. Daarnaast zijn er alternatieve apps, zoals ‘Domoticz’ waarmee je nog steeds een en ander kan instellen vanaf afstand. Voor mijn afwezigheidsdetectie is het rooten van Toon nodig, zodat er via het aanroepen van specifieke URL’s programma’s ingesteld kunnen worden.

LET OP: JE DOET DIT OP EIGEN RISICO! ALS HET MIS GAAT HEB JE EEN ONBRUIKBAAR APPARAAT

Benodigdheden

Je hebt de volgende zaken nodig om Toon te rooten:

  • Een Toon (duh!) versie 1, met U-Boot firmware versie 2010.09-R6 of 2010.09-R8. De versie van deze firmware zie je echter pas als je de Toon aangesloten hebt via een seriële poort.
  • Een USB naar seriële kabel (bijvoorbeeld deze).
  • Een programma om een seriële verbinding met Toon op te zetten. Mijn voorkeur gaat uit naar Putty en zal de werkwijze met dit programma hier ook beschrijven.
  • Een beetje kennis van Linux.

Toon openmaken

Maak Toon open door de witte rand langs het scherm voorzichtig naar voor te trekken. Hierna kun je het scherm naar voor kantelen. Let op dat het scherm met twee flatcables aan het moederbord zit. Haal deze voor het gemak los (let op: kwetsbaar!). Het moederbord zit met 6 klemmetjes vast (op elke hoek één en naast de stroomaansluiting twee). Duw de klemmetjes opzij en trek het moederbord voorzichtig naar voor. Aan het moederbord zitten nog drie antennes. Ik heb meerdere Toon versies gezien: bij sommigen kun je de antennes uit de behuizing tillen en bij anderen zitten ze geplakt. Zitten ze geplakt, haal dan de koperen aansluitingen voorzichtig los met een pincet en vergeet vooral niet hoe deze vast zaten.

Toon aansluiten

Ik ga er van uit dat, voordat je hiermee begint, de stuurprogramma’s voor je USB-seriële kabel geïnstalleerd hebt. Kijk nu naar het moederbord van Toon. Je ziet hier een 14-polige pinheader zitten. Linksboven staat een markering ‘1’, welke de eerste pin aangeeft. De pinvolgorde van deze header is als volgt:

01  02
03  04
05  06
07  08
09  10
11  12
13  14

De pinnen die we aansluiten zijn 11 (RX), 13 (TX) en 14 (GND). Zorg ervoor dat RX op Toon aangesloten wordt op de TX van je kabel en TX met RX.

Check even via het apparaatbeheer in configuratiescherm welke COM-poort Toon heeft (in mijn geval COM4):

Stroomvoorziening

Toon heeft natuurlijk stroomvoorziening nodig. De adapter die normaal tussen de ketel en thermostaat zit, kun je daar prima voor gebruiken. Sluit de adapter met bijvoorbeeld twee jumperwires aan op het groene blokje op de muurplaat van Toon. Dit blokje kun je er makkelijk uitwippen. Als alles aangesloten is, sluit het groene blokje dan aan op de stroomaansluitingen op het moederbord. Polariteit is niet belangrijk.

Verbinding maken Putty

Open Putty op en stel een seriële verbinding in op de juiste COM poort met baudrate 115200.

Klik op ‘Open’ om verbinding te maken met Toon en druk nog even op de resetknop van Toon om opnieuw op te starten. In Putty wordt nu uitvoer getoond:

Belangrijk is nu dat je zoekt naar de term ‘U-Boot’ met er achter een versienummer, in mijn geval ‘U-Boot 2010.09-R8’. Afhankelijk van het versienummer kopieer je een wachtwoord naar het klembord:

U-Boot 2010.09-R6 = f4E9J
U-Boot 2010.09-R8 = 3BHf2

Kopieer het juiste wachtwoord naar het klembord en herstart de Toon met de reset-knop. Zorg ervoor dat zodra de tekst ‘Enter password – autoboot in 2 sec…’ verschijnt, je op de rechter muisknop drukt. De rechtermuisknop is in Putty een snelkoppeling voor plakken, gevolgd door de entertoets.

Voila! We zijn binnen.

U-Boot aanpassen

Om straks in te kunnen loggen op Toon met root-toegang moet U-Boot aangepast worden met een commando zodat de shell geladen wordt. Neem dit letterlijk over. Een typefout kan ervoor zorgen dat Toon niet langer werkt.

setenv addmisc setenv bootargs \$\{bootargs\} mem=\$\{mem\} lpj=999424 init=/bin/sh

Zorg ervoor dat Toon verder geladen wordt met het volgende commando:

run boot_nand

Het kan zijn dat je aan het einde van het laden een foutmelding krijgt, echter is dit geen probleem.

Seriële console toevoegen

Open het bestand ‘/etc/inittab’ met een editor en zorg ervoor dat het je aan het blok ‘# HCBv2 static stuff’ de volgende regel toevoegt:

gett:235:respawn:/sbin/getty -L 115200 ttymxc0 vt102

Zorg ervoor dat je van de regel, beginnend met ‘ovpn:’ commentaar maakt door er een ‘#’ (hekje / hashtag) voor te zetten. Hiermee zorg je ervoor dat Toon niet langer data kan uploaden naar het datacenter.

Sla het bestand op en sluit de editor.

Root toegang instellen

Open bestand ‘/etc/passwd’ met een editor en haal het woord ‘DISABLED’ in de regel beginnend met ‘root’ weg, zodat het er als volgt uitziet:

root::0:0:root:/root:/bin/sh

BusyBox installeren

In nieuwere versies van de Toon firmware is Getty verwijderd om toegang via de seriële poort te bemoeilijken. Hiervoor installeren we een nieuwe versie van BusyBox. Download BusyBox en open deze in bijvoorbeeld Notepad++. Maak het bestand busybox.ipk.b64 aan:

cat > busybox.ipk.b64

Plak de inhoud van het bestand naar Putty en sluit af met CTRL+D. Zet het bestand vervolgens om naar een ipk-bestand en installeer het:

base64 -d busybox.ipk.b64 > busybox.ipk
opkg install busybox.ipk

Herstart Toon via de knop aan de onderzijde. Je ziet in Putty dat Toon opstart. Zodra de meeste drukte voorbij is, typ je:

root
passwd

Er wordt nu gevraagd naar een nieuw wachtwoord voor gebruiker root. Vul tweemaal een sterk wachtwoord in en onthoud deze.

Dropbear installeren

Download dropbear.ipk.b64 en open het in een editor, bijvoorbeeld Notepad++. Voer het volgende commando in, in de shell:

cat > dropbear.ipk.b64

Kopieer nu de inhoud van het bestand en plak deze met de rechtermuisknop in Putty, waarna je het bestand afsluit met CTRL+D. Converteer het bestand naar een ipk-bestand met het volgende command:

base64 -d dropbear.ipk.b64 > dropbear.ipk

Installeer met:

opkg install dropbear.ipk

Open bestand ‘/etc/default/iptables.conf’ in een editor. Zorg dat de volgende vier regels aanwezig zijn en niet als commentaar staan (vooraf gegaan met een hekje/hashtag):

-A HCB-INPUT -p tcp -m tcp --dport 22 --tcp-flags SYN,RST,ACK SYN -j ACCEPT
-A HCB-INPUT -p tcp -m tcp --dport 7080 --tcp-flags SYN,RST,ACK SYN -j ACCEPT
-A HCB-INPUT -p tcp -m tcp --dport 10080 --tcp-flags SYN,RST,ACK SYN -j ACCEPT
-A HCB-INPUT -p tcp -m tcp --dport 80 --tcp-flags SYN,RST,ACK SYN -j ACCEPT

Sla het bestand op en herstart de firewall:

/etc/init.d/iptables restart

Herstart Toon met het volgende commando:

reboot

Deze keer duurt het opstarten iets langer omdat er een SSH sleutel gegenereerd moet worden. Wacht tot je de volgende tekst ziet verschijnen:

Eneco Toon by Quby

eneco-001-XXXXXX login:

Log in met ‘root’ en het eerder opgegeven wachtwoord. Vraag daarna de netwerkinstellingen op:

ifconfig

Deze geeft de volgende uitvoer:

Kijk naar het IP-adres van interface ‘wlan0’, in mijn geval ‘192.168.0.58’. Open een nieuw venster in Putty en maak verbinding met een SSH server op het opgegeven adres, poort 22:

Klik op ‘Open’. Het kan zijn dat je de vraag krijgt of je de sleutel van de server wilt vertrouwen. Klik in dat geval op ‘Yes’. Als je nu kunt inloggen, dan is alles goed verlopen!

Je kunt Toon weer in elkaar zetten. Let goed op dat de antennes juist worden aangesloten.

Toon bijwerken

Om Toon bij te werken kan er een gemakkelijk script uitgevoerd worden. Download het script. Maak het bestand ‘update_rooted.sh’ aan:

cat > update_rooted.sh

Plak de inhoud van het bestand en sla op met CTRL+D. Maak het bestand vervolgens uitvoerbaar en voer het uit.

chmod +x update_rooted.sh
./update_rooted.sh

Volg de stappen waarna Toon geüpdatet wordt. Je kunt dit script ook gebruiken om enkel bijvoorbeeld de ‘Geen verbinding met Service-Center’ waarschuwing te verbergen. Gebruik hiervoor:

./update_rooted.sh -f

Timeserver aanpassen 

Om Toon niet langer periodiek met een server van Quby (de fabrikant) contact te laten maken, zorgen we er ook voor dat de timeserver aangepast wordt. Open ‘/etc/chrony.conf’ en verander de volgende twee regels:

server time.quby.nl minpoll 8
initstepslew 30 time.quby.nl

in:

server www.nist.gov minpoll 8
initstepslew 30 www.nist.gov

Alle aanvragen naar Quby.nl blokkeren

Om Toon helemaal geen verbinding meer met quby.nl te laten maken voeg je de volgende regel toe aan ‘/etc/hosts’:

127.0.0.1 quby.nl

Als je nu quby.nl pingt, dan krijg je de volgende output:

eneco-001-095873:/# ping -c 4 quby.nl
PING quby.nl (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: seq=0 ttl=64 time=1.051 ms
64 bytes from 127.0.0.1: seq=1 ttl=64 time=0.713 ms
64 bytes from 127.0.0.1: seq=2 ttl=64 time=0.596 ms
64 bytes from 127.0.0.1: seq=3 ttl=64 time=0.595 ms

--- quby.nl ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.595/0.738/1.051 ms

Aanpassingen gebruikersinterface

In bestand ‘/mnt/data/qmf/config/config_happ_scsync.xml’ kan de gebruikersinterface aangepast worden door middel van volgende tags (bron: domoticaforum.eu).

<internalAddress>agreementDetail</internalAddress>

<visibility>0</visibility>
"0" or "1"

<StartDate>1234567890</StartDate>
Linux timestamp, timestamp van eerste activatie.

<EndDate>-1</EndDate>
Linux timestamp, een vervallen timestamp zal het volledige menu blokkeren. Waarde '-1' geeft oneindige activatietijd

<ProductVariant>Toon</ProductVariant>
"Standalone" of "Toon", waarde "Standalone" blokkeert het volledige menu. Waarden "Toon" en een geldige EndDate activeren het volledige menu.

<activated>1</activated>
"0" of "1", activatiestatus van Toon

<wizardDone>1</wizardDone>
"0" of "1", waarde "0" toont de initiële setup-stappen na een herstart. Aan te raden bij verkoop van toon

<SortwareUpdates>1</SortwareUpdates>
"0" of "1", waarde "1" activeert automatische updates.

<BoilerManagement activated="0">0</BoilerManagement>
"0" of "1"

<ElectricityDisplay>1</ElectricityDisplay>
"0" of "1", toont verbruik en grafieken van electriciteitsverbruik.

<GasDisplay>1</GasDisplay>
"0" of "1", toont vernbruik en grafieken van gasverbruik.

<HeatDisplay>0</HeatDisplay>
"0" of "1"

<SolarDisplay>1</SolarDisplay>
"0" of "1", waarde "1" activeert ZonOpToon widgets.

<SolarActivated>1</SolarActivated>
"0" of "1", waarde "1"activeert ZonOpToon opties.

<OtherProviderElec>0</OtherProviderElec>
"0" of "1"

<OtherProviderGas>0</OtherProviderGas>
"0" of "1"

<SME>0</SME>
"0" of "1"

<HeatWinner>0</HeatWinner>
"0" of "1"

<ContentApps>1</ContentApps>
"0" of "1"

<TelmiEnabled>0</TelmiEnabled>
"0" of "1"

<mobileAccess>0</mobileAccess>
"0" of "1", waarde "1" activeert de "Toon op Afstand" app, maar kan alleen gebruikt worden bij een actief Toon abonnement.

<supportEnabled>0</supportEnabled>
"0" of "1", waarde "1" activeert de mogelijkheid tot ondersteuning op afstand via de Eneco/Toon helpdesk.

<supportEnabledStart>0</supportEnabledStart>
"0" of "1", waarde "1" activeert hulp op afstand via de Eneco/Toon helpdesk.

<researchEnabled>0</researchEnabled>
"0" of "1"

<doSolarWhatsnew>0</doSolarWhatsnew>
"0" of "1", waarde "1" activeert "Wat is Zon op Toon" welkomstscherm.

<latestWhatsnewVersion>qt-gui - 1.6755-ene-release-ene-3.7</latestWhatsnewVersion>

Firmwareversie na afronden van de initiële setup.

<statusUsageFirstUse>0</statusUsageFirstUse>
"0" of "1"

<scStatusFlags>0</scStatusFlags>
"0" of "1"
<commissionState>0</commissionState>
"0" of "1"

F

Afwezigheidsdetectie

Elk jaar krijg je van je energieleverancier, in mijn geval Eneco, de afrekening. Ondanks dat ik elk jaar mijn termijnbedrag verhoog, hou ik steeds minder over. Het zet aan tot nadenken: waar kan je op besparen.

Ik heb twee koters, nu 7 en 9 jaar, die altijd vergeten verlichting, televisies en spelcomputers uit te zetten. Met Philips Hue en bewegingssensoren in hallen, toilet en badkamer heb ik een deel van de verlichting reeds kunnen tackelen. Toch wil ik verder gaan.

Het idee: Afwezigheidsdetectie

Iedereen zit tegenwoordig vergroeid met zijn/haar mobiele telefoon. De bedoeling is op mijn Linux Server een script periodiek draait dat kijkt of tenminste één van onze mobiele telefoons op het WiFi-netwerk actief is. Niemand actief? Dan moet alle Philips Hue verlichting uitgeschakeld worden (behalve lampen op timers, zoals nachtverlichting in de tuin) en de Toon Thermostaat op programma ‘Weg’.

Philips Hue

Philips Hue kan door middel van JSON-opdrachten naar de bridge verlichting schakelen en hier verwacht ik dan ook geen problemen.

Eneco Toon

De slimme thermostaat, Toon, van Eneco, is een lastiger verhaal. Standaard werkt toon met een maandelijks abonnement van enkele euro’s. Hiervoor krijg je terug dat je op afstand je thermostaat kan schakelen en je verbruik inzichtelijk wordt. Helaas snijdt Eneco zichzelf wel in de vingers: tegenwoordig krijg je maandelijks een energierapport per mail met tussenstanden. De voor mij aantrekkelijkste functie van toon is hiermee dan ook verdwenen. Tegenwoordig staan er how-to’s op internet, hoe Toon geroot kan worden, waarmee het abonnement niet langer nodig is en je ook via het aanroepen van een specifieke URL data kan opvragen en je thermostaat kan instellen.

Het serverscript

Het script moet een simpel bash-script worden dat via cron elke 5 minuten draait. Onderstaand script is een eerste versie, waarin afwezigheidsdetectie werkt. Het script haalt de ARP-tabel op en kijkt checkt of tenminste een van de opgegeven apparaten (opgegeven als MAC-adres in de array ‘deviceArray’) voorkomt.

Belangrijk te weten dat de update-interval van de ARP-tabel in te stellen is. Op mijn server staat deze op 60 seconden, echter leert de ervaring dat het opmerken van een uitgeschakeld apparaat veelal langer (meestal 2-3 minuten) duurt.

Het script op dit moment:


#!/bin/bash
# Script door Dennis Bor

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

# 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..."

# Tenminste een apparaat online?
else

        # ja

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

fi

Volgende halte: Toon-rooten!

Update 08.01.2020

Ondanks weinig online updates, heb ik de afgelopen tijd toch flink wat vooruitgang geboekt. De volgende zaken werken nu:

  • Pond[CTRL] versie 3.3 werkt zoals verwacht met aansluitingen voor 2 pH- en 2 temperatuursensoren en een vlotterschakelaar en vijf schakelbare stekkers. Stekkers zijn schakelbaar afhankelijk van meetwaarden of instellingen en voor elke sensor of stekker kunnen alarmparameters ingesteld worden. Alle aansluitingen zijn getest en werken. pH-sensoren kunnen gekalibreerd worden. Voor de temperatuursensoren kan een correctie (zowel positief als negatief) opgegeven worden.
  • De besturingssoftware ondersteunt communicatie met de ESP8266 WiFi-module via I2C. De ESP8266 is master en stuurt 17 bytes (byte 1 = commando, byte 2-17 data) waarop Pond[CTRL] reageert met een antwoord van 16 bytes.
  • De WiFi-module werkt als access-point indien er niet verbonden kan worden met het opgegeven netwerk. Anders werkt deze als draadloze client.
  • De WiFi-module heeft een beperkte webinterface en ondersteunt http-requests als voorbereiding op een eventuele app.
  • De WiFi-module heeft een mDNS-responder voor gemakkelijke toegang. Deze is werkend getest op Windows (met Bonjour), iOS en macOS. Android lijkt (nog) niet te werken.
Meetwaarden van de sensoren tijdens de testfase. Dit is uiteraard niet de definitieve gebruikersinterface.