Beispiel #1
1
class RileyLink(PacketRadio):
    def __init__(self):
        self.peripheral = None
        self.pa_level_index = PA_LEVELS.index(0x84)
        self.data_handle = None
        self.logger = getLogger()
        self.address = None
        if os.path.exists(RILEYLINK_MAC_FILE):
            with open(RILEYLINK_MAC_FILE, "r") as stream:
                self.address = stream.read()
        self.service = None
        self.response_handle = None
        self.notify_event = Event()
        self.initialized = False

    def connect(self, force_initialize=False):
        try:
            if self.address is None:
                self.address = self._findRileyLink()

            if self.peripheral is None:
                self.peripheral = Peripheral()

            try:
                state = self.peripheral.getState()
                if state == "conn":
                    return
            except BTLEException:
                pass

            self._connect_retry(3)

            self.service = self.peripheral.getServiceByUUID(RILEYLINK_SERVICE_UUID)
            data_char = self.service.getCharacteristics(RILEYLINK_DATA_CHAR_UUID)[0]
            self.data_handle = data_char.getHandle()

            char_response = self.service.getCharacteristics(RILEYLINK_RESPONSE_CHAR_UUID)[0]
            self.response_handle = char_response.getHandle()

            response_notify_handle = self.response_handle + 1
            notify_setup = b"\x01\x00"
            self.peripheral.writeCharacteristic(response_notify_handle, notify_setup)

            while self.peripheral.waitForNotifications(0.05):
                self.peripheral.readCharacteristic(self.data_handle)

            if self.initialized:
                self.init_radio(force_initialize)
            else:
                self.init_radio(True)
        except BTLEException:
            if self.peripheral is not None:
                self.disconnect()
            raise

    def disconnect(self, ignore_errors=True):
        try:
            if self.peripheral is None:
                self.logger.info("Already disconnected")
                return
            self.logger.info("Disconnecting..")
            if self.response_handle is not None:
                response_notify_handle = self.response_handle + 1
                notify_setup = b"\x00\x00"
                self.peripheral.writeCharacteristic(response_notify_handle, notify_setup)
        except BTLEException:
            if not ignore_errors:
                raise
        finally:
            try:
                if self.peripheral is not None:
                    self.peripheral.disconnect()
                    self.peripheral = None
            except BTLEException:
                if ignore_errors:
                    self.logger.exception("Ignoring btle exception during disconnect")
                else:
                    raise

    def get_info(self):
        try:
            self.connect()
            bs = self.peripheral.getServiceByUUID(XGATT_BATTERYSERVICE_UUID)
            bc = bs.getCharacteristics(XGATT_BATTERY_CHAR_UUID)[0]
            bch = bc.getHandle()
            battery_value = int(self.peripheral.readCharacteristic(bch)[0])
            self.logger.debug("Battery level read: %d", battery_value)
            version, v_major, v_minor = self._read_version()
            return { "battery_level": battery_value, "mac_address": self.address,
                    "version_string": version, "version_major": v_major, "version_minor": v_minor }
        except BTLEException as btlee:
            raise PacketRadioError("Error communicating with RileyLink") from btlee
        finally:
            self.disconnect()

    def _read_version(self):
        version = None
        try:
            if os.path.exists(RILEYLINK_VERSION_FILE):
                with open(RILEYLINK_VERSION_FILE, "r") as stream:
                    version = stream.read()
            else:
                response = self._command(Command.GET_VERSION)
                if response is not None and len(response) > 0:
                    version = response.decode("ascii")
                    self.logger.debug("RL reports version string: %s" % version)

                    try:
                        with open(RILEYLINK_VERSION_FILE, "w") as stream:
                            stream.write(version)
                    except IOError:
                        self.logger.exception("Failed to store version in file")

            if version is None:
                return "0.0", 0, 0

            try:
                m = re.search(".+([0-9]+)\\.([0-9]+)", version)
                if m is None:
                    raise PacketRadioError("Failed to parse firmware version string: %s" % version)

                v_major = int(m.group(1))
                v_minor = int(m.group(2))
                self.logger.debug("Interpreted version major: %d minor: %d" % (v_major, v_minor))

                return version, v_major, v_minor

            except Exception as ex:
                raise PacketRadioError("Failed to parse firmware version string: %s" % version) from ex

        except IOError:
            self.logger.exception("Error reading version file")
        except PacketRadioError:
            raise

        response = self._command(Command.GET_VERSION)
        if response is not None and len(response) > 0:
            version = response.decode("ascii")
            self.logger.debug("RL reports version string: %s" % version)
            try:
                m = re.search(".+([0-9]+)\\.([0-9]+)", version)
                if m is None:
                    raise PacketRadioError("Failed to parse firmware version string: %s" % version)

                v_major = int(m.group(1))
                v_minor = int(m.group(2))
                self.logger.debug("Interpreted version major: %d minor: %d" % (v_major, v_minor))

                return (version, v_major, v_minor)
            except PacketRadioError:
                raise
            except Exception as ex:
                raise PacketRadioError("Failed to parse firmware version string: %s" % version) from ex

    def init_radio(self, force_init=False):
        try:
            version, v_major, v_minor = self._read_version()

            if v_major < 2:
                self.logger.error("Firmware version is below 2.0")
                raise PacketRadioError("Unsupported RileyLink firmware %d.%d (%s)" %
                                        (v_major, v_minor, version))

            if not force_init:
                if v_major == 2 and v_minor < 3:
                    response = self._command(Command.READ_REGISTER, bytes([Register.SYNC1, 0x00]))
                else:
                    response = self._command(Command.READ_REGISTER, bytes([Register.SYNC1]))
                if response is not None and len(response) > 0 and response[0] == 0xA5:
                    return

            self._command(Command.RADIO_RESET_CONFIG)
            self._command(Command.SET_SW_ENCODING, bytes([Encoding.MANCHESTER]))
            frequency = int(433910000 / (24000000 / pow(2, 16)))
            self._command(Command.SET_PREAMBLE, bytes([0x66, 0x65]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.FREQ0, frequency & 0xff]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.FREQ1, (frequency >> 8) & 0xff]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.FREQ2, (frequency >> 16) & 0xff]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.PKTCTRL1, 0x20]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.PKTCTRL0, 0x00]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.FSCTRL1, 0x06]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG4, 0xCA]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG3, 0xBC]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG2, 0x06]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG1, 0x70]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG0, 0x11]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.DEVIATN, 0x44]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.MCSM0, 0x18]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.FOCCFG, 0x17]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL3, 0xE9]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL2, 0x2A]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL1, 0x00]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL0, 0x1F]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.TEST1, 35]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.TEST0, 0x09]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.PATABLE0, PA_LEVELS[self.pa_level_index]]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.FREND0, 0x00]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.SYNC1, 0xA5]))
            self._command(Command.UPDATE_REGISTER, bytes([Register.SYNC0, 0x5A]))

            response = self._command(Command.GET_STATE)
            if response != b"OK":
                raise PacketRadioError("Rileylink state is not OK. Response returned: %s" % response)

            self.initialized = True

        except PacketRadioError as rle:
            self.logger.error("Error while initializing rileylink radio: %s", rle)
            raise

    def tx_up(self):
        if self.pa_level_index < len(PA_LEVELS) - 1:
            self.pa_level_index += 1
            self._set_amp(self.pa_level_index)

    def tx_down(self):
        if self.pa_level_index > 0:
            self.pa_level_index -= 1
            self._set_amp(self.pa_level_index)

    def set_tx_power(self, tx_power):
        if tx_power is None:
            return
        elif tx_power == TxPower.Lowest:
            self._set_amp(0)
        elif tx_power == TxPower.Low:
            self._set_amp(PA_LEVELS.index(0x12))
        elif tx_power == TxPower.Normal:
            self._set_amp(PA_LEVELS.index(0x60))
        elif tx_power == TxPower.High:
            self._set_amp(PA_LEVELS.index(0xC8))
        elif tx_power == TxPower.Highest:
            self._set_amp(PA_LEVELS.index(0xC0))

    def get_packet(self, timeout=5.0):
        try:
            self.connect()
            return self._command(Command.GET_PACKET, struct.pack(">BL", 0, int(timeout * 1000)),
                                 timeout=float(timeout)+0.5)
        except PacketRadioError as rle:
            self.logger.error("Error while receiving data: %s", rle)
            raise

    def send_and_receive_packet(self, packet, repeat_count, delay_ms, timeout_ms, retry_count, preamble_ext_ms):

        try:
            self.connect()
            return self._command(Command.SEND_AND_LISTEN,
                                  struct.pack(">BBHBLBH",
                                              0,
                                              repeat_count,
                                              delay_ms,
                                              0,
                                              timeout_ms,
                                              retry_count,
                                              preamble_ext_ms)
                                              + packet,
                                  timeout=30)
        except PacketRadioError as rle:
            self.logger.error("Error while sending and receiving data: %s", rle)
            raise

    def send_packet(self, packet, repeat_count, delay_ms, preamble_extension_ms):
        try:
            self.connect()
            result = self._command(Command.SEND_PACKET, struct.pack(">BBHH", 0, repeat_count, delay_ms,
                                                                   preamble_extension_ms) + packet,
                                  timeout=30)
            return result
        except PacketRadioError as rle:
            self.logger.error("Error while sending data: %s", rle)
            raise

    def _set_amp(self, index=None):
        try:
            self.connect()
            if index is not None:
                self.pa_level_index = index
            self._command(Command.UPDATE_REGISTER, bytes([Register.PATABLE0, PA_LEVELS[self.pa_level_index]]))
            self.logger.debug("Setting pa level to index %d of %d" % (self.pa_level_index, len(PA_LEVELS)))
        except PacketRadioError:
            self.logger.exception("Error while setting tx amplification")
            raise


    def _findRileyLink(self):
        scanner = Scanner()
        found = None
        self.logger.debug("Scanning for RileyLink")
        retries = 10
        while found is None and retries > 0:
            retries -= 1
            for result in scanner.scan(1.0):
                if result.getValueText(7) == RILEYLINK_SERVICE_UUID:
                    self.logger.debug("Found RileyLink")
                    found = result.addr
                    try:
                        with open(RILEYLINK_MAC_FILE, "w") as stream:
                            stream.write(result.addr)
                    except IOError:
                        self.logger.warning("Cannot store rileylink mac radio_address for later")
                    break

        if found is None:
            raise PacketRadioError("Could not find RileyLink")

        return found

    def _connect_retry(self, retries):
        while retries > 0:
            retries -= 1
            self.logger.info("Connecting to RileyLink, retries left: %d" % retries)

            try:
                self.peripheral.connect(self.address)
                self.logger.info("Connected")
                break
            except BTLEException as btlee:
                self.logger.warning("BTLE exception trying to connect: %s" % btlee)
                try:
                    p = subprocess.Popen(["ps", "-A"], stdout=subprocess.PIPE)
                    out, err = p.communicate()
                    for line in out.splitlines():
                        if "bluepy-helper" in line:
                            pid = int(line.split(None, 1)[0])
                            os.kill(pid, 9)
                            break
                except:
                    self.logger.warning("Failed to kill bluepy-helper")
                time.sleep(1)

    def _command(self, command_type, command_data=None, timeout=10.0):
        try:
            if command_data is None:
                data = bytes([1, command_type])
            else:
                data = bytes([len(command_data) + 1, command_type]) + command_data

            self.peripheral.writeCharacteristic(self.data_handle, data, withResponse=True)

            if not self.peripheral.waitForNotifications(timeout):
                raise PacketRadioError("Timed out while waiting for a response from RileyLink")

            response = self.peripheral.readCharacteristic(self.data_handle)

            if response is None or len(response) == 0:
                raise PacketRadioError("RileyLink returned no response")
            else:
                if response[0] == Response.COMMAND_SUCCESS:
                    return response[1:]
                elif response[0] == Response.COMMAND_INTERRUPTED:
                    self.logger.warning("A previous command was interrupted")
                    return response[1:]
                elif response[0] == Response.RX_TIMEOUT:
                    return None
                else:
                    raise PacketRadioError("RileyLink returned error code: %02X. Additional response data: %s"
                                         % (response[0], response[1:]), response[0])
        except Exception as e:
            raise PacketRadioError("Error executing command") from e
Beispiel #2
0
class Nuimo(object):

    SERVICE_UUIDS = [
        UUID('0000180f-0000-1000-8000-00805f9b34fb'), # Battery
        UUID('f29b1525-cb19-40f3-be5c-7241ecb82fd2'), # Sensors
        UUID('f29b1523-cb19-40f3-be5c-7241ecb82fd1')  # LED Matrix
    ]

    CHARACTERISTIC_UUIDS = {
        UUID('00002a19-0000-1000-8000-00805f9b34fb'): 'BATTERY',
        UUID('f29b1529-cb19-40f3-be5c-7241ecb82fd2'): 'BUTTON',
        UUID('f29b1528-cb19-40f3-be5c-7241ecb82fd2'): 'ROTATION',
        UUID('f29b1527-cb19-40f3-be5c-7241ecb82fd2'): 'SWIPE',
        UUID('f29b1526-cb19-40f3-be5c-7241ecb82fd2'): 'FLY',
        UUID('f29b1524-cb19-40f3-be5c-7241ecb82fd1'): 'LED_MATRIX'
    }

    NOTIFICATION_CHARACTERISTIC_UUIDS = [
        'BATTERY', # Uncomment only if you are not using the iOS emulator (iOS does't support battery updates without authentication)
        'BUTTON',
        'ROTATION',
        'SWIPE',
        'FLY']

    # Notification data
    NOTIFICATION_ON = struct.pack("BB", 0x01, 0x00)
    NOTIFICATION_OFF = struct.pack("BB", 0x00, 0x00)

    def __init__(self, mac_address):
        self.macAddress = mac_address
        self.delegate=NuimoDelegate(self)

    def set_delegate(self, delegate):
        self.delegate = delegate

    def connect(self):
        self.peripheral = Peripheral(self.macAddress, addrType='random')
        # Retrieve all characteristics from desired services and map them from their UUID
        characteristics = list(itertools.chain(*[self.peripheral.getServiceByUUID(uuid).getCharacteristics() for uuid in Nuimo.SERVICE_UUIDS]))
        characteristics = dict((c.uuid, c) for c in characteristics)
        # Store each characteristic's value handle for each characteristic name
        self.characteristicValueHandles = dict((name, characteristics[uuid].getHandle()) for uuid, name in Nuimo.CHARACTERISTIC_UUIDS.items())
        # Subscribe for notifications
        for name in Nuimo.NOTIFICATION_CHARACTERISTIC_UUIDS:
            self.peripheral.writeCharacteristic(self.characteristicValueHandles[name] + 1, Nuimo.NOTIFICATION_ON, True)
        self.peripheral.setDelegate(self.delegate)

    def wait_for_notifications(self):
        self.peripheral.wait_for_notifications(1.0)

    def display_led_matrix(self, matrix, timeout, brightness=1.0):
        matrix = '{:<81}'.format(matrix[:81])
        bites = list(map(lambda leds: reduce(lambda acc, led: acc + (1 << led if leds[led] not in [' ', '0'] else 0), range(0, len(leds)), 0), [matrix[i:i+8] for i in range(0, len(matrix), 8)]))
        self.peripheral.writeCharacteristic(self.characteristicValueHandles['LED_MATRIX'], struct.pack('BBBBBBBBBBBBB', bites[0], bites[1], bites[2], bites[3], bites[4], bites[5], bites[6], bites[7], bites[8], bites[9], bites[10], max(0, min(255, int(255.0 * brightness))), max(0, min(255, int(timeout * 10.0)))), True)
Beispiel #3
0
class MyTest():
    BLE_Connected=False
    p=None
    bRunning=False
    DoWorkThread = 0
    start_time=time.time()
    ReconnectIntervalSecond = 10

    def __init__(self, index, mac_address):
        self.index=index
        self.mac_address=mac_address

    def Connect(self):
        print("Start To Connect BLE-" + str(self.index))
        try:
            self.p = Peripheral(self.mac_address)
            self.p.setDelegate(MyDelegate(self.index))
            self.BLE_Connected = True

            try:

                se10=self.p.getServiceByUUID('ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6')
                ch10=se10.getCharacteristics('ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6')
                ccc_desc = ch10[0].getDescriptors(forUUID=0x2902)[0]
                ccc_desc.write(b"\x02")
            except:
                print("Machine-" + str(self.index) + " Set Notification Error")
         
        except:
            self.BLE_Connected = False
            print("Machine-" + str(self.index) + " Connect Error")

    def Run(self):
        try:
            self.bRunning = True
            self.DoWorkThread = threading.Thread(target=self.DoWork)
            self.DoWorkThread.start()
        except:
            print("Machine-" + str(self.index) + " Run Threading Fail")
        finally:
            print("Machine-" + str(self.index) + " Run Threading Success")

    def DoWork(self):
        while self.bRunning:
            if self.BLE_Connected:
                try:
                    self.p.waitForNotifications(0.5)
                except:
                    self.BLE_Connected = False
                    print("Machine-" + str(self.index) + " - Wait For Notification Error")
            else:
                if (int(time.time()-self.start_time)>self.ReconnectIntervalSecond):
                    self.start_time=time.time()
                    self.Connect()
            time.sleep(0.5)
    
    def Close(self):
        self.bRunning = False
        if self.BLE_Connected == True:
            try:
                self.p.disconnect()
                print("Machine-" + str(self.index) + " - Disconnect Success")
            except:
                self.p=None
                print("Machine-" + str(self.index) + " - Disconnect Fail")
Beispiel #4
0
                device_msg[0] = dev.addr
                print("%s %s %s" % (device_msg[0], value, device_msg[1]))
                break
    return device_msg
  
#scan for your device    
scanner = Scanner().withDelegate(ScanDelegate()) 
devices = scanner.scan(3.0)
remote = blue_scan()

#connect your device
print(remote) 
p = Peripheral(remote[0]) #remote[0]
p.setDelegate(ScanDelegate())

s = p.getServiceByUUID("0000ffe0-0000-1000-8000-00805f9b34fb")
c = s.getCharacteristics()[0]
 

def notification_thread():
    while True:
        if p.waitForNotifications(2.0):
            # handleNotification() was called
            continue
def send_thread():
    while True:
        cmd = input()
        tmp = c.write(bytes(cmd, "utf-8"), True)
        if tmp :
            print("Send: " +  cmd)
        
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 #6
0
class CharDelegate(DefaultDelegate):
    def __init__(self):
        DefaultDelegate.__init__(self)

    def handleNotification(self, cHandle, data):
        [lux] = struct.unpack('<f', data)
        print("Value: " + str(lux) + " lux")


YOUR_ADDRESS = "c0:98:e5:49:00:00"  # Replace address with your device address
YOUR_SERVICE_UUID = "de97aeee-0e7e-4720-8038-4dc47aa9562f"
YOUR_CHAR_UUID = "de97aeef-0e7e-4720-8038-4dc47aa9562f"

try:
    buckler = Peripheral(YOUR_ADDRESS)
    buckler.setDelegate(CharDelegate())

    print("connected")

    sv = buckler.getServiceByUUID(YOUR_SERVICE_UUID)

    # Enable notifications:
    ch = sv.getCharacteristics(YOUR_CHAR_UUID)[0]
    buckler.writeCharacteristic(ch.valHandle + 1, b"\x01\x00")

    while True:
        if buckler.waitForNotifications(2.0):
            print("Got notification")
finally:
    buckler.disconnect()
    def handleNotification(self, cHandle, data):
        print "inside notification"
        print "Handle=", cHandle, "Battery level=", ord(data)


devices = Scanner()  # Scanner Object
temp = devices.scan(10)  # Start Scan

for dev in temp:
    if dev.getScanData(
    )[3][2] == "mr. singh":  # Check for the target BLE device name
        if dev.connectable:
            p = Peripheral(dev.addr, "random")  # Connect to the Device
            p.setDelegate(
                ScanDelegate()
            )  # Create internal Object of scandelegate class to handle the notifications

