ansible/roles/bluetooth-monitor/templates/my_btmonitor.py

97 lines
3.2 KiB
Python
Raw Normal View History

2024-03-01 15:01:08 +01:00
#!/usr/bin/env python3
import asyncio
from bleak import BleakScanner
from bleak.assigned_numbers import AdvertisementDataType
from bleak.backends.bluezdbus.advertisement_monitor import OrPattern
from bleak.backends.bluezdbus.scanner import BlueZScannerArgs
from functools import partial
import asyncio_mqtt
import json
2024-07-28 08:45:01 +02:00
from datetime import datetime
import os
import time
2024-03-01 15:01:08 +01:00
# ------------------- Config ----------------------------------------------------------------
config = {
"mqtt": {
"hostname": "homeassistant.fritz.box",
"username": "{{my_btmonitor_mqtt_username}}",
"password": "{{my_btmonitor_mqtt_password}}",
"room": "{{sensor_room_name_ascii}}"
},
2024-07-28 08:45:01 +02:00
"watchdog_seconds": {{my_bt_monitor_watchdog_seconds | default(None)}},
"restart_ble_interface": {{my_btmonitor_restart_ble_interface | default(None)}},
2024-03-01 15:01:08 +01:00
}
2024-07-28 08:45:01 +02:00
stop_event = asyncio.Event()
time_last_package_received = datetime.now()
2024-03-01 15:01:08 +01:00
2024-07-28 08:45:01 +02:00
async def on_device_found_callback(mqtt_client, room, device, advertising_data):
global time_last_package_received
time_last_package_received = datetime.now()
2024-03-01 15:01:08 +01:00
rssi = advertising_data.rssi
tx_power = advertising_data.tx_power
2024-03-29 09:32:59 +01:00
if tx_power is not None and rssi is not None:
topic = f"my_btmonitor/raw_measurements/{room}"
data = {"address": device.address,
2024-03-01 15:01:08 +01:00
"rssi": rssi,
2024-03-29 09:32:59 +01:00
"tx_power": tx_power}
2024-03-08 13:02:55 +01:00
try:
await mqtt_client.publish(topic, json.dumps(data).encode())
except Exception:
print("Probably mqtt isn't running - exit whole script and let systemd restart it")
exit(1)
2024-03-01 15:01:08 +01:00
2024-07-28 08:45:01 +02:00
async def watchdog():
global time_last_package_received
timeout = config["watchdog_seconds"]
if not timeout or timeout <= 0:
return
while True:
restart = (datetime.now() - time_last_package_received).seconds > timeout
if restart:
stop_event.set()
await asyncio.sleep(60)
2024-03-01 15:01:08 +01:00
2024-07-28 08:45:01 +02:00
async def ble_scan():
2024-03-01 15:01:08 +01:00
mqtt_conf = config['mqtt']
while True:
try:
async with asyncio_mqtt.Client(hostname=mqtt_conf["hostname"],
2024-03-29 09:32:59 +01:00
username=mqtt_conf["username"],
password=mqtt_conf['password']) as mqtt_client:
2024-07-28 08:45:01 +02:00
cb = partial(on_device_found_callback, mqtt_client, mqtt_conf['room'])
2024-03-01 15:01:08 +01:00
active_scan = True
if active_scan:
async with BleakScanner(cb) as scanner:
await stop_event.wait()
else:
# Doesn't work, because of the strange or_patters
args = BlueZScannerArgs(
or_patterns=[OrPattern(0, AdvertisementDataType.MANUFACTURER_SPECIFIC_DATA, b"\x00\x4c")]
)
async with BleakScanner(cb, bluez=args, scanning_mode="passive") as scanner:
await stop_event.wait()
except Exception as e:
print("Error", e)
print("Starting again")
2024-07-28 08:45:01 +02:00
async def main():
await asyncio.gather(ble_scan(), watchdog())
if __name__ == "__main__":
restart_interface = config["restart_ble_interface"]
if restart_interface:
print(f"Restarting {restart_interface}")
os.system(f"hciconfig {restart_interface} down")
time.sleep(3)
os.system(f"hciconfig {restart_interface} up")
time.sleep(3)
print("Done")
asyncio.run(main())