DD-WRT Presence Detection with ARP for openHAB

You could try something like a Wireless Sensor Tag Pro which has a memory and will sync its data when reconnected to the wifi network. You might be able to use the http binding to detect it’s data string and determine presence, but you’d have to have the tag poll data every 30seconds or you might be home for however long your polling period is before it recognises your presence.

1 Like

Thank you @jheide44 for your post. :slight_smile:

I use the script now to detect devices on two DD-WRT devices. But I had to modify the script as it was not working for me right away. The main difference here is that I kicked out user and password (as this cannot be set in openHAB 2.0/2.1 at the moment) and also I changed the command which gets the MAC addresses for all online devices arp -i br0 did not work for my Atheros based DD-WRT devices and also original Atheros command from Aaron did only show some devices as already mentioned. But wl_atheros assoclist gets all online devices from all ath wireless interfaces, which is exactly what I wanted here.

You can find the modified script here: https://git.hot-chilli.net/msebald/openhab/src/master/presence/ddwrt.sh

1 Like

You are amazing. Thanks Roi! You fixed it for me as well.

@Roi & @Yellow-Snow

The main difference in commands is based in the Broadcom vs Atheros chip-sets. My original script was on a device with Broadcom chip-set and leveraged the Broadcom commands.

I can’t speak to the user/password or Openhab 2.x… I’m still sporting OpenHAB 1.8.2, I need to upgrade. That said, my “if it isn’t broke, don’t fix it” logic has brought me this far.

Just out of curiosity what devices do you have running DD-WRT? My original script (talking to OpenHAB 1.8.2) is now running on a Netgear R6400.

Glad it is working for you both!

I’m running DD-WRT v3.0-r34411 on TP-Link Archer AC1750 which says "Qualcomm Atheros QCA9558 ver 1 rev 1.0 (0x1130) "

Now i’m digging into the damn iPhone deep sleep mode :frowning: BLAHHHH


Found that tho. hmmm

@jheide44 I am using two TP-Link Archer C7 v2.0 devices (running DD-WRT v3.0-r33525 at the moment, CPU also reports Qualcomm Atheros QCA9558 ver 1 rev 1.0 (0x1130)). What you said is true, the main difference is the chipset thing. It was not hard to find out, but it needed some time and some Google searches to get the right command. Stripping out the user/password thing was easy then. Not nice anyway but everything is done in a local network.

@Yellow-Snow Is your newer DD-WRT build running ok for you? We use the same CPU so upgrading could be interesting for me as well. As I had a lot of bad build for my C7 devices I do not upgrade very often.

@Roi haha funny you asked… my router rebooted all night. Not sure what caused it. The script, my settings, or ddwrt build. Stay tuned.

@Yellow-Snow OMG. :wink: Use the version I mentioned above. It runs stable for me. Including the presence script.

Factory defaults seems like it fixed it. Uptime - 2 days, 20:51
My settings were botched bc I was messing around with WDS to increase my 2.4 range for my cheap Chinese ip cams connected to BI.

Also I dont think im going to use this presence detection. iPhones go into a deep sleep but you can wake it up using

hping3 -2 -c 10 -p 5353 -i u1 xxx.xxx.x.x -q >/dev/null 2>&1 

So far it’s working like a charm, thanks @jheide44 and @Roi for you guys! Great work!
I would like to point out this script can also working great by a padavan router, althrough the wl_atheros by Roi’s can’t work, turns out changing to jheide44’s arp -i br0 can work ^^…

Some questions for the script itself (from a noob-of-view’s dump questions)

  1. delay_occupied=4, delay_unoccupied=2 what does the 4 means(a minute unit)?
  2. limit=120 what does this means?
  3. What if I want to add one more device? Do I need to modify to adding macconnected5 like this one?
    let currentconnected=$macconnected1+$macconnected2+$macconnected3+$macconnected4
  4. How long when the script get executed? All the time or by a period of time? What settings to tweak this time?Here is what the terminal shown after execute the script.
BusyBox v1.24.2 (2018-01-14 12:59:04 CST) multi-call binary.

Usage: sleep [N]...