try:
    ch = p.getServiceByUUID(0x180F).getCharacteristics()[
        0]  # get battery level characteristic for battery Service
    print ch
    if (ch.read()):
        print ord(ch.read())  # Read the characteristic Value
        while True:
            if p.waitForNotifications(1.0):
                # handleNotification() was called
                continue
            print "Waiting..."
finally:
    p.disconnect()
Beispiel #8
0
class RobotController():
    def __init__(self, address):

        self.robot = Peripheral(addr)
        self.robot.setDelegate(RobotDelegate(self))
        print("connected")

        # get service from robot
        # get characteristic handles from service/robot
        self.sv = self.robot.getServiceByUUID(SERVICE_UUID)
        self.drive_command = self.sv.getCharacteristics(CHAR_UUID)[0]
        self.data = self.sv.getCharacteristics(DATA_UUID)[0]
        self.ack = self.sv.getCharacteristics(ACK_UUID)[0]
        self.data_ready = self.sv.getCharacteristics(DATA_READY_UUID)[0]
        self.data_list = []

        # Set trial parameters
        ls = float(input("Left Speed: "))
        rs = float(input("Right Speed: "))
        t = float(input("Time (float seconds): "))
        name = "l_{}_r_{}".format(int(ls), int(rs))
        n = int(input("Num trials: "))

        for i in range(n):
            while True:
                ack = struct.unpack("B", self.ack.read())[0]
                # Send command when the robot is ready
                if ack == 0:
                    if self.data_list:
                        self.write_data(ls, rs, "{}_{}".format(name, i))
                        self.data_list = []
                        break
                    input("Ready?")
                    self.send_command(ls, rs, t)
                    ack = struct.unpack("B", self.ack.read())[0]

                    # Wait until robot acknowledges before proceding
                    while ack != 1:
                        continue
                else:
                    data_ready = struct.unpack("B", self.data_ready.read())[0]
                    # Read data when the robot is ready
                    if data_ready:
                        data = struct.unpack("f" * 30, self.data.read())
                        self.data_list.extend(data)
                        self.data_ready.write(struct.pack('B', *[False]))
                    else:
                        print("Waiting...")

    def send_command(self, ls, rs, t):
        # Tell Romi to drive specified left and right speeds for set amount of time
        self.ch.write(struct.pack('fff', *[ls, rs, t]))

    def write_data(self, ls, rs, name):
        # Write data to CSV file

        left_input = [ls] * (len(self.data_list) // 3)
        right_input = [rs] * (len(self.data_list) // 3)
        left_dists = self.data_list[::3]
        right_dists = self.data_list[1::3]
        times = self.data_list[2::3]
        header = [
            "left_input", "right_input", "left_distance", "right_distance",
            "time"
        ]
        all_data = [left_input, right_input, left_dists, right_dists, times]
        df = pd.DataFrame(all_data).transpose()
        df.columns = header
        print(df)
        df.to_csv("data/{}.csv".format(name), index=False)

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.robot.disconnect()
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:
                            self.session.lock.acquire()
                            self.session.perip.waitForNotifications(1.0)
                            self.session.lock.release()
                        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(1)

    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}")
            if not self.restart_notification_event.is_set():
                return
            params = self.handles[handle]
            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 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" }

        if self.status == self.INITIAL and method == 'discover':
            scanner = Scanner()
            devices = scanner.scan(1.0)
            for dev in devices:
                if self.matches(dev, params['filters']):
                    self.found_devices.append(dev)
            if len(self.found_devices) == 0:
                err_msg = f"BLE service not found for {params['filters']}"
                res["error"] = { "message": 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']
            charas = self.perip.getCharacteristics(uuid=chara_id)
            c = charas[0]
            if c.uuid != UUID(chara_id):
                logger.error("Failed to get characteristic {chara_id}")
                self.status = self.DONE
            else:
                self.lock.acquire()
                b = c.read()
                self.lock.release()
                message = base64.standard_b64encode(b).decode('ascii')
                res['result'] = { 'message': message, 'encode': 'base64' }
            if params['startNotifications'] == True:
                logger.debug(f"start notification for {chara_id}")
                service = self.perip.getServiceByUUID(UUID(service_id))
                chas = service.getCharacteristics(forUUID=chara_id)
                handle = chas[0].getHandle()
                # prepare notification handler
                self.delegate.add_handle(service_id, chara_id, handle)
                # request notification to the BLE device
                self.lock.acquire()
                self.perip.writeCharacteristic(chas[0].getHandle() + 1,
                                               b"\x01\x00", True)
                self.lock.release()

        elif self.status == self.CONNECTED and method == 'write':
            logger.debug("handle write request")
            service_id = params['serviceId']
            chara_id = params['characteristicId']
            charas = self.perip.getCharacteristics(uuid=chara_id)
            c = charas[0]
            if 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)
                self.lock.acquire()
                c.write(data)
                self.lock.release()
                res['result'] = len(data)

        logger.debug(res)
        return res

    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 #10
0
import sys
import binascii
import struct
import time
from bluepy.btle import UUID, Peripheral

button_service_uuid = UUID(0xA000)
button_char_uuid = UUID(0xA001)
LED_service_uuid = UUID(0xA100)
LED_char_uuid = UUID(0xA101)

p = Peripheral("e5:d3:1d:fc:b0:d3", "random")
ButtonService = p.getServiceByUUID(button_service_uuid)
LEDService = p.getServiceByUUID(LED_service_uuid)

try:
    ch = ButtonService.getCharacteristics(button_char_uuid)[0]
    ch2 = LEDService.getCharacteristics(LED_char_uuid)[0]
    while 1:
        if (ch.supportsRead()):
            val = binascii.b2a_hex(ch.read())
            if (val == "00"):
                print("The button is not pressed.")
            else:
                print("The button is pressed.")
        LED_in = raw_input("Turn on the LED ? (y/n)")
        writevalue = 0
        if (LED_in == 'y'):
            writevalue = "01"
        else:
            writevalue = "00"
class LightBlueBean(DefaultDelegate):
    # https://github.com/PunchThrough/bean-documentation/blob/master/app_message_types.md
    MSG_ID_SERIAL_DATA        = 0x0000
    MSG_ID_BT_SET_ADV         = 0x0500
    MSG_ID_BT_SET_CONN        = 0x0502
    MSG_ID_BT_SET_LOCAL_NAME  = 0x0504
    MSG_ID_BT_SET_PIN         = 0x0506
    MSG_ID_BT_SET_TX_PWR      = 0x0508
    MSG_ID_BT_GET_CONFIG      = 0x0510
    MSG_ID_BT_ADV_ONOFF       = 0x0512
    MSG_ID_BT_SET_SCRATCH     = 0x0514
    MSG_ID_BT_GET_SCRATCH     = 0x0515
    MSG_ID_BT_RESTART         = 0x0520
    MSG_ID_GATING             = 0x0550
    MSG_ID_BL_CMD             = 0x1000
    MSG_ID_BL_FW_BLOCK        = 0x1001
    MSG_ID_BL_STATUS          = 0x1002
    MSG_ID_CC_LED_WRITE       = 0x2000
    MSG_ID_CC_LED_WRITE_ALL   = 0x2001
    MSG_ID_CC_LED_READ_ALL    = 0x2002
    MSG_ID_CC_LED_DATA        = 0x2082
    MSG_ID_CC_ACCEL_READ      = 0x2010
    MSG_ID_CC_ACCEL_DATA      = 0x2090
    MSG_ID_CC_TEMP_READ       = 0x2011
    MSG_ID_CC_TEMP_DATA       = 0x2091
    MSG_ID_CC_BATT_READ       = 0x2015
    MSG_ID_CC_BATT_DATA       = 0x2095
    MSG_ID_AR_SET_POWER       = 0x3000
    MSG_ID_AR_GET_CONFIG      = 0x3006
    MSG_ID_DB_LOOPBACK        = 0xFE00
    MSG_ID_DB_COUNTER         = 0xFE01
    
    def __init__(self, mac):
        self.conn = Peripheral(mac)
        self.conn.setDelegate(self)
        self.count = 0
        self.buffin = [None]*10
        self.got1 = False
        print('connected')
        
        self.service = self.conn.getServiceByUUID(_LBN_UUID(0x10))
        self.serial = self.service.getCharacteristics(_LBN_UUID(0x11)) [0]
        
        #print(self.serial.propertiesToString())

        # Turn on notificiations
        self.conn.writeCharacteristic(0x2f, '\x01\x00', False)
        
        i = 0
        while True:
            #print(self.serial.read())
            self.write("a" * 60)
            #self.write("a" * 5)
            #self.sendCmd(LightBlueBean.MSG_ID_CC_ACCEL_READ)
            
            self.conn.waitForNotifications(1)
            time.sleep(1)
        self.conn.disconnect()
        
    def write(self, data):
        self.sendCmd(LightBlueBean.MSG_ID_SERIAL_DATA, data)

    def sendCmd(self, cmd, data = ""):
        # https://github.com/PunchThrough/bean-documentation/blob/master/serial_message_protocol.md
        gst = struct.pack("!BxH", len(data)+2, cmd) + data
        crc = struct.pack("<H", crc16(gst, 0xFFFF))
        gst += crc
        
        gt_qty = len(gst)/19
        if len(gst) % 19 != 0:
            gt_qty += 1
        
        #amnt = len(gst) / gt_qty
        optimal_packet_size = 19
        
        for ch in xrange(0, gt_qty):
            data = gst[:optimal_packet_size]
            gst = gst[optimal_packet_size:]
            
            gt = 0
            if ch == 0:
                gt = 0x80
            gt |= self.count << 5
            gt |= gt_qty - ch - 1
            
            gt = struct.pack("B", gt) + data
        
            #print("<", hexdump(gt))
            self.serial.write(gt)
            #time.sleep(0.1)
        
        self.count = (self.count + 1) % 4

    def writeRaw(self, data):
        self.conn.writeCharacteristic(0x0b, data, False)

    def handleNotification(self, cHandle, data):
        #print(">", hexdump(data))
        gt = struct.unpack("B", data[0]) [0]
        #gt_cntr = gt & 0x60
        gt_left = gt & 0x1F
        
        if gt & 0x80:
            self.got1 = True
            self.buffin = self.buffin[:gt_left+1]
            
        self.buffin[gt_left] = data[1:]
        
        if self.got1 and not self.buffin.count(None):
            #print("Got ", len(self.buffin), "packets")
            self.buffin.reverse()
            self.buffin = ''.join(self.buffin)
            
            crc_ = crc16(self.buffin[:-2], 0xFFFF)
            dlen, cmd = struct.unpack("!BxH", self.buffin[:4])
            crc = struct.unpack("<H", self.buffin[-2:]) [0]
            if crc == crc_:
                print(self.buffin[4:-2])
            else:
                print("CRC check failure")
            
            self.buffin = [None]*10
            self.got1 = False
class SBrickCommunications(threading.Thread, IdleObject):
    def __init__(self, sbrick_addr):
        threading.Thread.__init__(self)
        IdleObject.__init__(self)

        self.lock = threading.RLock()
        self.drivingLock = threading.RLock()
        self.eventSend = threading.Event()

        self.sBrickAddr = sbrick_addr
        self.owner_password = None

        self.brickChannels = [
            SBrickChannelDrive(0, self.eventSend),
            SBrickChannelDrive(1, self.eventSend),
            SBrickChannelDrive(2, self.eventSend),
            SBrickChannelDrive(3, self.eventSend),
        ]
        self.SBrickPeripheral = None
        self.stopFlag = False
        self.characteristicRemote = None
        self.need_authentication = False
        self.authenticated = False
        self.channel_config_ids = dict()

    def set_channel_config_id(self, channel, config_id):
        self.channel_config_ids[config_id] = channel
        self.brickChannels[channel].set_config_id(config_id)

    def terminate(self):
        self.stopFlag = True

    def is_driving(self):
        locked = self.drivingLock.acquire(False)
        if locked:
            self.drivingLock.release()
        return not locked

    def connect_to_sbrick(self, owner_password):
        self.owner_password = owner_password
        self.start()

    def run(self):
        try:
            monotime = 0.0

            self.SBrickPeripheral = Peripheral()
            self.SBrickPeripheral.connect(self.sBrickAddr)
            service = self.SBrickPeripheral.getServiceByUUID('4dc591b0-857c-41de-b5f1-15abda665b0c')
            characteristics = service.getCharacteristics('02b8cbcc-0e25-4bda-8790-a15f53e6010f')
            for characteristic in characteristics:
                if characteristic.uuid == '02b8cbcc-0e25-4bda-8790-a15f53e6010f':
                    self.characteristicRemote = characteristic

            if self.characteristicRemote is None:
                return

            self.emit('sbrick_connected')

            self.need_authentication = self.get_need_authentication()
            self.authenticated = not self.need_authentication
            if self.need_authentication:
                if self.password_owner is not None:
                    self.authenticate_owner(self.password_owner)

            while not self.stopFlag:
                if self.authenticated:
                    if monotonic.monotonic() - monotime >= 0.05:
                        self.send_command()
                        monotime = monotonic.monotonic()
                    self.eventSend.wait(0.01)
                    for channel in self.brickChannels:
                        if channel.decrement_run_timer():
                            monotime = 0.0
                            self.drivingLock.release()
                            # print("stop run normal")
                            self.emit("sbrick_channel_stop", channel.channel)
                        if channel.decrement_brake_timer():
                            self.drivingLock.release()
                            # print("stop brake timer")
                            monotime = 0.0
                            self.emit("sbrick_channel_stop", channel.channel)

            if self.authenticated:
                self.stop_all()
                self.send_command()
            self.SBrickPeripheral.disconnect()
            self.emit('sbrick_disconnected_ok')
        except BTLEException as ex:
            self.emit("sbrick_disconnected_error", ex.message)

    def get_channel(self, channel):
        if isinstance(channel, six.integer_types):
            return self.brickChannels[channel]
        if isinstance(channel, six.string_types):
            return self.brickChannels[self.channel_config_ids[channel]]
        return None

    def drive(self, channel, pwm, reverse, time, brake_after_time=False):
        with self.lock:
            ch = self.get_channel(channel)
            if ch is not None:
                ch.drive(pwm, reverse, time, brake_after_time)
                self.emit("sbrick_drive_sent", ch.channel, time)
            self.eventSend.set()

    def stop(self, channel, braked=False):
        with self.lock:
            ch = self.get_channel(channel)
            if ch is not None:
                ch.stop(braked)
                self.emit("sbrick_drive_sent", ch.channel, -2)
            self.eventSend.set()

    def stop_all(self):
        with self.lock:
            for channel in self.brickChannels:
                channel.stop()
            self.eventSend.set()

    def change_pwm(self, channel, pwm, change_reverse=False):
        with self.lock:
            ch = self.get_channel(channel)
            if ch is not None:
                ch.set_pwm(pwm, change_reverse)
            self.eventSend.set()

    def change_reverse(self, channel, reverse):
        with self.lock:
            ch = self.get_channel(channel)
            if ch is not None:
                ch.set_reverse(reverse)
            self.eventSend.set()

    def send_command(self):
        with self.lock:
            # try:
            drivecmd = bytearray([0x01])
            brakecmd = bytearray([0x00])
            for channel in self.brickChannels:
                drivecmd = channel.get_command_drive(drivecmd)
                brakecmd = channel.get_command_brake(brakecmd)
            if len(drivecmd) > 1:
                self.drivingLock.acquire()
                self.characteristicRemote.write(drivecmd, True)
                self.print_hex_string("drive sent", drivecmd)

            if len(brakecmd) > 1:
                self.characteristicRemote.write(brakecmd, True)
                self.print_hex_string("brake sent", brakecmd)
                # return True
                # except Exception as ex:
                #     self.emit("sbrick_disconnected_error",ex.message)
                #     return False

    def disconnect_sbrick(self):
        with self.lock:
            self.stopFlag = True

    @staticmethod
    def print_hex_string(what, strin):
        out = what + " -> "
        for chrx in strin:
            out = "%s %0X" % (out, chrx)
        print(out)

    def get_voltage(self):
        with self.lock:
            try:
                self.characteristicRemote.write(b"\x0f\x00")
                value = self.characteristicRemote.read()
                valueint = struct.unpack("<H", value)[0]
                return (valueint * 0.83875) / 2047.0
            except BTLEException as ex:
                self.emit("sbrick_disconnected_error", ex.message)

    def get_temperature(self):
        with self.lock:
            try:
                self.characteristicRemote.write(b"\x0f\x0e")
                value = self.characteristicRemote.read()
                valueint = struct.unpack("<H", value)[0]
                return valueint / 118.85795 - 160
            except BTLEException as ex:
                self.emit("sbrick_disconnected_error", ex.message)

    def get_thermal_limit(self):
        with self.lock:
            try:
                self.characteristicRemote.write(b'\x15')
                value = self.characteristicRemote.read()
                valueint = struct.unpack("<H", value)[0]
                return valueint / 118.85795 - 160
            except BTLEException as ex:
                self.emit("sbrick_disconnected_error", ex.message)

    def get_watchdog_timeout(self):
        with self.lock:
            try:
                self.characteristicRemote.write(b'\x0e')
                value = self.characteristicRemote.read()
                return struct.unpack("<B", value)[0] * 0.1
            except BTLEException as ex:
                self.emit("sbrick_disconnected_error", ex.message)

    def get_authentication_timeout(self):
        with self.lock:
            try:
                self.characteristicRemote.write(b'\x09')
                value = self.characteristicRemote.read()
                return struct.unpack("<B", value)[0] * 0.1
            except BTLEException as ex:
                self.emit("sbrick_disconnected_error", ex.message)

    def get_power_cycle_counter(self):
        with self.lock:
            try:
                self.characteristicRemote.write(b'\x28')
                value = self.characteristicRemote.read()
                return struct.unpack("<I", value)[0]
            except BTLEException as ex:
                self.emit("sbrick_disconnected_error", ex.message)

    def get_uptime(self):
        with self.lock:
            try:
                self.characteristicRemote.write(b'\x29')
                value = self.characteristicRemote.read()
                seconds = struct.unpack("<I", value)[0] * 0.1
                minutes = seconds // 60
                hours = minutes // 60
                return "%02d:%02d:%02d" % (hours, minutes % 60, seconds % 60)
            except BTLEException as ex:
                self.emit("sbrick_disconnected_error", ex.message)

    def get_hardware_version(self):
        try:
            return self.SBrickPeripheral.readCharacteristic(0x000c).decode("utf-8")
        except BTLEException as ex:
            self.emit("sbrick_disconnected_error", ex.message)

    def get_software_version(self):
        try:
            return self.SBrickPeripheral.readCharacteristic(0x000a).decode("utf-8")
        except BTLEException as ex:
            self.emit("sbrick_disconnected_error", ex.message)

    def get_brick_id(self):
        with self.lock:
            try:
                self.characteristicRemote.write(b'\x0a')
                value = self.characteristicRemote.read()
                return "%0X %0X %0X %0X %0X %0X" % (
                    value[0], value[1], value[2], value[3], value[4], value[5])
            except BTLEException as ex:
                self.emit("sbrick_disconnected_error", ex.message)

    def get_need_authentication(self):
        with self.lock:
            try:
                self.characteristicRemote.write(b'\x02')
                value = self.characteristicRemote.read()
                return struct.unpack("<B", value)[0] == 1
            except BTLEException as ex:
                self.emit("sbrick_disconnected_error", ex.message)

    def get_is_authenticated(self):
        with self.lock:
            try:
                self.characteristicRemote.write(b'\x03')
                value = self.characteristicRemote.read()
                return struct.unpack("<B", value)[0] == 1
            except BTLEException as ex:
                self.emit("sbrick_disconnected_error", ex.message)

    def get_user_id(self):
        with self.lock:
            try:
                self.characteristicRemote.write(b'\x04')
                value = self.characteristicRemote.read()
                return struct.unpack("<B", value)[0] == 1
            except BTLEException as ex:
                self.emit("sbrick_error", ex.message)

    def authenticate_owner(self, password):
        with self.lock:
            try:
                self.authenticated = False
                cmd = bytearray([0x05, 0x00])
                for ch in password:
                    cmd.append(ord(ch))
                self.characteristicRemote.write(cmd)
                self.authenticated = True
            except BTLEException as ex:
                self.emit("sbrick_error", ex.message)

    def authenticate_guest(self, password):
        with self.lock:
            try:
                self.authenticated = False
                cmd = bytearray([0x05, 0x01])
                for ch in password:
                    cmd.append(ord(ch))
                self.characteristicRemote.write(cmd)
                self.authenticated = True
            except BTLEException as ex:
                self.emit("sbrick_error", ex.message)

    def clear_owner_password(self):
        with self.lock:
            try:
                self.characteristicRemote.write(b'\x06\x00')
            except BTLEException as ex:
                self.emit("sbrick_error", ex.message)

    def clear_guest_password(self):
        with self.lock:
            try:
                self.characteristicRemote.write(b'\x06\x01')
            except BTLEException as ex:
                self.emit("sbrick_error", ex.message)

    def set_owner_password(self, password):
        with self.lock:
            try:
                cmd = bytearray([0x07, 0x00])
                for ch in password:
                    cmd.append(ord(ch))
                self.characteristicRemote.write(cmd)
            except BTLEException as ex:
                self.emit("sbrick_error", ex.message)

    def set_guest_password(self, password):
        with self.lock:
            try:
                cmd = bytearray([0x07, 0x01])
                for ch in password:
                    cmd.append(ord(ch))
                self.characteristicRemote.write(cmd)
            except BTLEException as ex:
                self.emit("sbrick_error", ex.message)

    def set_authentication_timeout(self, seconds):
        with self.lock:
            try:
                cmd = bytearray([0x08, seconds / 0.1])
                self.characteristicRemote.write(cmd)
            except BTLEException as ex:
                self.emit("sbrick_error", ex.message)
Beispiel #13
0
class OpenBCIGanglion(object):
    """
    Handle a connection to an OpenBCI board.

    Args:
      port: MAC address of the Ganglion Board. "None" to attempt auto-detect.
      aux: enable on not aux channels (i.e. switch to 18bit mode if set)
      impedance: measures impedance when start streaming
      timeout: in seconds, if set will try to disconnect / reconnect after a period without new data
       -- should be high if impedance check
      max_packets_to_skip: will try to disconnect / reconnect after too many packets are skipped
      baud, filter_data, daisy: Not used, for compatibility with v3
    """

    def __init__(self, port=None, baud=0, filter_data=False,
                 scaled_output=True, daisy=False, log=True, aux=False, impedance=False, timeout=2,
                 max_packets_to_skip=20):
        # unused, for compatibility with Cyton v3 API
        self.daisy = False
        # these one are used
        self.log = log  # print_incoming_text needs log
        self.aux = aux
        self.streaming = False
        self.timeout = timeout
        self.max_packets_to_skip = max_packets_to_skip
        self.scaling_output = scaled_output
        self.impedance = impedance

        # might be handy to know API
        self.board_type = "ganglion"

        print("Looking for Ganglion board")
        if port == None:
            port = self.find_port()
        self.port = port  # find_port might not return string

        self.connect()

        self.streaming = False
        # number of EEG channels and (optionally) accelerometer channel
        self.eeg_channels_per_sample = 4
        self.aux_channels_per_sample = 3
        self.imp_channels_per_sample = 5
        self.read_state = 0
        self.log_packet_count = 0
        self.packets_dropped = 0
        self.time_last_packet = 0

        # Disconnects from board when terminated
        atexit.register(self.disconnect)

    def getBoardType(self):
        """ Returns the version of the board """
        return self.board_type

    def setImpedance(self, flag):
        """ Enable/disable impedance measure """
        self.impedance = bool(flag)

    def connect(self):
        """ Connect to the board and configure it. Note: recreates various objects upon call. """
        print("Init BLE connection with MAC: " + self.port)
        print("NB: if it fails, try with root privileges.")
        self.gang = Peripheral(self.port, 'random')  # ADDR_TYPE_RANDOM

        print("Get mainservice...")
        self.service = self.gang.getServiceByUUID(BLE_SERVICE)
        print("Got:" + str(self.service))

        print("Get characteristics...")
        self.char_read = self.service.getCharacteristics(BLE_CHAR_RECEIVE)[0]
        print("receive, properties: " + str(self.char_read.propertiesToString()) +
              ", supports read: " + str(self.char_read.supportsRead()))

        self.char_write = self.service.getCharacteristics(BLE_CHAR_SEND)[0]
        print("write, properties: " + str(self.char_write.propertiesToString()) +
              ", supports read: " + str(self.char_write.supportsRead()))

        self.char_discon = self.service.getCharacteristics(BLE_CHAR_DISCONNECT)[0]
        print("disconnect, properties: " + str(self.char_discon.propertiesToString()) +
              ", supports read: " + str(self.char_discon.supportsRead()))

        # set delegate to handle incoming data
        self.delegate = GanglionDelegate(self.scaling_output)
        self.gang.setDelegate(self.delegate)

        # enable AUX channel
        if self.aux:
            print("Enabling AUX data...")
            try:
                self.ser_write(b'n')
            except Exception as e:
                print("Something went wrong while enabling aux channels: " + str(e))

        print("Turn on notifications")
        # nead up-to-date bluepy, cf https://github.com/IanHarvey/bluepy/issues/53
        self.desc_notify = self.char_read.getDescriptors(forUUID=0x2902)[0]
        try:
            self.desc_notify.write(b"\x01")
        except Exception as e:
            print("Something went wrong while trying to enable notification: " + str(e))

        print("Connection established")

    def init_streaming(self):
        """ Tell the board to record like crazy. """
        try:
            if self.impedance:
                print("Starting with impedance testing")
                self.ser_write(b'z')
            else:
                self.ser_write(b'b')
        except Exception as e:
            print("Something went wrong while asking the board to start streaming: " + str(e))
        self.streaming = True
        self.packets_dropped = 0
        self.time_last_packet = timeit.default_timer()

    def find_port(self):
        """Detects Ganglion board MAC address
        If more than 1 around, will select first. Needs root privilege.
        """

        print("Try to detect Ganglion MAC address. "
              "NB: Turn on bluetooth and run as root for this to work!"
              "Might not work with every BLE dongles.")
        scan_time = 5
        print("Scanning for 5 seconds nearby devices...")

        #   From bluepy example
        class ScanDelegate(DefaultDelegate):
            def __init__(self):
                DefaultDelegate.__init__(self)

            def handleDiscovery(self, dev, isNewDev, isNewData):
                if isNewDev:
                    print("Discovered device: " + dev.addr)
                elif isNewData:
                    print("Received new data from: " + dev.addr)

        scanner = Scanner().withDelegate(ScanDelegate())
        devices = scanner.scan(scan_time)

        nb_devices = len(devices)
        if nb_devices < 1:
            print("No BLE devices found. Check connectivity.")
            return ""
        else:
            print("Found " + str(nb_devices) + ", detecting Ganglion")
            list_mac = []
            list_id = []

            for dev in devices:
                # "Ganglion" should appear inside the "value" associated
                # to "Complete Local Name", e.g. "Ganglion-b2a6"
                for (adtype, desc, value) in dev.getScanData():
                    if desc == "Complete Local Name" and value.startswith("Ganglion"):
                        list_mac.append(dev.addr)
                        list_id.append(value)
                        print("Got Ganglion: " + value +
                              ", with MAC: " + dev.addr)
                        break
        nb_ganglions = len(list_mac)

        if nb_ganglions < 1:
            print("No Ganglion found ;(")
            raise OSError('Cannot find OpenBCI Ganglion MAC address')

        if nb_ganglions > 1:
            print("Found " + str(nb_ganglions) + ", selecting first")

        print("Selecting MAC address " + list_mac[0] + " for " + list_id[0])
        return list_mac[0]

    def ser_write(self, b):
        """Access serial port object for write"""
        self.char_write.write(b)

    def ser_read(self):
        """Access serial port object for read"""
        return self.char_read.read()

    def ser_inWaiting(self):
        """ Slightly different from Cyton API, return True if ASCII messages are incoming."""
        # FIXME: might have a slight problem with thread because of notifications...
        if self.delegate.receiving_ASCII:
            # in case the packet indicating the end of the message drops, we use a 1s timeout
            if timeit.default_timer() - self.delegate.time_last_ASCII > 2:
                self.delegate.receiving_ASCII = False
        return self.delegate.receiving_ASCII

    def getSampleRate(self):
        return SAMPLE_RATE

    def getNbEEGChannels(self):
        """Will not get new data on impedance check."""
        return self.eeg_channels_per_sample

    def getNbAUXChannels(self):
        """Might not be used depending on the mode."""
        return self.aux_channels_per_sample

    def getNbImpChannels(self):
        """Might not be used depending on the mode."""
        return self.imp_channels_per_sample

    def start_streaming(self, callback, lapse=-1):
        """
        Start handling streaming data from the board. Call a provided callback
        for every single sample that is processed

        Args:
          callback: A callback function or a list of functions that will receive a single argument
                    of the OpenBCISample object captured.
        """
        if not self.streaming:
            self.init_streaming()

        start_time = timeit.default_timer()

        # Enclose callback funtion in a list if it comes alone
        if not isinstance(callback, list):
            callback = [callback]

        while self.streaming:
            # should the board get disconnected and we could not wait for notification
            # anymore, a reco should be attempted through timeout mechanism
            try:
                # at most we will get one sample per packet
                self.waitForNotifications(1. / self.getSampleRate())
            except Exception as e:
                print("Something went wrong while waiting for a new sample: " + str(e))
            # retrieve current samples on the stack
            samples = self.delegate.getSamples()
            self.packets_dropped = self.delegate.getMaxPacketsDropped()
            if samples:
                self.time_last_packet = timeit.default_timer()
                for call in callback:
                    for sample in samples:
                        call(sample)

            if (lapse > 0 and timeit.default_timer() - start_time > lapse):
                self.stop()
            if self.log:
                self.log_packet_count = self.log_packet_count + 1

            # Checking connection -- timeout and packets dropped
            self.check_connection()

    def waitForNotifications(self, delay):
        """ Allow some time for the board to receive new data. """
        self.gang.waitForNotifications(delay)

    def test_signal(self, signal):
        """ Enable / disable test signal """
        if signal == 0:
            self.warn("Disabling synthetic square wave")
            try:
                self.char_write.write(b']')
            except Exception as e:
                print("Something went wrong while setting signal: " + str(e))
        elif signal == 1:
            self.warn("Eisabling synthetic square wave")
            try:
                self.char_write.write(b'[')
            except Exception as e:
                print("Something went wrong while setting signal: " + str(e))
        else:
            self.warn(
                "%s is not a known test signal. Valid signal is 0-1" % signal)

    def set_channel(self, channel, toggle_position):
        """ Enable / disable channels """
        try:
            # Commands to set toggle to on position
            if toggle_position == 1:
                if channel is 1:
                    self.ser.write(b'!')
                if channel is 2:
                    self.ser.write(b'@')
                if channel is 3:
                    self.ser.write(b'#')
                if channel is 4:
                    self.ser.write(b'$')
            # Commands to set toggle to off position
            elif toggle_position == 0:
                if channel is 1:
                    self.ser.write(b'1')
                if channel is 2:
                    self.ser.write(b'2')
                if channel is 3:
                    self.ser.write(b'3')
                if channel is 4:
                    self.ser.write(b'4')
        except Exception as e:
            print("Something went wrong while setting channels: " + str(e))

    """
  
    Clean Up (atexit)
  
    """

    def stop(self):
        print("Stopping streaming...")
        self.streaming = False
        # connection might be already down here
        try:
            if self.impedance:
                print("Stopping with impedance testing")
                self.ser_write(b'Z')
            else:
                self.ser_write(b's')
        except Exception as e:
            print("Something went wrong while asking the board to stop streaming: " + str(e))
        if self.log:
            logging.warning('sent <s>: stopped streaming')

    def disconnect(self):
        if (self.streaming == True):
            self.stop()
        print("Closing BLE..")
        try:
            self.char_discon.write(b' ')
        except Exception as e:
            print("Something went wrong while asking the board to disconnect: " + str(e))
        # should not try to read/write anything after that, will crash
        try:
            self.gang.disconnect()
        except Exception as e:
            print("Something went wrong while shutting down BLE link: " + str(e))
        logging.warning('BLE closed')

    """
  
        SETTINGS AND HELPERS
  
    """

    def warn(self, text):
        if self.log:
            # log how many packets where sent succesfully in between warnings
            if self.log_packet_count:
                logging.info('Data packets received:' +
                             str(self.log_packet_count))
                self.log_packet_count = 0
            logging.warning(text)
        print("Warning: %s" % text)

    def check_connection(self):
        """ Check connection quality in term of lag and number of packets drop.
         Reinit connection if necessary.
         FIXME: parameters given to the board will be lost.
         """
        # stop checking when we're no longer streaming
        if not self.streaming:
            return
        # check number of dropped packets and duration without new packets, deco/reco if too large
        if self.packets_dropped > self.max_packets_to_skip:
            self.warn("Too many packets dropped, attempt to reconnect")
            self.reconnect()
        elif self.timeout > 0 and timeit.default_timer() - self.time_last_packet > self.timeout:
            self.warn("Too long since got new data, attempt to reconnect")
            # if error, attempt to reconect
            self.reconnect()

    def reconnect(self):
        """ In case of poor connection, will shut down and relaunch everything.
        FIXME: parameters given to the board will be lost."""
        self.warn('Reconnecting')
        self.stop()
        self.disconnect()
        self.connect()
        self.init_streaming()
Beispiel #14
0
washer_service_uuid = UUID("6e400001-b5a3-f393-e0a9-e50e24dcca9e")
battery_service_uuid = UUID("180F")

#washer service Characteristics의 센서에서 전송하는 정보이며 Access Permisson은 Notify 
washerTX_char_uuid = UUID("6e400003-b5a3-f393-e0a9-e50e24dcca9e")
battery_level_uuid = UUID("00002a19-0000-1000-8000-00805f9b34fb")

#if len(sys.argv) != 2:
#  print ("Fatal, must pass device address:", sys.argv[0], "<device address="">")
#  quit()

p = Peripheral("c7:74:31:9A:F8:D1","random")
p.setDelegate( MyDelegate(p) )

#WasherService, BatteryService를 가져온다.
WasherService=p.getServiceByUUID(washer_service_uuid)
BatteryService =p.getServiceByUUID(battery_service_uuid)

#TX의 Characteristics를 가져온다.
WasherTX_C = WasherService.getCharacteristics(washerTX_char_uuid)[0]
#battey의 Characteristics를 가져온다.
Battery_C = BatteryService.getCharacteristics(battery_level_uuid)[0]


# Client Characteristic Descriptor의 handler
# 0x13 은 washerTX, 0x0F는 battery
 # notifications의 비트를 1로 바꿔 활성화한다.



# 메인 루프 -----------------------
Beispiel #15
0
class BleCam(object):
    locked = True
    
    def __init__(self, address, pincode):
        print("Connecting to %s..." % address)
        self.pincode = pincode
        self.periph = Peripheral(address)
        self._ipcamservice()
        self.name = self.periph.getCharacteristics(uuid=0x2a00)[0].read().decode() # wellknown name characteristic
        print("Connected to '%s'" % self.name)

    def _ipcamservice(self):
        try:
            print("Verifying IPCam service")
            self.service = self.periph.getServiceByUUID(0xd001)
            self.handles = self.service.getCharacteristics()
        except BTLEEException:
            print("no IPCam service found for %s" % periph.address)

    def dumpchars(self):
        print("%s supports these characteristics:" % self.name)
        for h in self.handles:
            print("%s - Handle=%#06x (%s)" % (h.uuid, h.getHandle(), h.propertiesToString()))

    def unlock(self):
        if not self.locked:
            return True
        auth = self.service.getCharacteristics(0xa001)[0]
        state = kv2dict(auth.read().decode())

        # already unlocked?
        if state["M"] == 0:
            self.locked = False
            return True

        self.challenge = state["C"]
        hashit = self.name + self.pincode + self.challenge
        self.key = base64.b64encode(hashlib.md5(hashit.encode()).digest())[:16]
        try:
            auth.write("M=0;K=".encode() + self.key, True)
            self.locked = False
        except:
            print("ERROR: failed to unlock %s - wrong pincode?" % self.name)
        return not self.locked

    def get_ipconfig(self):
        if not self.unlock(): return
        return kv2dict(self.service.getCharacteristics(0xa104)[0].read().decode())

    def get_wificonfig(self):
        if not self.unlock(): return
        return kv2dict(self.service.getCharacteristics(0xa101)[0].read().decode())

    def wifilink(self):
        if not self.unlock(): return
        r = kv2dict(self.service.getCharacteristics(0xa103)[0].read().decode())
        return r["S"] == "1"

    def sysinfo(self):
        if not self.unlock(): return
        return kv2dict(self.service.getCharacteristics(0xa200)[0].read().decode())

    def setup_wifi(self, essid, passwd):
        for net in self.wifi_scan():
            if net["I"] == essid:
                cfg = "M=" + net["M"] + ";I=" + essid + ";S=" + net["S"] + ";E=" + net["E"] + ";K=" + passwd
                print("Will configure: %s" % cfg)
                self.service.getCharacteristics(0xa101)[0].write(cfg.encode(), True)
                self.service.getCharacteristics(0xa102)[0].write("C=1".encode(), True)
                return True
        print("%s cannot see the '%s' network" % (self.name, essid))
        return False
    
    def wifi_scan(self):
        def _wifi2dict(wifistr):
            return kv2dict(wifistr[2:], ",")
        
        if not self.unlock(): return
        print("%s is scanning for WiFi networks..." % self.name)
        scan = self.service.getCharacteristics(0xa100)[0]
        p = -1
        n = 0
        result = ""
        while p < n:
            t = scan.read().decode().split(";", 3)
            result = result + t[2]
            if not t[0].startswith("N=") or not t[1].startswith("P="):
                return
            n = int(t[0].split("=",2)[1])
            p = int(t[1].split("=",2)[1])
            # print("read page %d of %d" % (p, n))
        return map(_wifi2dict, result.split("&", 50))
                    
    def run_command(self, command):
        if not self.unlock(): return
        
        run = "P=" + self.pincode + ";N=" + self.pincode + "&&(" + command + ")&"
        if len(run) > 128:
            print("ERROR: command is too long")
            return
        print("Attempting to run '%s' on %s by abusing the 'set admin password' request" % (command, self.name))
        try:
            self.service.getCharacteristics(0xa201)[0].write(run.encode(), True)
        except:
            # try repeating with an empty password, which seems to be te initial state after factory reset
            run = "P=;N=" + self.pincode + "&&(" + command + ")&"
            try:
                self.service.getCharacteristics(0xa201)[0].write(run.encode(), True)
            except:
                print("ERROR: Failed - is the admin password different from the pincode?")
Beispiel #16
0
class BleCam(object):
    locked = True

    def __init__(self, address, pincode):
        print("Connecting to %s..." % address)
        self.pincode = pincode
        self.periph = Peripheral(address)
        self.periph.setMTU(256)
        self._ipcamservice()
        #self.name = self.periph.getCharacteristics(uuid=0xa200)[0].read().decode() # wellknown name characteristic
        #self.idstring = self.periph.getCharacteristics(uuid=0xa200)[0]
        #self.idstate = kv2dict(self.idstring.read().decode())
        #self.name = self.idstate["N"] + "-" + self.idstate["M"][8:]
        # CHANGE #### to the last two mac octets in hex when using on a factory reset DCS-8600LH
        self.name = "DCS-8600LH-####"
        print("Connected to '%s'" % self.name)

    def _ipcamservice(self):
        try:
            print("Verifying IPCam service")
            self.service = self.periph.getServiceByUUID(0xd001)
            self.handles = self.service.getCharacteristics()
            #print("self.service = '%s'" % self.service)
            #print("self.handles = '%s'" % '\n'.join(map(str,self.handles)))
        except BTLEEException:
            print("no IPCam service found for %s" % periph.address)

    def dumpchars(self):
        print("%s supports these characteristics:" % self.name)
        for h in self.handles:
            print("%s - Handle=%#06x (%s)" %
                  (h.uuid, h.getHandle(), h.propertiesToString()))

    def unlock(self):
        if not self.locked:
            return True
        auth = self.service.getCharacteristics(0xa001)[0]
        state = kv2dict(auth.read().decode())
        #print("auth = '%s'" % auth)
        #print("state = '%s'" % state)

        # already unlocked?
        if state["M"] == 0:
            self.locked = False
            return True

        self.challenge = state["C"]
        #print("self.challenge = '%s'" % self.challenge)
        hashit = self.name + self.pincode + self.challenge
        #print("hashit = '%s'" % hashit)
        self.key = base64.b64encode(hashlib.md5(hashit.encode()).digest())[:16]
        #print("self.key = '%s'" % self.key)
        try:
            auth.write("M=0;K=".encode() + self.key, True)
            self.locked = False
        except:
            print("ERROR: failed to unlock %s - wrong pincode?" % self.name)
        return not self.locked

    def get_ipconfig(self):
        if not self.unlock(): return
        return kv2dict(
            self.service.getCharacteristics(0xa104)[0].read().decode())

    def get_wificonfig(self):
        if not self.unlock(): return
        return kv2dict(
            self.service.getCharacteristics(0xa101)[0].read().decode())

    def wifilink(self):
        if not self.unlock(): return
        r = kv2dict(self.service.getCharacteristics(0xa103)[0].read().decode())
        return r["S"] == "1"

    def sysinfo(self):
        if not self.unlock(): return
        return kv2dict(
            self.service.getCharacteristics(0xa200)[0].read().decode())

    def setup_wifi(self, essid, passwd):
        for net in self.wifi_scan():
            if net["I"] == essid:
                cfg = "M=" + net["M"] + ";I=" + essid + ";S=" + net[
                    "S"] + ";E=" + net["E"] + ";K=" + passwd
                print("Will configure: %s" % cfg)
                self.service.getCharacteristics(0xa101)[0].write(
                    cfg.encode(), True)
                self.service.getCharacteristics(0xa102)[0].write(
                    "C=1".encode(), True)
                return True
        print("%s cannot see the '%s' network" % (self.name, essid))
        return False

    def wifi_scan(self):
        def _wifi2dict(wifistr):
            return kv2dict(wifistr[2:], ",")

        if not self.unlock(): return
        print("%s is scanning for WiFi networks..." % self.name)
        scan = self.service.getCharacteristics(0xa100)[0]
        p = -1
        n = 0
        result = ""
        while p < n:
            t = scan.read().decode().split(";", 3)
            result = result + t[2]
            if not t[0].startswith("N=") or not t[1].startswith("P="):
                return
            n = int(t[0].split("=", 2)[1])
            p = int(t[1].split("=", 2)[1])
            # print("read page %d of %d" % (p, n))
        return map(_wifi2dict, result.split("&", 50))

    def run_command(self, command):
        if not self.unlock(): return

        #run = "P=" + self.pincode + ";N=" + self.pincode + "\"&&(" + command + ")&\""
        runa = "P=" + self.pincode + ";N="
        runb = self.pincode + "\"&&(" + command + ")&\""
        run = runa + runb
        #run = "P=;N=" + self.pincode
        # sprintf(cmd,"/usr/bin/mdb set admin_passwd \"%s\"",newpass);
        #print("COMMAND: /usr/bin/mdb set admin_passwd \"%s\"" % runb)

        #print("run = '%s'" % run)
        #return
        if len(run) > 128:
            print("ERROR: command is too long")
            return
        print(
            "Attempting to run '%s' on %s by abusing the 'set admin password' request"
            % (command, self.name))
        try:
            self.service.getCharacteristics(0xa201)[0].write(
                run.encode(), True)
        except:
            print("Failed - trying again with blank password")
            # try repeating with an empty password, which seems to be the initial state after factory reset
            #run = "P=;N=" + self.pincode + "&&(" + command + ")&"
            run = "P=;N=" + runb + "\"&&(" + command + ")&\""
            try:
                self.service.getCharacteristics(0xa201)[0].write(
                    run.encode(), True)
            except:
                print(
                    "ERROR: Failed - is the admin password different from the pincode?"
                )
Beispiel #17
0
class OpenBCIGanglion(object):
    """ OpenBCIGanglion handles the connection to an OpenBCI Ganglion board.

    The OpenBCIGanglion class interfaces with the Cyton Dongle and the Cyton board to parse the data received and output it to Python as a OpenBCISample object.

    Args:
        mac: A string representing the Ganglion board mac address. It should be a string comprising six hex bytes separated by colons, e.g. "11:22:33:ab:cd:ed". If no mac address specified, a connection will be stablished with the first Ganglion found (Will need root privilages).

        max_packets_skipped: An integer specifying how many packets can be dropped before attempting to reconnect.
    """
    def __init__(self, mac=None, max_packets_skipped=15):
        if not mac:
            self.mac_address = self.find_mac()
        else:
            self.mac_address = mac
        self.max_packets_skipped = max_packets_skipped
        self.streaming = False
        self.board_type = 'Ganglion'

        atexit.register(self.disconnect)

        self.connect()

    def write_command(self, command):
        """Sends string command to the Ganglion board."""
        self.char_write.write(str.encode(command))

    def connect(self):
        """Establishes connection with the specified Ganglion board."""
        self.ganglion = Peripheral(self.mac_address, 'random')

        self.service = self.ganglion.getServiceByUUID(BLE_SERVICE)

        self.char_read = self.service.getCharacteristics(BLE_CHAR_RECEIVE)[0]

        self.char_write = self.service.getCharacteristics(BLE_CHAR_SEND)[0]

        self.char_discon = self.service.getCharacteristics(BLE_CHAR_DISCONNECT)[0]

        self.ble_delegate = GanglionDelegate(self.max_packets_skipped)
        self.ganglion.setDelegate(self.ble_delegate)

        self.desc_notify = self.char_read.getDescriptors(forUUID=0x2902)[0]

        try:
            self.desc_notify.write(b"\x01")
        except Exception as e:
            print("Something went wrong while trying to enable notification: " + str(e))
            sys.exit(2)

        print("Connection established")

    def disconnect(self):
        """Disconnets from the Ganglion board."""
        if self.streaming:
            self.stop_stream()

        self.char_discon.write(b' ')
        self.ganglion.disconnect()

    def find_mac(self):
        """Finds and returns the mac address of the first Ganglion board found"""
        scanner = Scanner()
        devices = scanner.scan(5)

        if len(devices) < 1:
            raise OSError('No nearby Devices found. Make sure your Bluetooth Connection is on.')

        else:
            gang_macs = []
            for dev in devices:
                for adtype, desc, value in dev.getScanData():
                    if desc == 'Complete Local Name' and value.startswith('Ganglion'):
                        gang_macs.append(dev.addr)
                        print(value)

        if len(gang_macs) < 1:
            raise OSError('Cannot find OpenBCI Ganglion Mac address.')
        else:
            print("Connecting to Ganglion with mac address: "+ gang_macs[0])
            return gang_macs[0]

    def stop_stream(self):
        """Stops Ganglion Stream."""
        self.streaming = False
        self.write_command('s')

    def start_stream(self, callback):
        """Start handling streaming data from the Ganglion board. Call a provided callback for every single sample that is processed."""
        if not self.streaming:
            self.streaming = True
            self.dropped_packets = 0
            self.write_command('b')

        if not isinstance(callback, list):
            callback = [callback]

        while self.streaming:
            try:
                self.ganglion.waitForNotifications(1./SAMPLE_RATE)
            except Exception as e:
                print(e)
                print('Something went wrong')
                sys.exit(1)

            samples = self.ble_delegate.getSamples()
            if samples:
                for sample in samples:
                    for call in callback:
                        call(sample)
Address = input_str.split("&")[1].split("=")[1]
AES_Key = input_str.split("&")[2].split("=")[1]
Service_UUID = input_str.split("&")[3].split("=")[1]
TotalChar_UUID = [input_str.split("&")[4].split("=")[1][i:i+8] for i in range(0, len(input_str.split("&")[4].split("=")[1]), 8)]


print ("Wallet Address:",Address)

Characteristics = ["Transaction_UUID","Txn_UUID","AddERC20_UUID","Balance_UUID","General_CMD_UUID","General_Data_UUID"]
MainCharacteristics_head = {"Transaction_UUID":TotalChar_UUID[0],"Txn_UUID":TotalChar_UUID[1],"AddERC20_UUID":TotalChar_UUID[2],"Balance_UUID":TotalChar_UUID[3],"General_CMD_UUID":TotalChar_UUID[4],"General_Data_UUID":TotalChar_UUID[5]}
MainCharacteristics = {"Transaction_UUID":"","Txn_UUID":"","AddERC20_UUID":"","Balance_UUID":"","General_CMD_UUID":"","General_Data_UUID":""}



service = p.getServiceByUUID(Service_UUID)


for Character in service.getCharacteristics():
  print(Character.uuid)
  for x in range(len(MainCharacteristics_head)):
    if Character.uuid.getCommonName().startswith(MainCharacteristics_head[Characteristics[x]]):
      print(Characteristics[x]," Get!")
      MainCharacteristics[Characteristics[x]] = Character.uuid
    pass
  


Balance_GATT = p.getCharacteristics(uuid=MainCharacteristics['Balance_UUID'])[0]

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
	    print "    %s = %s" % (desc, value)
	print " "

#Debugging loop
#while 1:
#    print "BNDSW %s  || SPIRO %s" % (bndsw.getServiceByUUID(UART_UUID).getCharacteristics()[0].read(),
#				     spiro.getServiceByUUID(UART_UUID).getCharacteristics()[0].read())
#end

#Writing to doc loop
#delete old file for testing
os.system("sudo rm data/data_from_nodes.csv")
file = open("data/data_from_nodes.csv", "a")
while 1:
    try:
    	bndsw_data = str(bndsw.getServiceByUUID(UART_UUID).getCharacteristics()[0].read())
    	spiro_data = str(spiro.getServiceByUUID(UART_UUID).getCharacteristics()[0].read())
	
	#Drop bad packets data from sensor
	if (bndsw_data[0]=='X') and ('Y' in bndsw_data) and ('Z' in bndsw_data):
	    split_accel_data(bndsw_data)
	    file.write("N1, " + strftime("%Y-%m-%d %H:%M:%S", gmtime()) + ", " +datax +", " +datay +", " +dataz)
	file.write("N2, " + strftime("%Y-%m-%d %H:%M:%S", gmtime()) + ", " + str(spiro_data))

	print "."
    except KeyboardInterrupt:
	print "Python script was killed, closing file..."
	file.close()
	sys.exit()
end
class BLEJoyController:
    def __init__(self, save_name=""):
        self.prev_fwd = 0
        self.prev_turn = 0
        self.prev_vert = 0
        self.prev_side = 0
        
        self.fwd = 0
        self.turn = 0
        self.vert = 0
        self.side = 0
        
        self.joy_sub = rospy.Subscriber("joy", Joy, self.joyCallback)
        #self.batt_pub = rospy.Publisher("batt", Int16, queue_size=1)
        self.orientation_pub = rospy.Publisher("yaw", Int16, queue_size=1)
        self.blimp = Peripheral("84:68:3e:03:eb:aa")
        #self.blimp = Peripheral("84:68:3e:03:ee:df")
        self.initConnections()
        
        #self.onOff = False
        
        #self.battService = self.blimp.getServiceByUUID(0x180F)
        #self.battChar = self.battService.getCharacteristics(0x2A19)[0]

        #self.orientationService = self.blimp.getServiceByUUID("BEEF")
        #self.orientationChar = self.orientationService.getCharacteristics("FEED")[0]
        
        #self.orientationService = self.blimp.getServiceByUUID("19b10000e8f2537e4f6cd104768a1216")
        #self.orientationChar = self.orientationService.getCharacteristics("FFF2")[0]
        #self.orientationChar2 = self.orientationService.getCharacteristics("FFF1")[0]
        
        #self.commandService = self.blimp.getServiceByUUID("19b10000e8f2537e4f6cd104768a1214")
        #self.commandChar = self.commandService.getCharacteristics("19b10001e8f2537e4f6cd104768a1214")[0]
        
        self.uxSub = rospy.Subscriber("u_x", Float64, self.uxCallback)
        self.uySub = rospy.Subscriber("u_y", Float64, self.uyCallback)
        self.uzSub = rospy.Subscriber("u_z", Float64, self.uzCallback)
        self.desiredYawSub = rospy.Subscriber("cmd_yaw", Int16, self.yawCallback)
        
        #print self.commandChar.getHandle(), 
        #print self.orientationChar.getHandle(), 
        #print self.orientationChar2.getHandle()
        #print self.battChar.getHandle()
        
        #self.enable_notify("301c9b44-a61b-408a-a8bf-5efcd95a3486")
        #self.enable_notify(self.battChar.uuid)
        #self.enable_notify("FEED")
        #self.enable_notify("FFF2")
        #self.enable_notify("FFF1")
        self.save = len(save_name) > 0
        if self.save:
            self.file_name = pathname+"/log/"+time.strftime("%m%d_%H%M")+"-commands_"+save_name+".csv"
            self.f = open(self.file_name, 'wb')
            self.writer = csv.writer(self.f)
            self.writer.writerow("time,u_x,u_y,u_z,desired_yaw".split(','))

    
    def initConnections(self):
        self.commandService = self.blimp.getServiceByUUID("301c9b20-a61b-408a-a8bf-5efcd95a3486")
        self.commandChar = self.commandService.getCharacteristics("301c9b21-a61b-408a-a8bf-5efcd95a3486")[0]
        
        self.orientationService = self.blimp.getServiceByUUID("301c9b40-a61b-408a-a8bf-5efcd95a3486")
        self.orientationChar = self.orientationService.getCharacteristics("301c9b44-a61b-408a-a8bf-5efcd95a3486")[0]
        
        self.blimp.setDelegate(MyDelegate(self))
        self.enable_notify(self.orientationChar.uuid)
    
    def joyCallback(self, msg):
        if abs(msg.axes[3]) > 0.005 or abs(msg.axes[2]) > 0.005 or abs(msg.axes[1] > 0.005) or abs(msg.axes[0]) > 0.005:
            self.fwd = int(round(64*msg.axes[3]))
            self.turn = int(round(64*msg.axes[2]))
            self.vert = int(round(-64*msg.axes[1]))
            self.side = int(round(-64*msg.axes[0]))
    
    def uxCallback(self, msg):
        self.prev_fwd = self.fwd
        self.fwd = int(round(msg.data))
        
        
    def uyCallback(self, msg):
        self.prev_side = self.side
        self.side = int(round(msg.data))
        
        
    def uzCallback(self, msg):
        self.prev_vert = self.vert
        self.vert = int(round(msg.data))
        
        
    def yawCallback(self, msg):
        self.prev_turn = self.turn
        self.turn = msg.data
        
        
    def writeJoy(self):
        if (self.fwd != self.prev_fwd) or (self.turn != self.prev_turn) or (self.vert != self.prev_vert) or (self.side != self.prev_side):
            # Value changed, need to send new message to the blimp
            cmd = pack(">hhhh", self.fwd, self.turn, self.vert, self.side)
            self.commandChar.write(cmd)
            self.prev_fwd = self.fwd
            self.prev_turn = self.turn
            self.prev_vert = self.vert
            self.prev_side = self.side
#            rospy.loginfo("Command sent")
            if self.save:
                try:
                    data = "%.6f,%d,%d,%d,%d" % (rospy.Time.now().to_time(), self.fwd, self.side, self.vert, self.turn)
                    self.writer.writerow(data.split(','))
                except csv.Error as e:
                    sys.exit('File %s, line %d: %s' % (self.file_name, self.writer.line_num, e))
#        cmd = pack(">hhhh", random.randint(-255,255), random.randint(-255,255), random.randint(-255,255), random.randint(-255,255))
#        self.commandChar.write(cmd)
        #rospy.loginfo("Command sent")
            
            
    def enable_notify(self,  chara_uuid):
        setup_data = b"\x01\x00"
        notify = self.blimp.getCharacteristics(uuid=chara_uuid)[0]
        print notify
        notify_handle = notify.getHandle() + 1
        print notify_handle
        self.blimp.writeCharacteristic(notify_handle, setup_data, withResponse=False)
Beispiel #22
0
parser = argparse.ArgumentParser(description='OMRONの環境センサーからLatestDataを取得します')
parser.add_argument("--addr",
                    required=True,
                    type=str,
                    help='環境センサーのMACアドレスを指定する')

args = parser.parse_args()

# 環境センサーに接続する
ble_peripheral = Peripheral()
print(f"connecting... {args.addr}")
ble_peripheral.connect(addr=args.addr, addrType="random")
print(f"ble_peripheral={ble_peripheral}")

# BLE サービスを取得
service = ble_peripheral.getServiceByUUID(uuidVal=OMRON_SENSOR_SERVICE_UUID)
print(f"service = {service}")

# BLE Characteristicsを取得
ble_char = service.getCharacteristics(forUUID=OMRON_LATEST_DATA_UUID)[0]
print(f"ble_char = {ble_char}")

# LatestDataから測定データの読み出し
raw_data = ble_char.read()
print(f"raw_data = {raw_data}")

# 生の測定データを変換
(row_number, temperature, humidity, light, uv_index, pressure, noise,
 discomfort_index, heat_stroke,
 battery_level) = struct.unpack('<BhhhhhhhhH', raw_data)
temperature /= 100
from bluepy.btle import Peripheral

with open(os.path.join(sys.path[0], "config", "test.yaml"), "r") as f:
    config = yaml.safe_load(f)

info = config['devices']
#print("info =", info)
print("info mac address:", info['mac'])
print("info service uuid:", str(info['service_uuid']))

p_device = Peripheral(info['mac'], "random")
#p_device = Peripheral.connect(info['mac'],"random")

try:
    #for p_service in p_device.getServices():
    p_service = p_device.getServiceByUUID(info['service_uuid'])
    print("p_service uuid is ", p_service.uuid)
    print("p_service common name is ", p_service.uuid.getCommonName())

    print("Characteristics information")
    chars = p_service.getCharacteristics()
    for char in chars:
        #hnd = char.getHandle()
        print("   -----------------------------------")
        print("   common name: ", char.uuid.getCommonName())
        print("   uuid       : ", char.uuid)
        print("   properties : ", char.propertiesToString())
        if char.supportsRead():
            #print("   READ value : ", char.read())
            val = char.read()
            txt = ""
class OpenBCIGanglion(object):
  """
  Handle a connection to an OpenBCI board.

  Args:
    port: MAC address of the Ganglion Board. "None" to attempt auto-detect.
    aux: enable on not aux channels (i.e. switch to 18bit mode if set)
    impedance: measures impedance when start streaming
    timeout: in seconds, if set will try to disconnect / reconnect after a period without new data -- should be high if impedance check
    max_packets_to_skip: will try to disconnect / reconnect after too many packets are skipped
    baud, filter_data, daisy: Not used, for compatibility with v3
  """

  def __init__(self, port=None, baud=0, filter_data=False,
    scaled_output=True, daisy=False, log=True, aux=False, impedance=False, timeout=2, max_packets_to_skip=20):
    # unused, for compatibility with Cyton v3 API
    self.daisy = False
    # these one are used 
    self.log = log # print_incoming_text needs log
    self.aux = aux
    self.streaming = False
    self.timeout = timeout
    self.max_packets_to_skip = max_packets_to_skip
    self.scaling_output = scaled_output
    self.impedance = impedance

    # might be handy to know API
    self.board_type = "ganglion"

    print("Looking for Ganglion board")
    if port == None:
      port = self.find_port()   
    self.port = port # find_port might not return string

    self.connect()

    self.streaming = False
    # number of EEG channels and (optionally) accelerometer channel
    self.eeg_channels_per_sample = 4
    self.aux_channels_per_sample = 3 
    self.imp_channels_per_sample = 5 
    self.read_state = 0
    self.log_packet_count = 0
    self.packets_dropped = 0
    self.time_last_packet = 0

    # Disconnects from board when terminated
    atexit.register(self.disconnect)

  def getBoardType(self):
    """ Returns the version of the board """
    return self.board_type

  def setImpedance(self, flag):
    """ Enable/disable impedance measure """
    self.impedance = bool(flag)
  
  def connect(self):
    """ Connect to the board and configure it. Note: recreates various objects upon call. """
    print ("Init BLE connection with MAC: " + self.port)
    print ("NB: if it fails, try with root privileges.")
    self.gang = Peripheral(self.port, 'random') # ADDR_TYPE_RANDOM

    print ("Get mainservice...")
    self.service = self.gang.getServiceByUUID(BLE_SERVICE)
    print ("Got:" + str(self.service))
    
    print ("Get characteristics...")
    self.char_read = self.service.getCharacteristics(BLE_CHAR_RECEIVE)[0]
    print ("receive, properties: " + str(self.char_read.propertiesToString()) + ", supports read: " + str(self.char_read.supportsRead()))

    self.char_write = self.service.getCharacteristics(BLE_CHAR_SEND)[0]
    print ("write, properties: " + str(self.char_write.propertiesToString()) + ", supports read: " + str(self.char_write.supportsRead()))

    self.char_discon = self.service.getCharacteristics(BLE_CHAR_DISCONNECT)[0]
    print ("disconnect, properties: " + str(self.char_discon.propertiesToString()) + ", supports read: " + str(self.char_discon.supportsRead()))

    # set delegate to handle incoming data
    self.delegate = GanglionDelegate(self.scaling_output)
    self.gang.setDelegate(self.delegate)

    # enable AUX channel
    if self.aux:
      print("Enabling AUX data...")
      try:
        self.ser_write(b'n')
      except Exception as e:
        print("Something went wrong while enabling aux channels: " + str(e))
    
    print("Turn on notifications")
    # nead up-to-date bluepy, cf https://github.com/IanHarvey/bluepy/issues/53
    self.desc_notify = self.char_read.getDescriptors(forUUID=0x2902)[0]
    try:
      self.desc_notify.write(b"\x01")
    except Exception as e:
      print("Something went wrong while trying to enable notification: " + str(e))
    
    print("Connection established")

  def init_streaming(self):
    """ Tell the board to record like crazy. """
    try:
      if self.impedance:
        print("Starting with impedance testing")
        self.ser_write(b'z')
      else:
        self.ser_write(b'b')
    except Exception as e:
      print("Something went wrong while asking the board to start streaming: " + str(e))
    self.streaming = True
    self.packets_dropped = 0
    self.time_last_packet = timeit.default_timer() 
    
  def find_port(self):
    """Detects Ganglion board MAC address -- if more than 1 around, will select first. Needs root privilege."""

    print("Try to detect Ganglion MAC address. NB: Turn on bluetooth and run as root for this to work! Might not work with every BLE dongles.")
    scan_time = 5
    print("Scanning for 5 seconds nearby devices...")

    #   From bluepy example
    class ScanDelegate(DefaultDelegate):
      def __init__(self):
        DefaultDelegate.__init__(self)

      def handleDiscovery(self, dev, isNewDev, isNewData):
        if isNewDev:
          print ("Discovered device: " + dev.addr)
        elif isNewData:
          print ("Received new data from: " + dev.addr)
  
    scanner = Scanner().withDelegate(ScanDelegate())
    devices = scanner.scan(scan_time)

    nb_devices = len(devices)
    if nb_devices < 1:
      print("No BLE devices found. Check connectivity.")
      return ""
    else:
      print("Found " + str(nb_devices) + ", detecting Ganglion")
      list_mac = []
      list_id = []
  
      for dev in devices:
        # "Ganglion" should appear inside the "value" associated to "Complete Local Name", e.g. "Ganglion-b2a6"
        for (adtype, desc, value) in dev.getScanData():
          if desc == "Complete Local Name" and   value.startswith("Ganglion"): 
            list_mac.append(dev.addr)
            list_id.append(value)
            print("Got Ganglion: " + value + ", with MAC: " + dev.addr)
            break
    nb_ganglions = len(list_mac)
  
    if nb_ganglions < 1:
      #print("No Ganglion found ;(")

      raise OSError('Cannot find OpenBCI Ganglion MAC address')

    if nb_ganglions > 1:
      print("Found " + str(nb_ganglions) + ", selecting first")

    print("Selecting MAC address " + list_mac[0] + " for " + list_id[0])
    return list_mac[0]
    
  def ser_write(self, b):
    """Access serial port object for write""" 
    self.char_write.write(b)

  def ser_read(self):
    """Access serial port object for read""" 
    return self.char_read.read()

  def ser_inWaiting(self):
      """ Slightly different from Cyton API, return True if ASCII messages are incoming."""
      # FIXME: might have a slight problem with thread because of notifications...
      if self.delegate.receiving_ASCII:
        # in case the packet indicating the end of the message drops, we use a 1s timeout
        if timeit.default_timer() - self.delegate.time_last_ASCII > 2:
          self.delegate.receiving_ASCII = False
      return self.delegate.receiving_ASCII
  
  def getSampleRate(self):
      return SAMPLE_RATE
  
  def getNbEEGChannels(self):
    """Will not get new data on impedance check."""
    return self.eeg_channels_per_sample
  
  def getNbAUXChannels(self):
    """Might not be used depending on the mode."""
    return self.aux_channels_per_sample

  def getNbImpChannels(self):
    """Might not be used depending on the mode."""
    return  self.imp_channels_per_sample

  def start_streaming(self, callback, lapse=-1):
    """
    Start handling streaming data from the board. Call a provided callback
    for every single sample that is processed

    Args:
      callback: A callback function -- or a list of functions -- that will receive a single argument of the
          OpenBCISample object captured.
    """
    if not self.streaming:
      self.init_streaming()

    start_time = timeit.default_timer()

    # Enclose callback funtion in a list if it comes alone
    if not isinstance(callback, list):
      callback = [callback]

    while self.streaming:
      # should the board get disconnected and we could not wait for notification anymore, a reco should be attempted through timeout mechanism
      try:
        # at most we will get one sample per packet
        self.waitForNotifications(1./self.getSampleRate())
      except Exception as e:
        print("Something went wrong while waiting for a new sample: " + str(e))
      # retrieve current samples on the stack
      samples = self.delegate.getSamples()
      self.packets_dropped = self.delegate.getMaxPacketsDropped()
      if samples:
        self.time_last_packet = timeit.default_timer() 
        for call in callback:
          for sample in samples:
            call(sample)
      
      if(lapse > 0 and timeit.default_timer() - start_time > lapse):
        self.stop();
      if self.log:
        self.log_packet_count = self.log_packet_count + 1;
  
      # Checking connection -- timeout and packets dropped
      self.check_connection()

  def waitForNotifications(self, delay):
    """ Allow some time for the board to receive new data. """
    self.gang.waitForNotifications(delay)


  def test_signal(self, signal):
    """ Enable / disable test signal """
    if signal == 0:
      self.warn("Disabling synthetic square wave")
      try:
        self.char_write.write(b']')
      except Exception as e:
        print("Something went wrong while setting signal: " + str(e))
    elif signal == 1:
      self.warn("Eisabling synthetic square wave")
      try:
        self.char_write.write(b'[')
      except Exception as e:
        print("Something went wrong while setting signal: " + str(e))
    else:
      self.warn("%s is not a known test signal. Valid signal is 0-1" %(signal))

  def set_channel(self, channel, toggle_position):
    """ Enable / disable channels """
    try:
      #Commands to set toggle to on position
      if toggle_position == 1:
        if channel is 1:
          self.ser.write(b'!')
        if channel is 2:
          self.ser.write(b'@')
        if channel is 3:
          self.ser.write(b'#')
        if channel is 4:
          self.ser.write(b'$')
      #Commands to set toggle to off position
      elif toggle_position == 0:
        if channel is 1:
          self.ser.write(b'1')
        if channel is 2:
          self.ser.write(b'2')
        if channel is 3:
          self.ser.write(b'3')
        if channel is 4:
          self.ser.write(b'4')
    except Exception as e:
      print("Something went wrong while setting channels: " + str(e))
    
  """

  Clean Up (atexit)

  """
  def stop(self):
    print("Stopping streaming...")
    self.streaming = False
    # connection might be already down here
    try:
      if self.impedance:
        print("Stopping with impedance testing")
        self.ser_write(b'Z')
      else:
        self.ser_write(b's')
    except Exception as e:
      print("Something went wrong while asking the board to stop streaming: " + str(e))
    if self.log:
      logging.warning('sent <s>: stopped streaming')

  def disconnect(self):
    if(self.streaming == True):
      self.stop()
    print("Closing BLE..")
    try:
      self.char_discon.write(b' ')
    except Exception as e:
      print("Something went wrong while asking the board to disconnect: " + str(e))
    # should not try to read/write anything after that, will crash
    try:
      self.gang.disconnect()
    except Exception as e:
      print("Something went wrong while shutting down BLE link: " + str(e))
    logging.warning('BLE closed')
       

  """

      SETTINGS AND HELPERS

  """
  def warn(self, text):
    if self.log:
      #log how many packets where sent succesfully in between warnings
      if self.log_packet_count:
        logging.info('Data packets received:'+str(self.log_packet_count))
        self.log_packet_count = 0;
      logging.warning(text)
    print("Warning: %s" % text)

  def check_connection(self):
    """ Check connection quality in term of lag and number of packets drop. Reinit connection if necessary. FIXME: parameters given to the board will be lost."""
    # stop checking when we're no longer streaming
    if not self.streaming:
      return
    #check number of dropped packets and duration without new packets, deco/reco if too large
    if self.packets_dropped > self.max_packets_to_skip:
      self.warn("Too many packets dropped, attempt to reconnect")
      self.reconnect()
    elif self.timeout > 0 and timeit.default_timer() - self.time_last_packet > self.timeout:
      self.warn("Too long since got new data, attempt to reconnect")
      #if error, attempt to reconect
      self.reconnect()

  def reconnect(self):
    """ In case of poor connection, will shut down and relaunch everything. FIXME: parameters given to the board will be lost."""
    self.warn('Reconnecting')
    self.stop()
    self.disconnect()
    self.connect()
    self.init_streaming()
Beispiel #25
0
p.setDelegate(MyDelegate())
try:
    chList = p.getCharacteristics()

    seList = p.getServices()
    for se in seList:
        try:
            print(se.uuid)
        except:
            print("SE Error")

    print("---------------------------")
    print("Get Service1")

    try:
        se1 = p.getServiceByUUID('0000180f-0000-1000-8000-00805f9b34fb')
        ch1 = se1.getCharacteristics('00002a19-0000-1000-8000-00805f9b34fb')

        for _ch1 in ch1:
            print(_ch1.uuid)

        print("----------------------------------")
        print(ch1[0].uuid)
        print(ch1[0].read())
    except:
        print("SE1 Error")

    print("-------------------------------------------")
    print("Get Data")
    try:
        se10 = p.getServiceByUUID('ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6')
    else:
        suppString = str(i[1])
    if len(str(i[0])) == 1:
        suppString = '0' + str(i[0]) + suppString
    else:
        suppString = str(i[0]) + suppString
    ble_data.append(str.encode(suppString))
    # if int(i[1]/1000) == 0:
    #     ble_data.append(str.encode(str(i[0])+'0'+str(i[1])))
    # elif len(str(i[0])) == 1:
    #     ble_data.append(str.encode('0'+str(i[0])+'0'+str(i[1])))
    # else:
    #     ble_data.append(str.encode(str(i[0])+str(i[1])))
print(ble_data)
input()
p = Peripheral(addr, 'public')
s = p.getServiceByUUID("20ff121c-94e1-11e9-bc42-526af7764f64")
print("conectado")
carachteristics = s.getCharacteristics()[0]
for j in ble_data:
    carac = int(carachteristics.read())
    print(carac)
    # p.waitForNotifications()
    while carac != 33:
        if carac == 32:
            break
        pass
    carachteristics.write(j)
    print(j)
p.disconnect()
import binascii
from bluepy.btle import Peripheral

entity_id = data.get('entity_id')
action = data.get('action')

# find your switchbot address by
# pi@raspberry:~ $ sudo hcitool lescan
# replace "ff:..:ff"

# if entity_id == "stefan_radiator":
#     switchbot = "EA:E1:11:E2:B1:77"

p = Peripheral("EA:E1:11:E2:B1:77", "random")
hand_service = p.getServiceByUUID("cba20d00-224d-11e6-9fb8-0002a5d5c51b")
hand = hand_service.getCharacteristics("cba20002-224d-11e6-9fb8-0002a5d5c51b")[0]

if action == 'turn_on':
    hand.write(binascii.a2b_hex("570101"))
    hass.states.set('sensor.stefan_radiator', 'on')

elif action == 'turn_off':
    hand.write(binascii.a2b_hex("570102"))
    hass.states.set('sensor.' + entity_id, 'off')
    
elif action == 'press':
    hand.write(binascii.a2b_hex("570100"))

else:
    hass.states.set('sensor.' + entity_id , 'unknown')
Beispiel #28
0
os.system("rfkill unblock bluetooth")
time.sleep(3)

nowSecs = 0
nowKitchenSecs = 0
connectedToKitchen = False

kitchenMAC = "empty"
kitchenMAC = getMAC("KitchenBin")
kitchenPConnected = False

if kitchenMAC != "empty":
    kitchenP = Peripheral(kitchenMAC)
    kitchenP.setDelegate(MyDelegate())
    # Setup to turn notifications on, e.g.
    svc = kitchenP.getServiceByUUID('9a4587b1-4d85-4c75-b88b-faa619295a18')
    ch = svc.getCharacteristics()[0]
    print(ch.valHandle)

    kitchenP.writeCharacteristic(ch.valHandle + 1, "\x01\x00")
    kitchenPConnected = True

    print("connected to KitchenBin and waiting for notifications")

while 1:

    if kitchenPConnected == True:
        if kitchenP.waitForNotifications(1.0):
            continue
    else:
        while nowKitchenSecs < time.time():
Beispiel #29
0
    tempContainer = []
    chList = p.getCharacteristics()
    for ch in chList:
        tempContainer.append(str(ch.uuid))
    return tempContainer.pop()


print("Setting up connection with beacon stones...")
p = Peripheral(sys.argv[1], "public")
led_service_uuid = getSvc(p)
led_char_uuid = getChar(p)

print("SVC : " + led_service_uuid + " / Char : " + led_char_uuid)
print("Setup is complete. Starting service...")

LedService = p.getServiceByUUID(led_service_uuid)

try:
    ch = LedService.getCharacteristics(led_char_uuid)[0]
    while True:
        ch.write(struct.pack('<B', 0x00))
        print("Led2 on")
        time.sleep(2)
        ch.write(struct.pack('<B', 0x01))
        print("Led2 off")
        time.sleep(2)
except (KeyboardInterrupt, SystemExit):
    print("Pressed keyboard for interruption!")
finally:
    p.disconnect()
    print("Terminating program...")
Beispiel #30
0
    scanDeviceList.append(dev.addr)
    for (adtype, desc, value) in dev.getScanData():
        print "  %s = %s" % (desc, value)

# deviceIndex = 1
deviceIndex = int(input("Input the device Index:"))

# print deviceIndex
print scanDeviceList[deviceIndex]

p = Peripheral(scanDeviceList[deviceIndex], "random")
pq = MyDelegate(p)
p.setDelegate(pq)

#Get UART Service
UARTService = p.getServiceByUUID(UART_service_uuid)
# Get The UART-Characteristics
UARTC = UARTService.getCharacteristics(UART_rx_char_uuid)[0]
#Get The handle the  UART-Characteristics
hUARTC = UARTC.getHandle()
# Search and get Get The UART-Characteristics "property" (UUID-0x2902 CCC-Client Characteristic Configuration))
#  wich is located in a handle in the range defined by the boundries of the UARTService
for desriptor in p.getDescriptors(
        hUARTC):  # The handle range should be read from the services
    if (desriptor.uuid == 0x2902
        ):  #      but is not done due to a Bluez/BluePy bug :(
        print(
            "Trackle UART Client Characteristic Configuration found at handle 0x"
            + format(desriptor.handle, "02X"))
        hUARTCCC = desriptor.handle
deviceFrequency = 30

#takes input of device adress and if no adress present then logs error
if len(sys.argv) != 2:
    print("Fatal, must pass device address:", sys.argv[0], "<device address="
          ">")
    quit()

p = Peripheral(sys.argv[1])
print("Connected")

#for service in p.getServices():
#    print(service.uuid)

ButtonService = p.getServiceByUUID(service_uuid)
print("Got service")
try:
    ch_light = ButtonService.getCharacteristics(char_light_uuid)[0]
    ch_temp = ButtonService.getCharacteristics(char_temp_uuid)[0]
    ch_humid = ButtonService.getCharacteristics(char_humid_uuid)[0]
    ch_pressure = ButtonService.getCharacteristics(char_pressure_uuid)[0]
    ch_voc = ButtonService.getCharacteristics(char_voc_uuid)[0]
    ch_co2 = ButtonService.getCharacteristics(char_co2_uuid)[0]

    print("Got characteristics")
    while 1:
        print("read")

        lightVal = str(struct.unpack('<f', ch_light.read())[0])
        tempVal = str(struct.unpack('<f', ch_temp.read())[0])
Beispiel #32
0
class BtleDevice(DefaultDelegate):
    """Base class for a connection to a Bluetooth Low Energy device.

    Built on bluepy (which is built on bluez), and uses some bluepy classes,
    so this class is just a helper not really a full API.

    See https://ianharvey.github.io/bluepy-doc/index.html

    **** NOTE *****
    As of 2019-12-06, you have to fix a bug in the btle.py library:
    Comment out the _getResp in disconnect() or it can hang on readline
        when a 'stat disc' reply from the helper is overlooked (as in
        during a call to setMTU).
    **** NOTE *****
    """
    # Common Services:
    generic_access_service_uuid = UUID('1800')
    device_info_service_uuid = UUID('180a')

    # Common Descriptors:
    characteristic_description_uuid = UUID('2901')
    characteristic_configure_uuid = UUID('2902')

    # Common Characteristics:
    model_number_uuid = UUID('2a24')  # As string
    serial_number_uuid = UUID('2a25')  # As string
    firmware_revision_uuid = UUID('2a26')  # As string
    hardware_revision_uuid = UUID('2a27')  # As string
    manufacturer_name_uuid = UUID('2a29')  # As string

    def __init__(self, mac_addy, mtu=0xff, max_rate=10):
        """Connect to the device at the specified addy.

        Max_rate is the maximum rate in messages/second at which we will
            send messages to the device.  I imagine this could/should be
            automatically inferred from the Peripheral Preferred Connection
            Parameters, but that's above my pay grade.  All I know is,
            if you don't meter the outgoing messages, some peripherals
            will just start dropping requests, resulting in timeouts
            waiting for the acks.  For an Airthings Wave+, max_rate 10
            works with dumb throttling, or max_rate 5 with smart.  The
            whole issue seems fishy to me -- seems like the lower level
            protocols should handle this better.  But pragmatically, it
            regularly hangs without this.
        """
        DefaultDelegate.__init__(self)
        assert mac_addy

        self.mac_addy = mac_addy
        self.mtu = mtu
        self.min_delay = 1. / max_rate
        self.peripheral = None

        self.cache = None  # Will be dict mapping UUIDs to Characteristics; Created/cleared by connect()
        self.last_notification = None  # This is only used transiently by the default notification handler.  Ignore!

        if smart_throttle:
            self.last_xmt_time = 0  # Used to throttle outgoing messages.  Is the time() of last sent message (or 0)

    #=====
    # If you don't know the MAC address, you'll need to scan, but that requires root...
    #=====

    @staticmethod
    def scan(match=None, duration=5, just_one=False, match_scan_entry=False):
        """This looks through any advertising btle devices for matching ones.

        *** Note you must run this as root ***

        match(scan_data) should return True if scan_data is from the device you're looking for.

            If no match function is provided, it is treated as always returning True (you want
                to see all devices that respond within duration).

            scan_data is a dict mapping description to value, unless match_scan_entry is True
                in which case scan_data is a btle ScanEntry object.

        Duration is how many seconds to scan before returning.

        If just_one is False, scans for the full duration and then
            returns a list of matching ScanEntry objects. (https://ianharvey.github.io/bluepy-doc/scanentry.html)

        If just_one is True, this returns the first match immediately (no list)
            or None after full duration timeout.

        Note (match==None and just_one==True) results in just the first device
            to respond being immediately returned.
        """
        scanner = Scanner().withDelegate(DefaultDelegate())
        scanner.clear()
        scanner.start()
        if not just_one:
            found = []
        try:
            for i in range(max(int(duration * 10),
                               1)):  # Break duration into 10ths
                scanner.process(
                    0.1)  # Handle incoming messages for 1/10th of a second...
                devices = scanner.getDevices()
                for dev in devices:
                    if debug:
                        BtleDevice.dump_scan_entry(dev)
                    if match_scan_entry:
                        params = dev
                    else:
                        params = {
                            name: val
                            for tag, name, val in dev.getScanData()
                        }
                    if match is None or match(params):
                        if just_one:
                            return dev
                        else:
                            found.append(dev)
                scanner.clear()
        finally:
            scanner.stop()

        if just_one:
            return None
        return found

    def disconnect(self):
        """Do call this when you are done!
        (But no sooner.  This will automatically re-connect if you try to do anything after this...)
        """
        if self.peripheral is not None:
            if debug:
                print(
                    "Disconnecting."
                )  # TODO - need to wrap all these debugging messages in a better logger...
            self.throttle()
            self.peripheral.disconnect()
            self.peripheral = None
            self.cache = None
            return True
        return False

    def connect(self):
        """Optional: Connects to the device.

        Returns True if this causes the connection, or False if we were already connected.

        Most of the methods here will call this on-demand, so you only need to call this
            explicitly if you want to access self.peripheral directly straight away.
        """
        if self.peripheral is None:
            if debug:
                print("Connecting to %s." % (self.mac_addy, ))
            self.cache = {}
            self.peripheral = Peripheral(self.mac_addy).withDelegate(self)
            if self.mtu is not None:
                self.throttle()
                self.peripheral.setMTU(
                    self.mtu
                )  # Notification and Indications may not happen if we don't do this.
            return True
        return False

    #=====
    # Most of the good stuff is here -- ways to read and write values:
    #=====

    def __getitem__(self, char):
        """This reads and returns the current value of the given characteristic.
        Char can be a uuid string, UUID object, or Characteristic object.
        """
        self.throttle()
        return self.get_characteristic(char).read()

    def __setitem__(self, char, val):
        """This writes the byte array val to the specified characteristic.
        Char can be a uuid string, UUID object, or Characteristic object.
        """
        self.throttle()
        self.get_characteristic(char).write(val, True)

    def get_handle(self, char):
        """This returns the Handle (small integer) of the specified characteristic.
        Char can be a uuid string, UUID object, or Characteristic object.
        Mainly useful to compare to the handle returned by wait_for_notification().
        """
        return self.get_characteristic(char).getHandle()

    def get_characteristic(self, uuid):
        """If you're just doing basic sets, queries and commands with the high
            level interface you probably don't need to call this directly.

        This accepts strings or UUID objects and returns the associated
            Characteristic object.  (Or the first one if there are multiple
            with the same UUID.  So, this is mainly useful for high level
            variables, not for meta information attributes or such.)
        As a convenience, if you pass a Characteristic, this will return it.
            (Which is useful for functions that want to allow flexible
            identification of the Characteristic.)
        Results are cached for efficiency, so don't be shy about using it.
        Raises KeyError if uuid can't be found.
        """
        self.connect()
        if isinstance(uuid, Characteristic):
            return uuid
        if not isinstance(uuid, UUID):
            uuid = UUID(uuid)
        if uuid not in self.cache:
            self.throttle()
            c = self.peripheral.getCharacteristics(uuid=uuid)
            if c:
                self.cache[uuid] = c[0]
            else:
                self.cache[uuid] = None
        c = self.cache[uuid]
        if c is None:
            raise KeyError("Can't find characteristic %s" % (uuid, ))
        return c

    #=====
    # If you want Notifications or Indications, you need to enable each characteristic accordingly.
    #=====

    def can_notify(self, char):
        """Returns non-zero if this characteristic can generate Notifications or Indications.

        Char can be a uuid string, UUID object, or Characteristic object.

        Technically the return value can be further inspected to discern whether it
            supports Notifications, Indications, or (does this ever happen?) both.
        """
        char = self.get_characteristic(char)
        return char.properties & (char.props['INDICATE']
                                  | char.props['NOTIFY'])

    def enable(self, char):
        """Usually this is the easiest way to enable notifications from a characteristic.

        You have to call this (or one of the more specific ones below) before you will
            get any notifications from a given Characteristic.

        This enables characteristic char for either Indications or Notifications depending
            on which sort it claims to generate.

        Char can be a uuid string, UUID object, or Characteristic object.
        """
        char = self.get_characteristic(char)
        if char.properties & char.props['INDICATE']:
            self.configure_characteristic(char, 2)
        elif char.properties & char.props['NOTIFY']:
            self.configure_characteristic(char, 1)
        else:
            raise Exception(
                "Characteristic does not support any notifications: %s" %
                (char, ))

    def disable(self, char):
        self.configure_characteristic(char, 0)

    def wait_for_notification(self, timeout):
        """This waits for the next notification and then returns it
            as a (handle, data) tuple.

        This only works if you do not override handleNotifications.

        This only handles notifications that come in during this call.
            If you call this successively, you should catch all notifications
            as long as you don't make any other Btle calls in between (since
            those calls could let a notification slip into the gap).

        Returns None on timeout.
        """
        try:
            self.last_notification = False  # False means we're waiting; None means we're not!
            if self.peripheral.waitForNotifications(timeout):
                return self.last_notification  # Python should grab it before the finally clause resets it...
            else:
                return (None, None)
        finally:
            self.last_notification = None  # Make sure handleNotifications alerts us to notifications that come out of band.

    def handleNotification(self, handle, data):
        """Subclass can override this to do whatever it wants with notifications/indications.
        """
        if self.last_notification is None:
            # Don't put this under Debug flag.  This alerts you to Notifications you aren't handling yet...
            print("UNHANDLED NOTIFICATION: handle=%r data=%r" %
                  (handle, data.hex()))
        else:
            self.last_notification = (handle, data)

    #--- Unlikely you need to call any of these directly:

    def enable_notifications(self, char):
        """If you want to get Notifications from a characteristic, you need to call this first.
        Char can be a uuid string, UUID object, or Characteristic object.
        Note this also disables Indications for this characteristic.  Use configure_characteristic to enable both.
        """
        self.configure_characteristic(char, 1)

    def enable_indications(self, char):
        """If you want to get value Indications from a characteristic, you need to call this first.
        Char can be a uuid string, UUID object, or Characteristic object.
        Note this also disables Notifications for this characteristic.  Use configure_characteristic to enable both.
        """
        self.configure_characteristic(char, 2)

    def configure_characteristic(self, char, val):
        """This looks up the handle for the configuration descriptor for this characteristic,
            and then sends val to it.
        Char can be a uuid string, UUID object, or Characteristic object.
        Typically val here is 1 (enable Notifications) or 2 (enable Indications) or 0 (disable).
        """
        char = self.get_characteristic(char)
        if debug:
            print("Finding configuration descriptor for handle 0x%x" %
                  (char.getHandle(), ))
        self.throttle()
        d, = char.getDescriptors(forUUID=self.characteristic_configure_uuid)
        if debug:
            print("Found (handle 0x%x).  Setting to 0x%02x" % (d.handle, val))
        self.throttle()
        d.write(struct.pack('<H', val), True)

    def throttle(self):
        """This internal utility just waits until self.min_delay from the
            last time this method was called.
        """
        if smart_throttle:
            now = time()
            delay = self.last_xmt_time + self.min_delay - now
            if delay > 0:
                sleep(delay)
                now += delay
            self.last_xmt_time = now
        else:
            sleep(self.min_delay)

    #=====
    # Below here are just utilities for displaying the device's data layout,
    # Which also serve as examples of how to access the devices's attributes.
    #=====

    def dump_services(self, read=False, indent=''):
        """This calls dump_service on all services this device provides.
        If read is True, it reads and prints each characteristic's value.

        This is a good way to manually discover the layout of your device.
        """
        self.connect()
        for s in self.peripheral.getServices():
            self.dump_service(s, read, indent)

    def dump_service(self, service, read=False, indent=''):
        """This calls dump_characteristic on all the characteristics in the given service.
        If service is not a Service object, it's assumed to be a UUID and the service
            will be looked up by that.
        """
        self.connect()

        if not isinstance(service, Service):
            try:
                service = self.peripheral.getServiceByUUID(service)
            except BTLEEException:
                print("%sService %r: NOT FOUND" % (indent, service))
                return

        print("%sService %r (%s):" %
              (indent, service.uuid.getCommonName(), service.uuid))
        for c in service.getCharacteristics():
            self.dump_characteristic(c, read, indent + "    ")

    def dump_characteristic(self, c, read=False, indent=''):
        """Dumps whatever we can find out about a characteristic.
        If read is True, also reads and prints its value (when applicable)
        """
        print("%sCharacteristic %r (%s):" %
              (indent, c.uuid.getCommonName(), c.uuid))
        print("%s          Handle: %s (%x)" %
              (indent, c.getHandle(), c.getHandle()))
        print("%s        Readable: %s" % (
            indent,
            c.supportsRead(),
        ))
        print("%s      Properties: %s" % (
            indent,
            c.propertiesToString(),
        ))

        try:
            descriptors = c.getDescriptors()
        except BTLEGattError:
            pass
        else:
            for d in descriptors:
                print("%s      Descriptor: %s (handle 0x%x; uuid %s)" %
                      (indent, d, d.handle, d.uuid))
                if d.uuid == self.characteristic_description_uuid:
                    self.throttle()
                    print("%s          ------> %r" % (indent, d.read()))

        if c.supportsRead():
            try:
                self.throttle()
                val = c.read()
            except:
                print("%s    (Read) Value: [READ FAILED]" % (indent, ))
            else:
                print("%s    (Read) Value: %s (0x%s)" %
                      (indent, val, val.hex()))

    @staticmethod
    def dump_scan_entry(se):
        """Just prints the info that comes back from a Scan response.
        """
        print("Scanned Device:")
        print("              MAC: %s" % (se.addr, ))
        print("        Addr Type: %s" % (se.addrType, ))
        print("  Signal Strength: %s dB" % (se.rssi, ))
        print("      Connectable: %s" % (se.connectable, ))
        print("     Update Count: %s" % (se.updateCount, ))
        for t in se.getScanData():
            print("  %r" % (t, ))
Beispiel #33
0
class BLEClient():
    def __init__(self, address="", duty=0):
        self.address = address
        self.tx_active = False
        self.tx_start = 0
        self.tx_end = 0
        self.nexttransmit = 0
        self.duty = duty
        self.service = None
        self.periph = None

    def setdestination(self, address):
        self.address = address.lower().strip(
        )  # only lower case address is supported by BluePy!!

    def connect(self):
        if self.tx_active or self.address == "":
            return False
        self.tx_start = millis()
        if self.tx_start < self.nexttransmit:
            print("Next possible transmit ", self.nexttransmit)
            return False
        self.tx_active = True
        try:
            self.periph = Peripheral(self.address)
        except Exception as e:
            #        print("connect error",e)
            self.tx_active = False
            self.tx_end = millis()
            return False
        try:
            self.service = self.periph.getServiceByUUID(BLE_SERVICE_ID)
        except Exception as e:
            #        print("service error ",e)
            self.tx_active = False
            self.tx_end = millis()
            return False
        return True

    def writepayload(self, apayload):
        try:
            ch = self.service.getCharacteristics(BLE_RECEIVER_CHAR)[0]
            #        print(ch,apayload,len(apayload))
            ch.write(base64.b64encode(apayload), True)
        except Exception as e:
            #        print("write error ",e)
            return False
        return True

    def readpayload(self):
        payload = []
        try:
            ch = self.service.getCharacteristics(BLE_INFO_CHAR)[0]
            payload = ch.read()
            payload = base64.b64decode(payload.decode("utf-8"))
        except Exception as e:
            #        print("read error ",e)
            pass
        return payload

    def disconnect(self):
        try:
            self.periph.disconnect()
        except:
            return False
        self.tx_active = False
        self.tx_end = millis()
        if self.duty > 0:
            self.nexttransmit = (
                (self.tx_end - self.tx_start) * self.duty) + self.tx_end
        return True

    def send(self, spayload):
        if self.connect():
            self.writepayload(spayload)
            self.disconnect()
Beispiel #34
0
class BleDevice:
    def __init__(self):
        self.scanner = Scanner().withDelegate(ScanDelegate())

    def Scan(self, time):
        self.devices = self.scanner.scan(time)

    def ListDevices(self, scan_data_flag):
        for dev in self.devices:
            # print("Device {} ({}){}, RSSI={} dB".format(dev.addr, dev.addrType, ' [Connectable]' if dev.connectable else '', dev.rssi))

            if scan_data_flag:
                for (adtype, desc, value) in dev.getScanData():
                    print("  {} {} = {}".format(adtype, desc, value))

    def FindBoogie(self):

        boogie = None

        for dev in self.devices:
            for (adtype, desc, value) in dev.getScanData():
                if adtype == 9:
                    if value == "Boogie":
                        boogie = dev
                        print('boogie seen')

        self.boogie = dev
        return boogie

    def ConnectToDevice(self, device):
        # Function returns Connected to device when device is not there
        # print('EXIT IS : {}'.format(device))
        if device is None:
            print("No device detected")
        else:
            try:
                self.peripheral = Peripheral(device)
                print("Connected to device")
            except BTLEException:
                print('Connection failed')

#    def GetServices(self,):
#
#            services = self.peripheral.getServices()
#            print('Services')
#
#            for service in services:
#                print('My Service : {}'.format(service))
#                characteristics = service.getCharacteristics()
#
#                for characteristic in characteristics:
#                    print('{}'.format(characteristic))
#
#            self.services = services

    def GetCharacteristicsByUUID(self):

        CHARACT_ack = False
        CharacteristicUUID = "7895cecb-a923-4ce4-bc58-27e8ce6e00ea"

        Service_characteristics = self.service.getCharacteristics()

        for characteristic in Service_characteristics:
            print('My CHARACT : {}'.format(characteristic))

            if str(characteristic.uuid) == CharacteristicUUID:
                CHARACT_ack = True
                print('Characteristic Verified')
            else:
                print('Characteristic error')

    def GetServiceByUUID(self):

        UUID_ack = False
        ServiceUUID = "1a49d922-e3a9-4b00-9253-c4c72a1bcb5d"

        self.service = self.peripheral.getServiceByUUID(ServiceUUID)
        #        print('service found is {}, service searched is {}'.format(str(self.service.uuid), ServiceUUID))

        if str(self.service.uuid) == ServiceUUID:
            print('My UUID service : {}'.format(self.service.uuid))
            UUID_ack = True

        else:
            UUID_ack = False

        if UUID_ack is True:
            print('Search Service by UUID Verified')
            ble.GetCharacteristicsByUUID()

        else:
            print('Search Service by UUID error')

#    def ReadNotification(self)

    def ConnectToBoogie(self):
        # Function find & connect to boogie. Validate connection with a known service and characteristic
        # Service : 1a49d922-e3a9-4b00-9253-c4c72a1bcb5d      Characteristic : 7895cecb-a923-4ce4-bc58-27e8ce6e00ea   Expected value : (0x32) or if char = (2)

        boogie = ble.FindBoogie()
        ble.ConnectToDevice(boogie)
        ble.GetServiceByUUID()
devices = Scanner()                 # Scanner Object
temp=devices.scan(10)               # Start Scan
try:
    for dev in temp:
        if dev.getScanData()[3][2] == "mr. singh":      # Check for the target BLE device name
            if dev.connectable:
                p = Peripheral(dev.addr, "random")
                p.setDelegate(ScanDelegate())           # Create internal Object of scandelegate class to handle the notifications
except (RuntimeError, TypeError, NameError):
    print "Device not found"
    exit(1)
################### Check for the Services and its characteristics #################
print p.getServices()[0]
print p.getServices()[1]
print p.getServices()[2]
Heart_Rate_Measurement = p.getServiceByUUID(0x180D).getCharacteristics()[0]
Body_Sensor_Location = p.getServiceByUUID(0x180D).getCharacteristics()[1]
Heart_Rate_Control_Point = p.getServiceByUUID(0x180D).getCharacteristics()[2]
print Heart_Rate_Measurement , Heart_Rate_Measurement.uuid              # Print characteristic and its uuid
print Body_Sensor_Location , Body_Sensor_Location.uuid
print Heart_Rate_Control_Point , Heart_Rate_Control_Point.uuid

################## Print the Value ###########################

body_sensor=["not_selected","Chest","Wrist","Finger","Hand","Ear Lobe","Foot"]  # List for body sensor location

try:
    ch = p.getServiceByUUID(0x180D).getCharacteristics()[1]                     # body sensor location characteristics
    print ch
    if (ch.read()):
        print ord(ch.read())                                                    # Print the location
Beispiel #36
0
class MiKettle(object):
    """"
    A class to control mi kettle device.
    """

    def __init__(self, mac, product_id, cache_timeout=600, retries=3, iface=None, token=None):
        """
        Initialize a Mi Kettle for the given MAC address.
        """
        _LOGGER.debug('Init Mikettle with mac %s and pid %s', mac, product_id)

        self._mac = mac
        self._reversed_mac = MiKettle.reverseMac(mac)

        self._cache = None
        self._cache_timeout = timedelta(seconds=cache_timeout)
        self._last_read = None
        self.retries = retries
        self.ble_timeout = 10
        self.lock = Lock()

        self._product_id = product_id
        self._iface = iface
        # Generate token if not supplied
        if token is None:
            token = MiKettle.generateRandomToken()
        self._token = token

        self._p = None
        self._authenticated = False

    def connect(self):
        if self._p is None:
            self._p = Peripheral(deviceAddr=self._mac, iface=self._iface)
            self._p.setDelegate(self)

    def name(self):
        """Return the name of the device."""
        self.connect()
        self.auth()
        name = self._p.readCharacteristic(_HANDLE_READ_NAME)

        if not name:
            raise Exception("Could not read NAME using handle %s"
                            " from Mi Kettle %s" % (_HANDLE_READ_NAME, self._mac))
        return ''.join(chr(n) for n in name)

    def firmware_version(self):
        """Return the firmware version."""
        self.connect()
        self.auth()
        firmware_version = self._p.readCharacteristic(_HANDLE_READ_FIRMWARE_VERSION)

        if not firmware_version:
            raise Exception("Could not read FIRMWARE_VERSION using handle %s"
                            " from Mi Kettle %s" % (_HANDLE_READ_FIRMWARE_VERSION, self._mac))
        return ''.join(chr(n) for n in firmware_version)

    def parameter_value(self, parameter, read_cached=True):
        """Return a value of one of the monitored paramaters.
        This method will try to retrieve the data from cache and only
        request it by bluetooth if no cached value is stored or the cache is
        expired.
        This behaviour can be overwritten by the "read_cached" parameter.
        """
        # Use the lock to make sure the cache isn't updated multiple times
        with self.lock:
            if (read_cached is False) or \
                    (self._last_read is None) or \
                    (datetime.now() - self._cache_timeout > self._last_read):
                self.fill_cache()
            else:
                _LOGGER.debug("Using cache (%s < %s)",
                              datetime.now() - self._last_read,
                              self._cache_timeout)

        if self.cache_available():
            return self._cache[parameter]
        else:
            raise Exception("Could not read data from MiKettle %s" % self._mac)

    def fill_cache(self):
        """Fill the cache with new data from the sensor."""
        _LOGGER.debug('Filling cache with new sensor data.')
        try:
            _LOGGER.debug('Connect')
            self.connect()
            _LOGGER.debug('Auth')
            self.auth()
            _LOGGER.debug('Subscribe')
            self.subscribeToData()
            _LOGGER.debug('Wait for data')
            self._p.waitForNotifications(self.ble_timeout)
            # If a sensor doesn't work, wait 5 minutes before retrying
        except Exception as error:
            _LOGGER.debug('Error %s', error)
            self._last_read = datetime.now() - self._cache_timeout + \
                timedelta(seconds=300)
            return

    def clear_cache(self):
        """Manually force the cache to be cleared."""
        self._cache = None
        self._last_read = None

    def cache_available(self):
        """Check if there is data in the cache."""
        return self._cache is not None

    def _parse_data(self, data):
        """Parses the byte array returned by the sensor."""
        _LOGGER.debug('parsing')
        res = dict()
        res[MI_ACTION] = MI_ACTION_MAP[int(data[0])]
        res[MI_MODE] = MI_MODE_MAP[int(data[1])]
        res[MI_SET_TEMPERATURE] = int(data[4])
        _LOGGER.debug('Mode: %s', int(data[1]))
        res[MI_CURRENT_TEMPERATURE] = int(data[5])
        res[MI_KW_TYPE] = MI_KW_TYPE_MAP[int(data[6])]
        res[MI_KW_TIME] = MiKettle.bytes_to_int(data[7:8])
        return res

    @staticmethod
    def bytes_to_int(bytes):
        result = 0
        for b in bytes:
            result = result * 256 + int(b)

        return result

    def auth(self):
        if self._authenticated:
            return
        auth_service = self._p.getServiceByUUID(_UUID_SERVICE_KETTLE)
        auth_descriptors = auth_service.getDescriptors()

        self._p.writeCharacteristic(_HANDLE_AUTH_INIT, _KEY1, "true")

        auth_descriptors[1].write(_SUBSCRIBE_TRUE, "true")

        self._p.writeCharacteristic(_HANDLE_AUTH,
                                    MiKettle.cipher(MiKettle.mixA(self._reversed_mac, self._product_id), self._token),
                                    "true")

        self._p.waitForNotifications(10.0)

        self._p.writeCharacteristic(_HANDLE_AUTH, MiKettle.cipher(self._token, _KEY2), "true")

        self._p.readCharacteristic(_HANDLE_VERSION)
        self._authenticated = True

    def subscribeToData(self):
        controlService = self._p.getServiceByUUID(_UUID_SERVICE_KETTLE_DATA)
        controlDescriptors = controlService.getDescriptors()
        controlDescriptors[3].write(_SUBSCRIBE_TRUE, "true")

    # TODO: Actually generate random token instead of static one
    @staticmethod
    def generateRandomToken() -> bytes:
        return bytes([0x01, 0x5C, 0xCB, 0xA8, 0x80, 0x0A, 0xBD, 0xC1, 0x2E, 0xB8, 0xED, 0x82])

    @staticmethod
    def reverseMac(mac) -> bytes:
        parts = mac.split(":")
        reversedMac = bytearray()
        leng = len(parts)
        for i in range(1, leng + 1):
            reversedMac.extend(bytearray.fromhex(parts[leng - i]))
        return reversedMac

    @staticmethod
    def mixA(mac, productID) -> bytes:
        return bytes([mac[0], mac[2], mac[5], (productID & 0xff), (productID & 0xff), mac[4], mac[5], mac[1]])

    @staticmethod
    def mixB(mac, productID) -> bytes:
        return bytes([mac[0], mac[2], mac[5], ((productID >> 8) & 0xff), mac[4], mac[0], mac[5], (productID & 0xff)])

    @staticmethod
    def _cipherInit(key) -> bytes:
        perm = bytearray()
        for i in range(0, 256):
            perm.extend(bytes([i & 0xff]))
        keyLen = len(key)
        j = 0
        for i in range(0, 256):
            j += perm[i] + key[i % keyLen]
            j = j & 0xff
            perm[i], perm[j] = perm[j], perm[i]
        return perm

    @staticmethod
    def _cipherCrypt(input, perm) -> bytes:
        index1 = 0
        index2 = 0
        output = bytearray()
        for i in range(0, len(input)):
            index1 = index1 + 1
            index1 = index1 & 0xff
            index2 += perm[index1]
            index2 = index2 & 0xff
            perm[index1], perm[index2] = perm[index2], perm[index1]
            idx = perm[index1] + perm[index2]
            idx = idx & 0xff
            outputByte = input[i] ^ perm[idx]
            output.extend(bytes([outputByte & 0xff]))

        return output

    @staticmethod
    def cipher(key, input) -> bytes:
        perm = MiKettle._cipherInit(key)
        return MiKettle._cipherCrypt(input, perm)

    def handleNotification(self, cHandle, data):
        if cHandle == _HANDLE_AUTH:
            if(MiKettle.cipher(MiKettle.mixB(self._reversed_mac, self._product_id),
                               MiKettle.cipher(MiKettle.mixA(self._reversed_mac,
                                                             self._product_id),
                                               data)) != self._token):
                raise Exception("Authentication failed.")
        elif cHandle == _HANDLE_STATUS:
            _LOGGER.debug("Status update:")
            if data is None:
              return

            _LOGGER.debug("Parse data: %s", data)
            self._cache = self._parse_data(data)
            _LOGGER.debug("data parsed %s", self._cache)

            if self.cache_available():
                self._last_read = datetime.now()
            else:
                # If a sensor doesn't work, wait 5 minutes before retrying
                self._last_read = datetime.now() - self._cache_timeout + \
                    timedelta(seconds=300)
        else:
            _LOGGER.error("Unknown notification from handle: %s with Data: %s", cHandle, data.hex())
Beispiel #37
0
import struct
import time

import urllib
import json
import httplib
import requests

from bluepy.btle import UUID, Peripheral

temp_uuid = UUID(0x180A)

p = Peripheral("E0:95:58:90:DD:9D", "random")
prev = ''
try:
    svc = p.getServiceByUUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
    ch = svc.getCharacteristics("6E400003-B5A3-F393-E0A9-E50E24DCCA9E")[0]
    print(ch.read())
    prev = ''
    state = 0
    while 1:
        try:
            data = str(ch.read()).strip()
            if data == 'st1' or data == 'st2':
                print("stop")
                state = 0
                prev = 'stop'
            elif data == "wlk":
                print("walk")
                state = 1
                prev = 'walk'
Beispiel #38
0
class Bluepy(DefaultDelegate):
   def __init__(self):
      DefaultDelegate.__init__(self)

      self._peripheral_address = None
      self._peripheral_address_type = btle.ADDR_TYPE_PUBLIC
      self._peripheral = None

      self._scanner = Scanner().withDelegate(self)
      self._devicesScanned = []

      self._service_uuid = "b9e875c0-1cfa-11e6-b797-0002a5d5c51b"
      self._char_read_uuid = "1ed9e2c0-266f-11e6-850b-0002a5d5c51b"
      self._char_write_uuid = "0c68d100-266f-11e6-b388-0002a5d5c51b"

      self._descs = None
      self._svc = None

      self._ch_read = None
      self._ch_write = None

      self.lastValue = {}
      self.lastValue['value'] = 0
      self.lastValue['datetime'] = "placeholder"

   def reset(self):
      self._peripheral = None
      self._descs = None
      self._svc = None

      self._ch_read = None
      self._ch_write = None

   def scan(self, time=3):

      devices = self._scanner.scan(time)

      for dev in devices:
         print ("Device "+dev.addr+" ("+dev.addrType+"), RSSI="+str(dev.rssi)+" dB")
         for (adtype, desc, value) in dev.getScanData():
            if desc == "Complete Local Name":
               self._devicesScanned.append({
                  'name': value,
                  'id': dev.addr
               })

   def findXDKAddress(self):
      for dev in self._devicesScanned:
         if dev['name'].startswith( 'XDK' ):
            self._peripheral_address = dev['id']

      return self._peripheral_address

   def setXDKAddress(self, xdk_mac):
      self._peripheral_address = xdk_mac

   def handleNotification(self, cHandle, data):

      data = re.findall(r'\d+', str(data))[0]

      self.lastValue['value'] = data
      self.lastValue['datetime'] = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]

      print ("Received data: "+ str(data))

      #self.lastValue = int(data)

   def handleDiscovery(self, dev, isNewDev, isNewData):
      if isNewDev:
         print ("Discovered device "+ dev.addr)
      elif isNewData:
         print ("Received new data from"+ dev.addr)

   def connect(self):

      triesCounter = 0

      while self._peripheral == None :

         try:
            print ('\nConnecting...')
            self._peripheral = Peripheral(self._peripheral_address, "random")
            if(self._peripheral != None):
               print ('Connected!')

         except BTLEException as e:
            triesCounter = triesCounter + 1
            print ("Number of tries: "+str(triesCounter))
            print ("Trying to connect again after 5s\n")
            print(e)
            time.sleep(5)

   def setDelegate(self):
      self._peripheral.setDelegate(self)

   def discoverSvc(self):
      while self._svc == None or self._descs == None or self._ch_read == None or self._ch_write == None:
         print ("\nFinding service...")
         self._svc = self._peripheral.getServiceByUUID(self._service_uuid)
         print ("Finding descriptors...")
         self._descs = self._svc.getDescriptors()
         print ("Finding read characteristic...")
         self._ch_read = self._svc.getCharacteristics(self._char_read_uuid)[0]
         print ("Finding write characteristic...\n")
         self._ch_write = self._svc.getCharacteristics(self._char_write_uuid)[0]

      print ("All characteristics found!!!\n")


   def enableSensor(self):
      print ("Turning sensor on...\n")
      self._ch_write.write(b'\x31')
      time.sleep(1.0)

   def disableSensor(self):
      print ("Turning sensor off...\n")
      self._ch_write.write(b'\x30')
      time.sleep(1.0)

   def disconnect(self):
      print ('Disconnecting...')

      self._peripheral.disconnect()

      self._peripheral = None
      print ('Disconnected!\n')
   
   def readValues(self):
      print ("Reading values...\n")

      count = 0
      while True:
         if self._peripheral.waitForNotifications(5.0):
            count = count + 1
            continue

   def BluetoothFlow(self):

      self.scan(3)
      #time.sleep(3.0)

      xdk_address = self.findXDKAddress()
      print ("\nXDK MAC: "+ xdk_address)

      disconnectCounter = 0
      active = True
      
      while active == True:
         try:
            self.reset()
            
            self.connect()
               
            self.setDelegate()

            self.discoverSvc()

            self.enableSensor()
                  
            try:
               self.readValues()
            except BTLEException as e:
               print(e)
               disconnectCounter = disconnectCounter + 1
               print ("\nDisconnection number: "+str(disconnectCounter))
               print ("\nTrying to reconnect after 5s...")
               time.sleep(5.0)

            except KeyboardInterrupt:
               active = False
               print ("\nStopping...")

         except BTLEException as e:
            print(e)

      self.disableSensor()
      self.disconnect()

   def getLastValue(self):
      return (self.lastValue)
