Domoticz: Bash script for presence detection using NMAP

Today I programmed another part of the system I envision for my new house. It’s a bash shell script which checks if either my phone or that of my girlfriend is in range and depending on if it is or not it changes a switch in Domoticz. That way I can automate all the lights turning off when we are not home, and coming back on when we are!

Domoticz

Detecting the presence of the phones

I looked at several ways of doing this, from a simple ping script to a arp-scan, etc. 

 
Ping is not reliable enough because smartphones use aggressive sleep modes to try and conserve power when they can. Arp-scan was not usable for me since I use a routed wireless network and it can’t travel past my gateway.


NMAP

I searched a bit online and found that nmap has a “no port scan” presence detection ability and tried it out. With all of my Android devices and in my routed setup it has worked great. Even when a device does not respond to ping anymore it does still respond to this request and for the last few hours I have been testing with it, it has worked flawlessly.

Ping example:

root@domoticz:/home/quindor# ping -w 10s 10.24.1.10
PING 10.24.1.10 (10.24.1.10) 56(84) bytes of data.

--- 10.24.1.10 ping statistics ---
10 packets transmitted, 0 received, 100% packet loss, time 9072ms

root@domoticz:/home/quindor#

Nmap example:

root@domoticz:/home/quindor# nmap -sn 10.24.1.10