Pause for a time equal to the total of the args given, where each arg can
have an optional suffix of (s)econds, (m)inutes, (h)ours, or (d)ays
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 0
Server: Jetty(9.3.22.v20171030)

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 0
Server: Jetty(9.3.22.v20171030)

I’m using a few days old build of DD-WRT, and it doesn’t have curl, although arp works fine. Any workaround for me to get the router to update openHAB? My router doesn’t have a USB port, so I don’t think Optware is an option. Right now, I’m just scraping the rotuer’s info page from my server and sending it through MQTT like this (I already have the MQTT broker running for other things):

#!/usr/bin/python
# -*- coding: utf-8 -*-

import paho.mqtt.client as mqtt
import collections
import urllib2
import re
from bs4 import BeautifulSoup
import time

def on_connect(client, userdata, flags, rc):
	# Subscribing in on_connect() means that if we lose the connection and
	# reconnect then subscriptions will be renewed.
	print("Connected with result code "+str(rc))
	client.subscribe("router/unknownusers/status")
	for knownname in knownmacs.values():
		if knownname != 'Ignore' :
			client.subscribe("router/user/" + knownname + "/status")


knownmacs = { 'DE:AD:BE:EF:FA:D1': 'MansPhone', 'DE:AD:BE:EF:FA:D2': 'WomansPhone', 'DE:AD:BE:EF:FA:D3' : 'MansWatch', 'DE:AD:BE:EF:FA:D4' : 'WomansWatch', 'DE:AD:BE:EF:FA:D5': 'Harmony', 'DE:AD:BE:EF:FA:D6': 'PortableSpeaker', 'DE:AD:BE:EF:FA:D7': 'Ignore', 'DE:AD:BE:EF:FA:D8': 'Ignore', 'DE:AD:BE:EF:FA:D9': 'Ignore' }

quote_page = 'http://192.168.1.1/Info.htm'

client = mqtt.Client()
client.on_connect = on_connect
client.username_pw_set(username='somemqttuser', password='somemqttpassword')
mqtthost = "192.168.1.2"
mqttport = 1883
mqttkeepalive = 60

switches = {}
for name in knownmacs.values():
	switches[name] = "NULL"

unknowns = 100
i = 0
while True:
	page = urllib2.urlopen(quote_page)
	soup = BeautifulSoup(page, 'html.parser')
	soupstring = str(soup)

	# Search for MAC addresses
	X = '([a-fA-F0-9]{2}[:|\-]?){6}' # this is the regex
	c = re.compile(X).finditer(soupstring)
	macs = []
	if c:
		for y in c:
			macs.append(soupstring[y.start(): y.end()])

	onlinemacs = [item for item, count in collections.Counter(macs).items() if count > 1]
	unknowncount = 0
	onlinenames = []
	for mac in onlinemacs:
		try:
			if knownmacs[mac] != 'Ignore' :
				onlinenames.append(knownmacs[mac])
		except KeyError :
			unknowncount += 1

	while True:
			try:
				client.connect(mqtthost, mqttport, mqttkeepalive)
				break
			except:
				pass

	for knownname in knownmacs.values():
		if knownname in onlinenames and knownname != 'Ignore' :
			if ( switches[knownname] != 'ON' ) or ( i % 24 == 0 ):
				client.publish(topic="router/user/" + knownname + "/status", payload="ON", qos=0, retain=False)
				print knownname + " ON"
			switches[knownname] = "ON"
		elif  knownname != 'Ignore' :
			if ( switches[knownname] != 'OFF' ) or ( i % 24 == 0 ):
				client.publish(topic="router/user/" + knownname + "/status", payload="OFF", qos=0, retain=False)
				print knownname + " OFF"
			switches[knownname] = "OFF"

	if ( unknowncount != unknowns ) or ( i % 24 == 0 ):
		print str(unknowncount) + " Unknown count"
		client.publish(topic="router/unknownusers/status", payload=unknowncount, qos=0, retain=False)
		unknowns = unknowncount
	time.sleep(5)
	i += 1
