mirror of
https://github.com/Luzifer/streampulse.git
synced 2024-12-22 19:41:19 +00:00
Initial version
This commit is contained in:
commit
d4efac8f13
4 changed files with 163 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
.env
|
10
Makefile
Normal file
10
Makefile
Normal file
|
@ -0,0 +1,10 @@
|
|||
default:
|
||||
|
||||
run:
|
||||
vault2env -k secret/private/mqttcli -- envrun -- ./.venv/bin/python main.py
|
||||
|
||||
requirements: .venv
|
||||
./.venv/bin/pip install -r requirements.txt
|
||||
|
||||
.venv:
|
||||
python -m venv .venv
|
150
main.py
Normal file
150
main.py
Normal file
|
@ -0,0 +1,150 @@
|
|||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
from bluepy import btle
|
||||
from bluepy.btle import AssignedNumbers, BTLEDisconnectError
|
||||
import paho.mqtt.client as mqtt
|
||||
|
||||
|
||||
class TransformDelegate(btle.DefaultDelegate):
|
||||
def __init__(self, handle, callback, transform):
|
||||
btle.DefaultDelegate.__init__(self)
|
||||
self.handle = handle
|
||||
self.callback = callback
|
||||
self.transform = transform
|
||||
|
||||
def handleNotification(self, cHandle, data):
|
||||
if cHandle != self.handle:
|
||||
return
|
||||
|
||||
self.callback(self.transform(data))
|
||||
|
||||
|
||||
class StreamPulse:
|
||||
def __init__(self, **kwargs):
|
||||
self.address = kwargs['address']
|
||||
self.topic_prefix = kwargs['mqtt_prefix'] if 'mqtt_prefix' in kwargs else 'streampulse'
|
||||
|
||||
if 'mqtt_host' in kwargs and kwargs['mqtt_host'] != '':
|
||||
self.mqtt = mqtt.Client()
|
||||
if 'mqtt_user' in kwargs:
|
||||
self.mqtt.username_pw_set(
|
||||
kwargs['mqtt_user'],
|
||||
kwargs['mqtt_pass'],
|
||||
)
|
||||
self.mqtt.connect(
|
||||
kwargs['mqtt_host'],
|
||||
kwargs['mqtt_port'] if 'mqtt_port' in kwargs else 1883,
|
||||
)
|
||||
else:
|
||||
self.mqtt = None
|
||||
|
||||
def run(self):
|
||||
if self.mqtt is not None:
|
||||
self.mqtt.loop_start()
|
||||
|
||||
while True:
|
||||
dev = None
|
||||
|
||||
try:
|
||||
logging.debug("Connecting...")
|
||||
dev = btle.Peripheral(self.address)
|
||||
self.send_mqtt('connected', 'true', True)
|
||||
|
||||
self.add_subscription(
|
||||
dev,
|
||||
AssignedNumbers.batteryService,
|
||||
AssignedNumbers.batteryLevel,
|
||||
self.handle_battery,
|
||||
lambda data: int(data[0]),
|
||||
True,
|
||||
)
|
||||
|
||||
self.add_subscription(
|
||||
dev,
|
||||
AssignedNumbers.heartRate,
|
||||
AssignedNumbers.heart_rate_measurement,
|
||||
self.handle_heart_rate,
|
||||
lambda data: int(data[1]),
|
||||
False,
|
||||
)
|
||||
|
||||
while True:
|
||||
if dev.waitForNotifications(1.0):
|
||||
continue
|
||||
|
||||
except BTLEDisconnectError:
|
||||
logging.error("Device connection error")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
if dev is not None:
|
||||
dev.disconnect()
|
||||
break
|
||||
|
||||
finally:
|
||||
self.send_mqtt('connected', 'false', True)
|
||||
if dev is not None:
|
||||
dev.disconnect()
|
||||
|
||||
if self.mqtt is not None:
|
||||
self.mqtt.loop_stop()
|
||||
return 0
|
||||
|
||||
def add_subscription(self, dev, svc_uuid, char_uuid, callback, transform, initial_read=False):
|
||||
svc = dev.getServiceByUUID(svc_uuid)
|
||||
char = svc.getCharacteristics(char_uuid)[0]
|
||||
desc = char.getDescriptors(
|
||||
AssignedNumbers.client_characteristic_configuration,
|
||||
)
|
||||
|
||||
dev.setDelegate(TransformDelegate(
|
||||
char.getHandle(),
|
||||
callback,
|
||||
transform,
|
||||
))
|
||||
dev.writeCharacteristic(desc[0].handle, b"\x01\x00")
|
||||
|
||||
if initial_read:
|
||||
callback(transform(char.read()))
|
||||
|
||||
def get_full_topic(self, topic):
|
||||
return '/'.join([self.topic_prefix, topic])
|
||||
|
||||
def handle_battery(self, level):
|
||||
logging.debug("Battery Level: {}".format(level))
|
||||
self.send_mqtt('battery', level, True)
|
||||
|
||||
def handle_heart_rate(self, rate):
|
||||
logging.debug("Heart Rate: {}".format(rate))
|
||||
self.send_mqtt('heart_rate', rate, False)
|
||||
|
||||
def send_mqtt(self, topic, payload, retain=False):
|
||||
if self.mqtt is None:
|
||||
return
|
||||
|
||||
self.mqtt.publish(
|
||||
self.get_full_topic(topic),
|
||||
payload=payload,
|
||||
qos=1,
|
||||
retain=retain,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
logging.basicConfig(
|
||||
format='[%(asctime)s][%(levelname)s] %(message)s',
|
||||
level=logging.DEBUG,
|
||||
)
|
||||
|
||||
pulse = StreamPulse(
|
||||
address=os.environ['DEVICE'] if 'DEVICE' in os.environ else 'a0:9e:1a:70:54:5e',
|
||||
mqtt_host=os.environ['MQTT_HOST'] if 'MQTT_HOST' in os.environ else '',
|
||||
mqtt_pass=os.environ['MQTT_PASS'] if 'MQTT_PASS' in os.environ else '',
|
||||
mqtt_port=int(os.environ['MQTT_PORT']
|
||||
) if 'MQTT_PORT' in os.environ else 1883,
|
||||
mqtt_prefix=os.environ['MQTT_PREFIX'] if 'MQTT_PREFIX' in os.environ else 'streampulse',
|
||||
mqtt_user=os.environ['MQTT_USER'] if 'MQTT_USER' in os.environ else '',
|
||||
)
|
||||
|
||||
sys.exit(pulse.run())
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
bluepy==1.3.0
|
||||
paho-mqtt==1.5.1
|
Loading…
Reference in a new issue