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