Example #1
0
    def _query_systemstate(self):
        """Query the maximum number of connections supported by this adapter
        """
        def status_filter_func(event):
            if event.command_class == 3 and event.command == 0:
                return True

            return False

        try:
            response = self._send_command(0, 6, [])
            maxconn, = unpack("<B", response.payload)
        except InternalTimeoutError:
            return False, {'reason': 'Timeout waiting for command response'}

        events = self._wait_process_events(0.5, status_filter_func,
                                           lambda x: False)

        conns = []
        for event in events:
            handle, flags, addr, addr_type, interval, timeout, lat, bond = unpack(
                "<BB6sBHHHB", event.payload)

            if flags != 0:
                conns.append(handle)

        return True, {'max_connections': maxconn, 'active_connections': conns}
Example #2
0
 def notified_header(event):
     if event.command_class == 4 and event.command == 5:
         event_handle, att_handle = unpack("<BH", event.payload[0:3])
         return event_handle == conn and att_handle == receive_header
     elif event.command_class == 3 and event.command == 4:
         event_handle, reason = unpack("<BH", event.payload)
         return event_handle == conn
Example #3
0
    def _probe_services(self, handle):
        """Probe for all primary services and characteristics in those services

        Args:
            handle (int): the connection handle to probe
        """

        code = 0x2800

        def event_filter_func(event):
            if (event.command_class == 4 and event.command == 2):
                event_handle, = unpack("B", event.payload[0:1])
                return event_handle == handle

            return False

        def end_filter_func(event):
            if (event.command_class == 4 and event.command == 1):
                event_handle, = unpack("B", event.payload[0:1])
                return event_handle == handle

            return False

        payload = struct.pack('<BHHBH', handle, 1, 0xFFFF, 2, code)

        try:
            response = self._send_command(4, 1, payload)
        except InternalTimeoutError:
            return False, {'reason': 'Timeout waiting for command response'}

        handle, result = unpack("<BH", response.payload)
        if result != 0:
            return False, None

        events = self._wait_process_events(0.5, event_filter_func,
                                           end_filter_func)
        gatt_events = [x for x in events if event_filter_func(x)]
        end_events = [x for x in events if end_filter_func(x)]

        if len(end_events) == 0:
            return False, None

        #Make sure we successfully probed the gatt table
        end_event = end_events[0]
        _, result, _ = unpack("<BHH", end_event.payload)
        if result != 0:
            self._logger.warn(
                "Error enumerating GATT table, protocol error code = %d (0x%X)"
                % (result, result))
            return False, None

        services = {}
        for event in gatt_events:
            process_gatt_service(services, event)

        return True, {'services': services}
Example #4
0
    def _connect(self, address):
        """Connect to a device given its uuid
        """

        latency = 0
        conn_interval_min = 6
        conn_interval_max = 100
        timeout = 1.0

        try:
            #Allow passing either a binary address or a hex string
            if isinstance(address, basestring) and len(address) > 6:
                address = address.replace(':', '')
                address = bytes(bytearray.fromhex(address)[::-1])
        except ValueError:
            return False, None

        #Allow simple determination of whether a device has a public or private address
        #This is not foolproof
        private_bits = bytearray(address)[-1] >> 6
        if private_bits == 0b11:
            address_type = 1
        else:
            address_type = 0

        payload = struct.pack("<6sBHHHH", address, address_type,
                              conn_interval_min, conn_interval_max,
                              int(timeout * 100.0), latency)
        response = self._send_command(6, 3, payload)

        result, handle = unpack("<HB", response.payload)
        if result != 0:
            return False, None

        #Now wait for the connection event that says we connected or kill the attempt after timeout
        def conn_succeeded(event):
            if event.command_class == 3 and event.command == 0:
                event_handle, = unpack("B", event.payload[0:1])
                return event_handle == handle

        #FIXME Hardcoded timeout
        events = self._wait_process_events(4.0, lambda x: False,
                                           conn_succeeded)
        if len(events) != 1:
            self._stop_scan()
            return False, None

        handle, _, addr, _, interval, timeout, latency, _ = unpack(
            "<BB6sBHHHB", events[0].payload)
        formatted_addr = ":".join(["%02X" % x for x in bytearray(addr)])
        self._logger.info(
            'Connected to device %s with interval=%d, timeout=%d, latency=%d',
            formatted_addr, interval, timeout, latency)

        connection = {"handle": handle}
        return True, connection
