Exemplo n.º 1
0
class TestParseZigBeeIOData(unittest.TestCase):
    """
    Test parsing ZigBee specific IO data
    """

    def setUp(self):
        self.zigbee = ZigBee(None)

    def test_parse_dio_adc(self):
        data = b"\x01\x08\x00\x0e\x08\x00\x00\x00\x02P\x02\x06"
        expected_results = [{"dio-11": True, "adc-1": 0, "adc-2": 592, "adc-3": 518}]
        results = self.zigbee._parse_samples(data)
        self.assertEqual(results, expected_results)

    def test_parse_dio_adc_supply_voltage_not_clamped(self):
        """
        When bit 7 on the ADC mask is set, the supply voltage is included
        in the ADC I/O sample section. This sample may exceed 10 bits of
        precision, even though all other ADC channels are limited to a
        range of 0-1.2v with 10 bits of precision. I assume that a voltage
        divider and the firmware is used internally to compute the actual
        Vcc voltage.
        
        Therefore, the I/O sampling routine must not clamp this ADC
        channel to 10 bits of precision.
        """
        data = b"\x01\x00\x00\x80\x0D\x18"
        expected_results = [{"adc-7": 0xD18}]
        # import pdb
        # pdb.set_trace()
        results = self.zigbee._parse_samples(data)
        self.assertEqual(results, expected_results)
Exemplo n.º 2
0
class TestZigBee(unittest.TestCase):
    """
    Tests ZigBee-specific features
    """

    def setUp(self):
        self.zigbee = ZigBee(None)

    def test_null_terminated_field(self):
        """
        Packets with null-terminated fields
        should be properly parsed
        """
        expected_data = '\x01\x02\x03\x04'
        terminator = '\x00'
        node_identifier = '\x95' + '\x00' * 21 + expected_data + terminator + '\x00' * 8

        data = self.zigbee._split_response(node_identifier)

        self.assertEqual(data['node_id'], expected_data)

    def test_split_node_identification_identifier(self):
            data = '\x95\x00\x13\xa2\x00\x40\x52\x2b\xaa\x7d\x84\x02\x7d\x84\x00\x13\xa2\x00\x40\x52\x2b\xaa\x20\x00\xff\xfe\x01\x01\xc1\x05\x10\x1e'
            info = self.zigbee._split_response(data)
            expected_info = {
                'id': 'node_id_indicator',
                'sender_addr_long': '\x00\x13\xa2\x00\x40\x52\x2b\xaa',
                'sender_addr': '\x7d\x84',
                'options': '\x02',
                'source_addr': '\x7d\x84',
                'source_addr_long': '\x00\x13\xa2\x00\x40\x52\x2b\xaa',
                'node_id': ' ',
                'parent_source_addr': '\xff\xfe',
                'device_type': '\x01',
                'source_event': '\x01',
                'digi_profile_id': '\xc1\x05',
                'manufacturer_id': '\x10\x1e',
            }
            
            self.assertEqual(info, expected_info)
            
    def test_split_node_identification_identifier2(self):
            data = '\x95\x00\x13\xa2\x00\x40\x52\x2b\xaa\x7d\x84\x02\x7d\x84\x00\x13\xa2\x00\x40\x52\x2b\xaaCoordinator\x00\xff\xfe\x01\x01\xc1\x05\x10\x1e'
            info = self.zigbee._split_response(data)
            expected_info = {
                'id': 'node_id_indicator',
                'sender_addr_long': '\x00\x13\xa2\x00\x40\x52\x2b\xaa',
                'sender_addr': '\x7d\x84',
                'options': '\x02',
                'source_addr': '\x7d\x84',
                'source_addr_long': '\x00\x13\xa2\x00\x40\x52\x2b\xaa',
                'node_id': 'Coordinator',
                'parent_source_addr': '\xff\xfe',
                'device_type': '\x01',
                'source_event': '\x01',
                'digi_profile_id': '\xc1\x05',
                'manufacturer_id': '\x10\x1e',
            }
            
            self.assertEqual(info, expected_info)
Exemplo n.º 3
0
class TestZigBee(unittest.TestCase):
    """
    Tests ZigBee-specific features
    """
    def setUp(self):
        self.zigbee = ZigBee(None)

    def test_null_terminated_field(self):
        """
        Packets with null-terminated fields
        should be properly parsed
        """
        expected_data = '\x01\x02\x03\x04'
        terminator = '\x00'
        node_identifier = '\x95' + '\x00' * 21 + expected_data + terminator + '\x00' * 8

        data = self.zigbee._split_response(node_identifier)

        self.assertEqual(data['node_id'], expected_data)

    def test_split_node_identification_identifier(self):
        data = '\x95\x00\x13\xa2\x00\x40\x52\x2b\xaa\x7d\x84\x02\x7d\x84\x00\x13\xa2\x00\x40\x52\x2b\xaa\x20\x00\xff\xfe\x01\x01\xc1\x05\x10\x1e'
        info = self.zigbee._split_response(data)
        expected_info = {
            'id': 'node_id_indicator',
            'sender_addr_long': '\x00\x13\xa2\x00\x40\x52\x2b\xaa',
            'sender_addr': '\x7d\x84',
            'options': '\x02',
            'source_addr': '\x7d\x84',
            'source_addr_long': '\x00\x13\xa2\x00\x40\x52\x2b\xaa',
            'node_id': ' ',
            'parent_source_addr': '\xff\xfe',
            'device_type': '\x01',
            'source_event': '\x01',
            'digi_profile_id': '\xc1\x05',
            'manufacturer_id': '\x10\x1e',
        }

        self.assertEqual(info, expected_info)

    def test_split_node_identification_identifier2(self):
        data = '\x95\x00\x13\xa2\x00\x40\x52\x2b\xaa\x7d\x84\x02\x7d\x84\x00\x13\xa2\x00\x40\x52\x2b\xaaCoordinator\x00\xff\xfe\x01\x01\xc1\x05\x10\x1e'
        info = self.zigbee._split_response(data)
        expected_info = {
            'id': 'node_id_indicator',
            'sender_addr_long': '\x00\x13\xa2\x00\x40\x52\x2b\xaa',
            'sender_addr': '\x7d\x84',
            'options': '\x02',
            'source_addr': '\x7d\x84',
            'source_addr_long': '\x00\x13\xa2\x00\x40\x52\x2b\xaa',
            'node_id': 'Coordinator',
            'parent_source_addr': '\xff\xfe',
            'device_type': '\x01',
            'source_event': '\x01',
            'digi_profile_id': '\xc1\x05',
            'manufacturer_id': '\x10\x1e',
        }

        self.assertEqual(info, expected_info)