end
String Unknownwifi { mqtt="<[broker:router/unknownusers/status:state:default], >[broker:router/unknownusers/status:command:*:default]" }
Switch MansPhone { mqtt="<[broker:router/user/MansPhone/status:state:default], >[broker:router/user/MansPhone/status:command:*:default]" }
Switch WomansPhone { mqtt="<[broker:router/user/WomansPhone/status:state:default], >[broker:router/user/WomansPhone/status:command:*:default]" }
etc...

It works fine, but I was wondering if anyone has a better solution without curl and Optware. I’d rather just have the router send the info directly to openHAB if possible.

Thanks!

Have they removed curl? That’s bad news indeed… I’m using a versions that’s a couple months old and curl woks fine here. Why would they do such a thing?

Using curl or Optwire IS having the router send the message ditectly to OH.

Does wget still exist? You might be able to make something work with that. If not I don’t see how you can do it without installing these tools back on your router.

I’m aware, and that’s what I want, just can’t seem to do it with the things I’m working with. It does have wget, but it’s a really stripped down BusyBox version. The post attributes (–post-data and --post-file), and tons of other things, aren’t there.

It does have things I could send files with, like scp, but I’m not sure if doing anything like that is any better than web scraping.

@DanielMalmgren Maybe it’s just my router? It has pretty small flash by today’s standards (16MB), but I’m sure I’ve had routers with smaller flash in the past that had it. My router is a Dlink DIR859.

There is a not officially supported way to change an items state using GET which might work with the stripped down wget that is available.

You need to install ClassicUI I believe.

That can get you by until another solution presents itself.

1 Like

Awesome! Thanks! Looks like I have something to try, and I’ll report back. I really appreciate all you write on here too. Seems like your posts answer most of my questions outside of the guide.

Edit:
Okay, that works like a charm. Thanks again!

Hi all.
I’d just like to revive this good ol’ thread since it’s still useful. I’ve just set up presence detection on another router using the same concept and it took me an hour, so I’ll add some more useful info that I hopefully will find here next time I change router :sunglasses:

One thing worth mentioning is that arp isn’t really a good idea using for this. A device being connected and a device being in the router’s arp list is not the same thing. Also wl_atheros which has been mentioned in the thread only exists on routers running Atheros (while mine is Broadcom). And only “wl assoclist” only seems to return the devices connected to eth1.

So I ended up fetching the connected devices in my script using the following:

devices=“$(wl -i eth1 assoclist) $(wl -i eth2 assoclist)”

Works like a charm :slight_smile:

1 Like

Daniel,
the old link on the top of this thread is gone. And this thread is very old in deed. I don’t understand what exactly is the best of all mentioned options in this thread, Does this still work in Openhab 2.5 and will it work in OH3 which is gleeming on the horizon?
And could you list the steps you took to get it working as a tutorial? That would help me a lot!
I am trying to signal my son’s lights whenever he is online for an hour. So he has a reminder to do something else for some time.

I’m doing the exact same thing in OH3 as I did in OH2, no difference there. I think most of the stuff you need is hidden somewhere in this thread, but I’ll post how I did for completeness.

Things worth noting about how I did:

  1. My OH isn’t password protected for access via LAN and port 8080. If yours is, you need to add credentials to the end of the URL when calling the API. I think there are examples of that above.
  2. You need to change MAC address and Item name in the script. If you only want to monitor one device you can ignore the information for the other devices in the script. And of course you need to change the IP of the OH installation.
  3. It seems the commands for getting the active devices out of the router is different depending on hardware and dd-wrt version. For example see my last post above. It might be the fact that you need to telnet into the router and play around to find out the exact correct command for you.

Now go to Administration->Commands in the dd-wrt interface, paste the script in the Commands text area and click “Save Startup”. Now this script will be run the next time you reboot your router and (since it contains an eternal loop) then it will check the connected devices every 10 seconds and report them to OH.

Pasting my entire script below (after removing some personal names). Have fun! :sunglasses:

#!/bin/sh
# Proximity detection

# MAC address of each device to watch. Don't leave blank.
macdevice1="66:52:D1:73:C7:EE"    #Comment describing device 1
macdevice2="62:94:22:61:C6:B3"    #Comment describing device 2
macdevice3="D8:0B:9A:BD:17:86"    #Comment describing device 3
macdevice4="00:00:00:00:00:00"    #Comment describing device 4