Example #5
0
    def _write_handle(self, conn, handle, ack, value, timeout=1.0):
        """Write to a BLE device characteristic by its handle

        Args:
            conn (int): The connection handle for the device we should read from
            handle (int): The characteristics handle we should read
            ack (bool): Should this be an acknowledges write or unacknowledged
            timeout (float): How long to wait before failing
            value (bytearray): The value that we should write
        """

        conn_handle = conn
        char_handle = handle

        def write_handle_acked(event):
            if event.command_class == 4 and event.command == 1:
                conn, _, char = unpack("<BHH", event.payload)

                return conn_handle == conn and char_handle == char

        data_len = len(value)
        if data_len > 20:
            return False, {'reason': 'Data too long to write'}

        payload = struct.pack("<BHB%ds" % data_len, conn_handle, char_handle, data_len, value)

        try:
            if ack:
                response = self._send_command(4, 5, payload)
            else:
                response = self._send_command(4, 6, payload)
        except InternalTimeoutError:
            return False, {'reason': 'Timeout waiting for response to command in _write_handle'}

        _, result = unpack("<BH", response.payload)
        if result != 0:
            return False, {'reason': 'Error writing to handle', 'error_code': result}

        if ack:
            events = self._wait_process_events(timeout, lambda x: False, write_handle_acked)
            self._logger.info("Num events in _write_handle: %d", len(events))
            if len(events) == 0:
                return False, {'reason': 'Timeout waiting for acknowledge on write'}

            _, result, _ = unpack("<BHH", events[0].payload)
            if result != 0:
                return False, {'reason': 'Error received during write to handle', 'error_code': result}

        return True, None
Example #6
0
    def _disconnect(self, handle):
        """Disconnect from a device that we have previously connected to
        """

        payload = struct.pack('<B', handle)
        response = self._send_command(3, 0, payload)

        conn_handle, result = unpack("<BH", response.payload)
        if result != 0:
            self._logger.info("Disconnection failed result=%d", result)
            return False, None

        assert conn_handle == handle

        def disconnect_succeeded(event):
            if event.command_class == 3 and event.command == 4:
                event_handle, = unpack("B", event.payload[0:1])
                return event_handle == handle

            return False

        #FIXME Hardcoded timeout
        events = self._wait_process_events(3.0, lambda x: False,
                                           disconnect_succeeded)
        if len(events) != 1:
            return False, None

        return True, {'handle': handle}
Example #7
0
def process_read_handle(event):
    length = len(event.payload) - 5
    conn, att_handle, att_type, act_length, value = unpack("<BHBB%ds" % length, event.payload)

    assert act_length == length

    return att_type, bytearray(value)
Example #8
0
    def _send_notification(self, handle, value):
        """Send a notification to all connected clients on a characteristic

        Args:
            handle (int): The handle we wish to notify on
            value (bytearray): The value we wish to send
        """

        value_len = len(value)
        value = bytes(value)

        payload = struct.pack("<BHB%ds" % value_len, 0xFF, handle, value_len,
                              value)

        response = self._send_command(2, 5, payload)
        result, = unpack("<H", response.payload)
        if result != 0:
            return False, {
                'reason': 'Error code from BLED112 notifying a value',
                'code': result,
                'handle': handle,
                'value': value
            }

        return True, None
