Initial commit

This commit is contained in:
parra 2022-08-11 18:12:20 +02:00 committed by Parra
commit 4519d1ece8
5 changed files with 205 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
*.bak
unstable-*
*.log
.env

34
README.md Normal file
View file

@ -0,0 +1,34 @@
# Bluetooth LE to MQTT bridge for the Xiaomi Mijia Temperature & Humidity sensor
## Create environment file
Copy the `env_file` to `.env` and open it. Complete all environment variables:
- `MQTT_[SERVER/PORT/USER/PASSWORD/CLIENT_ID]` are used to connect with the broker.
- `MQTT_TOPIC_PREFIX` define the prefix for all topics with sensor info.
- `MQTT_TELE_PREFIX` is used to publish the sensor data, like battery or status.
- `MQTT_SENSOR_NAME` contains the sensor name, useful to split the telemetry data if you have more than one sensors.
- `MQTT_PUBLISH_DELAY` specify, in seconds, how many time should wait since the script take the measurements to publish in the broker
- `MIJIA_BTLE_ADDRESS` constant with the BLE address of your Mijia device.. This can be retrieved activating the pairing mode in the sensor and scanning the BT devices
## Install dependencies
You'll need to install bluez and python3. Then you'll need pip3 to install bluepy.
Example on a Raspberry Pi 3:
```sh
$ sudo apt-get install python-pip libglib2.0-dev
$ sudo pip3 install -r requirements.txt
```
## Run
You can execute the script directly using the command:
```sh
$ ./main.py
```
Or you can add a new entry in the `crontab`, like:
```sh
*/20 * * * * /usr/bin/python3 ~/scripts/mijia-temperature/main.py >~/scripts/mijia-temperature/last.log 2>&1
```

22
env_file Normal file
View file

@ -0,0 +1,22 @@
# Broker configuration
MQTT_SERVER=
MQTT_PORT=
MQTT_USER=
MQTT_PASSWORD=
MQTT_CLIENT_ID=
# Topic configuration
MQTT_TOPIC_PREFIX=home/sensor
MQTT_TELE_PREFIX=home/tele
MQTT_SENSOR_NAME=mijia-salon
MQTT_TOPIC_HUMIDITY=${MQTT_TOPIC_PREFIX}/humedad
MQTT_TOPIC_TEMPERATURE=${MQTT_TOPIC_PREFIX}/temperatura
MQTT_TOPIC_BATTERY=${MQTT_TELE_PREFIX}/${MQTT_SENSOR_NAME}/bateria
MQTT_TOPIC_STATE=${MQTT_TELE_PREFIX}/${MQTT_SENSOR_NAME}/event
MQTT_PUBLISH_DELAY=5
# Sensor configuration
MIJIA_BTLE_ADDRESS=

142
main.py Executable file
View file

