diff --git a/tests/test_api.py b/tests/test_api.py index 7e1bf04..3cc3df5 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -155,6 +155,25 @@ def test_frame_received(api, monkeypatch): my_handler.reset_mock() +def test_frame_received_no_handler(api, monkeypatch): + monkeypatch.setattr(t, 'deserialize', mock.MagicMock( + return_value=(b'deserialized data', b''))) + my_handler = mock.MagicMock() + cmd = 'no_handler' + cmd_id = 0x00 + xbee_api.COMMANDS[cmd] = (cmd_id, (), None) + api._commands_by_id[cmd_id] = cmd + + cmd_opts = xbee_api.COMMANDS[cmd] + cmd_id = cmd_opts[0] + payload = b'\x01\x02\x03\x04' + data = cmd_id.to_bytes(1, 'big') + payload + api.frame_received(data) + assert t.deserialize.call_count == 1 + assert t.deserialize.call_args[0][0] == payload + assert my_handler.call_count == 0 + + def _handle_at_response(api, tsn, status, at_response=b''): data = (tsn, 'AI'.encode('ascii'), status, at_response) response = asyncio.Future() @@ -346,3 +365,9 @@ def test_set_application(api): def test_handle_route_record_indicator(api): api._handle_route_record_indicator(mock.sentinel.ri) + + +def test_handle_many_to_one_rri(api): + ieee = t.EUI64([t.uint8_t(a) for a in range(0, 8)]) + nwk = 0x1234 + api._handle_many_to_one_rri([ieee, nwk, 0]) diff --git a/tests/test_types.py b/tests/test_types.py index c3c6dac..48cdcd7 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -71,3 +71,10 @@ class undEnum(t.uint8_t, t.UndefinedEnum): with pytest.raises(ValueError): undEnum(0xEE) + + +def test_nwk(): + nwk = t.NWK(0x1234) + + assert str(nwk) == '0x1234' + assert repr(nwk) == '0x1234' diff --git a/zigpy_xbee/api.py b/zigpy_xbee/api.py index 129e757..dbce120 100644 --- a/zigpy_xbee/api.py +++ b/zigpy_xbee/api.py @@ -1,4 +1,5 @@ import asyncio +import binascii import enum import logging @@ -32,21 +33,21 @@ class ModemStatus(t.uint8_t, t.UndefinedEnum): 'queued_at': (0x09, (t.uint8_t, t.ATCommand, t.Bytes), 0x88), 'remote_at': (0x17, (), None), 'tx': (0x10, (), None), - 'tx_explicit': (0x11, (t.uint8_t, t.EUI64, t.uint16_t, t.uint8_t, t.uint8_t, t.uint16_t, t.uint16_t, t.uint8_t, t.uint8_t, t.Bytes), None), - 'create_source_route': (0x21, (), None), + 'tx_explicit': (0x11, (t.uint8_t, t.EUI64, t.NWK, t.uint8_t, t.uint8_t, t.uint16_t, t.uint16_t, t.uint8_t, t.uint8_t, t.Bytes), None), + 'create_source_route': (0x21, (t.uint8_t, t.EUI64, t.NWK, t.uint8_t, LVList(t.NWK)), None), 'register_joining_device': (0x24, (), None), 'at_response': (0x88, (t.uint8_t, t.ATCommand, t.uint8_t, t.Bytes), None), 'modem_status': (0x8A, (ModemStatus, ), None), - 'tx_status': (0x8B, (t.uint8_t, t.uint16_t, t.uint8_t, t.uint8_t, t.uint8_t), None), + 'tx_status': (0x8B, (t.uint8_t, t.NWK, t.uint8_t, t.uint8_t, t.uint8_t), None), 'route_information': (0x8D, (), None), 'rx': (0x90, (), None), - 'explicit_rx_indicator': (0x91, (t.EUI64, t.uint16_t, t.uint8_t, t.uint8_t, t.uint16_t, t.uint16_t, t.uint8_t, t.Bytes), None), + 'explicit_rx_indicator': (0x91, (t.EUI64, t.NWK, t.uint8_t, t.uint8_t, t.uint16_t, t.uint16_t, t.uint8_t, t.Bytes), None), 'rx_io_data_long_addr': (0x92, (), None), 'remote_at_response': (0x97, (), None), 'extended_status': (0x98, (), None), - 'route_record_indicator': (0xA1, (t.EUI64, t.uint16_t, t.uint8_t, LVList(t.uint16_t)), None), - 'many_to_one_rri': (0xA3, (), None), + 'route_record_indicator': (0xA1, (t.EUI64, t.NWK, t.uint8_t, LVList(t.NWK)), None), + 'many_to_one_rri': (0xA3, (t.EUI64, t.NWK, t.uint8_t), None), 'node_id_indicator': (0x95, (), None), } @@ -261,7 +262,11 @@ def frame_received(self, data): command = self._commands_by_id[data[0]] LOGGER.debug("Frame received: %s", command) data, rest = t.deserialize(data[1:], COMMANDS[command][1]) - getattr(self, '_handle_%s' % (command, ))(data) + try: + getattr(self, '_handle_%s' % (command, ))(data) + except AttributeError: + LOGGER.error("No '%s' handler. Data: %s", command, + binascii.hexlify(data)) def _handle_at_response(self, data): fut, = self._awaiting.pop(data[0]) @@ -283,6 +288,9 @@ def _handle_at_response(self, data): response, remains = response_type.deserialize(data[3]) fut.set_result(response) + def _handle_many_to_one_rri(self, data): + LOGGER.debug("_handle_many_to_one_rri: %s", data) + def _handle_modem_status(self, data): LOGGER.debug("Handle modem status frame: %s", data) status = data[0] diff --git a/zigpy_xbee/types.py b/zigpy_xbee/types.py index 7b7a868..be3ac13 100644 --- a/zigpy_xbee/types.py +++ b/zigpy_xbee/types.py @@ -147,3 +147,11 @@ def __call__(cls, value=None, *args, **kwargs): class UndefinedEnum(enum.Enum, metaclass=UndefinedEnumMeta): pass + + +class NWK(uint16_t): + def __repr__(self): + return '0x{:04x}'.format(self) + + def __str__(self): + return '0x{:04x}'.format(self)