Example #9
0
    def _read_handle(self, conn, handle, timeout=1.0):
        conn_handle = conn
        payload = struct.pack("<BH", conn_handle, handle)

        try:
            response = self._send_command(4, 4, payload)
            ignored_handle, result = unpack("<BH", response.payload)
        except InternalTimeoutError:
            return False, {'reason': 'Timeout sending read handle command'}

        if result != 0:
            self._logger.warn("Error reading handle %d, result=%d" % (handle, result))
            return False, None

        def handle_value_func(event):
            if (event.command_class == 4 and event.command == 5):
                event_handle, = unpack("B", event.payload[0:1])
                return event_handle == conn_handle

        def handle_error_func(event):
            if (event.command_class == 4 and event.command == 1):
                event_handle, = unpack("B", event.payload[0:1])
                return event_handle == conn_handle

        events = self._wait_process_events(5.0, lambda x: False, lambda x: handle_value_func(x) or handle_error_func(x))
        if len(events) != 1:
            return False, None

        if handle_error_func(events[0]):
            return False, None

        handle_event = events[0]
        handle_type, handle_data = process_read_handle(handle_event)

        return True, {'type': handle_type, 'data': handle_data}
Example #10
0
def parse_characteristic_declaration(value):
    length = len(value)

    if length == 5:
        uuid_len = 2
    elif length == 19:
        uuid_len = 16
    else:
        raise ValueError(
            "Value has improper length for ble characteristic definition, length was %d"
            % len(value))

    propval, handle, uuid = unpack("<BH%ds" % uuid_len, value)

    #Process the properties
    properties = CharacteristicProperties(bool(propval & 0x1),
                                          bool(propval & 0x2),
                                          bool(propval & 0x4),
                                          bool(propval & 0x8),
                                          bool(propval & 0x10),
                                          bool(propval & 0x20),
                                          bool(propval & 0x40),
                                          bool(propval & 0x80))

    uuid = process_uuid(uuid)
    char = {}
    char['uuid'] = uuid
    char['properties'] = properties
    char['handle'] = handle

    return char
    def ReportLength(cls, header):
        """Given a header of HeaderLength bytes, calculate the size of this report"""

        first_word, = unpack("<L", header[:4])

        length = (first_word >> 8)
        return length
Example #12
0
    def _probe_characteristics(self, conn, services, timeout=5.0):
        """Probe gatt services for all associated characteristics in a BLE device

        Args:
            conn (int): the connection handle to probe
            services (dict): a dictionary of services produced by probe_services()
            timeout (float): the maximum number of seconds to spend in any single task
        """

        for service in viewvalues(services):
            success, result = self._enumerate_handles(conn,
                                                      service['start_handle'],
                                                      service['end_handle'])

            if not success:
                return False, None

            attributes = result['attributes']

            service['characteristics'] = {}

            last_char = None
            for handle, attribute in viewitems(attributes):
                if attribute['uuid'].hex[-4:] == '0328':
                    success, result = self._read_handle(conn, handle, timeout)
                    if not success:
                        return False, None

                    value = result['data']
                    char = parse_characteristic_declaration(value)
                    service['characteristics'][char['uuid']] = char
                    last_char = char
                elif attribute['uuid'].hex[-4:] == '0229':
                    if last_char is None:
                        return False, None

                    success, result = self._read_handle(conn, handle, timeout)
                    if not success:
                        return False, None

                    value = result['data']
                    assert len(value) == 2
                    value, = unpack("<H", value)

                    last_char['client_configuration'] = {
                        'handle': handle,
                        'value': value
                    }

        return True, {'services': services}