Beispiel #39
0
class RileyLink(PacketRadio):
    def __init__(self):
        self.peripheral = None
        self.pa_level_index = PA_LEVELS.index(0x84)
        self.data_handle = None
        self.logger = getLogger()
        self.packet_logger = get_packet_logger()
        self.address = g_rl_address
        self.service = None
        self.response_handle = None
        self.notify_event = Event()
        self.initialized = False
        self.manchester = ManchesterCodec()

    def connect(self, force_initialize=False):
        try:
            if self.address is None:
                self.address = self._findRileyLink()

            if self.peripheral is None:
                self.peripheral = Peripheral()

            try:
                state = self.peripheral.getState()
                if state == "conn":
                    return
            except BTLEException:
                pass

            self._connect_retry(3)

            self.service = self.peripheral.getServiceByUUID(
                RILEYLINK_SERVICE_UUID)
            data_char = self.service.getCharacteristics(
                RILEYLINK_DATA_CHAR_UUID)[0]
            self.data_handle = data_char.getHandle()

            char_response = self.service.getCharacteristics(
                RILEYLINK_RESPONSE_CHAR_UUID)[0]
            self.response_handle = char_response.getHandle()

            response_notify_handle = self.response_handle + 1
            notify_setup = b"\x01\x00"
            self.peripheral.writeCharacteristic(response_notify_handle,
                                                notify_setup)

            while self.peripheral.waitForNotifications(0.05):
                self.peripheral.readCharacteristic(self.data_handle)

            if self.initialized:
                self.init_radio(force_initialize)
            else:
                self.init_radio(True)
        except BTLEException as be:
            if self.peripheral is not None:
                self.disconnect()
            raise PacketRadioError("Error while connecting") from be
        except Exception as e:
            raise PacketRadioError("Error while connecting") from e

    def disconnect(self, ignore_errors=True):
        try:
            if self.peripheral is None:
                self.logger.info("Already disconnected")
                return
            self.logger.info("Disconnecting..")
            if self.response_handle is not None:
                response_notify_handle = self.response_handle + 1
                notify_setup = b"\x00\x00"
                self.peripheral.writeCharacteristic(response_notify_handle,
                                                    notify_setup)
        except Exception as e:
            if not ignore_errors:
                raise PacketRadioError("Error while disconnecting") from e
        finally:
            try:
                if self.peripheral is not None:
                    self.peripheral.disconnect()
                    self.peripheral = None
            except BTLEException as be:
                if ignore_errors:
                    self.logger.exception(
                        "Ignoring btle exception during disconnect")
                else:
                    raise PacketRadioError("Error while disconnecting") from be
            except Exception as e:
                raise PacketRadioError("Error while disconnecting") from e

    def get_info(self):
        try:
            self.connect()
            bs = self.peripheral.getServiceByUUID(XGATT_BATTERYSERVICE_UUID)
            bc = bs.getCharacteristics(XGATT_BATTERY_CHAR_UUID)[0]
            bch = bc.getHandle()
            battery_value = int(self.peripheral.readCharacteristic(bch)[0])
            self.logger.debug("Battery level read: %d", battery_value)
            version, v_major, v_minor = self._read_version()
            return {
                "battery_level": battery_value,
                "mac_address": self.address,
                "version_string": version,
                "version_major": v_major,
                "version_minor": v_minor
            }
        except Exception as e:
            raise PacketRadioError("Error communicating with RileyLink") from e
        finally:
            self.disconnect()

    def _read_version(self):
        global g_rl_version, g_rl_v_major, g_rl_v_minor
        version = None
        try:
            if g_rl_version is not None:
                return g_rl_version, g_rl_v_major, g_rl_v_minor
            else:
                response = self._command(Command.GET_VERSION)
                if response is not None and len(response) > 0:
                    version = response.decode("ascii")
                    self.logger.debug("RL reports version string: %s" %
                                      version)
                    g_rl_version = version

            if version is None:
                return "0.0", 0, 0

            try:
                m = re.search(".+([0-9]+)\\.([0-9]+)", version)
                if m is None:
                    raise PacketRadioError(
                        "Failed to parse firmware version string: %s" %
                        version)

                g_rl_v_major = int(m.group(1))
                g_rl_v_minor = int(m.group(2))
                self.logger.debug("Interpreted version major: %d minor: %d" %
                                  (g_rl_v_major, g_rl_v_minor))

                return g_rl_version, g_rl_v_major, g_rl_v_minor

            except Exception as ex:
                raise PacketRadioError(
                    "Failed to parse firmware version string: %s" %
                    version) from ex

        except PacketRadioError:
            raise
        except Exception as e:
            raise PacketRadioError("Error while reading version") from e

    def init_radio(self, force_init=False):
        try:
            version, v_major, v_minor = self._read_version()

            if v_major < 2:
                self.logger.error("Firmware version is below 2.0")
                raise PacketRadioError(
                    "Unsupported RileyLink firmware %d.%d (%s)" %
                    (v_major, v_minor, version))

            if not force_init:
                if v_major == 2 and v_minor < 3:
                    response = self._command(Command.READ_REGISTER,
                                             bytes([Register.PKTLEN, 0x00]))
                else:
                    response = self._command(Command.READ_REGISTER,
                                             bytes([Register.PKTLEN]))
                if response is not None and len(
                        response) > 0 and response[0] == 0x50:
                    return

            self._command(Command.RADIO_RESET_CONFIG)
            self._command(Command.SET_SW_ENCODING, bytes([Encoding.NONE]))
            self._command(Command.SET_PREAMBLE, bytes([0x66, 0x65]))
            #self._command(Command.SET_PREAMBLE, bytes([0, 0]))

            frequency = int(433910000 / (24000000 / pow(2, 16)))
            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.FREQ0, frequency & 0xff]))
            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.FREQ1, (frequency >> 8) & 0xff]))
            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.FREQ2, (frequency >> 16) & 0xff]))
            # self._command(Command.UPDATE_REGISTER, bytes([Register.FREQ0, 0x5f]))
            # self._command(Command.UPDATE_REGISTER, bytes([Register.FREQ1, 0x14]))
            # self._command(Command.UPDATE_REGISTER, bytes([Register.FREQ2, 0x12]))

            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.DEVIATN, 0x44]))
            # self._command(Command.UPDATE_REGISTER, bytes([Register.DEVIATN, 0x44]))

            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.PKTCTRL1, 0x20]))
            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.PKTCTRL0, 0x00]))
            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.PKTLEN, 0x50]))
            # self._command(Command.UPDATE_REGISTER, bytes([Register.PKTCTRL1, 0x60]))
            # self._command(Command.UPDATE_REGISTER, bytes([Register.PKTCTRL0, 0x04]))

            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.FSCTRL1, 0x06]))
            # self._command(Command.UPDATE_REGISTER, bytes([Register.FSCTRL1, 0x06]))

            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.MDMCFG4, 0xCA]))
            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.MDMCFG3, 0xBC]))
            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.MDMCFG2, 0x06]))
            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.MDMCFG1, 0x70]))
            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.MDMCFG0, 0x11]))
            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.MCSM0, 0x18]))
            # self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG4, 0xDA]))
            # self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG3, 0xB5]))
            # self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG2, 0x12]))
            # self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG1, 0x23]))
            # self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG0, 0x11]))
            # self._command(Command.UPDATE_REGISTER, bytes([Register.MCSM0, 0x18]))

            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.FOCCFG, 0x17]))
            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.FSCAL3, 0xE9]))
            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.FSCAL2, 0x2A]))
            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.FSCAL1, 0x00]))
            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.FSCAL0, 0x1F]))
            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.TEST1, 0x31]))
            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.TEST0, 0x09]))
            # self._command(Command.UPDATE_REGISTER, bytes([Register.FOCCFG, 0x17]))
            # self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL3, 0xE9]))
            # self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL2, 0x2A]))
            # self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL1, 0x00]))
            # self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL0, 0x1F]))
            # self._command(Command.UPDATE_REGISTER, bytes([Register.TEST2, 0x81])) ## register not defined on RL
            # self._command(Command.UPDATE_REGISTER, bytes([Register.TEST1, 0x35]))
            # self._command(Command.UPDATE_REGISTER, bytes([Register.TEST0, 0x09]))

            self._command(
                Command.UPDATE_REGISTER,
                bytes([Register.PATABLE0, PA_LEVELS[self.pa_level_index]]))
            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.FREND0, 0x00]))
            #self._command(Command.UPDATE_REGISTER, bytes([Register.SYNC1, 0xA5]))
            #self._command(Command.UPDATE_REGISTER, bytes([Register.SYNC0, 0x5A]))
            # self._command(Command.UPDATE_REGISTER, bytes([Register.PATABLE0, PA_LEVELS[self.pa_level_index]]))
            # self._command(Command.UPDATE_REGISTER, bytes([Register.FREND0, 0x00]))
            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.SYNC1, 0xA5]))
            self._command(Command.UPDATE_REGISTER,
                          bytes([Register.SYNC0, 0x5A]))

            response = self._command(Command.GET_STATE)
            if response != b"OK":
                raise PacketRadioError(
                    "Rileylink state is not OK. Response returned: %s" %
                    response)

            self.initialized = True

        except Exception as e:
            raise PacketRadioError(
                "Error while initializing rileylink radio: %s", e)

    def tx_up(self):
        try:
            if self.pa_level_index < len(PA_LEVELS) - 1:
                self.pa_level_index += 1
                self._set_amp(self.pa_level_index)
        except Exception as e:
            raise PacketRadioError("Error while setting tx up") from e

    def tx_down(self):
        try:
            if self.pa_level_index > 0:
                self.pa_level_index -= 1
                self._set_amp(self.pa_level_index)
        except Exception as e:
            raise PacketRadioError("Error while setting tx down") from e

    def set_tx_power(self, tx_power):
        try:
            if tx_power is None:
                return
            elif tx_power == TxPower.Lowest:
                self._set_amp(0)
            elif tx_power == TxPower.Low:
                self._set_amp(PA_LEVELS.index(0x1D))
            elif tx_power == TxPower.Normal:
                self._set_amp(PA_LEVELS.index(0x84))
            elif tx_power == TxPower.High:
                self._set_amp(PA_LEVELS.index(0xC8))
            elif tx_power == TxPower.Highest:
                self._set_amp(PA_LEVELS.index(0xC0))
        except Exception as e:
            raise PacketRadioError("Error while setting tx level") from e

    def get_packet(self, timeout=5.0):
        try:
            self.connect()
            result = self._command(Command.GET_PACKET,
                                   struct.pack(">BL", 0, int(timeout * 1000)),
                                   timeout=float(timeout) + 0.5)
            if result is not None:
                return result[0:2] + self.manchester.decode(result[2:])
            else:
                return None
        except Exception as e:
            raise PacketRadioError("Error while getting radio packet") from e

    def send_and_receive_packet(self, packet, repeat_count, delay_ms,
                                timeout_ms, retry_count, preamble_ext_ms):
        try:
            self.connect()
            data = self.manchester.encode(packet)
            result = self._command(
                Command.SEND_AND_LISTEN,
                struct.pack(">BBHBLBH", 0, repeat_count, delay_ms, 0,
                            timeout_ms, retry_count, preamble_ext_ms) + data,
                timeout=30)
            if result is not None:
                return result[0:2] + self.manchester.decode(result[2:])
            else:
                return None
        except Exception as e:
            raise PacketRadioError(
                "Error while sending and receiving data") from e

    def send_packet(self, packet, repeat_count, delay_ms,
                    preamble_extension_ms):
        try:
            self.connect()
            data = self.manchester.encode(packet)
            result = self._command(
                Command.SEND_PACKET,
                struct.pack(">BBHH", 0, repeat_count, delay_ms,
                            preamble_extension_ms) + data,
                timeout=30)
            return result
        except Exception as e:
            raise PacketRadioError("Error while sending data") from e

    def _set_amp(self, index=None):
        try:
            if index is not None:
                previous_level = self.pa_level_index
                self.pa_level_index = index
                if PA_LEVELS[previous_level] == PA_LEVELS[index]:
                    return
            self.connect()
            self._command(
                Command.UPDATE_REGISTER,
                bytes([Register.PATABLE0, PA_LEVELS[self.pa_level_index]]))
            self.packet_logger.debug("Setting pa to %02X (%d of %d)" %
                                     (PA_LEVELS[self.pa_level_index],
                                      self.pa_level_index, len(PA_LEVELS)))
        except PacketRadioError:
            self.logger.exception("Error while setting tx amplification")
            raise

    def _findRileyLink(self):
        global g_rl_address
        scanner = Scanner()
        g_rl_address = None
        self.logger.debug("Scanning for RileyLink")
        retries = 10
        while g_rl_address is None and retries > 0:
            retries -= 1
            for result in scanner.scan(1.0):
                if result.getValueText(7) == RILEYLINK_SERVICE_UUID:
                    self.logger.debug("Found RileyLink")
                    g_rl_address = result.addr

        if g_rl_address is None:
            raise PacketRadioError("Could not find RileyLink")

        return g_rl_address

    def _connect_retry(self, retries):
        while retries > 0:
            retries -= 1
            self.logger.info("Connecting to RileyLink, retries left: %d" %
                             retries)

            try:
                self.peripheral.connect(self.address)
                self.logger.info("Connected")
                break
            except BTLEException as btlee:
                self.logger.warning("BTLE exception trying to connect: %s" %
                                    btlee)
                try:
                    p = subprocess.Popen(["ps", "-A"], stdout=subprocess.PIPE)
                    out, err = p.communicate()
                    for line in out.splitlines():
                        if "bluepy-helper" in line:
                            pid = int(line.split(None, 1)[0])
                            os.kill(pid, 9)
                            break
                except:
                    self.logger.warning("Failed to kill bluepy-helper")
                time.sleep(1)

    def _command(self, command_type, command_data=None, timeout=10.0):
        try:
            if command_data is None:
                data = bytes([1, command_type])
            else:
                data = bytes([len(command_data) + 1, command_type
                              ]) + command_data

            self.peripheral.writeCharacteristic(self.data_handle,
                                                data,
                                                withResponse=True)

            if not self.peripheral.waitForNotifications(timeout):
                raise PacketRadioError(
                    "Timed out while waiting for a response from RileyLink")

            response = self.peripheral.readCharacteristic(self.data_handle)

            if response is None or len(response) == 0:
                raise PacketRadioError("RileyLink returned no response")
            else:
                if response[0] == Response.COMMAND_SUCCESS:
                    return response[1:]
                elif response[0] == Response.COMMAND_INTERRUPTED:
                    self.logger.warning("A previous command was interrupted")
                    return response[1:]
                elif response[0] == Response.RX_TIMEOUT:
                    return None
                else:
                    raise PacketRadioError(
                        "RileyLink returned error code: %02X. Additional response data: %s"
                        % (response[0], response[1:]), response[0])
        except PacketRadioError:
            raise
        except Exception as e:
            raise PacketRadioError("Error executing command") from e