class TestParseZigBeeIOData(unittest.TestCase):
    """
    Test parsing ZigBee specific IO data
    """

    def setUp(self):
        self.zigbee = ZigBee(None)

    def test_parse_dio_adc(self):
            data = b'\x01\x08\x00\x0e\x08\x00\x00\x00\x02P\x02\x06'
            expected_results = [{'dio-11': True,
                                 'adc-1': 0,
                                 'adc-2': 592,
                                 'adc-3': 518}]
            results = self.zigbee._parse_samples(data)
            self.assertEqual(results, expected_results)
            
    def test_parse_samples_ticket_44(self):
            """
            This example is from ticket 44 on Google Code:
            https://code.google.com/p/python-xbee/issues/detail?id=44
            
            The author claims the given data is generated by an
            Xbee Pro 900HP module, but I could only find a definition
            for packet with a response type of 0x92 in the XBee ZB
            specification.
            """
    
            data = (b'\x01' +        # Number of samples
                   b'\x10\x00' +     # Digital I/O mask (CD/DIO12 enabled)
                   b'\x0E' +         # ADC 1,2,3 enabled
                   b'\x10\x00' +     # DIO12 is high
                   b'\x03\xA4' +     # ADC1 = 932
                   b'\x01\x31' +     # ADC2 = 305
                   b'\x03\x31')      # ADC3 = 817
            expected_results = [{'dio-12': True,
                                 'adc-1': 932,
                                 'adc-2': 305,
                                 'adc-3': 817}]
            results = self.zigbee._parse_samples(data)
            self.assertEqual(results, expected_results)
            
    def test_parse_dio_adc_supply_voltage_not_clamped(self):
        """
        When bit 7 on the ADC mask is set, the supply voltage is included
        in the ADC I/O sample section. This sample may exceed 10 bits of
        precision, even though all other ADC channels are limited to a
        range of 0-1.2v with 10 bits of precision. I assume that a voltage
        divider and the firmware is used internally to compute the actual
        Vcc voltage.
        
        Therefore, the I/O sampling routine must not clamp this ADC
        channel to 10 bits of precision.
        """ 
        data = b'\x01\x00\x00\x80\x0D\x18'
        expected_results = [{'adc-7':0xD18}]
        results = self.zigbee._parse_samples(data)
        self.assertEqual(results, expected_results)
Exemplo n.º 5
0
 def test_send(self):
     """
     Test send() with AT command.
     """
     device = Serial()
     xbee = ZigBee(device)
     xbee.send('at', command='MY')
     result = device.get_data_written()
     expected = b'~\x00\x04\x08\x01MYP'
     self.assertEqual(result, expected)
Exemplo n.º 6
0
    def reconnect_xbee(self):
    #search for available ports
        port_to_connect = ''
        while port_to_connect == '':

            #detect platform and format port names
            if _platform.startswith('win'):
                ports = ['COM%s' % (i + 1) for i in range(256)]
            elif _platform.startswith('linux'):
                # this excludes your current terminal "/dev/tty"
                ports = glob.glob('/dev/ttyUSB*')
            else:
                raise EnvironmentError('Unsupported platform: ' + _platform)

            ports_avail = []

            #loop through all possible ports and try to connect
            for port in ports:
                try:
                    s = serial.Serial(port)
                    s.close()
                    ports_avail.append(port)
                except (OSError, serial.SerialException):
                    pass

            if len(ports_avail) ==1:
                port_to_connect = ports_avail[0]

            elif len(ports_avail)==0:
                #No Serial port found, continue looping.
                print( "No serial port detected. Trying again...")
                time.sleep(1)

            elif len(ports_avail)>1:
                #Multiple serial ports detected. Get user input to decide which one to connect to
                #com_input = raw_input("Multiple serial ports available. Which serial port do you want? \n"+str(self.ports_avail)+":").upper();
                if self.default_serial == None:
                    raise EnvironmentError('Incorrect command line parameters. If there are multiple serial devices, indicate what port you want to be used using --serialport')
                elif self.default_serial.upper() in ports_avail:
                    port_to_connect = self.default_serial.upper()
                else:
                    raise EnvironmentError('Incorrect command line parameters. Serial port is not known as a valid port. Valid ports are:'+ str(ports_avail))

        #connect to xbee or uart
        ser = serial.Serial(port_to_connect, 115200)
        if self.uart_connection:
            self.xbee = UARTConnection(ser)
        else:
            self.xbee = ZigBee(ser)

        print('xbee connected to port ' + port_to_connect)
        return self
Exemplo n.º 7
0
    def reconnect_xbee(self):
    #search for available ports
        port_to_connect = ''
        while port_to_connect == '': 

            #detect platform and format port names
            if _platform.startswith('win'):
                ports = ['COM%s' % (i + 1) for i in range(256)]
            elif _platform.startswith('linux'):
                # this excludes your current terminal "/dev/tty"
                ports = glob.glob('/dev/ttyUSB*')
            else:
                raise EnvironmentError('Unsupported platform: ' + _platform)
        
            ports_avail = []
            
            #loop through all possible ports and try to connect
            for port in ports:
                try:
                    s = serial.Serial(port)
                    s.close()
                    ports_avail.append(port)
                except (OSError, serial.SerialException):
                    pass
            
            if len(ports_avail) ==1:
                port_to_connect = ports_avail[0]
                
            elif len(ports_avail)==0:
                #No Serial port found, continue looping.
                print( "No serial port detected. Trying again...")
                time.sleep(1)

            elif len(ports_avail)>1:
                #Multiple serial ports detected. Get user input to decide which one to connect to
                #com_input = raw_input("Multiple serial ports available. Which serial port do you want? \n"+str(self.ports_avail)+":").upper();
                if self.default_serial == None:
                    raise EnvironmentError('Incorrect command line parameters. If there are multiple serial devices, indicate what port you want to be used using --serialport')
                elif self.default_serial.upper() in ports_avail:
                    port_to_connect = self.default_serial.upper()
                else:
                    raise EnvironmentError('Incorrect command line parameters. Serial port is not known as a valid port. Valid ports are:'+ str(ports_avail))

        #connect to xbee
        self.xbee = ZigBee(serial.Serial(port_to_connect, 115200))
        print('xbee connected to port ' + port_to_connect)
        return self
Exemplo n.º 8
0
 def __enter__(self):
     if _platform == "linux" or _platform == "linux2":
         self.ser = serial.Serial('/dev/ttyUSB0', 38400)
     elif _platform == "win32":
         self.ser = serial.Serial('COM5', 38400)
     self.xbee = ZigBee(self.ser)
     print 'xbee created/initialized'
     return self