Example #13
0
    def _process_scan_event(self, response):
        """Parse the BLE advertisement packet.

        If it's an IOTile device, parse and add to the scanned devices. Then,
        parse advertisement and determine if it matches V1 or V2.  There are
        two supported type of advertisements:

        v1: There is both an advertisement and a scan response (if active scanning
            is enabled).
        v2: There is only an advertisement and no scan response.
        """

        payload = response.payload
        length = len(payload) - 10

        if length < 0:
            return

        rssi, packet_type, sender, _addr_type, _bond, data = unpack(
            "<bB6sBB%ds" % length, payload)
        string_address = ':'.join(
            [format(x, "02X") for x in bytearray(sender[::-1])])

        # Scan data is prepended with a length
        if len(data) > 0:
            data = bytearray(data[1:])
        else:
            data = bytearray([])

        self._scan_event_count += 1

        # If this is an advertisement packet, see if its an IOTile device
        # packet_type = 4 is scan_response, 0, 2 and 6 are advertisements
        if packet_type in (0, 2, 6):
            if len(data) != 31:
                return

            if data[22] == 0xFF and data[23] == 0xC0 and data[24] == 0x3:
                self._v1_scan_count += 1
                self._parse_v1_advertisement(rssi, string_address, data)
            elif data[3] == 27 and data[4] == 0x16 and data[
                    5] == 0xdd and data[6] == 0xfd:
                self._v2_scan_count += 1
                self._parse_v2_advertisement(rssi, string_address, data)
            else:
                pass  # This just means the advertisement was from a non-IOTile device
        elif packet_type == 4:
            self._v1_scan_response_count += 1
            self._parse_v1_scan_response(string_address, data)
Example #14
0
def process_gatt_service(services, event):
    """Process a BGAPI event containing a GATT service description and add it to a dictionary

    Args:
        services (dict): A dictionary of discovered services that is updated with this event
        event (BGAPIPacket): An event containing a GATT service

    """

    length = len(event.payload) - 5

    handle, start, end, uuid = unpack('<BHH%ds' % length, event.payload)

    uuid = process_uuid(uuid)
    services[uuid] = {'uuid_raw': uuid, 'start_handle': start, 'end_handle': end}
Example #15
0
    def _set_advertising_data(self, packet_type, data):
        """Set the advertising data for advertisements sent out by this bled112

        Args:
            packet_type (int): 0 for advertisement, 1 for scan response
            data (bytearray): the data to set
        """

        payload = struct.pack("<BB%ss" % (len(data)), packet_type, len(data), bytes(data))
        response = self._send_command(6, 9, payload)

        result, = unpack("<H", response.payload)
        if result != 0:
            return False, {'reason': 'Error code from BLED112 setting advertising data', 'code': result}

        return True, None
Example #16
0
    def _parse_v2_advertisement(self, rssi, sender, data):
        """ Parse the IOTile Specific advertisement packet"""

        if len(data) != 31:
            return None, None, None, None, None, None, None

        # We have already verified that the device is an IOTile device
        # by checking its service data uuid in _process_scan_event so
        # here we just parse out the required information

        device_id, reboot_low, reboot_high_packed, flags, timestamp, \
        battery, counter_packed, broadcast_stream_packed, broadcast_value, \
        _mac = unpack("<LHBBLBBHLL", data[7:])

        reboots = (reboot_high_packed & 0xF) << 16 | reboot_low
        counter = counter_packed & ((1 << 5) - 1)
        broadcast_multiplex = counter_packed >> 5
        broadcast_toggle = broadcast_stream_packed >> 15
        broadcast_stream = broadcast_stream_packed & ((1 << 15) - 1)

        # Flags for version 2 are:
        #   bit 0: Has pending data to stream
        #   bit 1: Low voltage indication
        #   bit 2: User connected
        #   bit 3: Broadcast data is encrypted
        #   bit 4: Encryption key is device key
        #   bit 5: Encryption key is user key
        #   bit 6: broadcast data is time synchronized to avoid leaking
        #   information about when it changes

        self._device_scan_counts.setdefault(device_id, {'v1': 0, 'v2': 0})['v2'] += 1

        info = {'connection_string': sender,
                'uuid': device_id,
                'pending_data': bool(flags & (1 << 0)),
                'low_voltage': bool(flags & (1 << 1)),
                'user_connected': bool(flags & (1 << 2)),
                'signal_strength': rssi,
                'reboot_counter': reboots,
                'sequence': counter,
                'broadcast_toggle': broadcast_toggle,
                'timestamp': timestamp,
                'battery': battery / 32.0,
                'advertising_version':2}

        return info, timestamp, broadcast_stream, broadcast_value, \
            broadcast_toggle, counter, broadcast_multiplex
