Still struggling with the arp-scan methode.
Although all available scripts are working besides snmp scripts as my provider blocked it on my modem/router.
But non of those scripts available could satisfy my needs or having some side effects on the PI.
Like high CPU usage while scanning for multiple devices in the ARP-Table.
Until today i think...
Why didn't i came up with the idea earlier.
I used the device online script by Chopper_Rob for some time now.
And it pings for devices on your network.
While my modem/router has a nasty static ip bug as writter earlier i search a way of using arp instead of ping.
Other scripts using arp are calling the arp in a way which first scans the table then picks the mac and processes it.
And it does that for every device, 1 device or 2 aint a big problem.
But 13 devices which almost all are determine is someone is home is causing issues when using those arp scripts
Every time the arp-scan runs it takes a big chuck of CPU resources which means when Domoticz is at his busiest moment (evening) it get sluggish on triggering events while scanning almost every minute for 13 devices.
Yes every minute as all my scripts/event are relying on almost instant SomeOneHome or not

Not all are devices are important for the SomeOneHome switch but still.
So after digging into the arp and searching Google for several days i noticed the different commands you can use in shell.
The one i used before was "sudo arp-scan 192.168.178.0/24" which took almost 5 seconds to complete on just one scan and all my network devices it takes 65 seconds.
So the scanning is still running while a new scan is already called and there for slowing down Domoticz and causing issues with triggering events and such.
I could take it to 3 or more minutes but this kills the instant SomeOneHome feature.
Also a side effect is the CPU, the average CPU usage went from 5% t0 40%.
So i search a way for only scanning for one device at a time.
Somewhere i found a command "sudo arp -n | grep -o <mac address>" which takes less then a second to check the arp-table and only search for one device.
This way the process of determine a device is on/offline takes less time and resources.
And i don't have to worry about ip addresses which change weekly and me seeking the correspondent ip to mac and adjusting them over and over again.
So from there i just made a little adjustment to Chopper_Rob his check_device_online.py script
And replaced the ping command with the arp scan and (if i did right) added some extra imports to be able to run arp command.
Amended the crontab command from ip to my mac address:
# Siewert Laptop
*/10 * * * * sudo python /home/pi/domoticz/scripts/python/check_devices_online.py 192.168.178.2 119 60 60 > /dev/null 2>&1
to
# Siewert Laptop
*/10 * * * * sudo python /home/pi/domoticz/scripts/python/check_devices_online.py xx:xx:xx:xx:xx:xx 119 60 60 > /dev/null 2>&1
This went well and running fine, haven't test it yet with all my devices only tested it with three devices.
I did notice one device (Wife here android phone) is lost from the arp-table from time to time while online.
Setting the cooldown time a bit higher solved the issue.
Tomorrow i will test with all my devices and see how it behaves.
But for now it does work as planned.
If some else wants to test Chopper_Rob his adjusted script then you can use the script below.
Usage of the script is the same as written in the wiki only with use of mac addresses instead of ip.
Be aware you have to install arp-scan.
Code: Select all
#!/usr/bin/python
# Title: check_device_online_arp.py
# Original author: Chopper_Rob
# Modified for ARP by : Siewert308SW
# Date: 27-02-2016
# Info: Checks the presence of the given device on the network by checking your router by ARP Table and reports back to domoticz
# Version : 1.0
import sys
import datetime
import time
import os
import subprocess
import urllib2
import json
import base64
import socket
import string
import asynchat
import asyncore
# Settings for Domoticz
domoticzserver="127.0.0.1:8080"
domoticzusername = " "
domoticzpassword = " "
# 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[1]
switchid=sys.argv[2]
interval=sys.argv[3]
cooldownperiod=sys.argv[4]
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[0] + '_' + sys.argv[1] + '.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[0] + ' ' + sys.argv[1] + '\' | 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[0] + '_' + sys.argv[1] + '.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('sudo arp -n | 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()