Beispiel #1
0
	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);
Beispiel #2
0
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
Beispiel #4
0
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")
Beispiel #5
0
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
Beispiel #6
0
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)
Beispiel #7
0
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()
Beispiel #9
0
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
Beispiel #10
0
 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
Beispiel #11
0
    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
Beispiel #12
0
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
Beispiel #13
0
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
Beispiel #15
0
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()
Beispiel #16
0
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)
Beispiel #18
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"))
Beispiel #19
0
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
Beispiel #20
0
        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
Beispiel #21
0
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)
Beispiel #22
0
        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()
Beispiel #23
0
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
Beispiel #24
0
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))
Beispiel #26
0
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()
Beispiel #27
0
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
Beispiel #28
0
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))
Beispiel #29
0
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
Beispiel #30
0
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)
Beispiel #31
0

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()