Example #17
0
    def rpc(self, feature, cmd, *args, **kw):
        """
        Send an RPC call to this module, interpret the return value
        according to the result_type kw argument.  Unless raise keyword
        is passed with value False, raise an RPCException if the command
        is not successful.
        """

        if 'arg_format' in kw:
            packed_args = struct.pack("<{}".format(kw['arg_format']), *args)
            status, payload = self.stream.send_rpc(self.addr, feature, cmd,
                                                   packed_args, **kw)
        else:
            status, payload = self.stream.send_rpc(self.addr, feature, cmd,
                                                   *args, **kw)

        unpack_flag = False
        if "result_type" in kw:
            res_type = kw['result_type']
        elif "result_format" in kw:
            unpack_flag = True
            res_type = (0, True)
        else:
            res_type = (0, False)

        try:
            res = self._parse_rpc_result(status,
                                         payload,
                                         *res_type,
                                         command=(feature << 8) | cmd)
            if unpack_flag:
                return unpack("<%s" % kw["result_format"], res['buffer'])

            return res
        except ModuleBusyError:
            pass

        if "retries" not in kw:
            kw['retries'] = 10

        #Sleep 100 ms and try again unless we've exhausted our retry attempts
        if kw["retries"] > 0:
            kw['retries'] -= 1

            sleep(0.1)
            return self.rpc(feature, cmd, *args, **kw)
Example #18
0
    def _set_mode(self, discover_mode, connect_mode):
        """Set the mode of the BLED112, used to enable and disable advertising

        To enable advertising, use 4, 2.
        To disable advertising use 0, 0.

        Args:
            discover_mode (int): The discoverability mode, 0 for off, 4 for on (user data)
            connect_mode (int): The connectability mode, 0 for of, 2 for undirected connectable
        """

        payload = struct.pack("<BB", discover_mode, connect_mode)
        response = self._send_command(6, 1, payload)

        result, = unpack("<H", response.payload)
        if result != 0:
            return False, {'reason': 'Error code from BLED112 setting mode', 'code': result}

        return True, None
Example #19
0
    def _parse_v1_advertisement(self, rssi, sender, advert):
        if len(advert) != 31:
            return

        # Make sure the scan data comes back with an incomplete UUID list
        if advert[3] != 17 or advert[4] != 6:
            return

        # Make sure the uuid is our tilebus UUID
        if advert[5:21] == TileBusService.bytes_le:
            # Now parse out the manufacturer specific data
            manu_data = advert[21:]

            _length, _datatype, _manu_id, device_uuid, flags = unpack(
                "<BBHLH", manu_data)

            self._device_scan_counts.setdefault(device_uuid, {
                'v1': 0,
                'v2': 0
            })['v1'] += 1

            # Flags for version 1 are:
            #   bit 0: whether we have pending data
            #   bit 1: whether we are in a low voltage state
            #   bit 2: whether another user is connected
            #   bit 3: whether we support robust reports
            #   bit 4: whether we allow fast writes
            info = {
                'connection_string': sender,
                'uuid': device_uuid,
                'pending_data': bool(flags & (1 << 0)),
                'low_voltage': bool(flags & (1 << 1)),
                'user_connected': bool(flags & (1 << 2)),
                'signal_strength': rssi,
                'advertising_version': 1
            }

            if self._active_scan:
                self.partial_scan_responses[sender] = info
            else:
                self._trigger_callback('on_scan', self.id, info,
                                       self.ExpirationTime)
