def readData (self, mqttClient): print("reading data from device " + self.__mac) sys.stdout.flush() p = Peripheral(self.__mac, iface=HCI_INTERFACE) p.withDelegate(self.__delegate) try: battery = p.readCharacteristic(BATTERY_HANDLE) self.__lastBattery = battery[0] except: print("failed to read battery from " + self.__mac) sys.stdout.flush() p.writeCharacteristic(TEMP_HUM_WRITE_HANDLE, TEMP_HUM_WRITE_VALUE) if not p.waitForNotifications(3.0): print("failed to read data from " + self.__mac) sys.stdout.flush() try: p.disconnect() except: pass print("read data from " + self.__mac + " " + str(self.__lastTemp) + "," + str(self.__lastHum) + "," + str(self.__lastBattery)) sys.stdout.flush() msg =\ '{'\ '"idx" : ' + str(self.__id) + ','\ '"nvalue" : 0,'\ '"svalue" : "' + str(self.__lastTemp) + ';' + str(self.__lastHum) + ';0",'\ '"Battery" : ' + str(self.__lastBattery) + ' '\ '}' mqttClient.publish(DEFAULT_IN_TOPIC, msg);
def establishConnection(index, buffer_tuple): global connections, serviceChars addr = blunoAddress[index] print("Connecting to bluno " + str(index) + " with ip address: " + str(addr)) connection_attempt_count = 0 while True: try: p = Peripheral(addr) p.withDelegate(NotificationDelegate(index, buffer_tuple)) blunoService = p.getServiceByUUID( "0000dfb0-0000-1000-8000-00805f9b34fb") serviceChar = blunoService.getCharacteristics()[0] connections[index] = p serviceChars[index] = serviceChar print("Connected to " + str(index)) break except Exception as e: print(e) connection_attempt_count += 1 print("Established connection with bluno {}, attempt {}...".format( str(index), str(connection_attempt_count))) time.sleep(LONG_SLEEP_SEC) continue
class MiTemperature(object): '''' Class to read the temperature from the MiTemp sensor from Amazon. Heavily based on https://github.com/JsBergbau/MiTemperature2 but refactored for my application ''' def __init__(self, deviceAddr, addrType=ADDR_TYPE_PUBLIC, iface=0): self.deviceAddr = deviceAddr self.iface = iface self.addrType = addrType self.timeout = 10.0 def _connect(self): self._periph = Peripheral(deviceAddr=self.deviceAddr, addrType=self.addrType, iface=self.iface) enable_notification_temp_humidity = b'\x01\x00' self._periph.writeCharacteristic(0x0038, enable_notification_temp_humidity, True) self._periph.writeCharacteristic(0x0046, b'\xf4\x01\x00', True) self._delegate = _MiDelegate() self._periph.withDelegate(self._delegate) logging.debug("Connected to {}".format(self.deviceAddr)) self.measurement = self._delegate.measurement def _disconnect(self): self._periph.disconnect() def _reading(self): """ Returns the most recent temperature reading :rtype: Measurement """ self._connect() result = None if self._periph.waitForNotifications(self.timeout): logging.debug("Received notification") result = self._delegate.measurement else: logging.error("No trigger from delegate") self._disconnect() return result def reading(self): """" Return the readings a tuple of temperatiure, humidity, battery level :rtype: (float, float, float) """ measurement = self._reading() return measurement.temperature, measurement.humidity, measurement.batterylevel def temperature(self): measurement = self._reading() if measurement is None: return None else: return measurement.temperature
class TraiterDevBLE: def __init__(self, dev): self.dev = dev self.peripheral = None def afficher_info_dev(self): print("Device : %s" % str(dev)) for (adtype, desc, value) in dev.getScanData(): print("Adtype %s" % dev.getDescription(adtype)) print(" %s = %s" % (desc, value)) def connecter(self, addr=None): # self.peripheral = Peripheral.connect(self.dev.addr) self.peripheral = Peripheral() self.peripheral.connect(addr) def afficher_services(self): services = self.peripheral.services for service in services: print("Service : %s" % str(service)) for characteristic in service.getCharacteristics(): print(" Characteristic %s : %s" % (characteristic.uuid.getCommonName(), characteristic.propertiesToString())) def ecrire(self, contenu: str): service = self.peripheral.getServiceByUUID( '6e400001-b5a3-f393-e0a9-e50e24dcca9e') write_charact = service.getCharacteristics( '6e400002-b5a3-f393-e0a9-e50e24dcca9e')[0] write_charact.write(contenu.encode('utf-8'), withResponse=True) def lire(self): service = self.peripheral.getServiceByUUID( '6e400001-b5a3-f393-e0a9-e50e24dcca9e') read_charact = service.getCharacteristics( '6e400004-b5a3-f393-e0a9-e50e24dcca9e')[0] contenu = read_charact.read() print("Lecture du contenu : %s" % contenu.decode('utf-8')) def ecouter(self): self.peripheral.withDelegate(ListenDelegate(dict())) service = self.peripheral.getServiceByUUID( '6e400001-b5a3-f393-e0a9-e50e24dcca9e') notify_charact = service.getCharacteristics( '6e400003-b5a3-f393-e0a9-e50e24dcca9e')[0] # notify_charact.StartNotify() while True: if self.peripheral.waitForNotifications(5.0): print("Ye!!!") else: print("Rien recu")
class BleConnection(Thread): #device=None def __init__(self, wxWindow): Thread.__init__(self) print('initble') self.wxWindow = wxWindow def run(self): print('runble') self.find_device_by_service() self.connect_to_device() self.is_muted = soundControl.is_default_muted() while True: if self.p.waitForNotifications(0.5): continue self.update_button_icon() #self.p.waitForNotifications(0) def find_device_by_service(self): scanner = Scanner() #print ("Searching for device with service %s ..." % complete128service); devices = scanner.scan(10.0) print("Scan finished") for dev in devices: # print ("Device %s (%s), RSSI=%d dB" % (dev.addr, dev.addrType, dev.rssi)); for (adtype, desc, value) in dev.getScanData(): if (adtype == 7 and value == complete128service): print("Device Found!") self.device = dev def connect_to_device(self): if (self.device is not None): print("Connecting to device %s ..." % self.device.addr) self.p = Peripheral(self.device.addr) self.p.withDelegate(NotificationDelegate(None)) self.svc = self.p.getServiceByUUID(complete128service) global chOutput chOutput = self.svc.getCharacteristics(characteristicMuteStatus)[0] self.chInput = self.svc.getCharacteristics( characteristicMuteButton)[0] print("Connected!") def update_button_icon(self): if (self.is_muted): chOutput.write(bytes("MUTED", 'utf-8'), True) else: chOutput.write(bytes("UNMUTED", 'utf-8'), True) def update_mute_status(self, mute_status): self.is_muted = mute_status
def run(blunoAddr, index): global blunoHandshake global dataFlag global startTime while True: print("Trying to connect bluno " + str(index)) try: p = Peripheral(blunoAddr) p.withDelegate(NotificationDelegate(index)) blunoService = p.getServiceByUUID( "0000dfb0-0000-1000-8000-00805f9b34fb") serviceChara = blunoService.getCharacteristics()[0] break except: continue print("Connected to bluno " + str(index)) while blunoHandshake[index] == 0: initHandshake(serviceChara) if (p.waitForNotifications(1)): break else: continue while (sum(blunoHandshake) != len(blunoHandshake)): time.sleep(3) startTime = time.time() #dcStart = 0.0 while True: try: if (p.waitForNotifications(1)): dcStart = 0.0 # else: # if dataFlag == 1: # ackData(serviceChara) # print("ack") # dataFlag = 0 else: dcCurr = time.time() if dcCurr - dcStart >= 3: p.disconnect() reconnect(blunoAddr, index) else: pass continue except: print("Bluno " + str(index) + " has disconnected. Attempting to reconnect...") p.disconnect() break reconnect(blunoAddr, index)
def init(): peripheral = Peripheral("d8:e3:66:b3:b9:95", btle.ADDR_TYPE_RANDOM) peripheral.withDelegate(delegate) peripheral.writeCharacteristic(12, (1).to_bytes(2, byteorder='little')) try: while delegate.flag: if peripheral.waitForNotifications(10): continue break except: print("disconnected") finally: peripheral.disconnect() sys.exit(0)
class BluetoothPoller: _DATA_MODE_LISTEN = bytes([0x01, 0x00]) def __init__(self, address: str, label: str, results: List[SensorValues], iface: int): self.peripheral = Peripheral(deviceAddr=address, iface=iface) self.peripheral.withDelegate( ValueDelegate(label=label, results=results)) def wait_for_notification(self, handle: int, timeout=1.0): self.peripheral.writeCharacteristic(handle, BluetoothPoller._DATA_MODE_LISTEN) return self.peripheral.waitForNotifications(timeout) def disconnect(self): self.peripheral.disconnect()
def connect_to_aidlab(characteristics, aidlabMAC): global temperatureHandle, ecgHandle, batteryHandle, respirationHandle, motionHandle try: peripheral = Peripheral(aidlabMAC) print("Connected") except BTLEException: print("Could not connect to Aidlab") return peripheral.withDelegate(NotificationHandler()) userServiceUUID = "44366E80-CF3A-11E1-9AB4-0002A5D5C51B" userService = peripheral.getServiceByUUID(userServiceUUID) for characteristic in characteristics: if characteristic == "temperature": temperatureHandle = subscribe_to_characteristic( userService, peripheral, "45366E80-CF3A-11E1-9AB4-0002A5D5C51B") elif characteristic == "ecg": ecgHandle = subscribe_to_characteristic( userService, peripheral, "46366E80-CF3A-11E1-9AB4-0002A5D5C51B") elif characteristic == "battery": batteryHandle = subscribe_to_characteristic( userService, peripheral, "47366E80-CF3A-11E1-9AB4-0002A5D5C51B") elif characteristic == "respiration": respirationHandle = subscribe_to_characteristic( userService, peripheral, "48366E80-CF3A-11E1-9AB4-0002A5D5C51B") elif characteristic == "motion": motionHandle = subscribe_to_characteristic( userService, peripheral, "49366E80-CF3A-11E1-9AB4-0002A5D5C51B") while True: try: if peripheral.waitForNotifications(1.0): continue except BTLEDisconnectError: pass
def receive_ir(self, timeout=15): self._lock.acquire() self._receive_handle = None self._receive_buffer = False self._received_packet = 0 self._total_packet = -1 p = None try: p = Peripheral(self._mac, "public") ch_list = p.getCharacteristics() for ch in ch_list: if str(ch.uuid) == SERVICE_UUID: self._receive_handle = ch.getHandle() sequence = self.get_sequence() if p.writeCharacteristic(ch.getHandle(), b'\x55\x03' + bytes([sequence]) + b'\x05', True): data = ch.read() if len(data) == 4 and data[3] == 7: p.withDelegate(self) while self._received_packet != self._total_packet: if not p.waitForNotifications(timeout): self._receive_buffer = False break p.writeCharacteristic(ch.getHandle(), b'\x55\x03' + bytes([sequence]) + b'\x0b', True) p.writeCharacteristic(ch.getHandle(), b'\x55\x03' + bytes([sequence]) + b'\x06', True) else: self._receive_buffer = False break self._receive_handle = None except Exception as ex: print("Unexpected error: {}".format(ex)) self._receive_handle = None self._receive_buffer = False finally: if not p: p.disconnect() self._lock.release() return self._receive_buffer
def readData(self): print("reading data from device " + self.__mac) p = Peripheral(self.__mac, iface=0) p.withDelegate(self.__delegate) try: battery = p.readCharacteristic(BATTERY_HANDLE) self.__lastBattery = battery[0] except: print("failed to read battery from " + self.__mac) p.writeCharacteristic(TEMP_HUM_WRITE_HANDLE, TEMP_HUM_WRITE_VALUE) if not p.waitForNotifications(3.0): print("failed to read data from " + self.__mac) print('Temperature is ' + str(self.__lastTemp)) print('Humidity is ' + str(self.__lastHum)) print('Battery is ' + str(self.__lastBattery)) try: p.disconnect() except: pass
class BLESession(Session): """ Manage a session for Bluetooth Low Energy device such as micro:bit """ INITIAL = 1 DISCOVERY = 2 CONNECTED = 3 DONE = 4 ADTYPE_COMP_16B = 0x3 ADTYPE_COMP_128B = 0x7 class BLEThread(threading.Thread): """ Separated thread to control notifications to Scratch. It handles device discovery notification in DISCOVERY status and notifications from BLE devices in CONNECTED status. """ def __init__(self, session): threading.Thread.__init__(self) self.session = session def run(self): while True: logger.debug("loop in BLE thread") if self.session.status == self.session.DISCOVERY: logger.debug("send out found devices") devices = self.session.found_devices for d in devices: params = {'rssi': d.rssi} params['peripheralId'] = devices.index(d) params['name'] = d.getValueText(0x9) self.session.notify('didDiscoverPeripheral', params) time.sleep(1) elif self.session.status == self.session.CONNECTED: logger.debug("in connected status:") delegate = self.session.delegate if delegate and len(delegate.handles) > 0: if not delegate.restart_notification_event.is_set(): delegate.restart_notification_event.wait() try: logger.debug( "getting lock for waitForNotification") with self.session.lock: logger.debug("before waitForNotification") self.session.perip.waitForNotifications(0.0001) logger.debug("after waitForNotification") logger.debug( "released lock for waitForNotification") except Exception as e: logger.error(e) self.session.close() break else: time.sleep(0.0) # To avoid repeated lock by this single thread, # yield CPU to other lock waiting threads. time.sleep(0) else: # Nothing to do: time.sleep(0) class BLEDelegate(DefaultDelegate): """ A bluepy handler to receive notifictions from BLE devices. """ def __init__(self, session): DefaultDelegate.__init__(self) self.session = session self.handles = {} self.restart_notification_event = threading.Event() self.restart_notification_event.set() def add_handle(self, serviceId, charId, handle): logger.debug(f"add handle for notification: {handle}") params = { 'serviceId': UUID(serviceId).getCommonName(), 'characteristicId': charId, 'encoding': 'base64' } self.handles[handle] = params def handleNotification(self, handle, data): logger.debug(f"BLE notification: {handle} {data}") params = self.handles[handle].copy() params['message'] = base64.standard_b64encode(data).decode('ascii') self.session.notify('characteristicDidChange', params) def __init__(self, websocket, loop): super().__init__(websocket, loop) self.status = self.INITIAL self.found_devices = [] self.device = None self.perip = None self.delegate = None def close(self): self.status = self.DONE if self.perip: logger.info(f"disconnect to BLE peripheral: {self.perip}") self.perip.disconnect() def __del__(self): self.close() def matches(self, dev, filters): """ Check if the found BLE device mathces the filters Scracth specifies. """ logger.debug(f"in matches {dev} {filters}") for f in filters: if 'services' in f: for s in f['services']: logger.debug(f"sevice to check: {s}") given_uuid = s logger.debug(f"given: {given_uuid}") service_class_uuid = dev.getValueText( self.ADTYPE_COMP_128B) logger.debug(f"adtype 128b: {service_class_uuid}") if not service_class_uuid: service_class_uuid = dev.getValueText( self.ADTYPE_COMP_16B) logger.debug(f"adtype 16b: {service_class_uuid}") if not service_class_uuid: continue dev_uuid = UUID(service_class_uuid) logger.debug(f"dev: {dev_uuid}") logger.debug(given_uuid == dev_uuid) if given_uuid == dev_uuid: logger.debug("match...") return True if 'name' in f or 'manufactureData' in f: logger.error("name/manufactureData filters not implemented") # TODO: implement other filters defined: # ref: https://github.com/LLK/scratch-link/blob/develop/Documentation/BluetoothLE.md return False def _get_service(self, service_id): with self.lock: service = self.perip.getServiceByUUID(UUID(service_id)) def _get_characteristic(self, chara_id): if not self.perip: return None with self.lock: charas = self.perip.getCharacteristics(uuid=chara_id) return charas[0] def handle_request(self, method, params): """Handle requests from Scratch""" if self.delegate: # Do not allow notification during request handling to avoid # websocket server errors self.delegate.restart_notification_event.clear() logger.debug("handle request to BLE device") logger.debug(method) if len(params) > 0: logger.debug(params) res = {"jsonrpc": "2.0"} err_msg = None if self.status == self.INITIAL and method == 'discover': scanner = Scanner() try: devices = scanner.scan(1.0) for dev in devices: if self.matches(dev, params['filters']): self.found_devices.append(dev) except BTLEManagementError as e: logger.error(e) err_msg = "Can not scan BLE devices. Check BLE controller." logger.error(err_msg) res["error"] = {"message": err_msg} self.status = self.DONE if len(self.found_devices) == 0 and not err_msg: err_msg = (f"BLE service not found: {params['filters']}. " "Check BLE device.") res["error"] = {"message": err_msg} logger.error(err_msg) self.status = self.DONE else: res["result"] = None self.status = self.DISCOVERY self.ble_thread = self.BLEThread(self) self.ble_thread.start() elif self.status == self.DISCOVERY and method == 'connect': logger.debug("connecting to the BLE device") self.device = self.found_devices[params['peripheralId']] try: self.perip = Peripheral(self.device.addr, self.device.addrType) logger.info(f"connect to BLE peripheral: {self.perip}") except BTLEDisconnectError as e: logger.error(f"failed to connect to BLE device: {e}") self.status = self.DONE if self.perip: res["result"] = None self.status = self.CONNECTED self.delegate = self.BLEDelegate(self) self.perip.withDelegate(self.delegate) else: err_msg = f"BLE connect failed :{self.device}" res["error"] = {"message": err_msg} self.status = self.DONE elif self.status == self.CONNECTED and method == 'read': logger.debug("handle read request") service_id = params['serviceId'] chara_id = params['characteristicId'] c = self._get_characteristic(chara_id) if not c or c.uuid != UUID(chara_id): logger.error("Failed to get characteristic {chara_id}") self.status = self.DONE else: with self.lock: b = c.read() message = base64.standard_b64encode(b).decode('ascii') res['result'] = {'message': message, 'encode': 'base64'} if params.get('startNotifications') == True: self.startNotifications(service_id, chara_id) elif self.status == self.CONNECTED and method == 'startNotifications': logger.debug("handle startNotifications request") service_id = params['serviceId'] chara_id = params['characteristicId'] self.startNotifications(service_id, chara_id) elif self.status == self.CONNECTED and method == 'stopNotifications': logger.debug("handle stopNotifications request") service_id = params['serviceId'] chara_id = params['characteristicId'] self.stopNotifications(service_id, chara_id) elif self.status == self.CONNECTED and method == 'write': logger.debug("handle write request") service_id = params['serviceId'] chara_id = params['characteristicId'] c = self._get_characteristic(chara_id) if not c or c.uuid != UUID(chara_id): logger.error("Failed to get characteristic {chara_id}") self.status = self.DONE else: if params['encoding'] != 'base64': logger.error( "encoding other than base 64 is not " "yet supported: ", params['encoding']) msg_bstr = params['message'].encode('ascii') data = base64.standard_b64decode(msg_bstr) logger.debug("getting lock for c.write()") with self.lock: c.write(data) logger.debug("released lock for c.write()") res['result'] = len(data) logger.debug(res) return res def setNotifications(self, service_id, chara_id, value): service = self._get_service(service_id) c = self._get_characteristic(chara_id) handle = c.getHandle() # prepare notification handler self.delegate.add_handle(service_id, chara_id, handle) # request notification to the BLE device with self.lock: self.perip.writeCharacteristic(handle + 1, value, True) def startNotifications(self, service_id, chara_id): logger.debug(f"start notification for {chara_id}") self.setNotifications(service_id, chara_id, b"\x01\x00") def stopNotifications(self, service_id, chara_id): logger.debug(f"stop notification for {chara_id}") self.setNotifications(service_id, chara_id, b"\x00\x00") def end_request(self): logger.debug("end_request of BLESession") if self.delegate: self.delegate.restart_notification_event.set() return self.status == self.DONE
class BluepyBackend(AbstractBackend): """Backend for Miflora using the bluepy library.""" def __init__(self, adapter: str = 'hci0', address_type: str = 'public'): """Create new instance of the backend.""" super(BluepyBackend, self).__init__(adapter, address_type) self._peripheral = None @wrap_exception def connect(self, mac: str): """Connect to a device.""" from bluepy.btle import Peripheral match_result = re.search(r'hci([\d]+)', self.adapter) if match_result is None: raise BluetoothBackendException( 'Invalid pattern "{}" for BLuetooth adpater. ' 'Expetected something like "hci0".'.format(self.adapter)) iface = int(match_result.group(1)) self._peripheral = Peripheral(mac, iface=iface, addrType=self.address_type) @wrap_exception def disconnect(self): """Disconnect from a device if connected.""" if self._peripheral is None: return self._peripheral.disconnect() self._peripheral = None @wrap_exception def read_handle(self, handle: int) -> bytes: """Read a handle from the device. You must be connected to do this. """ if self._peripheral is None: raise BluetoothBackendException('not connected to backend') return self._peripheral.readCharacteristic(handle) @wrap_exception def write_handle(self, handle: int, value: bytes): """Write a handle from the device. You must be connected to do this. """ if self._peripheral is None: raise BluetoothBackendException('not connected to backend') return self._peripheral.writeCharacteristic(handle, value, True) @wrap_exception def wait_for_notification(self, handle: int, delegate, notification_timeout: float): if self._peripheral is None: raise BluetoothBackendException('not connected to backend') self.write_handle(handle, self._DATA_MODE_LISTEN) self._peripheral.withDelegate(delegate) return self._peripheral.waitForNotifications(notification_timeout) @wrap_exception def wait_for_notification_no_write(self, handle: int, delegate, notification_timeout: float): if self._peripheral is None: raise BluetoothBackendException('not connected to backend') self._peripheral.withDelegate(delegate) return self._peripheral.waitForNotifications(notification_timeout) @staticmethod def supports_scanning() -> bool: return True @staticmethod def check_backend() -> bool: """Check if the backend is available.""" try: import bluepy.btle # noqa: F401 #pylint: disable=unused-import return True except ImportError as importerror: _LOGGER.error('bluepy not found: %s', str(importerror)) return False @staticmethod @wrap_exception def scan_for_devices(timeout: float, adapter='hci0') -> List[Tuple[str, str]]: """Scan for bluetooth low energy devices. Note this must be run as root!""" from bluepy.btle import Scanner match_result = re.search(r'hci([\d]+)', adapter) if match_result is None: raise BluetoothBackendException( 'Invalid pattern "{}" for BLuetooth adpater. ' 'Expetected something like "hci0".'.format(adapter)) iface = int(match_result.group(1)) scanner = Scanner(iface=iface) result = [] for device in scanner.scan(timeout): result.append((device.addr, device.getValueText(9))) return result
class Miflora: def __init__(self, deviceInformation): self._deviceInformation = deviceInformation self.name = deviceInformation.localName self.id = deviceInformation.id def connectAndSetup(self): print("Connecting to", self._deviceInformation.addr) for i in range(0, 10): try: ADDR_TYPE_PUBLIC = "public" self.peripheral = Peripheral(self._deviceInformation.addr, ADDR_TYPE_PUBLIC) print("Connected to", self._deviceInformation.addr) return True except BTLEException as ex: if i < 9: print("Retrying (" + str(i) + ")") else: print("BTLE Exception", ex) continue print("Connection to", self._deviceInformation.addr, "failed") return False def __str__(self): str = '{{name: "{}" addr: "{}"'.format(self.name, self._deviceInformation.addr) if self._deviceInformation.eventData is not None: str = str + ' eventData: "{}"'.format( self._deviceInformation.eventData) str = str + "}}" return str ################ def readCharacteristic(self, serviceUuid, characteristicUuid): try: ch = self.peripheral.getCharacteristics(uuid=characteristicUuid)[0] if (ch.supportsRead()): val = ch.read() return val except BTLEException as ex: print("BTLE Exception", ex) print("Error on readCharacteristic") return None def readDataCharacteristic(self, serviceUuid, characteristicUuid): try: ch = self.peripheral.getCharacteristics(uuid=characteristicUuid)[0] if (ch.supportsRead()): val = ch.read() val = binascii.b2a_hex(val) val = binascii.unhexlify(val) return val except BTLEException as ex: print("BTLE Exception", ex) print("Error on readDataCharacteristic") return None class NotifyDelegate(DefaultDelegate): def __init__(self, miflora): DefaultDelegate.__init__(self) self.miflora = miflora def handleNotification(self, cHandle, data): print("handleNotification", cHandle, data) if cHandle == 33: self.miflora.onRealtimeData(data) def notifyCharacteristic(self, serviceUuid, characteristicUuid, enable): service = self.peripheral.getServiceByUUID(serviceUuid) char = service.getCharacteristics(forUUID=characteristicUuid)[0] charDescr = service.getDescriptors(forUUID=characteristicUuid)[0] charHandle = char.getHandle() if enable: notifyDelegate = Miflora.NotifyDelegate(self) self.peripheral.withDelegate(notifyDelegate) charDescr.write(struct.pack('<BB', 0xA0, 0x1F), True) else: charDescr.write(struct.pack('<BB', 0xC0, 0x1F), True) self.peripheral.withDelegate(None) return charHandle ################ def getBattery(self): data = self.readDataCharacteristic(DATA_SERVICE_UUID, DATA_BATTERY_VERSION_UUID) if data is None: return None #print("DATA", " ".join("{:02x}".format(c) for c in data)) try: batteryLevel = data[0] except Exception as ex: print("Error parsing battery level", ex, "Data=", data) batteryLevel = 0 return batteryLevel def getDeviceFirmwareVersion(self): data = self.readDataCharacteristic(DATA_SERVICE_UUID, DATA_BATTERY_VERSION_UUID) if data is None: return None try: version = data[5:] except Exception as ex: print("Error parsing version", ex) version = "Unknown" return batteryLevel def getEventData(self): return self._deviceInformation.eventData class RealtimeData: temperature = None unknown = None light = None moisture = None conductivity = None battery = None def __init__(self): self.temperature = None self.unknown = None self.light = None self.moisture = None self.conductivity = None self.battery = None def __str__(self): str = "" if self.moisture is not None: str = str + '{{moisture: "{}"}}'.format(self.moisture) if self.conductivity is not None: str = str + '{{conductivity: "{}"}}'.format(self.conductivity) if self.light is not None: str = str + '{{light: "{}"}}'.format(self.light) if self.temperature is not None: str = str + '{{temperature: "{}"}}'.format(self.temperature) if self.unknown is not None: str = str + '{{humidity: "{}"}}'.format(self.unknown) if self.battery is not None: str = str + '{{battery: "{}"}}'.format(self.battery) return str def getRealtimeData(self): realtimeData = Miflora.RealtimeData() try: self.notifyCharacteristic(DATA_SERVICE_UUID, DATA_WRITE_MODE_CHANGE_UUID, True) self.waitingForData = True while self.waitingForData: notified = self.peripheral.waitForNotifications(10) if notified: pass data = self.readDataCharacteristic(DATA_SERVICE_UUID, DATA_DATA_UUID) #print("DATA", " ".join("{:02x}".format(ord(c)) for c in data)) #09 01 #00 #fa 00 00 #00 #00 #00 00 #02 3c 00 fb 34 9b #f3 00 #00 #23 00 00 #00 #0d #30 00 #02 3c 00 fb 34 9b realtimeData.temperature = (struct.unpack(STRUCT_Int16LE, data[0:2])[0]) / 10.0 realtimeData.unknown = struct.unpack(STRUCT_UInt8LE, data[2:3])[0] realtimeData.light = struct.unpack(STRUCT_UInt32LE, data[3:6] + "\0")[0] realtimeData.moisture = struct.unpack(STRUCT_UInt8LE, data[7:8])[0] realtimeData.conductivity = struct.unpack(STRUCT_UInt16LE, data[8:10])[0] realtimeData.light = (realtimeData.light * 1.0) / 1000.0 self.notifyCharacteristic(DATA_SERVICE_UUID, DATA_WRITE_MODE_CHANGE_UUID, False) except Exception as ex: print(ex) return realtimeData def onRealtimeData(self, data): self.waitingForData = False
resp = 'empty' def serial_encode(command): global serial_id serial_id += 1 ret = f'#{serial_id} {command}\n'.encode() print(ret) return ret # delegate task to handle notifications from the BLE module class HiroDelegate(DefaultDelegate): def __init__(self, params=None): DefaultDelegate.__init__(self) def handleNotification(self, cHandle, data): global resp resp = f'Message from {cHandle}: {data}' print(resp) # instantiate and attach the delegate hiro_ble = hiro_ble.withDelegate(HiroDelegate(None)) # start by getting the firmware version status = hiro_channel.write(serial_encode(GET_FIRMWARE_VERSION)) hiro_ble.waitForNotifications(15) status = hiro_channel.write(serial_encode(GET_COOR)) hiro_ble.waitForNotifications(15) hiro_ble.disconnect()
class SmartGadget(DefaultDelegate): def __init__(self, device): self.present = True self.peripheral = None if isinstance(device, ScanEntry): self.addr, self.addrType, self.iface = device.addr, device.addrType, device.iface self.rssi = device.rssi else: self.addr, self.addrType, self.iface = device, ADDR_TYPE_RANDOM, None self.Temperature = Float32Service( "00002234-b38d-4985-720e-0f993a68ee41") self.RelativeHumidity = Float32Service( "00001234-b38d-4985-720e-0F993a68ee41", unit="%") self.Battery = Uint8Service("180F", unit="%") self.subscribable_services = [self.Temperature, self.RelativeHumidity] self.Logging = LoggingService("0000f234-b38d-4985-720e-0f993a68ee41", subscribables=self.subscribable_services) def connect(self): log.info("connect to '{}'...".format(self.addr)) self.peripheral = Peripheral(self.addr, self.addrType, self.iface) self.peripheral.withDelegate(self) self.Temperature.connect(self.peripheral) self.RelativeHumidity.connect(self.peripheral) self.Battery.connect(self.peripheral) self.Logging.connect(self.peripheral) log.info("connected to '{}'!".format(self.addr)) def is_connected(self): if self.peripheral is None: return False try: self.peripheral.status() return True except BTLEException: return False def handleNotification(self, cHandle, data): log.debug("received data: handle={}, data={}".format( cHandle, binascii.b2a_hex(data).decode('utf-8'))) for service in self.subscribable_services: if cHandle == service.getHandle(): return service.call_listeners(data) def listen_for_notifications(self, seconds=None): if seconds is None: while True: if self.peripheral.waitForNotifications(1.0): continue else: t0 = time.time() while time.time() - t0 < seconds: if self.peripheral.waitForNotifications(1.0): continue def __str__(self): return "Sensirion SmartGadget ({})".format(self.addr) def subscribe_temperature(self): self.Temperature.subscribe() def subscribe_relative_humidity(self): self.RelativeHumidity.subscribe() def subscribe_battery_level(self): self.Battery.subscribe() def read_temperature(self): return self.Temperature.read() def read_relative_humidity(self): return self.RelativeHumidity.read() def read_battery_level(self): return self.Battery.read() def download_temperature_and_relative_humidity(self, timeout=15): if not self.is_connected(): raise Exception("Gadget is not connected!") self.Temperature.subscribe() self.RelativeHumidity.subscribe() self.Logging.start_download() t0 = time.time() while self.Logging.downloading: self.listen_for_notifications(0.5) if self.Logging.downloading: log.info("downloading {:.0f}%".format(self.Logging.progress())) if (time.time() - t0) > timeout: break self.Temperature.unsubscribe() self.RelativeHumidity.unsubscribe() return self.Logging.data def disconnect(self): log.info("Disconnect from {}...".format(self.addr)) if self.peripheral is not None: self.peripheral.disconnect() log.info("Disconnected from {}...".format(self.addr))
if self.SYN is False: # and data.decode('ascii') is "SYN": self.SYN = True message = "SYN_ACK" print("received SYN") message = message.encode('ascii') self.peripheral.writeCharacteristic(hdl, message, True) elif self.SYN is True: if self.ACK is False: self.ACK = True message = "handshaking completed" print("received ACK") message = message.encode('ascii') self.peripheral.writeCharacteristic(hdl, message, True) elif self.ACK is True: print("Received message: " + data.decode('ascii')) message = input('>>') message = message.encode('ascii') self.peripheral.writeCharacteristic(hdl, message, True) else: print(data.decode('ascii')) hc08 = Peripheral() hc08.connect("a8:e2:c1:62:7f:bc", "public") hc08.withDelegate(MyDelegate(hc08)) while True: print("Start waiting for messages...") hc08.waitForNotifications(10.0)
self.ll1.LL_reset() #get battery level def get_battery_level(): return int(read_data_by_handle(battry_level_hndl)[0]) #main if len(sys.argv) != 2: print("Fatal, must pass device address:", sys.argv[0], "<device address=\"\">") quit() p = Peripheral(sys.argv[1], "public") p.withDelegate(MyDelegate()) hardware_rev_handle = 0x0040 firmware_rev_handle = 0x0042 software_rev_handle = 0x0040 manufact_nam_handle = 0x003E product_name_handle = 0x0039 try: #if (currunt_state_ch.supportsRead()): #it must support write #set_mode(1) print("Product Name:", read_data_by_handle(product_name_handle).decode("utf-8")) print("Hardware rev:", read_data_by_handle(hardware_rev_handle).decode("utf-8"))
class BlePeripheral(threading.Thread): def __init__( self, ble_addr, interval_secs, experiment_id, publish, index, qm, ): threading.Thread.__init__(self) self._per = Peripheral() self._is_run = True self._ble_addr = ble_addr self._interval_secs = interval_secs self._publish = publish self._experiment_id = experiment_id self._flag = 0 self._index = index self._qm = qm # XXX def _connect(self): # logger.debug({ # 'mac_addr': self._ble_addr['mac'], # }) try: self._per.connect(self._ble_addr['mac']) self._per.withDelegate( MyDelegate( self._per, self._interval_secs, self._ble_addr, self._experiment_id, self._publish, self._index, self._qm, )) except Exception as e: logger.exception(e) def _set_notify(self): return self._per.writeCharacteristic( CONFIG_HND1, bytearray.fromhex('01 00'), ) def get_status(self): return {self._ble_addr['mac']: self._is_run} def run(self): while self._is_run: try: self._connect() self._set_notify() while self._is_run: if self._per.waitForNotifications(1.0): continue except BTLEException as e: logger.exception(e) # finally: # time.sleep(1) def stop(self): self._is_run = False
for i in range(0, longueur, 5): self.read_val(dft_treated, i) if __name__ == "__main__": rx_uuid = UUID(0x2221) sample_size = 128 # p = Peripheral("D9:35:6A:75:9F:9D", "random") # Rfduino sur usb continuer = True while(continuer): try: p = Peripheral("D1:7F:06:ED:66:DC", "random") # Rfduino sur pcb continuer = False except: print "Module bluetooth deja connecte, nouvel essai dans 3 sec..." time.sleep(3) p.withDelegate(MyDelegate()) Analyser.set_p(p) print " device connected..." try: p.getServices() ch = p.getCharacteristics(uuid=rx_uuid)[0] print ("notify characteristic with uuid 0x" + rx_uuid.getCommonName()) cccid = btle.AssignedNumbers.client_characteristic_configuration # Ox000F : handle of Client Characteristic Configuration descriptor Rx - (generic uuid 0x2902) p.writeCharacteristic(0x000F, struct.pack('<bb', 0x01, 0x00), False) if ch.supportsRead(): while 1: p.waitForNotifications(604800) # 1 semaine d'attente # handleNotification() was called
if len(sys.argv) != 2: print("Fatal, must pass device address:", sys.argv[0], "<device address=\"\">") quit() p = Peripheral(sys.argv[1], "public") currunt_state_ch = p.getCharacteristics(uuid=currunt_state_uuid)[0] hardware_rev_ch = p.getCharacteristics(uuid=hardware_rev_uuid)[0] firmware_rev_ch = p.getCharacteristics(uuid=firmware_rev_uuid)[0] software_rev_ch = p.getCharacteristics(uuid=software_rev_uuid)[0] manufact_nam_ch = p.getCharacteristics(uuid=manufact_nam_uuid)[0] product_name_ch = p.getCharacteristics(uuid=product_name_uuid)[0] vibration_led_ch = p.getCharacteristics(uuid=vibration_led_uuid)[0] accel_ch = p.getCharacteristics(uuid=accel_uuid)[0] p.withDelegate( MyDelegate(p, currunt_state_ch) ) try: #if (currunt_state_ch.supportsRead()): #it must support write #set_mode(1) print ("Product Name:", product_name_ch.read().decode("utf-8")) print ("Hardware rev:", hardware_rev_ch.read().decode("utf-8")) print ("Firmware rev:", firmware_rev_ch.read().decode("utf-8")) print ("Software rev:", software_rev_ch.read().decode("utf-8")) print ("Manufacturer:", manufact_nam_ch.read().decode("utf-8")) set_time_epoch(int(time.time())) get_mode(currunt_state_ch.read()) ch2 = p.getCharacteristics(uuid=action_uuid)[0] turn_nutifications_on(p, ch2.valHandle)
if peripheral.waitForNotifications(handle): continue break '''Make peripheral object. Change first argument:"MacAddress" to a peripheral MacAddress. Second argument is "btle.ADDR_TYPE_RANDOM" or btle.ADDR_TYPE_PUBLIC" By getAddr.py shows these information. If you use this program, delete # and comment out peripheral= Peripheral("d8:e3:66:b3:b9:95",btle.ADDR_TYPE_RANDOM) ''' #peripheral = Peripheral("MacAddress",btle.ADDR_TYPE_"RANDOM/PUBLIC") peripheral = Peripheral("d8:e3:66:b3:b9:95", btle.ADDR_TYPE_RANDOM) '''show structure of GATT Service''' for srv in peripheral.getServices(): print(srv) for characteristic in srv.getCharacteristics(): print(characteristic) #instanciate Delegate object delegate = MyDelegate() peripheral.withDelegate(delegate) #To get notification,you need to do this function handle = 11 enableNotification(handle) print("disconnected") peripheral.disconnect()
class BLESession(Session): """ Manage a session for Bluetooth Low Energy device such as micro:bit """ INITIAL = 1 DISCOVERY = 2 CONNECTED = 3 DONE = 4 SERVICE_CLASS_UUID_ADTYPES = { 0x7: "adtype complete 128b", 0x3: "adtype complete 16b", 0x6: "adtype incomplete 128b", 0x5: "adtype complete 32b", 0x4: "adtype incomplete 32b", 0x2: "adtype incomplete 16b", } MAX_SCANNER_IF = 3 found_devices = [] nr_connected = 0 scan_lock = threading.RLock() scan_started = False class BLEThread(threading.Thread): """ Separated thread to control notifications to Scratch. It handles device discovery notification in DISCOVERY status and notifications from BLE devices in CONNECTED status. """ def __init__(self, session): threading.Thread.__init__(self) self.session = session def run(self): while True: logger.debug("loop in BLE thread") if self.session.status == self.session.DISCOVERY: logger.debug("send out found devices") devices = BLESession.found_devices for d in devices: params = { 'rssi': d.rssi } params['peripheralId'] = devices.index(d) params['name'] = d.getValueText(0x9) or d.getValueText(0x8) self.session.notify('didDiscoverPeripheral', params) time.sleep(1) elif self.session.status == self.session.CONNECTED: logger.debug("in connected status:") delegate = self.session.delegate if delegate and len(delegate.handles) > 0: if not delegate.restart_notification_event.is_set(): delegate.restart_notification_event.wait() try: logger.debug("getting lock for waitForNotification") with self.session.lock: logger.debug("before waitForNotification") self.session.perip.waitForNotifications(0.0001) logger.debug("after waitForNotification") logger.debug("released lock for waitForNotification") except Exception as e: logger.error(e) self.session.close() break else: time.sleep(0.0) # To avoid repeated lock by this single thread, # yield CPU to other lock waiting threads. time.sleep(0) else: # Nothing to do: time.sleep(0) class BLEDelegate(DefaultDelegate): """ A bluepy handler to receive notifictions from BLE devices. """ def __init__(self, session): DefaultDelegate.__init__(self) self.session = session self.handles = {} self.restart_notification_event = threading.Event() self.restart_notification_event.set() def add_handle(self, serviceId, charId, handle): logger.debug(f"add handle for notification: {handle}") params = { 'serviceId': UUID(serviceId).getCommonName(), 'characteristicId': charId, 'encoding': 'base64' } self.handles[handle] = params def handleNotification(self, handle, data): logger.debug(f"BLE notification: {handle} {data}") params = self.handles[handle].copy() params['message'] = base64.standard_b64encode(data).decode('ascii') self.session.notify('characteristicDidChange', params) def __init__(self, websocket, loop): super().__init__(websocket, loop) self.status = self.INITIAL self.device = None self.deviceName = None self.perip = None self.delegate = None self.characteristics_cache = [] def close(self): if self.status == self.CONNECTED: BLESession.nr_connected -= 1 logger.info(f"BLE session disconnected") logger.debug(f"BLE session connected={BLESession.nr_connected}") if BLESession.nr_connected == 0: logger.info("all BLE sessions disconnected") BLESession.scan_started = False self.status = self.DONE if self.perip: logger.info("disconnect from the BLE peripheral: " f"{self.deviceName}") with self.lock: self.perip.disconnect() self.perip = None def __del__(self): self.close() def _get_dev_uuids(self, dev): for adtype in self.SERVICE_CLASS_UUID_ADTYPES: service_class_uuids = dev.getValue(adtype) if service_class_uuids: for u in service_class_uuids: a = self.SERVICE_CLASS_UUID_ADTYPES[adtype] logger.debug(f"service class uuid for {a}/{adtype}: {u}") return service_class_uuids return None def matches(self, dev, filters): """ Check if the found BLE device matches the filters Scratch specifies. """ logger.debug(f"in matches {dev.addr} {filters}") for f in filters: if 'services' in f: for s in f['services']: logger.debug(f"service to check: {s}") given_uuid = s logger.debug(f"given UUID: {given_uuid} hash={UUID(given_uuid).__hash__()}") dev_uuids = self._get_dev_uuids(dev) if not dev_uuids: continue for u in dev_uuids: logger.debug(f"dev UUID: {u} hash={u.__hash__()}") logger.debug(given_uuid == u) if given_uuid == u: logger.debug("match...") return True if 'namePrefix' in f: # 0x08: Shortened Local Name deviceName = dev.getValueText(0x08) if not deviceName: continue logger.debug(f"Name of \"{deviceName}\" begins with: \"{f['namePrefix']}\"?") if(deviceName.startswith(f['namePrefix'])): logger.debug("Yes") return True logger.debug("No") if 'name' in f or 'manufactureData' in f: logger.error("name/manufactureData filters not implemented") # TODO: implement other filters defined: # ref: https://github.com/LLK/scratch-link/blob/develop/Documentation/BluetoothLE.md return False def _scan_devices(self, params): global scan_seconds if BLESession.nr_connected > 0: return len(BLESession.found_devices) > 0 found = False with BLESession.scan_lock: if not BLESession.scan_started: BLESession.scan_started = True BLESession.found_devices.clear() for i in range(self.MAX_SCANNER_IF): scanner = Scanner(iface=i) try: logger.debug(f"start BLE scan: {scan_seconds} seconds") devices = scanner.scan(scan_seconds) for dev in devices: if self.matches(dev, params['filters']): BLESession.found_devices.append(dev) found = True logger.debug(f"BLE device found with iface #{i}"); except BTLEManagementError as e: logger.debug(f"BLE iface #{i}: {e}"); else: found = len(BLESession.found_devices) > 0 return found def _get_service(self, service_id): with self.lock: service = self.perip.getServiceByUUID(UUID(service_id)) def _get_characteristic(self, chara_id): if not self.perip: return None with self.lock: charas = self.perip.getCharacteristics(uuid=chara_id) return charas[0] def _cache_characteristics(self): if not self.perip: return with self.lock: self.characteristics_cache = self.perip.getCharacteristics() if not self.characteristics_cache: logger.debug("Characteristics are not cached") def _get_characteristic_cached(self, chara_id): if not self.perip: return None if not self.characteristics_cache: self._cache_characteristics() if self.characteristics_cache: for characteristic in self.characteristics_cache: if characteristic.uuid == chara_id: return characteristic return _get_characteristic(chara_id) def handle_request(self, method, params): """Handle requests from Scratch""" if self.delegate: # Do not allow notification during request handling to avoid # websocket server errors self.delegate.restart_notification_event.clear() logger.debug("handle request to BLE device") logger.debug(method) if len(params) > 0: logger.debug(params) res = { "jsonrpc": "2.0" } err_msg = None if self.status == self.INITIAL and method == 'discover': if not bluepy_helper_cap.is_set(): logger.error("Capability is not set to bluepy helper.") logger.error("Run bluepy_helper_cap(.py).") logger.error("e.g. $ bluepy_helper_cap") logger.error("e.g. $ sudo bluepy_helper_cap.py") sys.exit(1) found = self._scan_devices(params) if not found: if BLESession.nr_connected > 0: err_msg = "Can not scan BLE devices. Disconnect other sessions." elif len(BLESession.found_devices) == 0: err_msg = "Can not scan BLE devices. Check BLE controller." logger.error(err_msg); res["error"] = { "message": err_msg } self.status = self.DONE if len(BLESession.found_devices) == 0 and not err_msg: err_msg = (f"No BLE device found: {params['filters']}. " "Check BLE device.") res["error"] = { "message": err_msg } logger.error(err_msg) self.status = self.DONE else: res["result"] = None self.status = self.DISCOVERY self.ble_thread = self.BLEThread(self) self.ble_thread.start() elif self.status == self.DISCOVERY and method == 'connect': logger.debug("connecting to the BLE device") self.device = BLESession.found_devices[params['peripheralId']] self.deviceName = self.device.getValueText(0x9) or self.device.getValueText(0x8) try: self.perip = Peripheral(self.device) logger.info(f"connected to the BLE peripheral: {self.deviceName}") BLESession.found_devices.remove(self.device) except BTLEDisconnectError as e: logger.error(f"failed to connect to the BLE device \"{self.deviceName}\": {e}") self.status = self.DONE if self.perip: res["result"] = None self.status = self.CONNECTED BLESession.nr_connected += 1 logger.debug(f"BLE session connected={BLESession.nr_connected}") self.delegate = self.BLEDelegate(self) self.perip.withDelegate(self.delegate) self._cache_characteristics() else: err_msg = f"BLE connect failed: {self.deviceName}" res["error"] = { "message": err_msg } self.status = self.DONE elif self.status == self.CONNECTED and method == 'read': logger.debug("handle read request") service_id = params['serviceId'] chara_id = params['characteristicId'] c = self._get_characteristic(chara_id) if not c or c.uuid != UUID(chara_id): logger.error(f"Failed to get characteristic {chara_id}") self.status = self.DONE else: with self.lock: b = c.read() message = base64.standard_b64encode(b).decode('ascii') res['result'] = { 'message': message, 'encode': 'base64' } if params.get('startNotifications') == True: self.startNotifications(service_id, chara_id) elif self.status == self.CONNECTED and method == 'startNotifications': logger.debug("handle startNotifications request") service_id = params['serviceId'] chara_id = params['characteristicId'] self.startNotifications(service_id, chara_id) elif self.status == self.CONNECTED and method == 'stopNotifications': logger.debug("handle stopNotifications request") service_id = params['serviceId'] chara_id = params['characteristicId'] self.stopNotifications(service_id, chara_id) elif self.status == self.CONNECTED and method == 'write': logger.debug("handle write request") service_id = params['serviceId'] chara_id = params['characteristicId'] c = self._get_characteristic_cached(chara_id) if not c or c.uuid != UUID(chara_id): logger.error(f"Failed to get characteristic {chara_id}") self.status = self.DONE else: if params['encoding'] != 'base64': logger.error("encoding other than base 64 is not " "yet supported: ", params['encoding']) msg_bstr = params['message'].encode('ascii') data = base64.standard_b64decode(msg_bstr) logger.debug("getting lock for c.write()") with self.lock: c.write(data) logger.debug("released lock for c.write()") res['result'] = len(data) logger.debug(res) return res def setNotifications(self, service_id, chara_id, value): service = self._get_service(service_id) c = self._get_characteristic(chara_id) handle = c.getHandle() # prepare notification handler self.delegate.add_handle(service_id, chara_id, handle) # request notification to the BLE device with self.lock: self.perip.writeCharacteristic(handle + 1, value, True) def startNotifications(self, service_id, chara_id): logger.debug(f"start notification for {chara_id}") self.setNotifications(service_id, chara_id, b"\x01\x00") def stopNotifications(self, service_id, chara_id): logger.debug(f"stop notification for {chara_id}") self.setNotifications(service_id, chara_id, b"\x00\x00") def end_request(self): logger.debug("end_request of BLESession") if self.delegate: self.delegate.restart_notification_event.set() return self.status == self.DONE
class delega(DefaultDelegate): def __init__(self): DefaultDelegate.__init__(self) def handleNotification(self, cHandle, data): print(data) crtS = None crtL = None dev = Peripheral(mac) dev.setMTU(1000) dlg = delega() dev.withDelegate(dlg) srvz = dev.getServices() for srv in srvz: crt = srv.getCharacteristics() for c in crt: cuuid = c.uuid.getCommonName() pts = c.propertiesToString() print(cuuid + ' : ' + pts) # if c.supportsRead(): # r = c.read() # print(str(r)) if str(c.uuid) == cuuid: if c.supportsRead(): crtL = c # c.getHandle() else:
class YeelightService: """ YeelightService is the yeelight control util for python author zhaohui.sol """ SERVICE = "0000FFF0-0000-1000-8000-00805F9B34FB" CHAR_CONTROL = "0000FFF1-0000-1000-8000-00805F9B34FB" CHAR_DELAY = "0000FFF2-0000-1000-8000-00805F9B34FB" CHAR_DELAY_QUERY = "0000FFF3-0000-1000-8000-00805F9B34FB" CHAR_DELAY_NOTIFY = "0000FFF4-0000-1000-8000-00805F9B34FB" CHAR_QUERY = "0000FFF5-0000-1000-8000-00805F9B34FB" CHAR_NOTIFY = "0000FFF6-0000-1000-8000-00805F9B34FB" CHAR_COLOR_FLOW = "0000FFF7-0000-1000-8000-00805F9B34FB" CHAR_NAME = "0000FFF8-0000-1000-8000-00805F9B34FB" CHAR_NAME_NOTIFY = "0000FFF9-0000-1000-8000-00805F9B34FB" CHAR_COLOR_EFFECT = "0000FFFC-0000-1000-8000-00805F9B34FB" class NotifyDelegate(DefaultDelegate): def __init__(self): DefaultDelegate.__init__(self) self.queue = list() def register(self,callback): self.queue.append(callback) def deregister(self,callback): self.queue.remove(callback) def handleNotification(self,handle,data): logging.warning("notify data %s from %s." % (data,handle)) res = dict() res['data'] = data res['handle'] = handle for c in self.queue: c(res) def __init__(self,address): """ address is the yeelight blue ble hardware address """ self.data = dict() self.address = address self.delegate = YeelightService.NotifyDelegate() self.peripher = Peripheral(deviceAddr = address) self.service = self.peripher.getServiceByUUID(YeelightService.SERVICE) self.peripher.withDelegate(self.delegate) def __character_by_uuid__(self,uuid): ''' get character by a special uuid ''' characters = self.service.getCharacteristics(forUUID=uuid) return characters[0] if characters else None def __write_character__(self,uuid,strdata): ''' write data to a special uuid ''' logging.info(u"write %s to %s." % (strdata,uuid)) character = self.__character_by_uuid__(uuid) if character: character.write(strdata) else: pass def __read_character__(self,uuid): ''' read data from a special uuid,may be it's wrong ''' logging.info(u"read data from %s." % uuid) character = self.__character_by_uuid__(uuid) if character: return character.read() else: return None def __notify_character__(self,_to,_write): ''' write data to the uuid and wait data notify ''' res = dict() def callback(data): for k in data: res[k] = data[k] self.delegate.register(callback) self.__write_character__(_to,_write) if self.peripher.waitForNotifications(5): logging.info("notify incoming.") self.delegate.deregister(callback) return res else: logging.warning("notify timeout.") self.delegate.deregister(callback) return None def __format_request__(self,strdata,length = 18): ''' format the data to a special length ''' if strdata and length >= len(strdata) > 0: l = len(strdata) strdata += "".join(["," for i in range(length - l)]) return strdata else: return "".join(["," for i in range(length)]) def turn_on(self, brightness = 100): ''' turn on the light with white and full brightness ''' self.control(255,255,255,brightness) def turn_off(self): ''' turn off the light ''' self.control(0,0,0,0) def control(self,r,g,b,a): ''' turn on the light with special color ''' assert 0 <= r <= 255 assert 0 <= g <= 255 assert 0 <= b <= 255 assert 0 <= a <= 100 self.__write_character__(YeelightService.CHAR_CONTROL,self.__format_request__("%d,%d,%d,%d"%(r,g,b,a),18)) def delay_on(self,mins = 5): ''' turn on the light with a min param delay ''' assert 0 < mins < 24 * 60 self.__write_character__(YeelightService.CHAR_DELAY,self.__format_request__("%d,1" % mins,8)) def delay_off(self,mins = 5): ''' turn off the light with a min param delay ''' assert 0 < mins < 24 * 60 self.__write_character__(YeelightService.CHAR_DELAY,self.__format_request__("%d,0" % mins,8)) def delay_status(self): ''' query the delay status , this method return a raw dict with two key 'handle' and 'data' see http://www.yeelight.com/download/yeelight_blue_message_interface_v1.0.pdf ''' return self.__notify_character__(YeelightService.CHAR_DELAY_QUERY,self.__format_request__("RT",2)) def control_status(self): ''' query the light status , this method return a raw dict with two key 'handle' and 'data' see http://www.yeelight.com/download/yeelight_blue_message_interface_v1.0.pdf ''' return self.__notify_character__(YeelightService.CHAR_QUERY,self.__format_request__("S",1)) def start_color_flow(self,flows): ''' start color flow with a list of params, each param should contain 5 element which is r,g,b,brightness,delay see http://www.yeelight.com/download/yeelight_blue_message_interface_v1.0.pdf ''' assert len(flows) <= 9 for i,e in enumerate(flows): assert len(e) == 5 assert 0 <= e[0] <= 255 assert 0 <= e[1] <= 255 assert 0 <= e[2] <= 255 assert 0 <= e[3] <= 100 assert 0 <= e[4] <= 10 self.__write_character__(YeelightService.CHAR_COLOR_FLOW,self.__format_request__("%d,%d,%d,%d,%d,%d" % (i,e[0],e[1],e[2],e[3],e[4]),20)) self.__write_character__(YeelightService.CHAR_COLOR_FLOW,self.__format_request__("CB",20)) def stop_color_flow(self): ''' stop color flow ''' self.__write_character__(YeelightService.CHAR_COLOR_FLOW,self.__format_request__("CE",20)) def effect_smooth(self): ''' make the color change smooth ''' self.__write_character__(YeelightService.CHAR_COLOR_EFFECT,self.__format_request__("TS",2)) def effect_immediate(self): ''' make the color changes immediate ''' self.__write_character__(YeelightService.CHAR_COLOR_EFFECT,self.__format_request__("TE",2)) def effect_current_color(self): ''' use the current color as a default startup color ''' self.__write_character__(YeelightService.CHAR_COLOR_EFFECT,self.__format_request__("DF",2))
class BlueTilePoller(object): _DATA_MODE_LISTEN = bytes([0x01, 0x00]) _DATA_MODE_LISTEN_CANCEL = bytes([0x00, 0x00]) def __init__(self, mac, cache_timeout=600, retries=3, adapter='hci0'): """ Initialize a BlueTile Poller for the given MAC address. """ _LOGGER.debug("Create BlueTilePoller %s [%s]", mac, adapter) self._mac = mac self._address_type = "random" match_result = re.search(r'hci([\d]+)', adapter) if match_result is None: _LOGGER.debug( "Invalid pattern %s for BLuetooth adpater. \ Expetected something like hci0", adapter) self._iface = int(match_result.group(1)) self._cache = None self._cache_timeout = timedelta(seconds=cache_timeout) self._last_read = None self._fw_last_read = None self._max_retries = retries self.ble_timeout = 10 self.lock = threading.Lock() self.battery = None self._data = dict() self._data[ST_BATTERY] = None self._data[ST_PRESSURE] = None self._data[ST_TEMPERATURE] = None self._data[ST_HUMIDITY] = None self._data[ST_PROXIMITY] = None def get_sensor_data_notify(self, handle, parameter): self._data[parameter] = None _LOGGER.debug("Waiting for notification of %s.", parameter) with self.lock: try: self._device = Peripheral() self._device.connect(self._mac, addrType=self._address_type, iface=self._iface) except BTLEException as ex: _LOGGER.warning("%s", ex) return try: self._device.writeCharacteristic(handle, self._DATA_MODE_LISTEN, True) self._device.withDelegate(self) self._device.waitForNotifications(self.ble_timeout) self._device.writeCharacteristic(handle, self._DATA_MODE_LISTEN_CANCEL, True) except BTLEException: _LOGGER.warning("Unable to read the data via bluetooth!!") self._device.disconnect() def parameter_value(self, parameter, read_cached=True): # Special handling for battery attribute if parameter == ST_BATTERY: self.get_sensor_data_notify(_HANDLE_READ_BATTERY_LEVEL + 2, parameter) return self._data[ST_BATTERY] if parameter == ST_PROXIMITY: self.get_sensor_data_notify(_HANDLE_READ_PROXIMITY + 2, parameter) return self._data[ST_PROXIMITY] if self._data[parameter]==None: self.get_sensor_data_notify(_HANDLE_READ_ENVIRONMENTAL_SENSORS + 2, parameter) __return = self._data[parameter] self._data[parameter] = None return __return def handleNotification(self, handle, raw_data): _LOGGER.debug("handleNotification 0x%x", handle) if handle==(_HANDLE_READ_BATTERY_LEVEL + 1): self._data[ST_BATTERY] = (raw_data[2]+(raw_data[3]<<8))/10 if handle==(_HANDLE_READ_PROXIMITY + 1): self._data[ST_PROXIMITY] = (raw_data[2]+((raw_data[3] & 0x7f)<<8))/10 if handle==(_HANDLE_READ_ENVIRONMENTAL_SENSORS + 1): self._data[ST_PRESSURE] = float((raw_data[2]+(raw_data[3]<<8)+(raw_data[4]<<16)+(raw_data[5]<<24))/100) self._data[ST_HUMIDITY] = float((raw_data[6]+(raw_data[7]<<8))/10) self._data[ST_TEMPERATURE] = float((raw_data[8]+(raw_data[9]<<8))/10) ############################################################################################ # Switch Function ############################################################################################ def light_status(self): _result = None with self.lock: try: self._device = Peripheral() self._device.connect(self._mac, addrType=self._address_type, iface=self._iface) except BTLEException as ex: _LOGGER.warning("%s", ex) return _result try: status = self._device.readCharacteristic(_HANDLE_READ_LIGHT+1); _LOGGER.debug("Light status update %02x", status[2]) _result = True if status[2]==1 else False except BTLEException: _LOGGER.warning("Unable to get the status") self._device.disconnect() return _result def light_control(self, onoff): connected = False retrycount = 0 with self.lock: self._device = Peripheral() while not connected and retrycount < self._max_retries: try: self._device.connect(self._mac, addrType=self._address_type, iface=self._iface) connected = True except BTLEException as ex: _LOGGER.debug("%s", ex) time.sleep(1) retrycount += 1 if not connected: _LOGGER.warning("Unable to connect to the device %s, retrying: %d", self._mac, retrycount) return try: self._device.writeCharacteristic(_HANDLE_READ_LIGHT + 2, b'\x01', False) self._device.writeCharacteristic(_HANDLE_WRITE_CONFIG + 1, \ b'\x20\x00\x00\x00\x01\x00' \ if onoff else b'\x20\x00\x00\x00\x00\x00', \ False) _LOGGER.debug("Sent config data to control LED!!") self._device.writeCharacteristic(_HANDLE_READ_LIGHT + 2, b'\x00', False) except BTLEException: _LOGGER.warning("Unable to control the device") self._device.disconnect()
class BTConnectedDevice(object): """Handles connecting and commands for bluetooth devices""" peripheral = None console_write_characteristic = None auth_characteristic = None console_enable_char = None batt_char = None delegate = None def connect(self, addr): """Connect to the device with the given address""" self.peripheral = Peripheral(str(addr), "random") self.peripheral.setMTU(272) # 256 16 for control or headers etc. self.delegate = BLEConsoleDelegate() self.peripheral.withDelegate(self.delegate) self.print_chars_and_handles() #self.auth_characteristic = self.get_char("6E400005-B5A3-F393-E0A9-E50E24DCCA9E") #self.auth_characteristic.write(struct.pack("16s", "iwanttobeajiobit"), False) self.console_enable_char = self.get_char( "6E400004-B5A3-F393-E0A9-E50E24DCCA9E") self.console_enable_char.write(struct.pack('16s', "iwanttobeajiobit"), False) self.console_write_characteristic = self.get_char( "6E400002-B5A3-F393-E0A9-E50E24DCCA9E") self.peripheral.waitForNotifications(1) def disconnect(self): """Disconnect from the bluetooth device""" self.peripheral.disconnect() def connect_strongest_mfg(self): """Connect to the strongest mfg signal""" returned_devices = self.scan_for_bt_devices(BTDeviceTypes.MFG) strongest_rssi_dev = returned_devices[0] self.connect(strongest_rssi_dev.addr) def connect_strongest_app(self): """Connect to the stronest app signal""" returned_devices = self.scan_for_bt_devices(BTDeviceTypes.ANY) strongest_rssi_dev = returned_devices[0] sleep(1) self.connect(strongest_rssi_dev.addr) def connect_advertised_name(self, ad_name): """Connect to the strongest signal with a given advertised name""" returned_devices = self.scan_for_bt_devices(BTDeviceTypes.ANY) for device in returned_devices: device_name = self.get_device_name(device) if device_name == ad_name: sleep(1) self.connect(device.addr) return True bt_info_print( "could not find advertising name {} in scan".format(ad_name)) return False def scan_for_bt_devices(self, device_type): """Scan for Bluetooth devices""" scanner = Scanner() devices = scanner.scan() # keep only desired kind of bt device names correct_type_devices = self.get_devices_typed(devices, device_type) #sort list by RSSI correct_type_devices.sort(key=lambda x: x.rssi, reverse=True) for count, entry in enumerate(correct_type_devices): bt_info_print( "{index}:RSSI - {rssi}, Device Name - {devicename}, Address - {address}" .format(index=(count + 1), rssi=entry.rssi, devicename=self.get_device_name(entry), address=entry.addr)) return correct_type_devices def get_devices_typed(self, devlist, device_type): """Filter a list of devices using a given device type""" desired_devices = list() for entry in devlist: device_name = self.get_device_name(entry) if (device_name != None): if ((device_type == BTDeviceTypes.ANY) or (device_type == BTDeviceTypes.MFG and len(device_name) == 12) or (device_type == BTDeviceTypes.APP and device_name == "Jiobit")): desired_devices.append(entry) return desired_devices def get_device_name(self, dev): """Return the name of the given device""" devname = None try: for (sdid, _, val) in dev.getScanData(): if (sdid == 9): devname = val except: return None return devname def send_console_cmd(self, command_str): """Send a bluetooth console command""" command_str = command_str + "\r\n" self.console_write_characteristic.write(command_str) #self.peripheral.waitForNotifications(1) def send_cmd_wait_resp_time(self, command_str, wait_sec): """Send a bluetooth console command and wait for a response""" command_str = command_str + "\r\n" self.console_write_characteristic.write(command_str) #sleep(wait_sec) for _ in range(0, wait_sec): self.peripheral.waitForNotifications(1) return_str = self.delegate.print_clear_console() return return_str def get_char(self, uuid): """Get the characteristic object associated with the given UUID""" characteristic = self.peripheral.getCharacteristics(uuid=uuid)[0] return characteristic def print_chars_and_handles(self): """ Print all characteristics and handles for the connected device Also, populate some members of the delegate """ characteristics = self.peripheral.getCharacteristics() for characteristic in characteristics: uuid = str(characteristic.uuid) handle = characteristic.getHandle() hex_handle = hex(handle) bt_info_print("characteristic " + uuid + " handle: " + hex_handle) if (uuid == CONSOLE_BIT_TX_UUID): self.delegate.console_bit_tx_handle = int(handle) if (uuid == MSGPACK_APPEND_UUID): self.delegate.msgpack_append_handle = int(handle) if (uuid == MSGPACK_DONE_UUID): self.delegate.msgpack_append_handle = int(handle) def read_batt_char(self): """Read the connected device's battery characteristic""" if (self.batt_char is None): self.batt_char = self.get_char( "00002A19-0000-1000-8000-00805F9B34FB") if (self.batt_char.supportsRead()): return str(self.batt_char.read()) else: bt_info_print("does not support read") return None
class YeelightService: """ YeelightService is the yeelight control util for python author zhaohui.sol """ SERVICE = "0000FFF0-0000-1000-8000-00805F9B34FB" CHAR_CONTROL = "0000FFF1-0000-1000-8000-00805F9B34FB" CHAR_DELAY = "0000FFF2-0000-1000-8000-00805F9B34FB" CHAR_DELAY_QUERY = "0000FFF3-0000-1000-8000-00805F9B34FB" CHAR_DELAY_NOTIFY = "0000FFF4-0000-1000-8000-00805F9B34FB" CHAR_QUERY = "0000FFF5-0000-1000-8000-00805F9B34FB" CHAR_NOTIFY = "0000FFF6-0000-1000-8000-00805F9B34FB" CHAR_COLOR_FLOW = "0000FFF7-0000-1000-8000-00805F9B34FB" CHAR_NAME = "0000FFF8-0000-1000-8000-00805F9B34FB" CHAR_NAME_NOTIFY = "0000FFF9-0000-1000-8000-00805F9B34FB" CHAR_COLOR_EFFECT = "0000FFFC-0000-1000-8000-00805F9B34FB" class NotifyDelegate(DefaultDelegate): def __init__(self): DefaultDelegate.__init__(self) self.queue = list() def register(self, callback): self.queue.append(callback) def deregister(self, callback): self.queue.remove(callback) def handleNotification(self, handle, data): logging.warning("notify data %s from %s." % (data, handle)) res = dict() res['data'] = data res['handle'] = handle for c in self.queue: c(res) def __init__(self, address): """ address is the yeelight blue ble hardware address """ self.data = dict() self.address = address self.delegate = YeelightService.NotifyDelegate() self.peripher = Peripheral(deviceAddr=address) self.service = self.peripher.getServiceByUUID(YeelightService.SERVICE) self.peripher.withDelegate(self.delegate) def __character_by_uuid__(self, uuid): ''' get character by a special uuid ''' characters = self.service.getCharacteristics(forUUID=uuid) return characters[0] if characters else None def __write_character__(self, uuid, strdata): ''' write data to a special uuid ''' logging.info(u"write %s to %s." % (strdata, uuid)) character = self.__character_by_uuid__(uuid) if character: character.write(strdata) else: pass def __read_character__(self, uuid): ''' read data from a special uuid,may be it's wrong ''' logging.info(u"read data from %s." % uuid) character = self.__character_by_uuid__(uuid) if character: return character.read() else: return None def __notify_character__(self, _to, _write): ''' write data to the uuid and wait data notify ''' res = dict() def callback(data): for k in data: res[k] = data[k] self.delegate.register(callback) self.__write_character__(_to, _write) if self.peripher.waitForNotifications(5): logging.info("notify incoming.") self.delegate.deregister(callback) return res else: logging.warning("notify timeout.") self.delegate.deregister(callback) return None def __format_request__(self, strdata, length=18): ''' format the data to a special length ''' if strdata and length >= len(strdata) > 0: l = len(strdata) strdata += "".join(["," for i in range(length - l)]) return strdata else: return "".join(["," for i in range(length)]) def turn_on(self, brightness=100): ''' turn on the light with white and full brightness ''' self.control(255, 255, 255, brightness) def turn_off(self): ''' turn off the light ''' self.control(0, 0, 0, 0) def control(self, r, g, b, a): ''' turn on the light with special color ''' assert 0 <= r <= 255 assert 0 <= g <= 255 assert 0 <= b <= 255 assert 0 <= a <= 100 self.__write_character__( YeelightService.CHAR_CONTROL, self.__format_request__("%d,%d,%d,%d" % (r, g, b, a), 18)) def delay_on(self, mins=5): ''' turn on the light with a min param delay ''' assert 0 < mins < 24 * 60 self.__write_character__(YeelightService.CHAR_DELAY, self.__format_request__("%d,1" % mins, 8)) def delay_off(self, mins=5): ''' turn off the light with a min param delay ''' assert 0 < mins < 24 * 60 self.__write_character__(YeelightService.CHAR_DELAY, self.__format_request__("%d,0" % mins, 8)) def delay_status(self): ''' query the delay status , this method return a raw dict with two key 'handle' and 'data' see http://www.yeelight.com/download/yeelight_blue_message_interface_v1.0.pdf ''' return self.__notify_character__(YeelightService.CHAR_DELAY_QUERY, self.__format_request__("RT", 2)) def control_status(self): ''' query the light status , this method return a raw dict with two key 'handle' and 'data' see http://www.yeelight.com/download/yeelight_blue_message_interface_v1.0.pdf ''' return self.__notify_character__(YeelightService.CHAR_QUERY, self.__format_request__("S", 1)) def start_color_flow(self, flows): ''' start color flow with a list of params, each param should contain 5 element which is r,g,b,brightness,delay see http://www.yeelight.com/download/yeelight_blue_message_interface_v1.0.pdf ''' assert len(flows) <= 9 for i, e in enumerate(flows): assert len(e) == 5 assert 0 <= e[0] <= 255 assert 0 <= e[1] <= 255 assert 0 <= e[2] <= 255 assert 0 <= e[3] <= 100 assert 0 <= e[4] <= 10 self.__write_character__( YeelightService.CHAR_COLOR_FLOW, self.__format_request__( "%d,%d,%d,%d,%d,%d" % (i, e[0], e[1], e[2], e[3], e[4]), 20)) self.__write_character__(YeelightService.CHAR_COLOR_FLOW, self.__format_request__("CB", 20)) def stop_color_flow(self): ''' stop color flow ''' self.__write_character__(YeelightService.CHAR_COLOR_FLOW, self.__format_request__("CE", 20)) def effect_smooth(self): ''' make the color change smooth ''' self.__write_character__(YeelightService.CHAR_COLOR_EFFECT, self.__format_request__("TS", 2)) def effect_immediate(self): ''' make the color changes immediate ''' self.__write_character__(YeelightService.CHAR_COLOR_EFFECT, self.__format_request__("TE", 2)) def effect_current_color(self): ''' use the current color as a default startup color ''' self.__write_character__(YeelightService.CHAR_COLOR_EFFECT, self.__format_request__("DF", 2))
logger.info("Start script...") logger.info(f"Input parameters:\r\n" f"Domoticz Server IP: {config.DOMOTICZ_SERVER_IP}\r\n" f"Domoticz Server Port: {config.DOMOTICZ_SERVER_PORT}\r\n" f"Domoticz Server User: {config.DOMOTICZ_USERNAME}\r\n" f"Domoticz Server Password: {config.DOMOTICZ_PASSWORD}") try: scanner = Scanner().withDelegate(TempHumDelegate()) scanner.scan(10.0, passive=True) except Exception as e: logger.error(str(e)) pass for number, sensor in config.sensors.items(): try: if sensor['UPDATED'] == False: sensor_id = sensor['TH_IDX'] logger.info(f"TH_IDX:{sensor['TH_IDX']}") p = Peripheral(sensor['MAC']) p.writeCharacteristic( 0x0038, b'\x01\x00', True ) #enable notifications of Temperature, Humidity and Battery voltage p.writeCharacteristic(0x0046, b'\xf4\x01\x00', True) p.withDelegate(TempHumDelegate()) handle_temp_hum_value() p.disconnect() except Exception as e: logger.error(str(e)) pass
class CarControl: scanner = None devices = [] devicetext = "" isConnected = False carAddr = None carName = None carDevice = None carPeripheral = None def __repr__(self): return "devices: {}, address: {}, carName: {}".format( len(self.devices), self.carAddr, self.carName) #return "devices: {}".format(len(self.devices)) def scan(self, timeout=10): foundDevices = 0 self.devices = [] self.devicetext = "" newdevices = [] scansuccess = False try: self.scanner = Scanner() self.scanner.withDelegate(MainDelegate()) newdevices = self.scanner.scan(timeout) scansuccess = True except Exception as e: scansuccess = False if scansuccess: for dev in newdevices: if dev.addrType == btle.ADDR_TYPE_PUBLIC: foundDevices = foundDevices + 1 self.devices.append({ "name": dev.getValueText(9), "addr": dev.addr }) self.devicetext = self.devicetext + "> Device #{} {} ({}), [{}], [{}]\n".format( foundDevices, dev.addr, dev.addrType, dev.getValueText(9), dev.getValueText(8)) # add known cars for k in KNOWN_CARS: foundDevices = foundDevices + 1 self.devices.append({"name": k["name"], "addr": k["addr"]}) self.devicetext = self.devicetext + "> Device #{} {} ({}), [{}]\n".format( foundDevices, k["addr"], "Known Device", k["name"]) return scansuccess def connect(self, carnum): if len(self.devices) == 0: print("connect: Nothing scanned") return if carnum < 0 or carnum > len(self.devices): print("connect: Car number invalid, {}".format(carnum)) return try: self.carAddr = self.devices[carnum]["addr"] self.carName = self.devices[carnum]["name"] self.carPeripheral = Peripheral() self.carPeripheral.withDelegate(MainDelegate()) self.carPeripheral.connect(self.carAddr) self.isConnected = True return True except Exception as e: self.carPeripheral = None print("connect: Error,", e) return False def listdescriptors(self): try: print("listdescriptors: ...") print("listdescriptors: listing descriptors") descriptors = self.carPeripheral.getDescriptors() for desc in descriptors: print(" -- DESCRIPTORS: {}, [{}], Handle: {} (0x{:04x})". format(desc.uuid, UUID(desc.uuid).getCommonName(), desc.handle, desc.handle)) except Exception as e: print("listdescriptors: Error,", e) def listservices(self): try: print("listservices: listing services") services = self.carPeripheral.getServices() for serv in services: print(" -- SERVICE: {} [{}]".format( serv.uuid, UUID(serv.uuid).getCommonName())) characteristics = serv.getCharacteristics() for chara in characteristics: print( " -- --> CHAR: {}, Handle: {} (0x{:04x}) - {} - [{}]" .format(chara.uuid, chara.getHandle(), chara.getHandle(), chara.propertiesToString(), UUID(chara.uuid).getCommonName())) except Exception as e: print("listservices: Error,", e) def disconnectcar(self): self.isConnected = False if self.carPeripheral is None: print("disconnectcar: No car connected") return False try: self.carPeripheral.disconnect() self.carPeripheral = None return True except Exception as e: print("disconnectcar: Error,", e) return False def readcharacteristics(self): try: if self.carPeripheral is None: print("readcharacteristics: No car connected") return print("readcharacteristics: reading the readables") chars = self.carPeripheral.getCharacteristics() for c in chars: if c.supportsRead(): print( " -- READ: {} [{}] (0x{:04x}), {}, Value: {}".format( c.uuid, UUID(c.uuid).getCommonName(), c.getHandle(), c.descs, c.read() if c.supportsRead() else "")) except Exception as e: print("readcharacteristics: Error,", e) def writevalue(self, handle, value, wait=False): try: if self.carPeripheral is None: #print("writevalue: No car connected") return #print("writevalue: writing to handle 0x{:04x} value {}".format(handle, value)) self.carPeripheral.writeCharacteristic(handle, value, wait) except Exception as e: print("writevalue: Error,", e) def readvalue(self, handle): try: if self.carPeripheral is None: print("readvalue: No car connected") return print("readvalue: reading handle 0x{:04x}".format(handle)) value = self.carPeripheral.readCharacteristic(handle) print("readvalue: Handle 0x{:04x} = {}".format(handle, value)) except Exception as e: print("readvalue: Error,", e) def sendhandshake(self): # handshake... for finger in BTCMD_HANDSHAKE: self.writevalue(BTDRIVE_HANDLE, finger, True) def carfiregun(self, intensity=0.5): self.writevalue(BTDRIVE_HANDLE, BTCMD_FIREGUN) def carforward(self, intensity=0.5): if intensity < 0.1 or intensity > 1: return scale = 0x1F actual_intensity = 0x00 + round(scale * intensity) # pull value list tx_list = [BTCMD_DRIVE, actual_intensity, 0x00] tx_data = bytes(tx_list) self.writevalue(BTDRIVE_HANDLE, tx_data, True) def carreverse(self, intensity=0.5): if intensity < 0.1 or intensity > 1: return scale = 0x1F actual_intensity = 0x20 + round(scale * intensity) # pull value list tx_list = [BTCMD_DRIVE, actual_intensity, 0x00] tx_data = bytes(tx_list) self.writevalue(BTDRIVE_HANDLE, tx_data, True) def carright(self, intensity=0.5): if intensity < 0.1 or intensity > 1: return scale = 0x1F actual_intensity = 0x40 + round(scale * intensity) # pull value list tx_list = [BTCMD_DRIVE, 0x00, actual_intensity] tx_data = bytes(tx_list) self.writevalue(BTDRIVE_HANDLE, tx_data, True) def carleft(self, intensity=0.5): if intensity < 0.1 or intensity > 1: return scale = 0x1F actual_intensity = 0x60 + round(scale * intensity) # pull value list tx_list = [BTCMD_DRIVE, 0x00, actual_intensity] tx_data = bytes(tx_list) self.writevalue(BTDRIVE_HANDLE, tx_data, True)
scanner = Scanner().withDelegate(ScanDelegate()) devices = scanner.scan(3.0) n = 0 for dev in devices: print(n, ": Device ",dev.addr, "(", dev.addrType, ")", ", RSSI= ", dev.rssi, " dB" ) n += 1 for (adtype, desc, value) in dev.getScanData(): print(desc, "=", value) number = input('Enter your device number: ') print('Device', number) print(list(devices)[int(number)].addr) print("Connecting...") dev = Peripheral(list(devices)[int(number)].addr, 'random') dev.withDelegate(ScanDelegate()) print("Services...") for svc in dev.services: print(str(svc)) try: testService = dev.getServiceByUUID(UUID(0xa000)) for ch in testService.getCharacteristics(): print(str(ch)) testService = dev.getServiceByUUID(UUID(0xb000)) for ch in testService.getCharacteristics(): print(str(ch)) but_ch = dev.getCharacteristics(uuid=UUID(0xa001))[0] cccd = but_ch.getHandle() + 1 dev.writeCharacteristic(cccd, bytes([0x01, 0x00])) if (but_ch.supportsRead()): print(but_ch.read())
class BleDevice: def __init__(self, addr, thing_id): self.addr = addr self.thing_id = thing_id def connect(self): print("--Connect") print(" -Connecting to Peripheral:", self.addr) self.per = Peripheral(self.addr) print(" -Connected to Peripheral:", self.addr) print() def disconnect(self): print("--Disconnect") print(" -Disconnecting from Peripheral:", self.addr) self.per.disconnect() print(" -Disconnected from Peripheral:", self.addr) def setup(self): print("--Setup") self.per.setMTU(112) print(" -MTU set to 112 bytes (109+3 bytes)") self.svc = self.per.getServiceByUUID( "2ea78970-7d44-44bb-b097-26183f402400") self.data_ch = self.svc.getCharacteristics( "2ea78970-7d44-44bb-b097-26183f402410")[0] self.data_ch_hnd = self.data_ch.getHandle() self.per.writeCharacteristic(self.data_ch_hnd + 1, b"\x01\00", True) print(" -Enabled notifications on characteristic 0x2410") self.ctrl_ch = self.svc.getCharacteristics( "2ea78970-7d44-44bb-b097-26183f402409")[0] self.ctrl_ch_hnd = self.ctrl_ch.getHandle() self.ctrl_ch.write(b"\x01", True) print(" -Wrote 0x01 to characteristic 0x2409") self.per.withDelegate(MyDelegate(self)) print() def set_handler(self, handler): self.handler = handler def print_svc_char(self): print("--Services and Characteristics") services = self.per.getServices() for svc in services: print(svc) print(svc.uuid) print() characteristics = svc.getCharacteristics() for char in characteristics: print("\t", char) print("\t", char.uuid) print("\t Handle:", char.getHandle()) print("\t", char.propertiesToString()) print() def get_data(self, mins): print("--Reports") t_end = time.time() + 60 * mins while time.time() < t_end: if self.per.waitForNotifications(1.0): continue print(" -Waiting...") print()