Presence detection based on IP and Bluetooth
Posted: Friday 27 October 2017 17:20
On request:
This is a script I run to detect the presence of 2 telephones together with Domoticz. It is ran with crontab and will update 2 dummy On/Off switches representing the Phones being home or not. It is based on an bash script I found earlier in this forum, converted to LUA and adapted to support multiple phones and using for bluetooth the command "hcitool name" in stead of l2ping as that used much more CPU and took longer.
Below the LUA script and the configuration documentation and prerequisites are included it the header.
Let me know when there are questions.
Jos
This is a script I run to detect the presence of 2 telephones together with Domoticz. It is ran with crontab and will update 2 dummy On/Off switches representing the Phones being home or not. It is based on an bash script I found earlier in this forum, converted to LUA and adapted to support multiple phones and using for bluetooth the command "hcitool name" in stead of l2ping as that used much more CPU and took longer.
Below the LUA script and the configuration documentation and prerequisites are included it the header.
Let me know when there are questions.
Jos
Code: Select all
-- ==============================================================================================
-- Presence detection script using both fixed IP and BlueTooth to ensure proper detection
-- ==============================================================================================
-- -Prerequisites -------------------------------------------------------------------------------
-- Install bluetooth support:
--> sudo apt-get update # Update package list
--> sudo apt-get upgrade # Upgrade system
--> sudo apt-get install bluetooth bluez-utils blueman
--
-- Perform a scan for BT devices: (make sure your device is visible on BT)
--> hcitool scan
--
-- Find the proper directory for your PI bt in /var/lib/bluetooth/??:??:??:??
-- For the found device add a pin code to pincodes file: # use existing MAC folder
--> echo "<MAC> 0000" > /var/lib/bluetooth/??:??:??:??/pincodes
-- Test device by doing: hcitool name <MAC>
--
-- optionally could need to first Pair device when previous doesn't work. Worked for me without this
--> rfcomm connect 0 <MAC> 10 #and fill pin 0000 on phone/device
-- The rfcomm command can fail, no problem.
--
-- Changed the timeout "Page timeout: 8192 slots (5120.00 ms)" to " Page timeout: 4096 slots (2560.00 ms)"
--> sudo hciconfig hci0 pageto 4096
--
-- Install LUA 5.2 when its not installed yet:
--> sudo apt-get install lua5.2
--
-- require some LUA libraries: SOCKET.HTTP/SOCKET/JSON/ssl.https
-- includes need to be located in: /usr/local/share/lua/5.2/ and /usr/local/lib/lua/5.2/
-- Download and install usrlocalsharelua52.tar.gz from: http://www.domoticz.com/forum/download/file.php?id=3677
--> wget -O /tmp/usrlocalsharelua52.tar.gz "http://www.domoticz.com/forum/download/file.php?id=3677"
--> cd /usr/local/share/lua/5.2/
--> sudo tar -xvf /tmp/usrlocalsharelua52.tar.gz
-- Download and install usrlocalliblua52.tar.gz from: http://www.domoticz.com/forum/download/file.php?id=3676
--> wget -O /tmp/usrlocalliblua52.tar.gz "http://www.domoticz.com/forum/download/file.php?id=3676"
--> cd /usr/local/lib/lua/5.2/
--> sudo tar -xvf /tmp/usrlocalliblua52.tar.gz
-- end - Prerequisites --------------------------------------------------------------------------
-- # Crontab entries ---------------------------------------------------------------------------
-- !!! do this part when you have completed setting all variables below and things are working. !!!
--> # presence detection script is shelled every 2 minutes to ensure it remains active. The script dected whether it is already running
--> */2 * * * * lua /home/pi/domoticz/scripts/presence.lua -batch >> /var/tmp/presence_check.log
--
--### Start Define all required variables #############################################
ScriptName="presence.lua" -- set to the scriptfilename, used for checking occurences
server_url="http://192.168.0.??:8080" -- domoticz url
-- update LoopLog with latest time. This file can be used to check whether the process is still running with monit
-- Define "" when you don't want to use Monit.
-- Monit definition used:
-- check file Presence with path /var/tmp/presence-monit.log
-- stop program = "/usr/bin/pkill -f presence" timeout 60 seconds
-- if timestamp > 2 minutes then restart
-- if 5 restarts within 5 cycles then timeout
-- Monit_check_log="" -- don't create this extra Monit logfile
Monit_check_log="/var/tmp/presence-monit.log"
--
LoopTimeShort=10 -- Sleep Timer used when one of the phones is not found to recheck it
LoopTimeLong=30 -- Sleep Timer used when all phones are present
FailTimeout=120 -- Time used to determine the phone is really gone.
LoopTime=LoopTimeLong
debug=2 -- defines the logging level.
-- 0=basic info is logged
-- 1=some extra info
-- 2=all available info used only in case of issues
-- Phone configuration table ---------------------------------------------------------------
TelName = {} -- name of the telephone
TelIP = {} -- Ip address (put on "" when on;y using BT)
TelBT = {} -- BT mac address
TelIDX = {} -- IDX of dummy Domoticz On/Off switch for the phone
TelFail = {} -- count of checks that failed subsequently -> used by the script
TelFailTime = {} -- Time of the first failure -> Used by the script
-- first phone to check -------------------------------------------------------------------
Rec = 0
TelName[Rec] = "Phone1"
TelIP[Rec] = "192.168.0.xx"
TelBT[Rec] = "AA:BB:CC:DD:EE:FF"
TelIDX[Rec] = "??"
TelFail[Rec] = 0 -- don't change
TelFailTime[Rec] = 0 -- don't change
-- Second phone to check (remove block when only one phone is needed ----------------------
Rec = Rec +1
TelName[Rec] = "Phone2"
TelIP[Rec] = "192.168.0.XX"
TelBT[Rec] = "AA:BB:CC:DD:EE:XX"
TelIDX[Rec] = "??"
TelFail[Rec] = 0 -- don't change
TelFailTime[Rec] = 0 -- don't change
--------------------------------------------------------------------------------------------
TelCheckVar="" -- User Variable in Domoticz which can be used to find the last time the phones were checked.
-- Leave empty when not used.
--### End Define all required variables #############################################
-- Load necessary Lua libraries
http = require "socket.http";
socket = require "socket";
https = require "ssl.https";
JSON = require "JSON";
--
-- "2" only works when shelled from crontab else use "3" when testing from commandline
-- so ensure that you add the -batch when running it from crontab!
-- check howmany scripts are running with this name.
local file = io.popen('ps x | grep "'..ScriptName..'"|grep -cv grep')
local output = file:read('*all')
local rc = {file:close()}
output = string.gsub(output,"\n","") -- remove newline from output
if debug > 1 then
print ('cmd-> ps x | grep "'..ScriptName..'"|grep -cv grep => rc:'..tostring(rc[1])..' output:'..tostring(output))
end
if arg[1] == "-batch" and output == "2" then
print("batch and no=2 so not running yet")
elseif output == "1" then
print("no=1 so not running yet")
else
print("Already running -> Exit")
os.exit(0)
end
--### Start Define all required LUA functions #############################################
function sleep(n)
os.execute("sleep " .. tonumber(n))
end
function retrieve_status(idx)
local t, jresponse, status, decoded_response
t = server_url.."/json.htm?type=devices&rid="..tostring(idx)
if debug>=2 then
print("JSON request <"..t..">")
end
jresponse, status = http.request(t)
decoded_response = JSON:decode(jresponse)
result = decoded_response["result"]
record = result[1]
status = record["Status"]
if debug>=2 then
print("Status JSON request "..status)
end
return status
end
-- domoticz
function SwitchName(idx,state)
local status
if string.lower(state) == "on" then
state = "On";
elseif string.lower(state) == "off" then
state = "Off";
end
t = server_url.."/json.htm?type=command¶m=switchlight&idx="..idx.."&switchcmd="..state;
if debug>=2 then print("JSON request <"..t..">") end
jresponse, status = http.request(t)
result = JSON:decode(jresponse)
status = result["status"]
if status ~= "OK" then
print(os.date("%X").." !!!! SwitchName Not OK -> JSON feedback: ", jresponse)
else
if debug>=2 then
print("JSON feedback: ", jresponse)
end
end
return
end
function domo_status(Name,Fail,PrevFail,FailTime,IDX,source,newstatus)
curstate = retrieve_status(IDX)
if debug>=1 then
print(os.date("%X").." "..IDX.." "..Name.." current="..curstate.." New="..newstatus.." from="..source.." fail="..Fail.." prevfail="..PrevFail.. " time="..os.time()-FailTime)
end
if Fail == 1 and curstate == "On" then
print(os.date("%X").." ---- phone failed -> retrying "..Name)
end
if PrevFail > Fail and curstate == "On" then
print(os.date("%X").." ---- phone connected again "..Name.." bron:"..source)
end
if (newstatus == "Off" and os.time()-FailTime >= FailTimeout and curstate ~= newstatus)
or (newstatus == "On" and curstate ~= newstatus) then
print(os.date("%X").." #### Switch phone "..Name.." "..newstatus.." bron:"..source.." ("..Fail..")" )
SwitchName(IDX,newstatus)
end
end
--### End Define all required functions #############################################
--############################################
--# Main loop
--############################################
-- List record to log that are being monitored
for Rec in pairs(TelName) do
curstate = retrieve_status(TelIDX[Rec])
print(os.date("%X").." ---- Starting monitoring for "..TelName[Rec].." ("..TelIDX[Rec]..") CurState="..curstate.." IPaddres="..TelIP[Rec].." BTmac="..TelBT[Rec])
end
print("Starting")
-- create poll file to check for Monit
if Monit_check_log ~= "" then
os.execute("echo " .. os.date("%Y-%m-%d %H:%M:%S") .. " > " .. Monit_check_log)
end
-- Loop to monitor all devices
while true do
--
timenow = os.time()
if debug>=1 then
print(os.date("%X").." ".."=================================================================")
end
for Rec in pairs(TelName) do
-- first try a IP ping as that is the fastest check
PrevFail=TelFail[Rec]
if TelIP[Rec] ~= "" then
ping_success=os.execute('ping -c1 -w2 ' .. TelIP[Rec].." > /dev/null")
else
ping_success=false
end
if ping_success then
TelFail[Rec] = 0
TelFailTime[Rec] = 0
domo_status(TelName[Rec],TelFail[Rec],PrevFail,os.time(),TelIDX[Rec],"Ping","On")
else
-- If Ping fails try the BT check with hcitool which is faster and less CPU than l2ping
bt_success=os.execute('hcitool name ' .. TelBT[Rec]..'|grep ".*" > /dev/null')
-- check for characters returned .. if nothing then phone not found
if bt_success==nil then
TelFail[Rec] = TelFail[Rec] + 1
-- set the failtime at first failure to find phone
if TelFail[Rec] == 1 then
TelFailTime[Rec] = os.time()
end
domo_status(TelName[Rec],TelFail[Rec],PrevFail,TelFailTime[Rec],TelIDX[Rec],"Both","Off")
LoopTime=LoopTimeShort -- set looptime lower when a phone isn't present
else
TelFail[Rec] = 0
TelFailTime[Rec] = 0
domo_status(TelName[Rec],TelFail[Rec],PrevFail,os.time(),TelIDX[Rec],"BT","On")
end
end
end
-- update uservariable with the last status check done time.
if TelCheckVar ~= "" then
t = server_url..'/json.htm?type=command¶m=updateuservariable&vname=' .. TelCheckVar .. '&vtype=2&vvalue=' .. os.date("%X") .. '';
if debug>=2 then print("JSON request <"..t..">") end
jresponse, status = http.request(t)
result = JSON:decode(jresponse)
status = result["status"]
if status ~= "OK" then
print(os.date("%X").." !!!! Set variable ".. TelCheckVar .." Not OK -> JSON feedback: ", jresponse)
else
if debug>=2 then
print("JSON feedback: ", jresponse)
end
end
end
-- sleep the number of seconds defined in LoopTime
if os.time() - timenow <= LoopTime then
if debug>=1 then
print(os.date("%X").." ".."sleep "..LoopTime-(os.time() - timenow))
end
sleep(LoopTime-(os.time() - timenow))
end
LoopTime=LoopTimeLong
-- update LoopLog with latest time. This file can be used to check whether the process is still running with monit
if Monit_check_log ~= "" then
os.execute("echo " .. os.date("%Y-%m-%d %H:%M:%S") .. " > " .. Monit_check_log)
end
end