This how-to will explain how to switch a virtual switch on and off based on a device (smartphone) being connected to wifi or not.
Other methods may use pinging your device, for me it just does not feel right to “nag” a mobile device with ping command. So this method checks via SNMP the DHCP table of a pfSense (4.2) router.
The information below has been lend from https://www.domoticz.com/wiki/Presence_detection_(SNMP)
Configure your router to accept SNMP
This script uses snmpwalk to get connected clients from your router. Your router needs to accept snmp v3 requests and you’ll probably need to create a SNMP configuration in your router for this. Please check the manual of your router how to configure SNMP.
The Python scripts needs python to be installed. You can install this by running:
sudo apt-get install python
You’ll also need to install snmpwalk on your Raspberry Pi. You can install this by running:
sudo apt-get install snmpd
sudo apt-get install snmp
Find the correct iso OIB
After snmpwalk is installed, walk through all snmp object id’s by running:
snmpwalk -v3 -c public <router ip> -u <username>
Search for the OIB that contains the current connected devices.
iso.126.96.36.199.188.8.131.52.2.1 PfSense OIB
iso.184.108.40.206.220.127.116.11.1.1 D-Link managed switches OIB
Create virtual hardware dummy and virtual switch(es)
This script needs a virtual switch for each device you want to poll.
Create virtual dummy hardware
- To create a virtual switch, you first have to create a virtual hardware device.
Go to Setup > Hardware. Type in a name (I use ‘Dummy’), choose ‘Dummy (Does nothing, use for virtual switches only)’ from the drop-down. Leave ‘Data timeout’ disabled. Click on ‘Add’
Create virtual switch(es)
To create a virtual switch, go to the ‘Switches’ tab. Click the ‘Manual Light/Switch’ button in the upper left corner. Choose the dummy hardware from the drop-down. Enter a name (‘John Doe smartphone’). Switch type ‘On/off’. Type ‘X10’. For ‘House code’ and ‘Unit code’, choose some random, doesn’t really matter. As ‘Main Device’.
It is advisable to make the switch ‘protected’, so the switch can only be operated by the detection script and not by you, by accidentally clicking on it 😉
Now you need to know the ID of the switch. This can be found by going to ‘Setup’ –> ‘Devices’. Find the switch you created in the list, and remember the IDX.
If you want multiple devices to be checked (smartphone of your wife) you will need to create a virtual switch for that device also. You will also need to find the id of that device.
Remember/write down this number, because you will need it later on.
Create the script
Create a new file in the \home\pi\domoticz\scripts\ folder. Name the file ‘check_device_snmp.py’. Copy the contents from the script on this page to the file, change the variables to your needs and save it.
Make script executable
In the terminal, go to cd /home/pi/domoticz/scripts
Execute the command chmod +x check_device_snmp.py
#!/usr/bin/python # Title: check_device_snmp.py # Original author: Chopper_Rob # Modified by: EBOOZ # Date: 14-01-2016 # Info: Checks the presence of the given device on the network by checking your router by SNMP and reports back to domoticz # Version : 1.0 import sys import datetime import time import os import subprocess import urllib2 import json import base64 # Settings for Domoticz and snmpwalk domoticzserver="192.168.0.2:8080" domoticzusername = "" domoticzpassword = "" router = "192.168.0.1" username = "admin" community = "public" isocode = "iso.18.104.22.168.22.214.171.124.2.12.1" # If enabled. The script will log to the file _.log # Logging to file only happens after the check for other instances, before that it only prints to screen. log_to_file = False # The script supports two types to check if another instance of the script is running. # One will use the ps command, but this does not work on all machine (Synology has problems) # The other option is to create a pid file named _.pid. The script will update the timestamp # every interval. If a new instance of the script spawns it will check the age of the pid file. # If the file doesn't exist or it is older then 3 * Interval it will keep running, otherwise is stops. # Please chose the option you want to use "ps" or "pid", if this option is kept empty it will not check and just run. check_for_instances = "pid" # DO NOT CHANGE BEYOND THIS LINE if len(sys.argv) != 5 : print ("Not enough parameters. Needs %Host %Switchid %Interval %Cooldownperiod.") sys.exit(0) device=sys.argv switchid=sys.argv interval=sys.argv cooldownperiod=sys.argv previousstate=-1 lastsuccess=datetime.datetime.now() lastreported=-1 base64string = base64.encodestring('%s:%s' % (domoticzusername, domoticzpassword)).replace('\n', '') domoticzurl = 'http://'+domoticzserver+'/json.htm?type=devices&filter=all&used=true&order=Name' if check_for_instances.lower() == "pid": pidfile = sys.argv + '_' + sys.argv + '.pid' if os.path.isfile( pidfile ): print datetime.datetime.now().strftime("%H:%M:%S") + "- pid file exists" if (time.time() - os.path.getmtime(pidfile)) < (float(interval) * 3): print datetime.datetime.now().strftime("%H:%M:%S") + "- script seems to be still running, exiting" print datetime.datetime.now().strftime("%H:%M:%S") + "- If this is not correct, please delete file " + pidfile sys.exit(0) else: print datetime.datetime.now().strftime("%H:%M:%S") + "- Seems to be an old file, ignoring." else: open(pidfile, 'w').close() if check_for_instances.lower() == "ps": if int(subprocess.check_output('ps x | grep \'' + sys.argv + ' ' + sys.argv + '\' | grep -cv grep', shell=True)) > 2 : print (datetime.datetime.now().strftime("%H:%M:%S") + "- script already running. exiting.") sys.exit(0) def log(message): print message if log_to_file == True: logfile = open(sys.argv + '_' + sys.argv + '.log', "a") logfile.write(message + "\n") logfile.close() def domoticzstatus (): json_object = json.loads(domoticzrequest(domoticzurl)) status = 0 switchfound = False if json_object["status"] == "OK": for i, v in enumerate(json_object["result"]): if json_object["result"][i]["idx"] == switchid and "Lighting" in json_object["result"][i]["Type"] : switchfound = True if json_object["result"][i]["Status"] == "On": status = 1 if json_object["result"][i]["Status"] == "Off": status = 0 if switchfound == False: print (datetime.datetime.now().strftime("%H:%M:%S") + "- Error. Could not find switch idx in Domoticz response. Defaulting to switch off.") return status def domoticzrequest (url): request = urllib2.Request(url) request.add_header("Authorization", "Basic %s" % base64string) response = urllib2.urlopen(request) return response.read() log (datetime.datetime.now().strftime("%H:%M:%S") + "- script started.") lastreported = domoticzstatus() if lastreported == 1 : log (datetime.datetime.now().strftime("%H:%M:%S") + "- according to Domoticz, " + device + " is online") if lastreported == 0 : log (datetime.datetime.now().strftime("%H:%M:%S") + "- according to Domoticz, " + device + " is offline") while 1==1: currentstate = subprocess.call('snmpwalk -v3 -c ' + community + ' ' + router + ' -u ' + username + ' ' + isocode + ' | grep -o "' + device + '" > /dev/null', shell=True) if currentstate == 0 : lastsuccess=datetime.datetime.now() if currentstate == 0 and currentstate != previousstate and lastreported == 1 : log (datetime.datetime.now().strftime("%H:%M:%S") + "- " + device + " online, no need to tell Domoticz") if currentstate == 0 and currentstate != previousstate and lastreported != 1 : if domoticzstatus() == 0 : log (datetime.datetime.now().strftime("%H:%M:%S") + "- " + device + " online, tell Domoticz it's back") domoticzrequest("http://" + domoticzserver + "/json.htm?type=command¶m=switchlight&idx=" + switchid + "&switchcmd=On&level=0") else: log (datetime.datetime.now().strftime("%H:%M:%S") + "- " + device + " online, but Domoticz already knew") lastreported=1 if currentstate == 1 and currentstate != previousstate : log (datetime.datetime.now().strftime("%H:%M:%S") + "- " + device + " offline, waiting for it to come back") if currentstate == 1 and (datetime.datetime.now()-lastsuccess).total_seconds() > float(cooldownperiod) and lastreported != 0 : if domoticzstatus() == 1 : log (datetime.datetime.now().strftime("%H:%M:%S") + "- " + device + " offline, tell Domoticz it's gone") domoticzrequest("http://" + domoticzserver + "/json.htm?type=command¶m=switchlight&idx=" + switchid + "&switchcmd=Off&level=0") else: log (datetime.datetime.now().strftime("%H:%M:%S") + "- " + device + " offline, but Domoticz already knew") lastreported=0 time.sleep (float(interval)) previousstate=currentstate if check_for_instances.lower() == "pid": open(pidfile, 'w').close()
Let the script run at certain times (cron)
To let the script work correctly, it needs to be run automatically at certain times. This can be done by using ‘cron’, the task scheduler on Linux.
On the terminal, run crontab -e. In the screen that opens, add a line for each device you want to check.
*/10 * * * * sudo python /home/pi/domoticz/scripts/check_device_snmp.py "00 50 56 9C 3D 58" 37 10 120
NOTE: The way how you enter the MAC-address depends on the output of your router (with spaces, with dashes (-) or with colons (:)
The script needs 4 parameters to run:
- The MAC-address of the device you want to check
- The ID of the virtual switch (In my case ’37’)
- The interval (seconds) on which to check if a device is present or not (In my case ’10’, for 10 seconds)
- The ‘cool-down’ period (seconds). If a device does not respond within this period, the virtual switch is turned off. (In my case ‘120’, for 120 seconds, or 2 minutes)
Exit the crontab editor by pressing CTRL-O (character ‘O’, not zero) and hitting Enter.
It can happen that the script is not working in your situation.
Try checking manually
First thing to try is, if you can check for your phone by using snmpwalk manually (
snmpwalk -v3 -c public <router ip> -u <username>).
Check if the script works
Now we need to check if the script works when we activate it by hand. Go to the scripts directory (
cd /home/pi/domoticz/scripts/) and then activate the script with the correct parameters:
python check_device_snmp.py MAC_ADDRESS IDX INTERVAL TIMEOUT. For testing you can use a interval of 10 seconds and a timeout of 30 seconds, so you don’t have to wait very long to see the switch turn on/off in Domoticz.
If everything works good, you should see some output like:
Script already running, exiting or
Needs more parameters. If you get other errors (unclear errors, that doesn’t seem to be related to the script), maybe you don’t have ‘python’ (not ‘python3’ !!) installed correctly (
sudo apt-get install python), or you made a mistake when you copied the script.
Check if crontab is correct
When the script is not yet activated by the crontab (in the setup above it should activate the script every 10 minutes) and you activate it by hand, you should see some output on the screen, like:
Device online, tell domoticz it's back. You now know the script is working correctly, so the error is probably in your crontab.
Check if ‘cron’ service is running
You can check if ‘cron’ is active by issueing
sudo service cron status.
If it is running, it should say
[ ok ] cron is running.. If it is not running, you can start it by
sudo service cron start.
To view the log of cron, you can issue
grep CRON /var/log/syslog. You should see some lines of text if cron is active.
To enable ‘cron’ on Raspbmc, have a look this tutorial: http://www.averagemanvsraspberrypi.com/2014/07/using-cron-with-raspbmc.html