Here is a solution for device_tracker on routed networks

Hi there!
I’m user of home assistant for years, switched from OpenHAB many months ago :slight_smile:
Now it’s time to share some ideas I used to solve my problem - maybe you will find them useful.

This is a device tracker solution for routed networks. In my case I have an apartment and a house in countryside. My HA is in house but I wanted to track devices in apartment too. I was trying to use device_tracker openwrt component since I have only openwrt routers on both locations. I wasn’t happy with that solution. After some months - it stopped working.

Those two locations are linked using OpenVPN with three networks: house, OpenVPN and apartment - In a house I found useful device tracker nmap component - which is robust and have no problems. But - I was wondering how to track devices in apartment. The problem is MAC address - nmap can scan routed networks but it can’t track MAC addresses of such devices. So I found a solution for that.

First, on OpenWRT router I use this script to scan for hosts being online in apartment:


#ip neigh flush eth0 2>&1 > /dev/null

ping -w 3 2>&1 > /dev/null

echo "["
#below line is for standard arp kernel cache - it is not always reflecting which devices are online
#for mac_ip in `cat /proc/net/arp | grep -v 00:00:00:00:00 | grep -v type | awk '{print $4 "_" $1}'`
#this is using iproute2 - it is working fine so far:
for mac_ip in `ip neigh show | grep REACHABLE | awk '{print $5 "_" $1}'`
    mac=`echo $mac_ip | awk -F _ '{print $1}'`
    ip=`echo $mac_ip | awk -F _ '{print $2}'`
    echo "$delim {\"mac\": \"$mac\", \"ip\": \"$ip\"}"
echo "]"

So what this script produce? A JSON data about my devices. It’s a table, with objects each containing mac an ip of device found on network. So far, so good.

MQTT - I use mosquitto. On OpenWRT there is package called mosquitto-client. That’s what I need :slight_smile:

So - I run first script to get JSON data - store it in file in /tmp directory. Then transfer it over MQTT to my mosquitto server for clients to consume.

mosquitto_pub -h -t /sites/kurdwanow -f /tmp/location.kurdwanow

I use crontab to first generate JSON data, save it in /tmp/location.kurdwanow file, then running mosquitto_pub to send it to broker on topic /sites/kurdwanow.
Ok - Who consumes those data? Node-Red comes handy.

On Node-red I have separate flow, called Location Kurdwanow. Why? Since I save information from that JSON file in ‘flow’ using ‘function’ node:

o = JSON.parse(msg.payload)
flow.set("kurdwanow", "");
flow.set("kurdwanow", o);

return msg;

Then I have two functions nodes:

  1. addDevices
  2. removeDevices
//as input - we receive devices found in HA in our location
ha_devices = msg.payload;
msg.payload = {};
//get recent network devices stored in flow