Example #20
0
    def _parse_scan_response(self, response):
        """
        Parse the BLE advertisement packet. If it's an IOTile device, parse and add to the scanned devices.
        Then, parse advertisement and determine if it matches V1 or V2.
        """
        payload = response.payload
        length = len(payload) - 10

        if length < 0:
            return

        rssi, packet_type, sender, addr_type, bond, data = unpack(
            "<bB6sBB%ds" % length, payload)

        # Scan data is prepended with a length
        if len(data) > 0:
            scan_data = bytearray(data[1:])
        else:
            scan_data = bytearray([])

        # If this is an advertisement response, see if its an IOTile device
        if packet_type in (0, 6):

            if (len(scan_data) > 4 and scan_data[3] == 17
                    and scan_data[4] == 6):

                self._parse_v1_scan_response(response)

            # See if the data length is 27 (0x1B), service = 0x16, ArchUUID = 0x03C0
            elif (len(scan_data) > 6 and scan_data[3] == 27
                  and scan_data[4] == 0x16 and scan_data[5] == 0xc0
                  and scan_data[6] == 0x03):

                self._parse_v2_scan_response(response)

            else:
                return

        else:
            self._parse_v1_scan_response(response)

        return
    def decode(self):
        """Decode this report into a single reading
        """

        fmt, _, stream, uuid, sent_timestamp, reading_timestamp, reading_value = unpack(
            "<BBHLLLL", self.raw_report)
        assert fmt == 0

        #Estimate the UTC time when this device was turned on
        time_base = self.received_time - datetime.timedelta(
            seconds=sent_timestamp)

        reading = IOTileReading(reading_timestamp,
                                stream,
                                reading_value,
                                time_base=time_base)
        self.origin = uuid
        self.sent_timestamp = sent_timestamp

        return [reading], []
Example #22
0
    def _parse_scan_response(self, response):
        """Parse the IOTile specific data structures in the BLE advertisement packets and add the device to our list of scanned devices
           Parse advertisement and determine if it matches V1 or V2.
        """
        payload = response.payload
        length = len(payload) - 10

        if length < 0:
            return

        rssi, packet_type, sender, addr_type, bond, data = unpack("<bB6sBB%ds" % length, payload)

        #Scan data is prepended with a length
        if len(data) > 0:
            scan_data = bytearray(data[1:])
        else:
            scan_data = bytearray([])

        #If this is an advertisement response, see if its an IOTile device
        if packet_type == 0 or packet_type == 6:

            if (scan_data[3] == 17 and
                scan_data[4] == 6):

                self._parse_v1_scan_response(response)

            # See if the data length is 27 (0x1B), service = 0x16, ArchUUID = 0x03C0
            elif (scan_data[3] == 27 and
                scan_data[4] == 0x16 and
                scan_data[5] == 0xc0 and
                scan_data[6] == 0x03):

                self._parse_v2_scan_response(response)

            else:
                self._logger.error("Invalid scan data: {0}".format(binascii.hexlify(scan_data)))

        else:
            self._parse_v1_scan_response(response)

        return
Example #23
0
    def _enumerate_handles(self, conn, start_handle, end_handle, timeout=1.0):
        conn_handle = conn

        def event_filter_func(event):
            if event.command_class == 4 and event.command == 4:
                event_handle, = unpack("B", event.payload[0:1])
                return event_handle == conn_handle

            return False

        def end_filter_func(event):
            if event.command_class == 4 and event.command == 1:
                event_handle, = unpack("B", event.payload[0:1])
                return event_handle == conn_handle

            return False

        payload = struct.pack("<BHH", conn_handle, start_handle, end_handle)

        try:
            response = self._send_command(4, 3, payload)
            handle, result = unpack("<BH", response.payload)
        except InternalTimeoutError:
            return False, {'reason': "Timeout enumerating handles"}

        if result != 0:
            return False, None

        events = self._wait_process_events(timeout, event_filter_func,
                                           end_filter_func)
        handle_events = [x for x in events if event_filter_func(x)]

        attrs = {}
        for event in handle_events:
            process_attribute(attrs, event)

        return True, {'attributes': attrs}