Starting Nmap 6.40 ( http://nmap.org ) at 2015-02-01 01:50 CET
Nmap scan report for 10.24.1.10
Host is up (0.43s latency).
Nmap done: 1 IP address (1 host up) scanned in 1.46 seconds
root@domoticz:/home/quindor#


As you can see, the ping response hangs (set maximum wait time to ~10s) but the nmap responds that same host is up. If I try to namp the same address but turn WiFi off on my phone it correctly mentions that the host is not up. There is no delay in this such as decaying arp records, etc.


So this seems to work for all my Android devices, I can’t say if Apple or anything else will work, but it’s worth a shot I would say!


The need for static IP addresses

Using any kind of presence detection (except ARP) will require your devices having static IP addresses. 
 
In my setup I have static reservations in my DHCP server (Running on a Mikrotik Routerboard) so that the phones always get the same IP address. This way we can always verify if the device is in the house and connected to the WiFi network, or not.


Script for using with Domoticz

In Domoticz you need to create a Dummy switch which we are going to switch using a script. The device will turn on when someone is at home and turn off when everyone leaves home. This way we can verify if someone is present at home, or not.
 
A quick description of how to do that is by going to Setup –> Hardware and use or add a “Dummy” device. Once you have created that, click “Create Virtual Sensors” and create a switch.
 
virtualsensor
Virtual Sensor
This switch will be controlled using a script. At first I looked at using a LUA script, as I do with my LED dimmer but I had trouble getting this working correctly. So instead I created a bash script which runs in the OS which in my case is an Ubuntu VM running on VMware ESXi 5.5. It controls the switch using HTTP JSON commands.
 

Bash Script

To use this script you will need nmap, curl and grep. You can easily install those on a Linux based system. For Windows you will probably need cygwin or the separate tools and put them in your system path.

–update 2015-02-03

I’ve made the whole script run from variable which can be stated in the beginning of the script. Only the URL is not a variable because 90% of the people will be running the script from the same machine as where Domoticz is running.

I have also implemented a check to see if the switch is already ON or OFF so that it doesn’t keep switching the Domoticz switch causing all kinds of weird things to happen. 😉 That also means that if you change something, those changes will not get overwritten except by the OFF or ON command when it occurs.


I also did a little bit of cleanup work and re-wrote some stuff.


The script I’m currently using is the following:

 

loop=1
sleep=10
switch=40

while [ "$loop" -eq "1" ]
do

echo ""
echo ""
echo ""
echo ""
echo Phone on = 0, Phone off = 1
nmap -sn 10.24.1.10 | grep -q latency
phone1=$?
echo Phone Name1 = $phone1
nmap -sn 10.24.1.11 | grep -q latency
phone2=$?
echo Phone Name2 = $phone2
total=$(($phone1 + $phone2))
echo Phones Total command = $total

if [ "$total" -gt "1" ];
 then
  echo Detected 0 phones, Checking status switch $switch;
  curl -s "http://127.0.0.1:8080/json.htm?type=devices&rid="$switch"" | grep '"Status" : "Off",'
  switchon=$?
   if [ "$switchon" -eq "0" ];
    then
     echo Switch $switch is already OFF, skipping sending command to Domoticz;
    else
     echo Switch $switch was ON, turning switch OFF; curl "http://127.0.0.1:8080/json.htm?type=command&param=switchlight&idx="$switch"&switchcmd=Off";
   fi
 else
  echo Detected 1 or more phones, Checking status switch $switch;
  curl -s "http://127.0.0.1:8080/json.htm?type=devices&rid="$switch"" | grep '"Status" : "On",'
  switchon=$?
   if [ "$switchon" -eq "0" ];
    then
     echo Switch $switch is already ON, skipping sending command to Domoticz;
    else
     echo Switch $switch was OFF, turning switch ON; curl "http://127.0.0.1:8080/json.htm?type=command&param=switchlight&idx="$switch"&switchcmd=On";
   fi
fi
echo ""
echo ""
echo ""
echo ""
echo Execution done, sleeping for $sleep seconds
sleep $sleep
done


What the script does is create a loop (sleep time at the end). This loop runs the NMAP command with a grep. If it’s succesful (grep finds the word “latency”) it returns with exit code 0. I check for this exit code and copy it to an environment variable. I do this also with the second phone.


After this I have two environment variable which are either 0 or 1 (or higher). I add these up to form another variable. Then I use a statement, if this variable is higher then 1, turn off the switch (no one is home). If it is 1 or lower, the switch needs to be on.


Be sure to change the IP address to where you are hosting Domoticz (probably localhost like mine) and to lookup your switch Device Index number and change it in the code. It’s the “idx=40″ part.


This will need to be changed reflecting the amount of devices you are checking.


Currently the sleep is 10 seconds. This is quite short, but I have not yet noticed any negative effects because of it. Domoticz checks it’s blocky scripts every 30 seconds, so the maximum time it can take to have an effect is ~39 seconds!


After this you just need to use a way of running the script. For now I’m starting it using a screen session after my machine boots. But you can also start it automatically during startup, or start it periodically using a cron job (remove the sleep line).


You can use this Switch in a LUA script or in a Blocky script inside of Domoticz. This post will not go into that right now, I might do another post about how to create that another time!


And that’s it! The switch should now get automatically updated in Domoticz.


Any questions or remarks, please let me know. If you are using it, drop me a line, always nice to know!

14 thoughts on “Domoticz: Bash script for presence detection using NMAP”

  1. Euhm, no.

    When you configure your network, if you do it correctly in your DHCP server your own devices will never change from IP, configure static leases for them.

    Also, I use a routed network and my subnet for Wireless clients is different then cabled and thus using ARP (MAC) to do this is impossible.

  2. Great how-to, actually all the stuff i find here is awesome, kudos for that! Think i found your hideout 😉
    I find many similarities with my own setup and (bit off topic here) have started playing with esp8266's to accomplish similar things.
    O.t: i also written a similar script like this to accomplish the same thing. Imho there is no need to use nmap; you can set the maximum wait time on ping to about 1 second (-w1), that should suffice. Also you can tell ping only to try once (-c1). I have been using it for a long time and i never had issues with it.

    My script is a lot shorter and think it does keep in mind a few things; when present it doesn't rapidly ping the phone, when a ping is missed it does and after about 5 minutes of consecutive missed pings the script flips a switch, but then just for 1 person. Also it only flips the switch if needed.
    Another virtual "master" switch keeps track if there is anybody at home at all. A lua script checks for devicechanges for the individual presence switches and makes a change if needed.
    I actually do no use a static IP or static lease construction, i just ping the hostname and it works fine.
    I am happy to share my code with you but this comment field isn't a good spot for that.

  3. Thank you for your awesome comment about my blog posts, when I figure something out, I try to share. 🙂

    For my, using nmap is a necessity because without it, I am unable to detect my phones while they are in sleep mode. I can wait 60 seconds on a ping, it will never come, but nmap finds it without fail. I understand technically there should not be much of a difference but in reality one works, the other doesn't!

    My script is kind of spartan, I know. 😉 I'm not a programmer and while I could probably shorten it and make it more efficient, the way it is setup right now is still readable for my self. If not, I would have to re-find-out how everything worked again every time I would want to make a change.

    I would like to make some additions to it though, right now it turns off the switch when one "ping" fails, that should be raised to 10 for instance. And maybe I'll split it up per device at some point, but for now, I never see that need really. Hostnames can work, but I don't want to keep doing a DNS lookup when I know the IP anyway (I have a static IP for known devices for multiple reasons).

    If you would share your code that would be awesome, maybe I could incorporate some stuff from it in my own scripts, always good to learn! 😀 You can mail it to quindor@gmail.com if you are willing! 🙂

  4. Blogs like these inspire me to start fiddling around with this stuff myself. I can't contribute yet with anything usefull, I just wanted to thank you for the time and effort of sharing and hope you'll keep doing so in the future.

    When do you reckon your first revision pcb for the LED dimmer will be finished?

  5. I was particularly interested to see if your script would detect iphones better, but apparently it does not: once an iphone does not respond to a ping anymore, it also does not respond to an nmap call. I will keep looking further to any better iphone presence detection over wifi than ping

    I like your blog by the way.

  6. Hi Daan. could you share with use how did you accomplished the same thing with the ESP8266? I am trying to do the same thing, but I cannot find nothing related, only occupancy based on a PIR…. which is totally diferent

  7. Hi Bogdan, the eps8266 stuff was off topic and nog relevant to the presence detection stuff, it was only for Quindor to get a picture of how interesting this site is to me.
    However, i'm sure if you google on eps8266 and nodemcu (+ping?) you will be able to find some on how to ping hosts with an esp8266, if that's what you try to accomplish, because i am not sure what you are asking here…anyways with the nodemcu firmware you can surely script some lua to get there. Hope it helps.

  8. Hi there again, it's been a while. With my new Galaxy S6 i kinda have the problem you have, i guess it has better power management 😉

    I had to up the number of needed missed pings, but i like to see how your approach works for the new phone. Currently the new phone is missing pings when it's asleep so when i go away it's kinda random how long it takes before the script decides it hasn't responded long enough to "flip da switch".
    Btw i am missing a 'shebang!' at the top of the code, haven't you had folks reporting the script doesn't work?

    So thanks for the nmap trick, i will have a look at how that works on the new phone, i'll share my findings.
    Btw off topic again on the esp stuff: have you had a look at https://github.com/esp8266/Arduino ? it allows you to write code for the esp like it's an arduino! I have been playing with it and it's awesome! There is also an OTA functionality, but that doesnt work stable yet….however when it does, how awesome would it be to create all kinds of dimmers and switches and update the 'firmware' without taking out the esp?

  9. I tried your script with a few different devices, and some are detected, but some aren’t.
    My phone isn’t detected all times, so, it basically works the same way of the standard ping hardware from domoticz.
    It’s nice, but not 100% reliable.

    1. Yeah, it’s very dependent on the device, it’s firmware but also the access-point, your router and their firmwares. When I have time I plan to investigate more but it works for some and doesn’t for others. 🙁

  10. Since this kind of script doesn’t works with every device, because sleep modes are different, I came to another simple solution:

    On android, I installed a task automation app (Automate, from llamalabs, but works with Tasker or any other, as long it can send json/http requests).
    I created a Domoticz dummy switch, and created a simple flow inside Automate app. It checks if i’m connect to my home wifi, and send a request through internet network IP, to switch on the dummy device. If I disconnect from wifi and activate Mobile data, then the flow assumes I left home and send a request to my DDNS address (since now I’m acessing my server from wan side), to switch off the dummy device.
    Make the flow a loop, and you’re good to go. It’s not dependent on hardware brand, and doesnt care about sleep and whatever.

    The only inconvenient is: You have to install a separated app and configure the script/flow on each device you want to detect.

  11. Hi,

    Thanks for the script. Works OK for me. One comment for cron job. I did not remove sleep line. I use “@reboot” timing and that is all.

Leave a Reply

Your email address will not be published. Required fields are marked *