function addDeviceStatus(value, index, array) {
        //node.debug(value, index, array);
        var data = {}
        data.mac = array[index].mac;
        data.location_name = "Kurdwanów";
        msg.payload = data;

Remove devices:

//as input - we receive devices found in HA in our location
ha_devices = msg.payload;
msg.payload = {};
//get recent network devices stored in flow
kurdwanow = flow.get("kurdwanow");

// remove devices from HA:

function remove(value, index, array) {

    var data = {};
    data.dev_id = array[index].entity_id.split(".")[1];
    data.location_name = "not_home"; 
    data.entity_id = array[index].entity_id;
    msg.payload = data;

But this is not enough. In Node Red we have Home Assistant integration. We can call service from HA. Service to be called is in domain device_tracker and service name is see.
Se here is a trick: we need to parse data from input payload and add it to service call data:

    "mac": "{{payload.mac}}",
    "location_name": "{{payload.location_name}}"

But we have mac information for adding devices (HA is responsible for parsing mac and assigning it for device. But to remove all devices from location - we don’t receive mac from HA - we can only use dev_id or entity_id. So to remove device - we need to add another call service from HA and configure it differently:

    "dev_id": "{{payload.dev_id}}",
    "location_name": "{{payload.location_name}}"

In documentation of call service for device_tracker.see we know - that only mac is required but this is or - mac or dev_id or both.

Of course - we need to add some delays. Complete flow is attached below:
[{"id":"d71c3f60.b5e18","type":"tab","label":"Location Kurdwanów","disabled":false,"info":""},{"id":"8ca3f925.12f188","type":"mqtt in","z":"d71c3f60.b5e18","name":"Kurdwanow","topic":"/sites/kurdwanow","qos":"2","broker":"9469504e.63082","x":90,"y":40,"wires":[["dbba5208.2dc1b","f99f72.e145609"]]},{"id":"dbba5208.2dc1b","type":"function","z":"d71c3f60.b5e18","name":"Save Values in Flow","func":"o = JSON.parse(msg.payload)\nflow.set(\"kurdwanow\", \"\");\nflow.set(\"kurdwanow\", o);\n\nreturn msg;","outputs":1,"noerr":0,"x":340,"y":40,"wires":[["dd566cd3.2a8a9"]]},{"id":"5a6c7d10.4bd214","type":"ha-get-entities","z":"d71c3f60.b5e18","server":"2c85af0d.c8fbc","name":"Device Tracker","rules":[{"property":"state","logic":"is","value":"Kurdwanów","valueType":"str"}],"output_type":"array","output_empty_results":true,"output_location_type":"msg","output_location":"payload","output_results_count":1,"x":220,"y":200,"wires":[["685692b9.30dd4c","2cd9ea9d.f6c5e6"]]},{"id":"5987def0.c3e72","type":"inject","z":"d71c3f60.b5e18","name":"Test","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":400,"wires":[["5a6c7d10.4bd214"]]},{"id":"685692b9.30dd4c","type":"function","z":"d71c3f60.b5e18","name":"removeDevices","func":"//as input - we receive devices found in HA in our location\nha_devices = msg.payload;\nmsg.payload = {};\n//get recent network devices stored in flow\nkurdwanow = flow.get(\"kurdwanow\");\n\n// remove devices from HA:\nha_devices.forEach(remove);\n\nfunction remove(value, index, array) {\n\n var data = {};\n data.dev_id = array[index].entity_id.split(\".\")[1];\n data.location_name = \"not_home\"; \n data.entity_id = array[index].entity_id;\n msg.payload = data;\n //node.log(JSON.stringify(msg.payload));\n node.send(msg);\n}\n","outputs":1,"noerr":0,"x":380,"y":280,"wires":[["97354342.eb9cc"]]},{"id":"97354342.eb9cc","type":"api-call-service","z":"d71c3f60.b5e18","name":"See Device by dev_id and entity_id","server":"2c85af0d.c8fbc","service_domain":"device_tracker","service":"see","data":"{\"dev_id\":\"{{payload.dev_id}}\",\"location_name\":\"{{payload.location_name}}\"}","mergecontext":"","output_location":"payload","output_location_type":"msg","x":680,"y":320,"wires":[[]]},{"id":"f99f72.e145609","type":"delay","z":"d71c3f60.b5e18","name":"Delay 900 milisecond - wait for flow variable update","pauseType":"delay","timeout":"900","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":230,"y":120,"wires":[["5a6c7d10.4bd214"]]},{"id":"dd566cd3.2a8a9","type":"function","z":"d71c3f60.b5e18","name":"addDevices","func":"//as input - we receive devices found in HA in our location\nha_devices = msg.payload;\nmsg.payload = {};\n//get recent network devices stored in flow\nflow.get(\"kurdwanow\").forEach(addDeviceStatus);\n\nfunction addDeviceStatus(value, index, array) {\n //node.debug(value, index, array);\n var data = {}\n data.mac = array[index].mac;\n data.location_name = \"Kurdwanów\";\n \n msg.payload = data;\n node.send(msg);\n}","outputs":1,"noerr":0,"x":610,"y":120,"wires":[["22154403.26844c","d3124c0a.7e856"]]},{"id":"22154403.26844c","type":"api-call-service","z":"d71c3f60.b5e18","name":"See device by mac","server":"2c85af0d.c8fbc","service_domain":"device_tracker","service":"see","data":"{\"mac\":\"{{payload.mac}}\",\"location_name\":\"{{payload.location_name}}\"}","mergecontext":"","output_location":"payload","output_location_type":"msg","x":830,"y":180,"wires":[["d3124c0a.7e856"]]},{"id":"2cd9ea9d.f6c5e6","type":"delay","z":"d71c3f60.b5e18","name":"","pauseType":"delay","timeout":"300","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":450,"y":200,"wires":[["dd566cd3.2a8a9"]]},{"id":"d3124c0a.7e856","type":"debug","z":"d71c3f60.b5e18","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":890,"y":100,"wires":[]},{"id":"9469504e.63082","type":"mqtt-broker","z":"","name":"nas","broker":"","port":"1883","clientid":"node-red","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"2c85af0d.c8fbc","type":"server","z":"","name":"Home Assistant","legacy":false,"hassio":false,"rejectUnauthorizedCerts":false,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true}]

Happy device tracking!

1 Like

Thanks for sharing. Marked^_^