def sensor_data(json): print('sensor message:', json) if(json['sensorStatus'] == 'True'): adapter = get_provider().get_adapter() observer = Observer(adapter) observer.on_advertising_data = on_advertisement observer.start() time.sleep(5) observer.stop() emit('tempHumSensorUpdate', govee_devices) else: emit('tempHumSensorUpdate', {'data': 'data disabled'})
class Observer(object): ''' ''' def __init__(self, url="https://thu-band.org"): self.adapter = get_provider().get_adapter() self.observer = OB(self.adapter) self.observer.on_advertising_data = self.on_advertisement def print_eddystone_values(self, data): """ :param data: :return: """ expected_keys = {'name space': 'hex_format', 'instance': 'hex_format', 'url': 'string_format', 'mac address': 'string_format', 'tx_power': 'int_format', 'rssi': 'int_format'} endian = 'big' print('New Eddystone data:') for prop in data: if prop in expected_keys: if expected_keys[prop] == 'string_format': print('\t{} = {}'.format(prop, data[prop])) if expected_keys[prop] == 'hex_format': print('\t{} = 0x{:X}'.format(prop, int.from_bytes(data[prop], endian))) if expected_keys[prop] == 'int_format': print('\t{} = {}'.format(prop, int(data[prop]))) def on_advertisement(self, advertisement): if (check_prefix("https://thu-band.org")): print(advertisement) # self.print_eddystone_values(advertisement.mfg_data) def start(self): self.observer.start() # observer.scan_eddystone(on_data=self.print_eddystone_values) def stop(self): self.observer.stop()
class BlesonClient(BluetoothAdaptor): '''Bluetooth LE communication with Bleson''' def handle_callback(self, advertisement): if not advertisement.mfg_data: return processed_data = { # WARNING, Apple sucks, so it does not provide mac address # in the advertisement! Go tell them that they suck! # https://forums.developer.apple.com/thread/8442 "address": advertisement.address.address if advertisement.address != None else None, # Linux returns bytearray for mfg_data, but mac os returns _NSInlineData # which casts to byte array. We need to explicitly cast it to use hex "raw_data": bytearray(advertisement.mfg_data).hex(), "tx_power": advertisement.tx_pwr_lvl, "rssi": advertisement.rssi, "name": advertisement.name, } if processed_data["raw_data"][0:2] != 'FF': processed_data["raw_data"] = 'FF' + processed_data["raw_data"] self.callback(processed_data) def __init__(self, callback, bt_device=''): ''' Arguments: callback: Function that receives the data from BLE device (string): BLE device (default 0) ''' super().__init__(callback, bt_device) self.observer = None if not bt_device: bt_device = 0 else: # Old communication used hci0 etc. bt_device = bt_device.replace('hci', '') log.info('Observing broadcasts (device %s)', bt_device) adapter = get_provider().get_adapter(int(bt_device)) self.observer = Observer(adapter) self.observer.on_advertising_data = self.handle_callback def start(self): if not self.observer: log.info('Cannot start a client that has not been setup') return self.observer.start() def stop(self): self.observer.stop()
#!/usr/bin/env python3 import sys from time import sleep from bleson import get_provider, Observer # Get the wait time from the first script argument or default it to 10 seconds WAIT_TIME = int(sys.argv[1]) if len(sys.argv) > 1 else 10 def on_advertisement(advertisement): print(advertisement) adapter = get_provider().get_adapter() observer = Observer(adapter) observer.on_advertising_data = on_advertisement observer.start() sleep(WAIT_TIME) observer.stop()
class GoveeSensor(Sensor): """Listens for Govee temp/humi sensor BTLE broadcases and publishes them.""" def __init__(self, publishers, params): """Initializes the listener and kicks off the listening thread.""" super().__init__(publishers, params) self.dest_root = params("Destination") self.log.info("Configuring Govee listener with destination %s", self.dest_root) self.adapter = get_provider().get_adapter() self.observer = Observer(self.adapter) self.observer.on_advertising_data = self.on_advertisement self.observer.start() # Store readings so they can be reported on demand. self.devices = {} def on_advertisement(self, advertisement): """Called when a BTLE advertisement is received. If it goes with one of the Govee H5075 sensors, the reading is parsed and published.""" self.log.debug("Received advertisement from %s", advertisement.address.address) if advertisement.address.address.startswith(GOVEE_BT_MAC_PREFIX): self.log.debug("Received Govee advertisement") mac = advertisement.address.address # Process a sensor reading. if H5075_UPDATE_UUID16 in advertisement.uuid16s: split = advertisement.name.split("'") name = split[0] if len(split) == 1 else split[1] if mac not in self.devices: self.devices[mac] = {} self.devices[mac]["name"] = name encoded_data = int(advertisement.mfg_data.hex()[6:12], 16) self.devices[mac]["battery"] = int( advertisement.mfg_data.hex()[12:14], 16) self.devices[mac]["temp_c"] = format((encoded_data / 10000), ".2f") self.devices[mac]["temp_f"] = format( (((encoded_data / 10000) * 1.8) + 32), ".2f") self.devices[mac]["humi"] = format( ((encoded_data % 1000) / 10), ".2f") self.log.debug("Govee data to publish: %s", self.devices) self.publish_state() # Process an rssi reading. Don't bother to publish now, wait for the # next sensor reading. if advertisement.rssi is not None and advertisement.rssi != 0: # Ignore rssi from devices that haven't reported a sensor # reading yet. if mac in self.devices: self.devices[mac]["rssi"] = advertisement.rssi def publish_state(self): """Publishes the most recent of all the readings.""" for conn in self.publishers: for mac in self.devices: if "name" in self.devices[mac]: name = self.devices[mac]["name"] dest = "{}/{}".format(self.dest_root, name) for dev in [ dev for dev in self.devices[mac] if dev != "name" ]: conn.publish(str(self.devices[mac][dev]), "{}/{}".format(dest, dev)) def cleanup(self): """Stop the observer.""" self.log.info("Stopping Govee observer") self.observer.stop()