Example #24
0
def process_attribute(attributes, event):
    length = len(event.payload) - 3
    handle, chrhandle, uuid = unpack("<BH%ds" % length, event.payload)
    uuid = process_uuid(uuid)

    attributes[chrhandle] = {'uuid': uuid}
Example #25
0
 def handle_error_func(event):
     if (event.command_class == 4 and event.command == 1):
         event_handle, = unpack("B", event.payload[0:1])
         return event_handle == conn_handle
Example #26
0
    def decode(self):
        """Decode this report into a list of readings
        """

        fmt, len_low, len_high, device_id, report_id, sent_timestamp, signature_flags, origin_streamer, streamer_selector = unpack("<BBHLLLBBH", self.raw_report[:20])

        assert fmt == 1
        length = (len_high << 8) | len_low

        self.origin = device_id
        self.report_id = report_id
        self.sent_timestamp = sent_timestamp
        self.origin_streamer = origin_streamer
        self.streamer_selector = streamer_selector
        self.signature_flags = signature_flags

        assert len(self.raw_report) == length

        remaining = self.raw_report[20:]
        assert len(remaining) >= 24
        readings = remaining[:-24]
        footer = remaining[-24:]

        lowest_id, highest_id, signature = unpack("<LL16s", footer)
        signature = bytearray(signature)

        self.lowest_id = lowest_id
        self.highest_id = highest_id
        self.signature = signature

        signed_data = self.raw_report[:-16]
        signer = ChainedAuthProvider()

        if signature_flags == AuthProvider.NoKey:
            self.encrypted = False
        else:
            self.encrypted = True

        try:
            verification = signer.verify_report(device_id, signature_flags, signed_data, signature, report_id=report_id, sent_timestamp=sent_timestamp)
            self.verified = verification['verified']
        except NotFoundError:
            self.verified = False

        # If we were not able to verify the report, do not try to parse or decrypt it since we
        # can't guarantee who it came from.
        if not self.verified:
            return [], []

        # If the report is encrypted, try to decrypt it before parsing the readings
        if self.encrypted:
            try:
                result = signer.decrypt_report(device_id, signature_flags, readings, report_id=report_id, sent_timestamp=sent_timestamp)
                readings = result['data']
            except NotFoundError:
                return [], []

        # Now parse all of the readings
        # Make sure this report has an integer number of readings
        assert (len(readings) % 16) == 0

        time_base = self.received_time - datetime.timedelta(seconds=sent_timestamp)
        parsed_readings = []

        for i in range(0, len(readings), 16):
            reading = readings[i:i+16]
            stream, _, reading_id, timestamp, value = unpack("<HHLLL", reading)

            parsed = IOTileReading(timestamp, stream, value, time_base=time_base, reading_id=reading_id)
            parsed_readings.append(parsed)

        return parsed_readings, []
Example #27
0
        def disconnect_succeeded(event):
            if event.command_class == 3 and event.command == 4:
                event_handle, = unpack("B", event.payload[0:1])
                return event_handle == handle

            return False
Example #28
0
        def write_handle_acked(event):
            if event.command_class == 4 and event.command == 1:
                conn, _, char = unpack("<BHH", event.payload)

                return conn_handle == conn and char_handle == char
Example #29
0
 def conn_succeeded(event):
     if event.command_class == 3 and event.command == 0:
         event_handle, = unpack("B", event.payload[0:1])
         return event_handle == handle
Example #30
0
 def notified_payload(event):
     if event.command_class == 4 and event.command == 5:
         event_handle, att_handle = unpack("<BH", event.payload[0:3])
         return event_handle == conn and att_handle == receive_payload