@ -0,0 +1,142 @@
#!/usr/bin/env python3
"""MiJia GATT to MQTT"""
import os
import re
import time
from dotenv import load_dotenv
import paho.mqtt.client as mqtt
from bluepy import btle
load_dotenv() # Cargamos las variables de entorno necesarias
MQTT_TOPIC_HUMIDITY = os.getenv('MQTT_TOPIC_HUMIDITY')
MQTT_TOPIC_TEMPERATURE = os.getenv('MQTT_TOPIC_TEMPERATURE')
MQTT_TOPIC_BATTERY = os.getenv('MQTT_TOPIC_BATTERY')
MQTT_TOPIC_STATE = os.getenv('MQTT_TOPIC_STATE')
MQTT_PUBLISH_DELAY = int(os.getenv('MQTT_PUBLISH_DELAY'))
MQTT_CLIENT_ID = os.getenv('MQTT_CLIENT_ID')
MQTT_SERVER = os.getenv('MQTT_SERVER')
MQTT_PORT = int(os.getenv('MQTT_PORT'))
MQTT_USER = os.getenv('MQTT_USER')
MQTT_PASSWORD = os.getenv('MQTT_PASSWORD')
MIJIA_BTLE_ADDRESS = os.getenv('MIJIA_BTLE_ADDRESS')
MIJIA_BATTERY_SERVICE_UUID = btle.UUID('180f')
MIJIA_BATTERY_CHARACTERISTIC_UUID = btle.UUID('2a19')
MIJIA_DATA_SERVICE_UUID = btle.UUID('226c0000-6476-4566-7562-66734470666d')
MIJIA_DATA_CHARACTERISTIC_UUID = btle.UUID('226caa55-6476-4566-7562-66734470666d')
MIJIA_DATA_CHARACTERISTIC_HANDLE = 0x0010
BTLE_SUBSCRIBE_VALUE = bytes([0x01, 0x00])
BTLE_UNSUBSCRIBE_VALUE = bytes([0x00, 0x00])
battery = None
temperature = None
humidity = None
def on_connect(client, userdata, flags, rc):
client.publish(MQTT_TOPIC_STATE, 'connected', 1, True)
class MyDelegate(btle.DefaultDelegate):
def __init__(self):
btle.DefaultDelegate.__init__(self)
def handleNotification(self, cHandle, data):
fetch_sensor_data(bytearray(data).decode('utf-8'))
def main():
mqttc = mqtt.Client(MQTT_CLIENT_ID)
mqttc.username_pw_set(MQTT_USER, MQTT_PASSWORD)
mqttc.will_set(MQTT_TOPIC_STATE, 'disconnected', 1, True)
mqttc.on_connect = on_connect
mqttc.connect(MQTT_SERVER, MQTT_PORT, 60)
mqttc.loop_start()
last_msg_time = time.time()
while True:
try:
print('Connecting to ' + MIJIA_BTLE_ADDRESS)
dev = btle.Peripheral(MIJIA_BTLE_ADDRESS)
print('Set delegate')
dev.setDelegate(MyDelegate())
# Get battery level
if battery is None:
fetch_battery_level(dev)
print('Battery level: ' + str(battery))
# Subscribe to data characteristic
if temperature is None or humidity is None:
dev.writeCharacteristic(MIJIA_DATA_CHARACTERISTIC_HANDLE, BTLE_SUBSCRIBE_VALUE, True)
while True:
if dev.waitForNotifications(1.0):
print('Temperature: ' + temperature)
print('Humidity: ' + humidity)
dev.writeCharacteristic(MIJIA_DATA_CHARACTERISTIC_HANDLE, BTLE_UNSUBSCRIBE_VALUE, True)
dev.disconnect()
break
if battery is not None and temperature is not None and humidity is not None:
delay_gap = time.time() - last_msg_time
if delay_gap < MQTT_PUBLISH_DELAY:
time.sleep(MQTT_PUBLISH_DELAY - delay_gap)
publish_sensor_data(mqttc)
last_msg_time = time.time()
reset_variables()
break
except (btle.BTLEDisconnectError, IOError):
print("Disconnected :(")
def reset_variables():
global battery
global temperature
global humidity
battery = None
temperature = None
humidity = None
def fetch_battery_level(dev):
global battery
battery_service = dev.getServiceByUUID(MIJIA_BATTERY_SERVICE_UUID)
battery_characteristic = battery_service.getCharacteristics(MIJIA_BATTERY_CHARACTERISTIC_UUID)[0]
battery = ord(battery_characteristic.read())
def fetch_sensor_data(temp_hum):
global temperature
global humidity
pattern = re.compile('T=([\d.-]+) H=([\d.-]+)')
match = re.match(pattern, temp_hum)
if match:
temperature = match.group(1)
humidity = match.group(2)
def publish_sensor_data(mqttc):
mqttc.publish(MQTT_TOPIC_TEMPERATURE, temperature, 1, True)
mqttc.publish(MQTT_TOPIC_HUMIDITY, humidity, 1, True)
mqttc.publish(MQTT_TOPIC_BATTERY, battery, 1, True)
time.sleep(MQTT_PUBLISH_DELAY)
if __name__ == '__main__':
print('Starting MiJia GATT client')
main()

3
requirements.txt Normal file
View file

@ -0,0 +1,3 @@
bluepy==1.3.0
paho-mqtt==1.6.1
python-dotenv==0.20.0