Exemplo n.º 9
0
class TestParseZigBeeIOData(unittest.TestCase):
    """
    Test parsing ZigBee specific IO data
    """
    def setUp(self):
        self.zigbee = ZigBee(None)

    def test_parse_dio_adc(self):
        data = b'\x01\x08\x00\x0e\x08\x00\x00\x00\x02P\x02\x06'
        expected_results = [{
            'dio-11': True,
            'adc-1': 0,
            'adc-2': 592,
            'adc-3': 518
        }]
        results = self.zigbee._parse_samples(data)
        self.assertEqual(results, expected_results)

    def test_parse_dio_adc_supply_voltage_not_clamped(self):
        """
        When bit 7 on the ADC mask is set, the supply voltage is included
        in the ADC I/O sample section. This sample may exceed 10 bits of
        precision, even though all other ADC channels are limited to a
        range of 0-1.2v with 10 bits of precision. I assume that a voltage
        divider and the firmware is used internally to compute the actual
        Vcc voltage.
        
        Therefore, the I/O sampling routine must not clamp this ADC
        channel to 10 bits of precision.
        """
        data = b'\x01\x00\x00\x80\x0D\x18'
        expected_results = [{'adc-7': 0xD18}]
        #import pdb
        #pdb.set_trace()
        results = self.zigbee._parse_samples(data)
        self.assertEqual(results, expected_results)
Exemplo n.º 10
0
class TestParseZigBeeIOData(unittest.TestCase):
    """
    Test parsing ZigBee specific IO data
    """

    def setUp(self):
        self.zigbee = ZigBee(None)

    def test_parse_dio_adc(self):
            data = b'\x01\x08\x00\x0e\x08\x00\x00\x00\x02P\x02\x06'
            expected_results = [{'dio-11': True,
                                 'adc-1': 0,
                                 'adc-2': 592,
                                 'adc-3': 518}]
            results = self.zigbee._parse_samples(data)
            self.assertEqual(results, expected_results)
Exemplo n.º 11
0
class TestParseZigBeeIOData(unittest.TestCase):
    """
    Test parsing ZigBee specific IO data
    """
    def setUp(self):
        self.zigbee = ZigBee(None)

    def test_parse_dio_adc(self):
        data = '\x01\x08\x00\x0e\x08\x00\x00\x00\x02P\x02\x06'
        expected_results = [{
            'dio-11': True,
            'adc-1': 0,
            'adc-2': 592,
            'adc-3': 518
        }]
        results = self.zigbee._parse_samples(data)
        self.assertEqual(results, expected_results)
Exemplo n.º 12
0
class Receiver:

    def __init__(self, db_type):
        self.data_shape = struct.Struct(
            ''.join(map(lambda x: x[0], db_type)))
        self.data_size = self.data_shape.size
        self.expected_packets = self.data_size / MAX_PACKET_SIZE + 1
        self.source_addr = None
        self.source_addr_long = None
        self.outbound = []

    def async_tx(self, command):
        """Eventually send a command
        """
        self.outbound.append(command)

    def __enter__(self):
        if _platform == "linux" or _platform == "linux2":
            self.ser = serial.Serial('/dev/ttyUSB0', 38400)
        elif _platform == "win32":
            self.ser = serial.Serial('COM5', 38400)
        self.xbee = ZigBee(self.ser)
        print 'xbee created/initialized'
        return self

    def data_lines(self):
        while True:
            payload = ''
            for x in xrange(self.expected_packets):

                packet = self.xbee.wait_read_frame()
                while packet.get('id', None) == 'tx_status':
                    print('got tx_status frame')
                    packet = self.xbee.wait_read_frame()
                self.source_addr_long = packet.get(
                        'source_addr_long', self.source_addr_long)
                self.source_addr = packet.get(
                        'source_addr', self.source_addr)

                payload += packet['rf_data']
                payload += packet['rssi']
                
                if x < self.expected_packets - 1 and len(payload) < 100:
                    break;

            # let our data be processed
            yield self.data_shape.unpack(payload)

            # flush the command queue to the xbee
            for cmd in self.outbound:
                self.xbee.tx(dest_addr_long=self.source_addr_long,
                        dest_addr=self.source_addr, data=cmd)
                print("command {}".format(' '.join("0x{:02x}".format(i) for i in cmd)))
                print "sent a command"
            self.outbound = []
            #self.xbee.tx(dest_addr_long=source_addr_long,
            #        dest_addr=source_addr, data=b'Hello world')


    def __exit__(self, type, value, traceback):
        self.xbee = None
        self.ser.close()
        if isinstance(value, serial.SerialException):
            print traceback
            return True
