3

my ultimate goal is to allow my raspberry pi detect when my iphone or pebble watch is nearby. I am presently focusing on the pebble as I believe iphone randomizes the MAC address. I have the static MAC address of the pebble watch.

My question is how to detect the presence of the MAC address through bluetooth?

I have tried hcitool rssi [mac address] or l2ping [mac address] however both needs a confirmation of connection on the watch before any response. I want it to be automatic...

I also tried hcitool scan, but it takes awhile, presumably it is going through all possibilities. I simply want to search for a particular Mac Address.

EDIT: I just tried "hcitool name [Mac Address]" which return the name of the device and if not there it returns a "null" so this is the idea... is there a python equivalent of this?

I am new to python, so hopefully someone can point to how I can simply ping the mac address and see how strong the RSSI value is?

user1117057
  • 57
  • 1
  • 1
  • 8

5 Answers5

5

Apple iDevices do use private resolvable addresses with Bluetooth Low Energy (BLE). They cycle to a different address every ~15 minutes. Only paired devices that have a so called Identity Resolving Key can "decipher" these seemingly random addresses and associate them back to the paired device.

So to do something like this with your iPhone, you need to pair it with your raspberry pi. Then what you can do is make a simple iOS app that advertises some data (what does not matter because when the app is backgrounded, only iOS itself gets to put data into the advertising packet). On the raspberry pi you can then use hcitool lescan to scan for the BLE advertisements. If the address of the advertisement can be resolved using the IRK, you know with high certainty that it's the iPhone. I'm not sure if hcitool does any IRK math out of the box, but the resolving algorithm is well specified by the Bluetooth spec.

Pebble currently does indeed use a fixed address. However, it is only advertising when it is disconnected from the phone it is supposed to be connected to. So, for your use case, using its BLE advertisements is not very useful. Currently, there is no API in the Pebble SDK to allow an app on the Pebble to advertise data.


