Bt monitor
This commit is contained in:
parent
e9ec94a5f8
commit
9092f08481
|
@ -1,5 +1,8 @@
|
|||
{
|
||||
"python.linting.pylintEnabled": false,
|
||||
"python.linting.enabled": true,
|
||||
"python.linting.flake8Enabled": true
|
||||
"python.linting.flake8Enabled": true,
|
||||
"files.associations": {
|
||||
"*.yaml": "home-assistant"
|
||||
}
|
||||
}
|
|
@ -12,12 +12,16 @@ all:
|
|||
sensor_room_name_ascii: prusaprinter
|
||||
sensor_room_name: prusaprinter
|
||||
dht_pin: 26
|
||||
main_user: root
|
||||
bedroompi:
|
||||
squeezelite_name: BedroomPi
|
||||
shairport_name: BedroomPi
|
||||
alsa_card_name: Codec
|
||||
sensor_room_name_ascii: schlafzimmer
|
||||
sensor_room_name: Schlafzimmer
|
||||
my_bt_monitor_watchdog_seconds: 600
|
||||
my_btmonitor_restart_ble_interface: hci0
|
||||
main_user: root
|
||||
kitchenpi:
|
||||
squeezelite_name: KitchenPi
|
||||
shairport_name: KitchenPi
|
||||
|
@ -25,6 +29,7 @@ all:
|
|||
sensor_room_name_ascii: kueche
|
||||
sensor_room_name: Küche
|
||||
hifiberry_overlay: hifiberry-amp
|
||||
main_user: root
|
||||
esszimmerradio: # oben, eltern
|
||||
squeezelite_name: Esszimmer
|
||||
shairport_name: _Oben_Esszimmer
|
||||
|
@ -32,6 +37,7 @@ all:
|
|||
squeezeserver: 192.168.178.100
|
||||
configure_wifi: true
|
||||
alsa_card_name: 1
|
||||
main_user: root
|
||||
musikserverwohnzimmeroben: # oben, eltern
|
||||
squeezelite_name: Wohnzimmer
|
||||
shairport_name: _Oben_Wohnzimmer
|
||||
|
@ -40,6 +46,7 @@ all:
|
|||
sensor_room_name_ascii: wohnzimmeroben
|
||||
sensor_room_name: WohnzimmerOben
|
||||
hifiberry_overlay: hifiberry-dacplus
|
||||
main_user: root
|
||||
musicmouse:
|
||||
squeezelite_name: MusicMouse
|
||||
shairport_name: MusicMouse
|
||||
|
@ -47,6 +54,7 @@ all:
|
|||
hifiberry_overlay: hifiberry-dacplus
|
||||
sensor_room_name: Kinderzimmer
|
||||
sensor_room_name_ascii: kinderzimmer
|
||||
main_user: root
|
||||
newrpi:
|
||||
squeezelite_name: MyTestRaspberry
|
||||
shairport_name: MyTestRaspberry
|
||||
|
@ -57,6 +65,9 @@ all:
|
|||
server:
|
||||
sensor_room_name: Arbeitszimmer
|
||||
sensor_room_name_ascii: arbeitszimmer
|
||||
my_bt_monitor_watchdog_seconds: 600
|
||||
my_btmonitor_restart_ble_interface: hci0
|
||||
main_user: core
|
||||
homeassistant:
|
||||
sensor_room_name: Anschlussraum
|
||||
sensor_room_name_ascii: anschlussraum
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
function dot -w git -d "Manages dotfiles"
|
||||
git --git-dir=$HOME/.dot --work-tree=$HOME $argv
|
||||
end
|
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
#
|
||||
- name: Install packages
|
||||
apt:
|
||||
name:
|
||||
- bat
|
||||
- broot
|
||||
- duf
|
||||
- fd-find
|
||||
- fish
|
||||
- fzf
|
||||
- git
|
||||
- glances
|
||||
- lsd
|
||||
- neovim
|
||||
- ripgrep
|
||||
- tmux
|
||||
- zoxide
|
||||
|
||||
# TODO: Dust, btm
|
||||
|
||||
block:
|
||||
become: true
|
||||
become_user: {{main_user}}
|
||||
- name: Create bin folder
|
||||
ansible.builtin.file:
|
||||
path: ~/bin
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
# Oh-my-fish
|
||||
- name: get oh-my-fish repo
|
||||
git:
|
||||
repo: 'https://github.com/oh-my-fish/oh-my-fish.git'
|
||||
dest: ~/.local/share/git/oh-my-fish
|
||||
- name: install oh-my-fish
|
||||
shell:
|
||||
cmd: "bin/install --offline --noninteractive"
|
||||
executable: /usr/bin/fish
|
||||
chdir: ~/.local/share/git/oh-my-fish
|
||||
creates:
|
||||
- ~/.local/share/omf
|
||||
- ~/.config/omf
|
||||
- copy:
|
||||
content: "bobthefish"
|
||||
dst: "~/.config/omf/theme"
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -4,13 +4,12 @@ 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 Cryptodome.Cipher import AES
|
||||
from functools import partial
|
||||
import asyncio_mqtt
|
||||
import json
|
||||
from typing import Dict
|
||||
import collections
|
||||
import numpy as np
|
||||
from datetime import datetime
|
||||
import os
|
||||
import time
|
||||
|
||||
# ------------------- Config ----------------------------------------------------------------
|
||||
|
||||
|
@ -21,95 +20,21 @@ config = {
|
|||
"password": "{{my_btmonitor_mqtt_password}}",
|
||||
"room": "{{sensor_room_name_ascii}}"
|
||||
},
|
||||
"irk_to_devicename": {
|
||||
"aa67542b82c0e05d65c27fb7e313aba5": "martins_apple_watch",
|
||||
"840e3892644c1ebd1594a9069c14ce0d" : "martins_iphone",
|
||||
}
|
||||
"watchdog_seconds": {{my_bt_monitor_watchdog_seconds | default(None)}},
|
||||
"restart_ble_interface": {{my_btmonitor_restart_ble_interface | default(None)}},
|
||||
}
|
||||
|
||||
# --------------------------------------------------------------------------------------------
|
||||
|
||||
def resolve_rpa(rpa: bytes, irk: bytes) -> bool:
|
||||
"""Compares the random address rpa to an irk (secret key) and return True if it matches"""
|
||||
assert len(rpa) == 6
|
||||
assert len(irk) == 16
|
||||
|
||||
key = irk
|
||||
plain_text = b'\x00' * 16
|
||||
plain_text = bytearray(plain_text)
|
||||
plain_text[15] = rpa[3]
|
||||
plain_text[14] = rpa[4]
|
||||
plain_text[13] = rpa[5]
|
||||
plain_text = bytes(plain_text)
|
||||
|
||||
cipher = AES.new(key, AES.MODE_ECB)
|
||||
cipher_text = cipher.encrypt(plain_text)
|
||||
return cipher_text[15] == rpa[0] and cipher_text[14] == rpa[1] and cipher_text[13] == rpa[2]
|
||||
stop_event = asyncio.Event()
|
||||
time_last_package_received = datetime.now()
|
||||
|
||||
|
||||
def addr_to_bytes(addr:str) -> bytes:
|
||||
"""Converts a bluetooth mac address string with semicolons to bytes"""
|
||||
str_without_colons = addr.replace(":", "")
|
||||
bytearr = bytearray.fromhex(str_without_colons)
|
||||
bytearr.reverse()
|
||||
return bytes(bytearr)
|
||||
|
||||
|
||||
def decode_address(addr: str, irks: Dict[str, str]):
|
||||
"""
|
||||
addr is a bluetooth address as a string e.g. 4d:24:12:12:34:10
|
||||
irks is dict with irk as a hex string, mapping to device name
|
||||
"""
|
||||
for irk, name in irks.items():
|
||||
if resolve_rpa(addr_to_bytes(addr), bytes.fromhex(irk)):
|
||||
return name
|
||||
return None
|
||||
|
||||
|
||||
def estimate_distance(rssi, tx_power, pl0=73):
|
||||
"""
|
||||
RSSI in dBm
|
||||
txPower is a transmitter parameter that calculated according to its physic layer and antenna in dBm
|
||||
Return value in meter
|
||||
|
||||
You should calculate "PL0" in calibration stage:
|
||||
PL0 = txPower - RSSI; // When distance is distance0 (distance0 = 1m or more)
|
||||
|
||||
SO, RSSI will be calculated by below formula:
|
||||
RSSI = txPower - PL0 - 10 * n * log(distance/distance0) - G(t)
|
||||
G(t) ~= 0 //This parameter is the main challenge in achiving to more accuracy.
|
||||
n = 2 (Path Loss Exponent, in the free space is 2)
|
||||
distance0 = 1 (m)
|
||||
distance = 10 ^ ((txPower - RSSI - PL0 ) / (10 * n))
|
||||
|
||||
Read more details:
|
||||
https://en.wikipedia.org/wiki/Log-distance_path_loss_model
|
||||
"""
|
||||
n = 3.5
|
||||
return 10**((tx_power - rssi - pl0) / (10 * n))
|
||||
|
||||
|
||||
def smooth(y, box_pts):
|
||||
box = np.ones(box_pts)/box_pts
|
||||
y_smooth = np.convolve(y, box, mode='same')
|
||||
return y_smooth
|
||||
|
||||
WINDOW_SIZE = 6
|
||||
last_values = collections.deque(maxlen=WINDOW_SIZE)
|
||||
def filter_distance(dist: float):
|
||||
global last_values
|
||||
last_values.append(dist)
|
||||
return smooth(np.array(last_values), 12)[-1]
|
||||
|
||||
|
||||
async def on_device_found_callback(irks, mqtt_client, room, device, advertising_data):
|
||||
#decoded_device_id = decode_address(device.address, irks)
|
||||
async def on_device_found_callback(mqtt_client, room, device, advertising_data):
|
||||
global time_last_package_received
|
||||
time_last_package_received = datetime.now()
|
||||
rssi = advertising_data.rssi
|
||||
tx_power = advertising_data.tx_power
|
||||
if tx_power is not None and rssi is not None:
|
||||
topic = f"my_btmonitor/raw_measurements/{room}"
|
||||
#distance = estimate_distance(rssi, tx_power, {{my_btmonitor_pl0 | default('73')}} )
|
||||
#filtered_distance = filter_distance(distance)
|
||||
data = {"address": device.address,
|
||||
"rssi": rssi,
|
||||
"tx_power": tx_power}
|
||||
|
@ -118,19 +43,28 @@ async def on_device_found_callback(irks, mqtt_client, room, device, advertising_
|
|||
except Exception:
|
||||
print("Probably mqtt isn't running - exit whole script and let systemd restart it")
|
||||
exit(1)
|
||||
#print(data)
|
||||
|
||||
|
||||
async def main():
|
||||
stop_event = asyncio.Event()
|
||||
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)
|
||||
|
||||
|
||||
async def ble_scan():
|
||||
mqtt_conf = config['mqtt']
|
||||
while True:
|
||||
try:
|
||||
async with asyncio_mqtt.Client(hostname=mqtt_conf["hostname"],
|
||||
username=mqtt_conf["username"],
|
||||
password=mqtt_conf['password']) as mqtt_client:
|
||||
cb = partial(on_device_found_callback, config['irk_to_devicename'], mqtt_client, mqtt_conf['room'])
|
||||
cb = partial(on_device_found_callback, mqtt_client, mqtt_conf['room'])
|
||||
active_scan = True
|
||||
if active_scan:
|
||||
async with BleakScanner(cb) as scanner:
|
||||
|
@ -146,4 +80,18 @@ async def main():
|
|||
print("Error", e)
|
||||
print("Starting again")
|
||||
|
||||
asyncio.run(main())
|
||||
|
||||
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())
|
|
@ -1,36 +0,0 @@
|
|||
---
|
||||
- name: Install packages
|
||||
apt:
|
||||
name:
|
||||
- bat
|
||||
- fish
|
||||
- fzf
|
||||
- fd-find
|
||||
- ripgrep
|
||||
- lsd
|
||||
- zoxide
|
||||
|
||||
- name: Check if oh-my-fish is installed
|
||||
stat:
|
||||
path: '/etc/omf.installed'
|
||||
register: omf
|
||||
|
||||
- name: Download omf installer
|
||||
get_url:
|
||||
url: https://raw.githubusercontent.com/oh-my-fish/oh-my-fish/master/bin/install
|
||||
|
||||
- name: Execute omf installer
|
||||
shell: /usr/bin/fish /tmp/install --noninteractive
|
||||
|
||||
- name: Execute omf installer
|
||||
shell: /usr/bin/fish -c omf install
|
||||
|
||||
- name: Remove the omf installer
|
||||
file:
|
||||
path: /tmp/install
|
||||
state: absent
|
||||
|
||||
- name: Mark oh-my-fish installed with /etc/omf.installed
|
||||
file:
|
||||
path: /etc/omf.installed
|
||||
state: touch
|
Loading…
Reference in New Issue