Exemplo n.º 13
0
class TestZigBee(unittest.TestCase):
    """
    Tests ZigBee-specific features
    """
    def setUp(self):
        self.zigbee = ZigBee(None)

    def test_null_terminated_field(self):
        """
        Packets with null-terminated fields
        should be properly parsed
        """
        expected_data = b'\x01\x02\x03\x04'
        terminator = b'\x00'
        node_identifier = b'\x95' + b'\x00' * 21 + expected_data + terminator + b'\x00' * 8

        data = self.zigbee._split_response(node_identifier)

        self.assertEqual(data['node_id'], expected_data)

    def test_split_node_identification_identifier(self):
        data = b'\x95\x00\x13\xa2\x00\x40\x52\x2b\xaa\x7d\x84\x02\x7d\x84\x00\x13\xa2\x00\x40\x52\x2b\xaa\x20\x00\xff\xfe\x01\x01\xc1\x05\x10\x1e'
        info = self.zigbee._split_response(data)
        expected_info = {
            'id': 'node_id_indicator',
            'sender_addr_long': b'\x00\x13\xa2\x00\x40\x52\x2b\xaa',
            'sender_addr': b'\x7d\x84',
            'options': b'\x02',
            'source_addr': b'\x7d\x84',
            'source_addr_long': b'\x00\x13\xa2\x00\x40\x52\x2b\xaa',
            'node_id': b' ',
            'parent_source_addr': b'\xff\xfe',
            'device_type': b'\x01',
            'source_event': b'\x01',
            'digi_profile_id': b'\xc1\x05',
            'manufacturer_id': b'\x10\x1e',
        }

        self.assertEqual(info, expected_info)

    def test_split_node_identification_identifier2(self):
        data = b'\x95\x00\x13\xa2\x00\x40\x52\x2b\xaa\x7d\x84\x02\x7d\x84\x00\x13\xa2\x00\x40\x52\x2b\xaaCoordinator\x00\xff\xfe\x01\x01\xc1\x05\x10\x1e'
        info = self.zigbee._split_response(data)
        expected_info = {
            'id': 'node_id_indicator',
            'sender_addr_long': b'\x00\x13\xa2\x00\x40\x52\x2b\xaa',
            'sender_addr': b'\x7d\x84',
            'options': b'\x02',
            'source_addr': b'\x7d\x84',
            'source_addr_long': b'\x00\x13\xa2\x00\x40\x52\x2b\xaa',
            'node_id': b'Coordinator',
            'parent_source_addr': b'\xff\xfe',
            'device_type': b'\x01',
            'source_event': b'\x01',
            'digi_profile_id': b'\xc1\x05',
            'manufacturer_id': b'\x10\x1e',
        }

        self.assertEqual(info, expected_info)

    def test_is_remote_at_response_parameter_parsed_as_io_samples(self):
        """
        A remote AT command of IS, to take a sample immediately and respond
        with the results, must be appropriately parsed for IO data.
        """
        data = b'\x97A\x00\x13\xa2\x00@oG\xe4v\x1aIS\x00\x01\x1c\xc0\x06\x18\x00\x02\x8c\x03\x96'
        info = self.zigbee._split_response(data)
        expected_info = {
            'id':
            'remote_at_response',
            'frame_id':
            b'A',
            'source_addr_long':
            b'\x00\x13\xa2\x00@oG\xe4',
            'source_addr':
            b'v\x1a',
            'command':
            b'IS',
            'status':
            b'\x00',
            'parameter': [{
                'dio-10': False,
                'adc-2': 918,
                'dio-6': False,
                'dio-11': True,
                'adc-1': 652
            }]
        }

        self.assertEqual(info, expected_info)

    def test_lowercase_is_remote_at_response_parameter_parsed_as_io_samples(
            self):
        """
        A remote AT command of lowercase is, to take a sample immediately and respond
        with the results, must be appropriately parsed for IO data.
        """
        data = b'\x97A\x00\x13\xa2\x00@oG\xe4v\x1ais\x00\x01\x1c\xc0\x06\x18\x00\x02\x8c\x03\x96'
        info = self.zigbee._split_response(data)
        expected_info = {
            'id':
            'remote_at_response',
            'frame_id':
            b'A',
            'source_addr_long':
            b'\x00\x13\xa2\x00@oG\xe4',
            'source_addr':
            b'v\x1a',
            'command':
            b'is',
            'status':
            b'\x00',
            'parameter': [{
                'dio-10': False,
                'adc-2': 918,
                'dio-6': False,
                'dio-11': True,
                'adc-1': 652
            }]
        }

        self.assertEqual(info, expected_info)

    def test_parsing_may_encounter_field_which_does_not_exist(self):
        """
        Some fields are optional and may not exist; parsing should not crash
        if/when they are not available.
        """
        data = b'\x97A\x00\x13\xa2\x00@oG\xe4v\x1aIS\x01'
        info = self.zigbee._split_response(data)
        expected_info = {
            'id': 'remote_at_response',
            'frame_id': b'A',
            'source_addr_long': b'\x00\x13\xa2\x00@oG\xe4',
            'source_addr': b'v\x1a',
            'command': b'IS',
            'status': b'\x01',
        }

        self.assertEqual(info, expected_info)

    def test_nd_at_response_parameter_parsed(self):
        """
        An at_response for an ND command must be parsed.
        """
        data = b'\x88AND\x00v\x1a\x00\x13\xa2\x00@oG\xe4ENDPOINT-1\x00\xff\xfe\x01\x00\xc1\x05\x10\x1e'
        info = self.zigbee._split_response(data)
        expected_info = {
            'id': 'at_response',
            'frame_id': b'A',
            'command': b'ND',
            'status': b'\x00',
            'parameter': {
                'source_addr': b'\x76\x1a',
                'source_addr_long': b'\x00\x13\xa2\x00\x40\x6f\x47\xe4',
                'node_identifier': b'ENDPOINT-1',
                'parent_address': b'\xff\xfe',
                'device_type': b'\x01',
                'status': b'\x00',
                'profile_id': b'\xc1\x05',
                'manufacturer': b'\x10\x1e',
            }
        }

        self.assertEqual(info, expected_info)

    def test_lowercase_nd_at_response_parameter_parsed(self):
        """
        An at_response for a lowercase nd command must be parsed.
        """
        data = b'\x88And\x00v\x1a\x00\x13\xa2\x00@oG\xe4ENDPOINT-1\x00\xff\xfe\x01\x00\xc1\x05\x10\x1e'
        info = self.zigbee._split_response(data)
        expected_info = {
            'id': 'at_response',
            'frame_id': b'A',
            'command': b'nd',
            'status': b'\x00',
            'parameter': {
                'source_addr': b'\x76\x1a',
                'source_addr_long': b'\x00\x13\xa2\x00\x40\x6f\x47\xe4',
                'node_identifier': b'ENDPOINT-1',
                'parent_address': b'\xff\xfe',
                'device_type': b'\x01',
                'status': b'\x00',
                'profile_id': b'\xc1\x05',
                'manufacturer': b'\x10\x1e',
            }
        }

        self.assertEqual(info, expected_info)
Exemplo n.º 14
0
 def setUp(self):
     self.zigbee = ZigBee(None)
Exemplo n.º 15
0
    logging.info('Reading nodes configuration "%s"', nodefilename)
    nodeconfig = configparser.ConfigParser()
    try:
        nodeconfig.read(nodefilename)
    except:
        logging.critical('Could not read the node configuration file "%s".',
                         nodefilename)
        raise

    # Open serial port
    with serial.Serial(args.port, args.rate) as ser:
        # Create an xbee ZigBee communication object
        dispatch = Dispatch(ser)
        logging.debug('Creating xbee object.')
        xbee = ZigBee(ser, callback=dispatch.dispatch)

        try:
            if args.command == 'listen':
                listen(xbee, dispatch, config, nodeconfig)
            elif args.command == 'configure':
                config_client(xbee, dispatch, config, nodeconfig)
            else:
                logging.critical('Unknown command "%s", terminating.',
                                 args.command)
        finally:
            # halt() must be called before closing the serial port in order to ensure proper thread shutdown
            logging.info('Halting xbee.')
            xbee.halt()
            ser.close()
    logging.info('Closed serial port.')