FWIW, the commands you mentioned are useful only for Bluetooth 2.1 ("Classic") and probably only useful if the other device is discoverable (basically never, unless it's in the Settings / Bluetooth menu).

Martijn Thé
  • 4,674
  • 3
  • 29
  • 42
  • Great detailed reply! – sarfata Dec 10 '14 at 22:45
  • Martijn, your detailed explanation for both iphone and pebble are excellent. Now, I know why I am having so much trouble, I learned the hard way! However, what has got me frustrated as a few days ago I had a working setup. Working meaning I had the RPi sense my iphone/pebble(I thought it was my iphone, but could have been my pebble as I forgot it was on my wrist) – user1117057 Dec 11 '14 at 08:49
  • I had it accurate turning off/on an LED. However my os got corrupted and now I can't repeat the setup. between working setup and new setup, the pebble got updated to the 2.8 firmware which changed it to BLE. So when you mention that pebble does not advertise, does this refer to only 2.8 and not previous firmwares? Because I can't make sense of how I had a working system given iphone or pebble does not adverstise. unless I had somehow forced background on ibeacon app from Radius network. Any clue of how I was able to work it before? – user1117057 Dec 11 '14 at 08:49
  • The Pebble will only advertise when there is a reason to do so. Generally, the reason is to reconnect to the (iOS) phone. If it's already connected, it will not advertise. Also note I mentioned that Pebble *currently* uses a fixed address. This might change in the future for improved security. – Martijn Thé Mar 30 '15 at 02:51
3

Thanks to Chris, i built my own script which detects if my phone is in range and locks/unlocks the screen, if the device is still away after a timeout of five seconds. It's a bit quick and dirty, but works for me :)

#!/bin/bash
#################################################################
# Check if Bluetooth device is in range and lock/unlock screen. #
#################################################################

MAC=AA:BB:CC:DD:EE:FF
TIMEOUT=5
DEBUG=0

LASTSEEN=0
STATUS=0
PREVSTATUS=0
while true; do
    DT="[$(date '+%F %T')]"
    pgrep xscreensaver >/dev/null || xscreensaver -no-splash >/dev/null     2>&1 &
    if [ -z "$RSSI" ]; then
        sudo hcitool cc $MAC 2>/dev/null
    fi
    RSSI=$(sudo hcitool rssi $MAC 2>/dev/null | cut -d ' ' -f4)

    [ $DEBUG -gt 0 ] && echo "$DT RSSI: $RSSI"

    if [[ -n "$RSSI" && $RSSI -gt 0  ]]; then
        LASTSEEN=$(date '+%s')
    fi

    if [[ $RSSI -eq 0 && $((`date '+%s'`-$LASTSEEN)) -gt $TIMEOUT ]]; then
        STATUS=0
        [ $DEBUG -gt 0 ] && echo "$DT Status: $STATUS Lastseen: $LASTSEEN     Timeout: $((`date '+%s'`-$LASTSEEN))"
    else
        STATUS=1
        [ $DEBUG -gt 0 ] && echo "$DT Status: $STATUS Lastseen: $LASTSEEN     Timeout: $((`date '+%s'`-$LASTSEEN))"
    fi

    if [ $STATUS -ne $PREVSTATUS ]; then
        PREVSTATUS=$STATUS
        if [ $STATUS -gt 0 ]; then
            [ $DEBUG -gt 0 ] && echo "$DT UnLock"
            pgrep xscreensaver >/dev/null && xscreensaver-command     -deactivate
            xset dpms force on
            pgrep xscreensaver >/dev/null && pkill xscreensaver
        else    
            [ $DEBUG -gt 0 ] && echo "$DT Lock"
            pgrep xscreensaver >/dev/null && xscreensaver-command -lock
        fi
    fi

    [ $DEBUG -gt 0 ] && sleep 1
done

One may need to add one line to /etc/sudoers:

username ALL = NOPASSWD: /usr/bin/hcitool

Maybe this helps someone. Cheers!

=========================

UPDATE 26.09.2017 !

I updated this a bit and wrote a Python script which detects the connected bluetooth device via DBus. Therefore the BT device should be paired first. The script also tries to reconnect to the device if the connection was lost. This is because some devices do not reconnect by themselves (as my cellphone does). This script does not read the RSSI signal strength because the DBus on my system does not report it (dunno why). Because I'm under Gnome I use org.gnome.ScreenSaver as DBus interface to lock the screen. If you are on KDE or whatever you might want to change this in the code.

#!/usr/local/bin/python3
# encoding: utf-8
'''
bluescreen -- Locks your screen
bluescreen is a little python script which locks your screen as long as a bluetooth device is disconnected.
It also unlocks the screen when you return.
It uses the DBus to check if the device is connected and it locks the screen through DBus message calls.
The script uses the first BT adapter found in the system, mainly "hci0". This might be incorrect on some systems.
If so, check the source code below and do the necessary changes.

@author:     Evil2000
@copyright:  2017 Evil2000
@license:    LGPL
@contact:    evil.2000@web.de
@deffield    updated: 26.09.2017
'''

import time
import dbus
from dbus.mainloop.glib import DBusGMainLoop
from gi.repository import GObject as gobject
from pprint import pprint

'''
Debug flag should be clear
1 = Verbose
2 = Debug
'''
DEBUG = 0

'''
The BT MAC address of the device to monitor
'''
MAC = "11:22:33:AA:BB:CC"

''' =================================================================================================================== '''

# Replace : with _ in device MAC address
DEV_ID = MAC.replace(":", "_")
# Access the DBus main loop
dbus_loop = DBusGMainLoop()
# Connect to the system bus
sysbus = dbus.SystemBus(mainloop=dbus_loop)
# Retrieve the device BlueZ object
device = sysbus.get_object('org.bluez', "/org/bluez/hci0/dev_" + DEV_ID)

# Read the property if the device is connected
deviceConnected = device.Get("org.bluez.Device1", "Connected", dbus_interface='org.freedesktop.DBus.Properties')

if DEBUG > 1:
    pprint(deviceConnected)

# Connect to the session bus
sesbus = dbus.SessionBus(mainloop=dbus_loop)
# Get the screen saver object
sSaver = sesbus.get_object('org.gnome.ScreenSaver', "/org/gnome/ScreenSaver")

# Lock the screen and start the screen saver (i.e. turn off the screen) if it isn't already
def lockScreen():
    if not sSaver.GetActive(dbus_interface='org.gnome.ScreenSaver'):
        if DEBUG:
            print("["+time.strftime('%Y-%m-%d %H:%M:%S')+"] Locking Screen")
        sSaver.Lock(dbus_interface='org.gnome.ScreenSaver')

# Try to connect to the device if it got disconnected. This is called from gobject.timeout_add_seconds() below
def tryConnect():
    if not deviceConnected:
        if DEBUG:
            print("["+time.strftime('%Y-%m-%d %H:%M:%S')+"] Trying device reconnect")
        device.Connect(dbus_interface='org.bluez.Device1')
    return True

# The callback function from connect_to_signal. This handles the events sent by the DBus.
def cb(*args, **kwargs):
    iface = args[0]
    chgprop = args[1]
    #extra = args[2]
    if DEBUG > 1:
        pprint(iface)
        pprint(chgprop)

    # chgprop contains a dictionary with the "Connected" key
    # If it is present and the interface in which the event triggered is Device1, then...
    if iface == "org.bluez.Device1" and "Connected" in chgprop:
        # ... lock screen if device is NOT connected, otherwise disable the screen saver
        if chgprop['Connected'] == True:
            print("["+time.strftime('%Y-%m-%d %H:%M:%S')+"] connected")
            deviceConnected = True
            sSaver.SetActive(False, dbus_interface='org.gnome.ScreenSaver')
        else:
            print("["+time.strftime('%Y-%m-%d %H:%M:%S')+"] disconnected")
            deviceConnected = False
            lockScreen()

# Register a callback function which is triggered if the properties of the bluetooth device changes.
device.connect_to_signal("PropertiesChanged", cb, dbus_interface=None, interface_keyword='iface', member_keyword='member', path_keyword='path', sender_keyword="sender", destination_keyword="dest", message_keyword="message")

# Every 3 seconds, call the tryConnect function
gobject.timeout_add_seconds(3, tryConnect)

# Now, start the main loop.
loop = gobject.MainLoop()
loop.run()

# EOF
Community
  • 1
  • 1
Evil.2000
  • 43
  • 5
0

Check PyBluez.

To detect the nearby nearby devices there is an example scrip on PyBluez website. Check inquiry.py

Another idea would be to use what you have already tried hcitool, but using subprocess to have everything is Python.

sk11
  • 1,779
  • 1
  • 17
  • 29
0

I'm using this code with my iPhone 7 and Raspberry Pi and it works great. The iPhone bluetooth MAC address is static.

#!/bin/bash

sudo hcitool cc AA:BB:CC:DD:EE:FF 2> /dev/null

while true
do
    bt=$(hcitool rssi AA:BB:CC:DD:EE:FF 2> /dev/null)
    if [ "$bt" == "" ]; then
        sudo hcitool cc AA:BB:CC:DD:EE:FF  2> /dev/null
        bt=$(hcitool rssi AA:BB:CC:DD:EE:FF 2> /dev/null)
    fi

    echo "$bt"
done
Chris
  • 41
  • 1
  • 2
0

Thank you all, inspired on your answers, from my raspberry pi:

1) to avoid sudo

sudo setcap cap_net_raw+ep /usr/bin/hcitool 

2) to find a device

hcitool cc "$mac" 2>/dev/null && hcitool rssi "$mac" 2>/dev/null && echo "found $mac"
iojancode
  • 610
  • 6
  • 7