#OpenHAB IP Address and port
IPAddr="192.168.168.9"
port="8080"

# OpenHAB switch items to be updated for each tracked MAC
item1="ItemName1"
item2="ItemName2"
item3="ItemName3"
item4="ItemName4"

# Occupied and unoccupied delay in seconds to check status
delay_occupied=20
delay_unoccupied=10

# initial testing loop count - uncomment the counter near the bottom of the script for testing only. 
limit=120

sleep 10

# status of each MAC. 0=disconnected. 1=connected.  -1 initially forces openHAB update first loop
macconnected1=-1
macconnected2=-1
macconnected3=-1
macconnected4=-1
# total number of currently conencted devices.
counter=0

# Initial testing loop.  Will run continually after testing is complete
while [ $counter -lt $limit ]; do

#reset current status. Two variables are used for each device.  The past known status and the current
# status.  Only a change is reported to openHAB.  Otherwise, it would constantly be updating openHAB with
# the current status creating unnecessary traffic for both the router and openHAB
maccurrent1=0;
maccurrent2=0;
maccurrent3=0;
maccurrent4=0;

# compare each device that is currently connected to the MAC devices we want to watch.
# changed the following to check for each MAC
devices="$(wl -i eth1 assoclist) $(wl -i eth2 assoclist)"

maccurrent1=$(echo $devices | grep -c $macdevice1)
if [ $maccurrent1 -gt 0 ]; then
	maccurrent1=1
fi

maccurrent2=$(echo $devices | grep -c $macdevice2)
if [ $maccurrent2 -gt 0 ]; then
	maccurrent2=1
fi

maccurrent3=$(echo $devices | grep -c $macdevice3)
if [ $maccurrent3 -gt 0 ]; then
	maccurrent3=1
fi

maccurrent4=$(echo $devices | grep -c $macdevice4)
if [ $maccurrent4 -gt 0 ]; then
	maccurrent4=1
fi

# Look for a change in status from the old known to the current status.
# If it changed, update openHAB. Otherwise it leaves it as is. 
if [ $macconnected1 -ne $maccurrent1 ]; then
	if [ $maccurrent1 -eq 1 ]; then
		macstatus1="ON";
	else
		macstatus1="OFF";
	fi
	curl -X POST -d $macstatus1 -H "Content-Type: text/plain" -i http://$IPAddr:$port/rest/items/$item1
	macconnected1=$maccurrent1;
fi

if [ $macconnected2 -ne $maccurrent2 ]; then
	if [ $maccurrent2 -eq 1 ]; then
		macstatus2="ON";
	else
		macstatus2="OFF";
	fi
	curl -X POST -d $macstatus2 -H "Content-Type: text/plain" -i http://$IPAddr:$port/rest/items/$item2
	macconnected2=$maccurrent2;
fi

if [ $macconnected3 -ne $maccurrent3 ]; then
	if [ $maccurrent3 -eq 1 ]; then
		macstatus3="ON";
	else
		macstatus3="OFF";
	fi
	curl -X POST -d $macstatus3 -H "Content-Type: text/plain" -i http://$IPAddr:$port/rest/items/$item3
	macconnected3=$maccurrent3;
fi

if [ $macconnected4 -ne $maccurrent4 ]; then
	if [ $maccurrent4 -eq 1 ]; then
		macstatus4="ON";
	else
		macstatus4="OFF";
	fi
	curl -X POST -d $macstatus4 -H "Content-Type: text/plain" -i http://$IPAddr:$port/rest/items/$item4
	macconnected4=$maccurrent4;
fi

# Total up the number of devices connected.
let connected=$macconnected1+$macconnected2+$macconnected3+$macconnected4

# Delay (sleep) depending on the connection status.
# No devices connected could delay less. Once a device is connected, it could delay longer.
if [ $connected -gt 0 ]; then
    sleep $delay_occupied
    else
    sleep $delay_unoccupied
fi

#for testing only - uncomment to have the looping stop at X loops defined in variable: limit.
#let counter=$counter+1
done

I have not been on the forum for a very very long time.
But I still want to thank you for your elaborate answer Cheers!

1 Like