It’s made by a UK company, and looks pretty good. It also claims to have an API for integration (see Specifications tab on the webpage) but I haven’t found their API docs or SDK on their website. It appears they should be here https://support.bluemaestro.com/support/solutions/articles/47000258573-android-sdk but there are no actual docs or any SDK links.
Looks like it could be a nice addition to HA (theoretically). Anyone?
I’ve got a few Pycom LoPy4s and have code running on two of them that receives the BLE adverts from a bunch of Tempo Discs and publishes to MQTT and on to HA.
So, If anyone wants to see a code snippet (MicroPython) for parsing their advert payloads I can provide it.
Here’s the class that I currently use on my LoPy4 BLE/MQTT gateway. I haven’t yet got around to changing the MQTT to be HA autodiscovery compatible, but I have a bunch of hand configured MQTT sensors working.
import time
import utime
import ustruct
import ubinascii
from network import Bluetooth
class Bleeater:
def __init__(self, node, interval = 11):
self.node = node
self.bluetooth = Bluetooth()
self.bluetooth.init()
self.interval = interval
self.running = True
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.running = False
def work(self):
print('Starting BLE scan')
self.bluetooth.start_scan(11)
while self.bluetooth.isscanning() and self.node.is_running():
utime.sleep_ms(100)
if self.node.is_running() and self.running:
now = utime.localtime()
nowstr = '{0:04d}-{1:02d}-{2:02d}T{3:02d}:{4:02d}:{5:02d}'.format(now[0],now[1],now[2],now[3],now[4],now[5])
adv_list = self.bluetooth.get_advertisements()
for adv in adv_list:
if adv != None:
#print("Time: {0} adv.rssi: {1}".format(time.time(), adv))
# try to get the complete name
name = self.bluetooth.resolve_adv_data(adv.data, Bluetooth.ADV_NAME_CMPL)
macstr = str(ubinascii.hexlify(adv.mac), "utf-8")
# try to get the manufacturer data
mfg_data = self.bluetooth.resolve_adv_data(adv.data, Bluetooth.ADV_MANUFACTURER_DATA)
if name != None:
publishables = {}
publishables.update({'advert/summary/'+macstr:name+' '+str(adv.rssi)+'dB'+' at '+nowstr})
#publishables.update({'advert/source/'+macstr+'/name':name})
#publishables.update({'advert/source/'+macstr+'/rssi':str(adv.rssi)})
#publishables.update({'advert/source/'+macstr+'/time':nowstr})
if mfg_data != None:
#publishables.update({'advert/source/'+macstr+'/manufdata':str(ubinascii.hexlify(mfg_data), "utf-8")})
# support for BlueMaestro Tempo Disc devices
fmt='!HBBIhHhH'
fmtsize=ustruct.calcsize(fmt)
if fmtsize == len(mfg_data):
values = ustruct.unpack(fmt, mfg_data)
if values[0] == 0x3301: # the BlueMaestro magic number
topic = 'sensor/'+name
# all Tempo Discs send battery level and temperature
publishables.update({topic+'/battery' :'{0:3.1f}'.format(float(values[2]))})
publishables.update({topic+'/temp' :'{0:3.1f}'.format(float(values[4])/10.0)})
if values[1] == 0x17 or values[1] == 0x1b: # THD or THPD (does not send D!)
publishables.update({topic+'/humidity' :'{0:3.1f}'.format(float(values[5])/10.0)})
if values[1] == 0x17: # THD
publishables.update({topic+'/dewpoint' :'{0:3.1f}'.format(float(values[6])/10.0)})
elif values[1] == 0x1b: # THPD (sends P instead of D, no D is sent)
publishables.update({topic+'/pressure' :'{0:3.1f}'.format(float(values[6])/10.0)})
else:
print('BLE: Ignored mismatched ID.')
else:
print('BLE: SIZE FAIL, fmtsize: '+str(fmtsize)+' len(mfg_data): '+str(len(mfg_data)))
print(name+' '+str(publishables))
for key,value in publishables.items():
if key != None and value != None:
self.node.client_try_publish(self.node.service+'/ble'+'/'+key, value, False, 0)
self.node.led_flash(0x000010)
utime.sleep_ms(10)
utime.sleep_ms(10)
return len(adv_list) > 0
else:
return False