Exemplo n.º 16
0
class TestZigBee(unittest.TestCase):
    """
    Tests ZigBee-specific features
    """

    def setUp(self):
        self.zigbee = ZigBee(None)

    def test_null_terminated_field(self):
        """
        Packets with null-terminated fields
        should be properly parsed
        """
        expected_data = b"\x01\x02\x03\x04"
        terminator = b"\x00"
        node_identifier = b"\x95" + b"\x00" * 21 + expected_data + terminator + b"\x00" * 8

        data = self.zigbee._split_response(node_identifier)

        self.assertEqual(data["node_id"], expected_data)

    def test_split_node_identification_identifier(self):
        data = b"\x95\x00\x13\xa2\x00\x40\x52\x2b\xaa\x7d\x84\x02\x7d\x84\x00\x13\xa2\x00\x40\x52\x2b\xaa\x20\x00\xff\xfe\x01\x01\xc1\x05\x10\x1e"
        info = self.zigbee._split_response(data)
        expected_info = {
            "id": "node_id_indicator",
            "sender_addr_long": b"\x00\x13\xa2\x00\x40\x52\x2b\xaa",
            "sender_addr": b"\x7d\x84",
            "options": b"\x02",
            "source_addr": b"\x7d\x84",
            "source_addr_long": b"\x00\x13\xa2\x00\x40\x52\x2b\xaa",
            "node_id": b" ",
            "parent_source_addr": b"\xff\xfe",
            "device_type": b"\x01",
            "source_event": b"\x01",
            "digi_profile_id": b"\xc1\x05",
            "manufacturer_id": b"\x10\x1e",
        }

        self.assertEqual(info, expected_info)

    def test_split_node_identification_identifier2(self):
        data = b"\x95\x00\x13\xa2\x00\x40\x52\x2b\xaa\x7d\x84\x02\x7d\x84\x00\x13\xa2\x00\x40\x52\x2b\xaaCoordinator\x00\xff\xfe\x01\x01\xc1\x05\x10\x1e"
        info = self.zigbee._split_response(data)
        expected_info = {
            "id": "node_id_indicator",
            "sender_addr_long": b"\x00\x13\xa2\x00\x40\x52\x2b\xaa",
            "sender_addr": b"\x7d\x84",
            "options": b"\x02",
            "source_addr": b"\x7d\x84",
            "source_addr_long": b"\x00\x13\xa2\x00\x40\x52\x2b\xaa",
            "node_id": b"Coordinator",
            "parent_source_addr": b"\xff\xfe",
            "device_type": b"\x01",
            "source_event": b"\x01",
            "digi_profile_id": b"\xc1\x05",
            "manufacturer_id": b"\x10\x1e",
        }

        self.assertEqual(info, expected_info)

    def test_is_remote_at_response_parameter_parsed_as_io_samples(self):
        """
        A remote AT command of IS, to take a sample immediately and respond
        with the results, must be appropriately parsed for IO data.
        """
        data = b"\x97A\x00\x13\xa2\x00@oG\xe4v\x1aIS\x00\x01\x1c\xc0\x06\x18\x00\x02\x8c\x03\x96"
        info = self.zigbee._split_response(data)
        expected_info = {
            "id": "remote_at_response",
            "frame_id": b"A",
            "source_addr_long": b"\x00\x13\xa2\x00@oG\xe4",
            "source_addr": b"v\x1a",
            "command": b"IS",
            "status": b"\x00",
            "parameter": [{"dio-10": False, "adc-2": 918, "dio-6": False, "dio-11": True, "adc-1": 652}],
        }

        self.assertEqual(info, expected_info)

    def test_lowercase_is_remote_at_response_parameter_parsed_as_io_samples(self):
        """
        A remote AT command of lowercase is, to take a sample immediately and respond
        with the results, must be appropriately parsed for IO data.
        """
        data = b"\x97A\x00\x13\xa2\x00@oG\xe4v\x1ais\x00\x01\x1c\xc0\x06\x18\x00\x02\x8c\x03\x96"
        info = self.zigbee._split_response(data)
        expected_info = {
            "id": "remote_at_response",
            "frame_id": b"A",
            "source_addr_long": b"\x00\x13\xa2\x00@oG\xe4",
            "source_addr": b"v\x1a",
            "command": b"is",
            "status": b"\x00",
            "parameter": [{"dio-10": False, "adc-2": 918, "dio-6": False, "dio-11": True, "adc-1": 652}],
        }

        self.assertEqual(info, expected_info)

    def test_parsing_may_encounter_field_which_does_not_exist(self):
        """
        Some fields are optional and may not exist; parsing should not crash
        if/when they are not available.
        """
        data = b"\x97A\x00\x13\xa2\x00@oG\xe4v\x1aIS\x01"
        info = self.zigbee._split_response(data)
        expected_info = {
            "id": "remote_at_response",
            "frame_id": b"A",
            "source_addr_long": b"\x00\x13\xa2\x00@oG\xe4",
            "source_addr": b"v\x1a",
            "command": b"IS",
            "status": b"\x01",
        }

        self.assertEqual(info, expected_info)

    def test_nd_at_response_parameter_parsed(self):
        """
        An at_response for an ND command must be parsed.
        """
        data = b"\x88AND\x00v\x1a\x00\x13\xa2\x00@oG\xe4ENDPOINT-1\x00\xff\xfe\x01\x00\xc1\x05\x10\x1e"
        info = self.zigbee._split_response(data)
        expected_info = {
            "id": "at_response",
            "frame_id": b"A",
            "command": b"ND",
            "status": b"\x00",
            "parameter": {
                "source_addr": b"\x76\x1a",
                "source_addr_long": b"\x00\x13\xa2\x00\x40\x6f\x47\xe4",
                "node_identifier": b"ENDPOINT-1",
                "parent_address": b"\xff\xfe",
                "device_type": b"\x01",
                "status": b"\x00",
                "profile_id": b"\xc1\x05",
                "manufacturer": b"\x10\x1e",
            },
        }

        self.assertEqual(info, expected_info)

    def test_lowercase_nd_at_response_parameter_parsed(self):
        """
        An at_response for a lowercase nd command must be parsed.
        """
        data = b"\x88And\x00v\x1a\x00\x13\xa2\x00@oG\xe4ENDPOINT-1\x00\xff\xfe\x01\x00\xc1\x05\x10\x1e"
        info = self.zigbee._split_response(data)
        expected_info = {
            "id": "at_response",
            "frame_id": b"A",
            "command": b"nd",
            "status": b"\x00",
            "parameter": {
                "source_addr": b"\x76\x1a",
                "source_addr_long": b"\x00\x13\xa2\x00\x40\x6f\x47\xe4",
                "node_identifier": b"ENDPOINT-1",
                "parent_address": b"\xff\xfe",
                "device_type": b"\x01",
                "status": b"\x00",
                "profile_id": b"\xc1\x05",
                "manufacturer": b"\x10\x1e",
            },
        }

        self.assertEqual(info, expected_info)
