Presence alert in OpenHAB by DHCP server in ASUS router

The home automation system OpenHAB has the ability to coordinate difference networked devices to perform automatic control under different conditions. One of the important conditions is the presence of different residents in the house. Using "Network Health" module of OpenHAB allows the presence test through the detection of devices carried by the residents. One simply create item such as

Switch Resident1 {nh="android-xxxxxx"}
Switch Resident2 {nh="android-yyyyyy"}
...

This works well except the time resolution for the arrival and departure is limited by the period of network checking (which is set by networkhealth:refresh and defaulted to 1 minute). For real-time arrival notification, it can be achieved by the DHCP server of the local area network. We can configure it to flip on the corresponding presence switch as soon as a DHCP lease is offered to the corresponding device. For ISC DHCP server running on a computer, this can be done using the "on commit" event handling block. For ASUS router, which runs dnsmasq for dhcpd, we will need to update to the Asuswrt-Merlin firmware to enable similar facility. Once that's done and you can login to the router for a shell, you make an addition to the "dnsmasq.conf" by creating the file, "/jffs/configs/dnsmasq.conf.add", with the single-line content:

dhcp-script=/jffs/machab.sh

This will make dnsmasq call the script, "/jffs/machab.sh", whenever a lease is offered. The content of the file is as follows:

#!/bin/sh
HABURL='URL for the OpenHAB server with authentication credential'
MAPDAT=${0%/*}/machab.map
mac=$2
item=''
if [ ! -r "${MAPDAT}" ]; then exit 1; fi
while read m n; do
	if [ "$mac" = "$m" ]; then item=${n}; fi
done < ${MAPDAT}
if [ -z "$item" ]; then exit 0; fi
wget -O /dev/null --post-data=ON --no-check-certificate \
 --header "Content-Type: text/plain" ${HABURL}/rest/items/$item

(Remember to change the HABURL setting to suit your situation.)
The script look up the MAC address of the device from the map file, "/jffs/machab.map", which contains something like:

xx:xx:xx:xx:xx:xx	Resident1
yy:yy:yy:yy:yy:yy	Resident2
...

and flip the corresponding switch to "ON".
Finally, enable the "JFFS custom scripts and configs" from the administration web page of the router as shown below.

You might need to reboot the router for the configuration to take effect.

Comments

This adds the ability to set a time that it shouldn't activate between. In my example if it is between the hours of 6am and 6pm nothing will happen. It will however turn a light on for 5 minutes (sleep 300) if it is after 6pm and before 6am. Note that it had to be done this way because the busybox shell offers very little comparison functionality as opposed to other full featured shells.

#!/bin/sh
currTime=`date +%k%M`
HABURL='192.168.1.4:8080'
MAPDAT=${0%/*}/machab.map
mac=$2
item=''
if [ expr $currTime ">" 600 ]; then exit 1; fi
if [ expr $currTime "<" 1800 ]; then exit 1; fi
if [ ! -r "${MAPDAT}" ]; then exit 1; fi
while read m n; do
if [ "$mac" = "$m" ]; then item=${n}; fi
done < ${MAPDAT}
if [ -z "$item" ]; then exit 0; fi
wget -O /dev/null --post-data=ON --no-check-certificate \
--header "Content-Type: text/plain" ${HABURL}/rest/items/$item
sleep 300
wget -O /dev/null --post-data=OFF --no-check-certificate \
--header "Content-Type: text/plain" ${HABURL}/rest/items/$item

This method is unfortunately broken in newer versions of AsusWRT Merlin because dhcp-script is already defined in the dnsmasq.conf.

The following worked for me:
Create a script called "/jffs/scripts/dnsmasq.postconf" and make it executable:

#!/bin/sh
CONFIG=$1
source /usr/sbin/helper.sh
pc_replace "/sbin/dhcpc_lease" "/jffs/machab.sh" $CONFIG

Then, in the actual script (machab.sh), call the original dhcp-script before proceeding.
I also replaced wget with curl because wget didn't work for me.

#!/bin/sh
#call original script
/sbin/dhcpc_lease

#own script
HABURL=XXX.XXX.XXX.XXX:8080
MAPDAT=${0%/*}/machab.map
mac=$2
item=''
if [ ! -r "${MAPDAT}" ]; then exit 1; fi
while read m n; do
if [ "$mac" = "$m" ]; then item=${n}; fi
done &lt; ${MAPDAT}
if [ -z "$item" ]; then exit 0; fi
curl -X POST --header "Content-Type: text/plain" --header "Accept: application/json" -d "ON" "http://$HABURL/rest/items/$item"

Hope this helps someone!