-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathbluetooth_passive_le_tracker.py
More file actions
135 lines (107 loc) · 4.28 KB
/
Copy pathbluetooth_passive_le_tracker.py
File metadata and controls
135 lines (107 loc) · 4.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
"""Tracking for bluetooth low energy devices."""
import logging
from datetime import timedelta
import threading
import voluptuous as vol
from homeassistant.components.device_tracker import (
YAML_DEVICES, CONF_TRACK_NEW, CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL,
PLATFORM_SCHEMA, load_config, DEFAULT_TRACK_NEW
)
from homeassistant.const import (EVENT_HOMEASSISTANT_STOP)
import homeassistant.util as util
import homeassistant.util.dt as dt_util
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
logging.getLogger(__name__).setLevel(logging.DEBUG)
REQUIREMENTS = ['pygatt==3.0.1']
BLE_PREFIX = 'BLE_'
MIN_SEEN_NEW = 5
CONF_BLUETOOTH_DEVICE = "device_id"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_BLUETOOTH_DEVICE, default="hci0"): cv.string
})
def setup_scanner(hass, config, see, discovery_info):
"""Setup the Bluetooth LE Scanner."""
import pygatt
class Monitor(threading.Thread):
"""Connection handling."""
def __init__(self, hass, see):
"""Construct interface object."""
threading.Thread.__init__(self)
self.daemon = False
self.hass = hass
self.keep_going = True
self.event = threading.Event()
def mycallback(self, address):
print("device %s" % address)
see_device(address)
def run(self):
"""Thread that keeps connection alive."""
import pygatt
adapter = pygatt.backends.GATTToolBackend()
adapter.passive_scan(1, callback=self.mycallback)
def terminate(self):
"""Signal runner to stop and join thread."""
self.keep_going = False
self.event.set()
self.join()
def see_device(address):
"""Mark a device as seen."""
if address in devs_donot_track:
return;
if track_new and address not in devs_to_track and address not in devs_donot_track:
if address in new_devices:
_LOGGER.debug("Seen %s %s times", address, new_devices[address])
new_devices[address] += 1
if new_devices[address] >= MIN_SEEN_NEW:
_LOGGER.debug("Adding %s to tracked devices", address)
devs_to_track.append(address)
else:
return
else:
_LOGGER.debug("Seen %s for the first time", address)
new_devices[address] = 1
return
if address in devs_to_track:
see(mac=BLE_PREFIX + address)
def discover_ble_devices():
"""Discover Bluetooth LE devices."""
_LOGGER.debug("Discovering Bluetooth LE devices")
try:
service = DiscoveryService(ble_dev_id)
devices = service.discover(duration)
_LOGGER.debug("Bluetooth LE devices discovered = %s", devices)
except RuntimeError as error:
_LOGGER.error("Error during Bluetooth LE scan: %s", error)
devices = []
return devices
def monitor_stop(_service_or_event):
"""Stop the monitor thread."""
_LOGGER.info("Stopping monitor for")
mon.terminate()
new_devices = {}
yaml_path = hass.config.path(YAML_DEVICES)
ble_dev_id = config.get(CONF_BLUETOOTH_DEVICE)
devs_to_track = []
devs_donot_track = []
# Load all known devices.
for device in load_config(yaml_path, hass, 0):
# check if device is a valid bluetooth device
if device.mac and device.mac[:4].upper() == BLE_PREFIX:
if device.track:
_LOGGER.debug("Adding %s to BLE tracker", device.mac)
devs_to_track.append(device.mac[4:])
else:
_LOGGER.debug("Adding %s to BLE do not track", device.mac)
devs_donot_track.append(device.mac[4:])
# if track new devices is true discover new devices
# on every scan.
track_new = util.convert(config.get(CONF_TRACK_NEW), bool,
DEFAULT_TRACK_NEW)
if not devs_to_track and not track_new:
_LOGGER.warning("No Bluetooth LE devices to track!")
return False
mon = Monitor(hass, see)
mon.start()
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, monitor_stop)
return True