Exemplo n.º 17
0
class TestZigBee(unittest.TestCase):
    """
    Tests ZigBee-specific features
    """

    def setUp(self):
        self.zigbee = ZigBee(None)

    def test_null_terminated_field(self):
        """
        Packets with null-terminated fields
        should be properly parsed
        """
        expected_data = b'\x01\x02\x03\x04'
        terminator = b'\x00'
        node_identifier = b'\x95' + b'\x00' * 21 + expected_data + terminator + b'\x00' * 8

        data = self.zigbee._split_response(node_identifier)

        self.assertEqual(data['node_id'], expected_data)

    def test_split_node_identification_identifier(self):
            data = b'\x95\x00\x13\xa2\x00\x40\x52\x2b\xaa\x7d\x84\x02\x7d\x84\x00\x13\xa2\x00\x40\x52\x2b\xaa\x20\x00\xff\xfe\x01\x01\xc1\x05\x10\x1e'
            info = self.zigbee._split_response(data)
            expected_info = {
                'id': 'node_id_indicator',
                'sender_addr_long': b'\x00\x13\xa2\x00\x40\x52\x2b\xaa',
                'sender_addr': b'\x7d\x84',
                'options': b'\x02',
                'source_addr': b'\x7d\x84',
                'source_addr_long': b'\x00\x13\xa2\x00\x40\x52\x2b\xaa',
                'node_id': b' ',
                'parent_source_addr': b'\xff\xfe',
                'device_type': b'\x01',
                'source_event': b'\x01',
                'digi_profile_id': b'\xc1\x05',
                'manufacturer_id': b'\x10\x1e',
            }
            
            self.assertEqual(info, expected_info)
            
    def test_split_node_identification_identifier2(self):
            data = b'\x95\x00\x13\xa2\x00\x40\x52\x2b\xaa\x7d\x84\x02\x7d\x84\x00\x13\xa2\x00\x40\x52\x2b\xaaCoordinator\x00\xff\xfe\x01\x01\xc1\x05\x10\x1e'
            info = self.zigbee._split_response(data)
            expected_info = {
                'id': 'node_id_indicator',
                'sender_addr_long': b'\x00\x13\xa2\x00\x40\x52\x2b\xaa',
                'sender_addr': b'\x7d\x84',
                'options': b'\x02',
                'source_addr': b'\x7d\x84',
                'source_addr_long': b'\x00\x13\xa2\x00\x40\x52\x2b\xaa',
                'node_id': b'Coordinator',
                'parent_source_addr': b'\xff\xfe',
                'device_type': b'\x01',
                'source_event': b'\x01',
                'digi_profile_id': b'\xc1\x05',
                'manufacturer_id': b'\x10\x1e',
            }
            
            self.assertEqual(info, expected_info)
    
    def test_is_remote_at_response_parameter_parsed_as_io_samples(self):
        """
        A remote AT command of IS, to take a sample immediately and respond
        with the results, must be appropriately parsed for IO data.
        """
        data = b'\x97A\x00\x13\xa2\x00@oG\xe4v\x1aIS\x00\x01\x1c\xc0\x06\x18\x00\x02\x8c\x03\x96'
        info = self.zigbee._split_response(data)
        expected_info = {
            'id': 'remote_at_response',
            'frame_id': b'A',
            'source_addr_long': b'\x00\x13\xa2\x00@oG\xe4',
            'source_addr': b'v\x1a',
            'command': b'IS',
            'status': b'\x00',
            'parameter': [{'adc-1': 652,
                           'adc-2': 918, 
                           'dio-10': False, 
                           'dio-11': True, 
                           'dio-12': True, 
                           'dio-6': False,
                           'dio-7': False
                           }]
        }
        
        self.assertEqual(info, expected_info)
        
    def test_lowercase_is_remote_at_response_parameter_parsed_as_io_samples(self):
        """
        A remote AT command of lowercase is, to take a sample immediately and respond
        with the results, must be appropriately parsed for IO data.
        """
        data = b'\x97A\x00\x13\xa2\x00@oG\xe4v\x1ais\x00\x01\x1c\xc0\x06\x18\x00\x02\x8c\x03\x96'
        info = self.zigbee._split_response(data)
        expected_info = {
            'id': 'remote_at_response',
            'frame_id': b'A',
            'source_addr_long': b'\x00\x13\xa2\x00@oG\xe4',
            'source_addr': b'v\x1a',
            'command': b'is',
            'status': b'\x00',
            'parameter': [{'adc-1': 652,
                           'adc-2': 918, 
                           'dio-10': False, 
                           'dio-11': True, 
                           'dio-12': True, 
                           'dio-6': False,
                           'dio-7': False
                           }]
        }
        
        self.assertEqual(info, expected_info)
        
    def test_parsing_may_encounter_field_which_does_not_exist(self):
        """
        Some fields are optional and may not exist; parsing should not crash
        if/when they are not available.
        """
        data = b'\x97A\x00\x13\xa2\x00@oG\xe4v\x1aIS\x01'
        info = self.zigbee._split_response(data)
        expected_info = {
            'id': 'remote_at_response',
            'frame_id': b'A',
            'source_addr_long': b'\x00\x13\xa2\x00@oG\xe4',
            'source_addr': b'v\x1a',
            'command': b'IS',
            'status': b'\x01',
        }
        
        self.assertEqual(info, expected_info)
        
    def test_nd_at_response_parameter_parsed(self):
        """
        An at_response for an ND command must be parsed.
        """
        data = b'\x88AND\x00v\x1a\x00\x13\xa2\x00@oG\xe4ENDPOINT-1\x00\xff\xfe\x01\x00\xc1\x05\x10\x1e'
        info = self.zigbee._split_response(data)
        expected_info = {
            'id': 'at_response',
            'frame_id': b'A',
            'command': b'ND',
            'status': b'\x00',
            'parameter': {'source_addr': b'\x76\x1a',
                          'source_addr_long': b'\x00\x13\xa2\x00\x40\x6f\x47\xe4',
                          'node_identifier': b'ENDPOINT-1',
                          'parent_address': b'\xff\xfe',
                          'device_type': b'\x01',
                          'status': b'\x00',
                          'profile_id': b'\xc1\x05',
                          'manufacturer': b'\x10\x1e',
                          }
        }
        
        self.assertEqual(info, expected_info)
        
    def test_lowercase_nd_at_response_parameter_parsed(self):
        """
        An at_response for a lowercase nd command must be parsed.
        """
        data = b'\x88And\x00v\x1a\x00\x13\xa2\x00@oG\xe4ENDPOINT-1\x00\xff\xfe\x01\x00\xc1\x05\x10\x1e'
        info = self.zigbee._split_response(data)
        expected_info = {
            'id': 'at_response',
            'frame_id': b'A',
            'command': b'nd',
            'status': b'\x00',
            'parameter': {'source_addr': b'\x76\x1a',
                          'source_addr_long': b'\x00\x13\xa2\x00\x40\x6f\x47\xe4',
                          'node_identifier': b'ENDPOINT-1',
                          'parent_address': b'\xff\xfe',
                          'device_type': b'\x01',
                          'status': b'\x00',
                          'profile_id': b'\xc1\x05',
                          'manufacturer': b'\x10\x1e',
                          }
        }
        
        self.assertEqual(info, expected_info)