Beispiel #40
0
class OmronEnvSensor:
    @retry(tries=3, delay=1, logger=logger)
    def __init__(self, address: str):
        super().__init__()
        self.address = address
        self.ble_peripheral = Peripheral(address,
                                         addrType=bluepy.btle.ADDR_TYPE_RANDOM)
        self.ble_peripheral.discoverServices()

    @property
    def peripheral(self):
        return self.ble_peripheral

    # @retry(tries=5, delay=1, logger=logger)
    def read_char_base(self, service_uuid,
                       char_uuid) -> (btle.Characteristic, bytes):
        """
        BLEのCharacteristicsを読む

        Args:
            service_uuid: Omron環境センサBLEサービスのshort UUID
            char_uuid: Omron環境センサBLE Characteristicsのshort UUID

        Returns:
            (btle.Characteristic, bytes):
        """
        time.sleep(BLE_READ_CHARA_WAIT_SEC)
        service = self.ble_peripheral.getServiceByUUID(uuidVal=service_uuid)
        ble_char = service.getCharacteristics(forUUID=char_uuid)[0]

        time.sleep(BLE_READ_WAIT_SEC)
        raw_data = ble_char.read()
        return ble_char, raw_data

    # @retry(tries=5, delay=1, logger=logger)
    def write_char_base(self, service_uuid, char_uuid,
                        write_value: bytes) -> None:
        """
        BLEのCharacteristicsに値を書く

        Args:
            service_uuid: Omron環境センサBLEサービスのshort UUID
            char_uuid: Omron環境センサBLE Characteristicsのshort UUID
            write_value: 書き込む値

        Returns:
            None
        """

        time.sleep(BLE_READ_CHARA_WAIT_SEC)
        service = self.ble_peripheral.getServiceByUUID(uuidVal=service_uuid)
        ble_char = service.getCharacteristics(forUUID=char_uuid)[0]

        time.sleep(BLE_WRITE_WAIT_SEC)
        ble_char.write(write_value)

    def __MSG(self, level: int, *args) -> None:
        """
        Bluetoothデバイスのアドレスをつけてログ出力する

        Args:
            level: ログ出力レベル
            *args: ログ出力内容

        Returns:
            None
        """

        msg = " ".join([str(a) for a in args])
        logger.log(level, F'{self.address}: {msg}')

    def read_device_name(self) -> str:
        """
        BLEデバイスの名前を読む(Generic Access:0x1800, Device Name:0x2a00)

        Returns:
            str: BLEデバイスの名前
        """
        (ble_chara, raw_data) = self.read_char_base(0x1800, 0x2a00)
        str_device_name = raw_data.decode('utf-8')
        self.__MSG(
            DEBUG,
            f'char={ble_chara}, raw_data={raw_data}, str={str_device_name}')
        return str_device_name

    def check_omron_env_sensor(self) -> bool:
        """
        デバイスがOmronの環境センサーかどうかを判定する

        Returns:
            bool: TrueならOmronの環境センサー

        """
        str_device_name = self.read_device_name()
        if str_device_name == "EnvSensor-BL01":
            return True
        else:
            self.__MSG(WARN, f'this device is not OMRON ')
            return False

    def read_latest_data(self) -> OmronLatestData:
        """
        環境センサーのLatest Dataを読み出す

        Returns:
            LatestData
        """

        latest_data = OmronLatestData()

        self.__MSG(DEBUG,
                   F'reading LatestData(uuid={latest_data.shortUuid:#04x})')
        (ble_chara, raw_data) = self.read_char_base(latest_data.serviceUuid,
                                                    latest_data.uuid)
        return latest_data.parse(raw_data)

    def read_latest_page(self) -> OmronLatestPage:
        """
        環境センサーのLatest Pageを読み出す

        Returns:
            LatestPage
        """

        latest_page = OmronLatestPage()

        self.__MSG(DEBUG,
                   F'reading LatestPage(uuid={latest_page.shortUuid:#04x})')
        (ble_chara, raw_data) = self.read_char_base(latest_page.serviceUuid,
                                                    latest_page.uuid)
        return latest_page.parse(raw_data)

    def write_request_page(self, page: int, row: int) -> None:
        """
        環境センサーへ読み出したいページのRequest Page要求を出す

        Args:
            page: 読み出したいページ番号 0から2047
            row: 読み出したい行数の指定 0で1行、 12で13行読み出されるっぽい

        Returns:
            None
        """
        assert 0 <= page <= 2047
        assert 0 <= row <= 12
        request_page = OmronRequestPage()
        data = request_page.encode_data(page, row)

        self.__MSG(
            DEBUG,
            F'writing RequestPage(uuid={request_page.shortUuid:#04x}): data={data}'
        )
        self.write_char_base(request_page.serviceUuid, request_page.uuid, data)

    def read_response_flag(self) -> OmronResponseFlag:
        """
        環境センサーのResponse Flagを読み出す。

        Returns:
            ResponseFlag
        """
        response_flag = OmronResponseFlag()

        self.__MSG(
            DEBUG,
            F'reading ResponseFlag(uuid={response_flag.shortUuid:#04x})')
        (ble_chara, raw_data) = self.read_char_base(response_flag.serviceUuid,
                                                    response_flag.uuid)
        return response_flag.parse(raw_data)

    def read_response_data(self) -> OmronResponseData:
        """
        環境センサーのResponse Dataを読み出す

        Returns:
            ResponseData
        """
        response_data = OmronResponseData()

        self.__MSG(
            DEBUG,
            F'reading ResponseData(uuid={response_data.shortUuid:#04x})')
        (ble_chara, raw_data) = self.read_char_base(response_data.serviceUuid,
                                                    response_data.uuid)
        return response_data.parse(raw_data)

    def read_time_information(self) -> OmronTimeInformation:
        """
        環境センサーのTime Informationを読み出す
        Returns:
            TimeInformation
        """

        time_information = OmronTimeInformation()

        self.__MSG(
            DEBUG,
            F'reading TimeInformation(uuid={time_information.shortUuid:#04x})')
        (ble_chara,
         raw_data) = self.read_char_base(time_information.serviceUuid,
                                         time_information.uuid)
        return time_information.parse(raw_data)

    def read_measurement_interval(self) -> OmronMeasurementInterval:
        """
        環境センサーのMeasurement Intervalを読み出す

        Returns:
            MeasurementInterval
        """
        measurement_interval = OmronMeasurementInterval()

        self.__MSG(
            DEBUG,
            F'reading MeasurementInterval(uuid={measurement_interval.shortUuid:#04x})'
        )
        (ble_chara,
         raw_data) = self.read_char_base(measurement_interval.serviceUuid,
                                         measurement_interval.uuid)
        return measurement_interval.parse(raw_data)

    def read_error_status(self) -> OmronErrorStatus:
        """
        環境センサーのError Statusを読み出す

        Returns:
            ErrorStatus
        """
        error_status = OmronErrorStatus()

        self.__MSG(DEBUG,
                   F'reading ErrorStatus(uuid={error_status.shortUuid:#04x})')
        (ble_chara, raw_data) = self.read_char_base(error_status.serviceUuid,
                                                    error_status.uuid)
        return error_status.parse(raw_data)

    # @retry(tries=3, delay=1, logger=logger)
    def write_read_page_and_wait_ready(self, page: int,
                                       row: int) -> OmronResponseFlag:
        """
        環境センサーに読み出したいページを指定して、準備が完了するまで待つ

        Args:
            page: 読み出したいページ番号 0から2047
            row: 読み出したい行数の指定 0で1行、 12で13行読み出されるっぽい

        Returns:
            ResponseFlag

        """
        assert 0 <= page <= 2047
        assert 0 <= row <= 12
        self.write_request_page(page, row)
        for i in range(3):
            response_flag = self.read_response_flag()
            self.__MSG(DEBUG, F'response_flag={response_flag}')
            if response_flag.update_flag == 0x01:  # 更新完了
                return response_flag
            elif response_flag.update_flag == 0x00:  # 更新中
                continue
            else:  # 更新失敗
                self.__MSG(ERROR, F'response flag failed.')
                raise IOError
        self.__MSG(ERROR, F'read response flag failed after retry.')
        raise IOError

    # @retry(tries=3, delay=1, logger=logger)
    def read_env_sensor_data(self, page: int,
                             latest_page: OmronLatestPage) -> List[LogData]:
        """
        環境センサーの指定ページを読み出す

        Args:
            page: 読み出し対象ページ
            latest_page: 環境センサーのLatestPage、最新ページの読み出し行数の指定と観測データの時刻計算にmeasurement_intervalを使う

        Returns:
            List[LogData]: 読みだした観測データのリスト
        """
        try:
            target_row = 12 if page != latest_page.page else latest_page.row  # 最新ページ以外は13行の読み出し

            response_flag = self.write_read_page_and_wait_ready(
                page, target_row)
            self.__MSG(
                INFO,
                f'page = {page}, start_time={response_flag.datetime} ({response_flag.unix_time})'
            )

            page_data = []
            for i in range(13 + 3):  # 1ページ分のデータは最大13件だけど、予備で3回追加しておく
                response_data = self.read_response_data()
                self.__MSG(DEBUG, F'response_data = {response_data}')
                page_data.append(response_data)
                if response_data is None or response_data.row == 0:
                    break

            list_log_data = list(
                map(
                    lambda it:
                    LogData(self.address, page, response_flag.unix_time,
                            latest_page.measurement_interval, it), page_data))

            self.__MSG(
                DEBUG,
                F'list_log_data={json.dumps(list_log_data, cls=DateTimeSupportJSONEncoder)}'
            )
            return list_log_data
        except Exception as e:
            logger.exception(f"read_env_sensor_data failed: {e}")
            raise e

    def write_measurement_interval(self,
                                   new_measurement_interval: int) -> None:
        """
        測定間隔を変更する

        Args:
            new_measurement_interval: 測定間隔(秒) 1から3600

        Returns:
            None
        """
        assert 1 <= new_measurement_interval <= 3600
        measurement_interval = OmronMeasurementInterval()
        data = measurement_interval.encode_data(new_measurement_interval)

        self.__MSG(
            DEBUG,
            F'writing MeasurementInterval(uuid={measurement_interval.shortUuid:#04x}): data={data}'
        )
        self.write_char_base(measurement_interval.serviceUuid,
                             measurement_interval.uuid, data)

    def write_time_information(self, unix_time) -> None:
        """
        環境センサーへ現在時刻を設定する

        Args:
            unix_time: 現在時刻のunixタイムスタンプ

        Returns:
            None
        """
        time_information = OmronTimeInformation()
        data = time_information.encode_data(unix_time)

        self.__MSG(
            DEBUG,
            F'writing TimeInformation(uuid={time_information.shortUuid:#04x}): data={data}'
        )
        self.write_char_base(time_information.serviceUuid,
                             time_information.uuid, data)
