Initial commit
This commit is contained in:
commit
26a396f8da
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.pyenv/
|
||||||
|
.vscode/
|
||||||
|
__pycache__/
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "bluetooth_utils"]
|
||||||
|
path = bluetooth_utils
|
||||||
|
url = https://github.com/colin-guyon/py-bluetooth-utils.git
|
24
Dockerfile
Normal file
24
Dockerfile
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
FROM python:3.8-slim-buster
|
||||||
|
|
||||||
|
# Keeps Python from generating .pyc files in the container
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE 1
|
||||||
|
|
||||||
|
# Turns off buffering for easier container logging
|
||||||
|
ENV PYTHONUNBUFFERED 1
|
||||||
|
|
||||||
|
RUN apt update
|
||||||
|
RUN apt install -y gcc libbluetooth-dev bluez bluetooth
|
||||||
|
RUN apt clean
|
||||||
|
|
||||||
|
ADD requirements.txt .
|
||||||
|
RUN python -m pip install -r requirements.txt
|
||||||
|
|
||||||
|
RUN useradd -d /app -m appuser
|
||||||
|
WORKDIR /app
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
COPY get_data.py /app
|
||||||
|
COPY bluetooth_utils /app/bluetooth_utils
|
||||||
|
COPY entrypoint.sh /app
|
||||||
|
|
||||||
|
ENTRYPOINT ["/bin/bash", "/app/entrypoint.sh"]
|
1
bluetooth_utils
Submodule
1
bluetooth_utils
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 416a8925682d6ee4ad607936d897646a3a83a331
|
18
docker-compose.yaml
Normal file
18
docker-compose.yaml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
xiaomi2mqtt:
|
||||||
|
image: xiaomi2mqtt:latest
|
||||||
|
container_name: xiaomi2mqtt
|
||||||
|
user: root:root
|
||||||
|
environment:
|
||||||
|
- TZ=Europe/London
|
||||||
|
- MQTT_HOST=mqtt.lan
|
||||||
|
devices:
|
||||||
|
- /dev/bus/usb
|
||||||
|
network_mode: host
|
||||||
|
cap_add:
|
||||||
|
- SYS_ADMIN
|
||||||
|
- CAP_SYSLOG
|
||||||
|
- NET_ADMIN
|
||||||
|
restart: unless-stopped
|
6
entrypoint.sh
Normal file
6
entrypoint.sh
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
service dbus start
|
||||||
|
service bluetooth start
|
||||||
|
|
||||||
|
python get_data.py ${@}
|
87
get_data.py
Normal file
87
get_data.py
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
"""
|
||||||
|
Read Xiaomi LYWSD03MMC advertised packets and send them to MQTT
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
import bluetooth._bluetooth as bluez
|
||||||
|
import paho.mqtt.publish as publish
|
||||||
|
|
||||||
|
from bluetooth_utils.bluetooth_utils import (disable_le_scan, enable_le_scan,
|
||||||
|
parse_le_advertising_events,
|
||||||
|
raw_packet_to_str, toggle_device)
|
||||||
|
|
||||||
|
|
||||||
|
def send_to_mqtt(data: dict):
|
||||||
|
"""
|
||||||
|
Send data from LYWSD03MMC to MQTT
|
||||||
|
"""
|
||||||
|
msgs = [
|
||||||
|
{
|
||||||
|
'topic': f'sensors/home/{data["mac"]}/temperature',
|
||||||
|
'payload': data['temperature'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'topic': f'sensors/home/{data["mac"]}/humidity',
|
||||||
|
'payload': data['humidity'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'topic': f'sensors/home/{data["mac"]}/battery',
|
||||||
|
'payload': data['battery'],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
publish.multiple(msgs, hostname=os.environ.get('MQTT_HOST', 'mqtt'))
|
||||||
|
|
||||||
|
|
||||||
|
def le_advertise_packet_handler(mac: str, _, data: bytes, __):
|
||||||
|
"""
|
||||||
|
Handle new Xiaomi LYWSD03MMC BTLE advertise packet
|
||||||
|
"""
|
||||||
|
data_str = raw_packet_to_str(data)
|
||||||
|
# Check for ATC preamble
|
||||||
|
if data_str[0:2] == '11' and data_str[6:10] == '1a18':
|
||||||
|
temp = int(data_str[22:26], 16) / 10
|
||||||
|
hum = int(data_str[26:28], 16)
|
||||||
|
batt = int(data_str[28:30], 16)
|
||||||
|
send_to_mqtt(
|
||||||
|
{
|
||||||
|
'timestamp': datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
'mac': mac,
|
||||||
|
'temperature': temp,
|
||||||
|
'humidity': hum,
|
||||||
|
'battery': batt,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""
|
||||||
|
Main program
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Use 0 for hci0
|
||||||
|
dev_id = 0
|
||||||
|
toggle_device(dev_id, True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
sock = bluez.hci_open_dev(dev_id) # pylint: disable=c-extension-no-member
|
||||||
|
except:
|
||||||
|
print(f'Cannot open bluetooth device {dev_id}')
|
||||||
|
raise
|
||||||
|
|
||||||
|
# Set filter to "True" to see only one packet per device
|
||||||
|
enable_le_scan(sock, filter_duplicates=False)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Called on new LE packet
|
||||||
|
parse_le_advertising_events(
|
||||||
|
sock, handler=le_advertise_packet_handler, debug=False
|
||||||
|
)
|
||||||
|
# Scan until Ctrl-C
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
disable_le_scan(sock)
|
||||||
|
|
||||||
|
|
||||||
|
main()
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
paho-mqtt
|
||||||
|
pybluez
|
Loading…
Reference in a new issue