Exemplo n.º 18
0
    nodefilename = config['DEFAULT']['nodefile']    
    
    logging.info('Reading nodes configuration "%s"', nodefilename)
    nodeconfig = configparser.ConfigParser()
    try:
        nodeconfig.read(nodefilename)
    except:
        logging.critical('Could not read the node configuration file "%s".', nodefilename)
        raise
    
    # Open serial port
    with serial.Serial(args.port,args.rate) as ser:
        # Create an xbee ZigBee communication object
        dispatch = Dispatch(ser)
        logging.debug('Creating xbee object.')
        xbee = ZigBee(ser,callback=dispatch.dispatch)

        try:
            if args.command == 'listen':
                listen(xbee,dispatch,config,nodeconfig)
            elif args.command == 'configure':
                config_client(xbee,dispatch,config,nodeconfig)
            else:
                logging.critical('Unknown command "%s", terminating.',args.command)
        finally:
            # halt() must be called before closing the serial port in order to ensure proper thread shutdown
            logging.info('Halting xbee.')
            xbee.halt()
            ser.close()
    logging.info('Closed serial port.')
Exemplo n.º 19
0
class Receiver:

    def __init__(self, db_type, serialport):
        self.data_shape = {key:struct.Struct(
            ''.join(map(lambda x: x[0], packet))) for key, packet in db_type.iteritems()}

        #Print out packet size
        for key, packet in self.data_shape.iteritems():
            print("Packet Size {}: {}".format(key,packet.size))
        
        #Check if all packets have the same size
        self.data_size = self.data_shape[self.data_shape.keys()[0]].size
        data_mismatch = False
        for i in xrange(1,len(self.data_shape)):
            if (self.data_shape[i].size != self.data_size):
                print("Data Packets are not the same in size: " + str(self.data_size) + " " + str(self.data_shape[i].size))
                data_mismatch = True
        if data_mismatch:
            raise ValueError("Data packet size mismatch")
        
        self.expected_packets = self.data_size / MAX_PACKET_SIZE + 1
        self.source_addr = None
        self.source_addr_long = None
        self.packet_type = None
        self.outbound = []
        self.rssi = -100
        self.stored_data = [tuple([None])]*len(self.data_shape.keys())
        self.default_serial = serialport

    def async_tx(self, command):
        """Eventually send a command
        """
        self.outbound.append(command)


    def reconnect_xbee(self):
    #search for available ports
        port_to_connect = ''
        while port_to_connect == '': 

            #detect platform and format port names
            if _platform.startswith('win'):
                ports = ['COM%s' % (i + 1) for i in range(256)]
            elif _platform.startswith('linux'):
                # this excludes your current terminal "/dev/tty"
                ports = glob.glob('/dev/ttyUSB*')
            else:
                raise EnvironmentError('Unsupported platform: ' + _platform)
        
            ports_avail = []
            
            #loop through all possible ports and try to connect
            for port in ports:
                try:
                    s = serial.Serial(port)
                    s.close()
                    ports_avail.append(port)
                except (OSError, serial.SerialException):
                    pass
            
            if len(ports_avail) ==1:
                port_to_connect = ports_avail[0]
                
            elif len(ports_avail)==0:
                #No Serial port found, continue looping.
                print( "No serial port detected. Trying again...")
                time.sleep(1)

            elif len(ports_avail)>1:
                #Multiple serial ports detected. Get user input to decide which one to connect to
                #com_input = raw_input("Multiple serial ports available. Which serial port do you want? \n"+str(self.ports_avail)+":").upper();
                if self.default_serial == None:
                    raise EnvironmentError('Incorrect command line parameters. If there are multiple serial devices, indicate what port you want to be used using --serialport')
                elif self.default_serial.upper() in ports_avail:
                    port_to_connect = self.default_serial.upper()
                else:
                    raise EnvironmentError('Incorrect command line parameters. Serial port is not known as a valid port. Valid ports are:'+ str(ports_avail))

        #connect to xbee
        self.xbee = ZigBee(serial.Serial(port_to_connect, 115200))
        print('xbee connected to port ' + port_to_connect)
        return self


    def __enter__(self):
        return self.reconnect_xbee()


    def data_lines(self):

        #counter to limit extra packets sent
        packetCnt=0
        while True:
            try:
                payload = ''
                yield_data = ()
                for x in xrange(self.expected_packets):

                    packet = self.xbee.wait_read_frame()

                    #limit packets by only sending decibel strength only when if statement is true
                    if(packetCnt>=10):
                        self.xbee.at(command="DB")
                        packetCnt=0
                    packetCnt=packetCnt+1

                    while packet.get('id', None) != 'rx':
                        #Checks for tx_response
                        if packet.get('id', None) == 'tx_status':
                            print('got tx_status frame')
                        #Checks for command response and signal strength
                        elif packet.get('id', None) == 'at_response':
                            if packet.get('command', None) == 'DB':
                                self.rssi = ord(packet.get('parameter',self.rssi))
                        packet = self.xbee.wait_read_frame()
                        
                    self.source_addr_long = packet.get(
                            'source_addr_long', self.source_addr_long)
                    self.source_addr = packet.get(
                            'source_addr', self.source_addr)

                    payload += packet['rf_data']

                    # Read first two bytes, to determine packet type
                    packet_type = struct.unpack("h", payload[:2])[0]
                
                    # Unpack Struct according to ID, and update global parameters
                    for data_type, data_shape in self.data_shape.iteritems():
                        if (packet_type == data_type):
                            self.stored_data[data_type] = data_shape.unpack(payload[2:])
                        else:
                            self.stored_data[data_type] = tuple([None] * len([i for i in data_shape.format if i != 'x']))
                    yield_data = tuple([i for j in self.stored_data for i in j])
                    
                    #Add RSSI to each packet
                    yield_data += tuple([self.rssi])

                # let our data be processed - unpacks an array of tuples into one single tuple
                yield yield_data

                # flush the command queue to the xbee
                for cmd in self.outbound:
                    self.xbee.tx(dest_addr_long=self.source_addr_long,
                            dest_addr=self.source_addr, data=cmd)
                    print("command {}".format(' '.join("0x{:02x}".format(i) for i in cmd)))
                    print("sent a command")
                self.outbound = []
            except (OSError, serial.SerialException, IOError):
                #catch exception if xbee is unplugged, and try to reconnect
                print("Xbee disconnected!")
                self.reconnect_xbee()


    def __exit__(self, type, value, traceback):
        self.xbee = None
        try:
            self.ser.close()
        except(AttributeError):
            #If the program exits before ser is initiallized, ser.close() will throw an AttributeError, which is caught and ignored
            pass
        if isinstance(value, serial.SerialException):
            print(traceback)
            return True
