Presence detection using bash and bluez

All kinds of 'OS' scripts
Post Reply
elmoke
Posts: 1
Joined: Saturday 11 August 2018 23:58
Target OS: Raspberry Pi
Domoticz version:
Contact:

Presence detection using bash and bluez

Post by elmoke » Sunday 12 August 2018 0:21

Wrote the following beacon detection application without the need for any other scripting language or related plugins.
Only requirement is a working bluez stack/package and hcetool (included), pkill and curl.

Features of this script:
  • It is passive, as in it only listens for LE beacons, it does not query them or ping them. This has some security benefits.
  • Every initial run it will register the configured beacons as user variables. No need to manually add them, sync IDs etc.
  • Beacon names are used as user variabel names
  • Domoticz is not updates unless beacon is not seen in 3 scans or immediately when seen
  • Retry settings are customizable
  • Detailed debugging if required
Bash code

Code: Select all

#!/bin/bash

# Backoff period when hcetool after which hcetool run
BACKOFF=5
# Nr of retries before updating Domoticz that device is $AWAY
RETRY=3

# BaseURL for Domoticz inc port
BASEURL="http://localhost:8888"


declare -A BEACONS=(
   #[   MAC Address   ]=Beaconname
    [7C:2F:80:97:1A:72]="Huissleutel_Daan"
    [7C:2F:80:97:1A:D3]="Hoofdsleutel_Hyundai"
    [7C:2F:80:96:3C:FA]="Hoofdsleutel_Up"
    [7C:2F:80:97:2E:5E]="Huissleutel_Naomi"
)
# How many seconds to wait before SIGINT hcitool, may need tweaking when more devices are visible/beacons are not that chatty
PKILLDELAY=5
# String value to register when beacon is visible
HOME="HOME"
# String value to register when beacon is not visible
AWAY="AWAY"
# If all else fails debug will assist
DEBUG=0
# Bluetooth interface to use
HCIINT="hci0"
# Path of hcitool
HCITOOL="/usr/bin/hcitool"
# Path to sleep
SLEEP="/bin/sleep"
# Path to pkill
PKILL="/usr/bin/pkill"
# Path to curl
CURL="/usr/bin/curl"

#####
### NO EDITS BELOW THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING###
#####

#URLs for saving/updating user variables
JSONSAVE="/json.htm?type=command&param=saveuservariable&vtype=2"
JSONUPDATE="/json.htm?type=command&param=updateuservariable&vtype=2"

# Array to store beacon status
declare -A CURRENTBCNSTS
# Array to store beacon retry count
declare -A BCNRETRY
# Linenumber when debugging
DBGCNTR=0

debug () {
  # Simple debug function
  if [[ $DEBUG -eq 1 ]]; then
    ((DBGCNTR++))
    printf "%05d" $DBGCNTR
    echo " DEBUG: $1"
  fi
}

scan_beacons () {
  # Using hcitool to listen for LE beacons
  debug "listening on $HCIINT for devices"
  SCAN=`$HCITOOL -i $HCIINT lescan --passive & $SLEEP $PKILLDELAY && $PKILL --signal SIGINT hcitool`
  debug "scan results: $SCAN"
}

beacon_alive () {
  # Regex function to determine if beacon MAC is part of scan results
  if [[ $SCAN =~ "$1" ]]; then
    SCANRESULT=$HOME
  else
    SCANRESULT=$AWAY
  fi
}

# Initial beacon scan
scan_beacons



for i in "${!BEACONS[@]}"
# Initial round at startup
do
  VNAME="&vname=${BEACONS[$i]}"
  beacon_alive $i
  VVALUE="&vvalue=$SCANRESULT"
  CURRENTBCNSTS[$i]=$SCANRESULT
  if [ "$SCANRESULT" = "$AWAY" ]; then
    BCNRETRY[$i]=1
  else
    BCNRETRY[$i]=0
  fi
  debug "setting current status of $i to ${CURRENTBCNSTS[$i]}"
  # Setting or updating user variables, Domoticz will ignore duplicate saves
  $CURL -s --request GET $BASEURL$JSONSAVE$VNAME$VVALUE > /dev/null
  $CURL -s --request GET $BASEURL$JSONUPDATE$VNAME$VVALUE > /dev/null
done

for (( ; ; ))
do
  $SLEEP $BACKOFF
  scan_beacons
  for i in "${!BEACONS[@]}"
  do
    VNAME="&vname=${BEACONS[$i]}"
    CURSTATUS=${CURRENTBCNSTS[$i]}
    debug "CURSTATUS $i:$CURSTATUS"
    beacon_alive $i
    debug "SCANRESULT $i:$SCANRESULT"
    if [ "$CURSTATUS" != "$SCANRESULT" ]; then
      VVALUE="&vvalue=$SCANRESULT"
      if [ "$SCANRESULT" = "$AWAY" ]; then
        ((BCNRETRY[$i]++))
        debug "beacon $i not found ${BCNRETRY[$i]} times"
        if [ ${BCNRETRY[$i]} -eq $RETRY ]; then
          CURRENTBCNSTS[$i]=$SCANRESULT
          debug "beacon $i status changed to $SCANRESULT after $RETRY retries updating Domoticz"
          $CURL -s --request GET $BASEURL$JSONUPDATE$VNAME$VVALUE > /dev/null
        fi
      else
        CURRENTBCNSTS[$i]=$SCANRESULT
        debug "beacon $i status changed to $SCANRESULT - updating Domoticz"
        $CURL -s --request GET $BASEURL$JSONUPDATE$VNAME$VVALUE > /dev/null
        BCNRETRY[$i]=0
      fi
    else
      if [ "$SCANRESULT" = "$HOME" ]; then
        if [ ${BCNRETRY[$i]} -ne 0 ]; then
          debug "beacon $i resetting retry count"
          BCNRETRY[$i]=0
        fi
      fi
      debug "Skipping Domoticz for beacon $i as status $CURSTATUS has not changed"
    fi
  done
  debug "Next round..."
done
Thanks author of https://stackoverflow.com/questions/268 ... -to-a-file for the tip on using pkill. timeout will cause interface to fail next scan.

The script can be run as a service using this systemd script

Code: Select all

[Unit]
Description=Beaconing Service
After=domoticz.service

[Service]
User=root
Type=idle
ExecStart=/usr/local/bin/beaconing
ExecStop=/usr/bin/pkill --signal SIGINT hcitool
KillMode=process

[Install]
WantedBy=multi-user.target
Currently working on
  • Delayed switch to AWAY something along the line of 3 times not seen in a scan really AWAY, but immediate HOME when showing in the scan.
  • Reduce load on Domoticz API when BACKOFF period is lowered

Post Reply

Who is online

Users browsing this forum: No registered users and 0 guests