Beispiel #41
0
class RobotController():

    def __init__(self, address):

        self.robot = Peripheral(addr)
        print("connected")

        # keep state for keypresses
        self.pressed = {"up": False, "down": False, "right": False, "left": False, 
        "d": False, "s": False, "a": False, "c": False, "o": False, "p": False, "h": False}
        # TODO get service from robot
        # TODO get characteristic handles from service/robot
        # TODO enable notifications if using notifications

        self.sv = self.robot.getServiceByUUID(SERVICE_UUID)
        self.ch = self.sv.getCharacteristics(CHAR_UUIDS)[0]

        keyboard.hook(self.on_key_event)

    def on_key_event(self, event):
        # print key name
        print(event.name)
        # if a key unrelated to direction keys is pressed, ignore
        if event.name not in self.pressed: return
        # if a key is pressed down
        if event.event_type == keyboard.KEY_DOWN:
            # if that key is already pressed down, ignore
            if self.pressed[event.name]: return
            # set state of key to pressed
            self.pressed[event.name] = True
            # TODO write to characteristic to change direction
            num = 0
            if (self.pressed["h"]): num = 11
            elif (self.pressed["p"]): num = 22
            elif (self.pressed["o"]): num = 33
            else:
                num = int(self.pressed["c"])
                num = num * 2 + int(self.pressed["a"])
                num = num * 2 + int(self.pressed["s"])
                num = num * 2 + int(self.pressed["d"])
                num = num * 2 + int(self.pressed["up"])
                num = num * 2 + int(self.pressed["down"])
                num = num * 2 + int(self.pressed["right"])
                num = num * 2 + int(self.pressed["left"])
            self.ch.write(bytes([num]))
        else:
            # set state of key to released
            self.pressed[event.name] = False
            # TODO write to characteristic to stop moving in this direction
            num = 0
            if (self.pressed["h"]): num = 11
            elif (self.pressed["p"]): num = 22
            elif (self.pressed["o"]): num = 33
            else:
                num = int(self.pressed["c"])
                num = num * 2 + int(self.pressed["a"])
                num = num * 2 + int(self.pressed["s"])
                num = num * 2 + int(self.pressed["d"])
                num = num * 2 + int(self.pressed["up"])
                num = num * 2 + int(self.pressed["down"])
                num = num * 2 + int(self.pressed["right"])
                num = num * 2 + int(self.pressed["left"])
            self.ch.write(bytes([num]))

    def __enter__(self):
        return self
    def __exit__(self, exc_type, exc_value, traceback):
        self.robot.disconnect()