Exemplo n.º 20
0
 def setUp(self):
     self.zigbee = ZigBee(None)
Exemplo n.º 21
0
class Receiver:

    def __init__(self, db_type, serialport, uart_connection=False):
        self.data_shape = {key:struct.Struct(
            ''.join(map(lambda x: x[0], packet))) for key, packet in db_type.iteritems()}

        #Print out packet size
        for key, packet in self.data_shape.iteritems():
            print("Packet Size {}: {}".format(key,packet.size))

        #number of packet frames we're expecting to receive. Only valid if we're sending over 100 bytes of data
        self.expected_packets = 1 #assume we will only send 1 packet down for now
        self.source_addr = None
        self.source_addr_long = None
        self.packet_type = None
        self.outbound = []
        self.rssi = 255
        self.stored_data = [tuple([None])]*len(self.data_shape.keys())
        self.default_serial = serialport
        self.uart_connection = uart_connection

    def async_tx(self, command):
        """Eventually send a command
        """
        self.outbound.append(command)


    def reconnect_xbee(self):
    #search for available ports
        port_to_connect = ''
        while port_to_connect == '':

            #detect platform and format port names
            if _platform.startswith('win'):
                ports = ['COM%s' % (i + 1) for i in range(256)]
            elif _platform.startswith('linux'):
                # this excludes your current terminal "/dev/tty"
                ports = glob.glob('/dev/ttyUSB*')
            else:
                raise EnvironmentError('Unsupported platform: ' + _platform)

            ports_avail = []

            #loop through all possible ports and try to connect
            for port in ports:
                try:
                    s = serial.Serial(port)
                    s.close()
                    ports_avail.append(port)
                except (OSError, serial.SerialException):
                    pass

            if len(ports_avail) ==1:
                port_to_connect = ports_avail[0]

            elif len(ports_avail)==0:
                #No Serial port found, continue looping.
                print( "No serial port detected. Trying again...")
                time.sleep(1)

            elif len(ports_avail)>1:
                #Multiple serial ports detected. Get user input to decide which one to connect to
                #com_input = raw_input("Multiple serial ports available. Which serial port do you want? \n"+str(self.ports_avail)+":").upper();
                if self.default_serial == None:
                    raise EnvironmentError('Incorrect command line parameters. If there are multiple serial devices, indicate what port you want to be used using --serialport')
                elif self.default_serial.upper() in ports_avail:
                    port_to_connect = self.default_serial.upper()
                else:
                    raise EnvironmentError('Incorrect command line parameters. Serial port is not known as a valid port. Valid ports are:'+ str(ports_avail))

        #connect to xbee or uart
        ser = serial.Serial(port_to_connect, 115200)
        if self.uart_connection:
            self.xbee = UARTConnection(ser)
        else:
            self.xbee = ZigBee(ser)

        print('xbee connected to port ' + port_to_connect)
        return self


    def __enter__(self):
        return self.reconnect_xbee()


    def data_lines(self):

        #counter to limit extra packets sent
        packetCnt=0
        while True:
            try:
                payload = ''
                yield_data = ()
                for x in xrange(self.expected_packets):

                    packet = self.xbee.wait_read_frame()
                    #limit packets by only sending decibel strength only when if statement is true
                    if(packetCnt>=10):
                        self.xbee.at(command="DB")
                        packetCnt=0
                    packetCnt=packetCnt+1

                    while packet.get('id', None) != 'rx':
                        #Checks for tx_response
                        if packet.get('id', None) == 'tx_status':
                            print('got tx_status frame')
                        #Checks for command response and signal strength
                        elif packet.get('id', None) == 'at_response':
                            if packet.get('command', None) == 'DB':
                                self.rssi = ord(packet.get('parameter',chr(self.rssi)))
                        packet = self.xbee.wait_read_frame()

                    self.source_addr_long = packet.get(
                            'source_addr_long', self.source_addr_long)
                    self.source_addr = packet.get(
                            'source_addr', self.source_addr)

                    payload += packet['rf_data']

                    # Read first two bytes, to determine packet type
                    packet_type = struct.unpack("h", payload[:2])[0]

                    # Unpack Struct according to ID, and update global parameters
                    for data_type, data_shape in self.data_shape.iteritems():
                        if (packet_type == data_type):
                            self.stored_data[data_type] = data_shape.unpack(payload[2:])
                        else:
                            self.stored_data[data_type] = tuple([None] * len([i for i in data_shape.format if i != 'x']))
                    yield_data = tuple([i for j in self.stored_data for i in j])

                    #Add RSSI to each packet
                    yield_data += tuple([self.rssi])

                # let our data be processed - unpacks an array of tuples into one single tuple
                yield yield_data

                # flush the command queue to the xbee
                for cmd in self.outbound:
                    self.xbee.tx(dest_addr_long=self.source_addr_long,
                            dest_addr=self.source_addr, data=cmd)
                    print("command {}".format(' '.join("0x{:02x}".format(i) for i in cmd)))
                    print("sent a command")
                self.outbound = []
            except (OSError, serial.SerialException, IOError):
                #catch exception if xbee is unplugged, and try to reconnect
                print("Xbee disconnected!")
                self.reconnect_xbee()


    def __exit__(self, type, value, traceback):
        self.xbee = None
        try:
            self.ser.close()
        except(AttributeError):
            #If the program exits before ser is initiallized, ser.close() will throw an AttributeError, which is caught and ignored
            pass
        if isinstance(value, serial.SerialException):
            print(traceback)
            return True