diff --git a/setup.py b/setup.py index 5ad03bd..62a1625 100644 --- a/setup.py +++ b/setup.py @@ -2,11 +2,11 @@ from setuptools import find_packages, setup -import zigpy_xbee.const as xbee_const +import zigpy_xbee setup( name="zigpy-xbee-homeassistant", - version=xbee_const.__version__, + version=zigpy_xbee.__version__, description="A library which communicates with XBee radios for zigpy", url="http://github.com/zigpy/zigpy-xbee", author="Russell Cloran", @@ -15,7 +15,7 @@ packages=find_packages(exclude=['*.tests']), install_requires=[ 'pyserial-asyncio', - 'zigpy-homeassistant', + 'zigpy-homeassistant >= 0.3.3', ], tests_require=[ 'pytest', diff --git a/tests/test_application.py b/tests/test_application.py index 8bb2ae7..5d293e5 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -5,6 +5,8 @@ from zigpy.exceptions import DeliveryError from zigpy.types import EUI64, uint16_t +from zigpy.zdo.types import LogicalType + from zigpy_xbee.api import ModemStatus, XBee from zigpy_xbee.zigbee import application @@ -12,6 +14,8 @@ @pytest.fixture def app(monkeypatch, database_file=None): monkeypatch.setattr(application, 'TIMEOUT_TX_STATUS', 0.1) + monkeypatch.setattr(application, 'TIMEOUT_REPLY', 0.1) + monkeypatch.setattr(application, 'TIMEOUT_REPLY_EXTENDED', 0.1) return application.ControllerApplication(XBee(), database_file=database_file) @@ -438,11 +442,14 @@ async def test_permit(app): async def _test_request(app, do_reply=True, expect_reply=True, - send_success=True, send_timeout=False, **kwargs): + send_success=True, send_timeout=False, + logical_type=None, **kwargs): seq = 123 nwk = 0x2345 ieee = EUI64(b'\x01\x02\x03\x04\x05\x06\x07\x08') - app.add_device(ieee, nwk) + dev = app.add_device(ieee, nwk) + dev.node_desc = mock.MagicMock() + dev.node_desc.logical_type = logical_type def _mock_command(cmdname, ieee, nwk, src_ep, dst_ep, cluster, profile, radius, options, data): @@ -490,6 +497,27 @@ async def test_request_send_fail(app): await _test_request(app, False, True, send_success=False, tries=2, timeout=0.1) +@pytest.mark.asyncio +async def test_request_extended_timeout(app): + lt = LogicalType.Router + assert await _test_request(app, True, True, logical_type=lt) == mock.sentinel.reply_result + assert app._api._command.call_count == 1 + assert app._api._command.call_args[0][8] & 0x40 == 0x00 + app._api._command.reset_mock() + + lt = None + assert await _test_request(app, True, True, logical_type=lt) == mock.sentinel.reply_result + assert app._api._command.call_count == 1 + assert app._api._command.call_args[0][8] & 0x40 == 0x40 + app._api._command.reset_mock() + + lt = LogicalType.EndDevice + assert await _test_request(app, True, True, logical_type=lt) == mock.sentinel.reply_result + assert app._api._command.call_count == 1 + assert app._api._command.call_args[0][8] & 0x40 == 0x40 + app._api._command.reset_mock() + + def _handle_reply(app, tsn): app.handle_message = mock.MagicMock() return app._handle_reply( diff --git a/tests/test_const.py b/tests/test_const.py deleted file mode 100644 index 2ec6a1a..0000000 --- a/tests/test_const.py +++ /dev/null @@ -1,6 +0,0 @@ -import zigpy_xbee.const - - -def test_version(): - assert isinstance(zigpy_xbee.const.__short_version__, str) - assert isinstance(zigpy_xbee.const.__version__, str) diff --git a/zigpy_xbee/__init__.py b/zigpy_xbee/__init__.py index e69de29..ee47b92 100644 --- a/zigpy_xbee/__init__.py +++ b/zigpy_xbee/__init__.py @@ -0,0 +1,5 @@ +MAJOR_VERSION = 0 +MINOR_VERSION = 2 +PATCH_VERSION = '1' +__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) +__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) diff --git a/zigpy_xbee/const.py b/zigpy_xbee/const.py deleted file mode 100644 index a0058a7..0000000 --- a/zigpy_xbee/const.py +++ /dev/null @@ -1,6 +0,0 @@ -"""Constants used by zigpy-xbee.""" -MAJOR_VERSION = 0 -MINOR_VERSION = 2 -PATCH_VERSION = '1.dev0' -__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) -__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) diff --git a/zigpy_xbee/zigbee/application.py b/zigpy_xbee/zigbee/application.py index 063ad43..d2240e7 100644 --- a/zigpy_xbee/zigbee/application.py +++ b/zigpy_xbee/zigbee/application.py @@ -6,7 +6,7 @@ import zigpy.exceptions import zigpy.types import zigpy.util -import zigpy.zdo.types +from zigpy.zdo.types import LogicalType from zigpy_xbee.types import UNKNOWN_IEEE @@ -16,6 +16,8 @@ # end device poll timeout = 3 * SN * SP * 10ms CONF_POLL_TIMEOUT = 0x029b TIMEOUT_TX_STATUS = 120 +TIMEOUT_REPLY = 5 +TIMEOUT_REPLY_EXTENDED = 28 LOGGER = logging.getLogger(__name__) @@ -119,7 +121,8 @@ async def _get_association_state(self): return state @zigpy.util.retryable_request - async def request(self, nwk, profile, cluster, src_ep, dst_ep, sequence, data, expect_reply=True, timeout=10): + async def request(self, nwk, profile, cluster, src_ep, dst_ep, sequence, + data, expect_reply=True, timeout=TIMEOUT_REPLY): LOGGER.debug("Zigbee request seq %s", sequence) assert sequence not in self._pending if expect_reply: @@ -127,6 +130,12 @@ async def request(self, nwk, profile, cluster, src_ep, dst_ep, sequence, data, e self._pending[sequence] = reply_fut dev = self.get_device(nwk=nwk) + if dev.node_desc.logical_type in (LogicalType.EndDevice, None): + tx_opts = 0x60 + rx_timeout = TIMEOUT_REPLY_EXTENDED + else: + tx_opts = 0x20 + rx_timeout = timeout send_req = self._api.tx_explicit( dev.ieee, nwk, @@ -135,7 +144,7 @@ async def request(self, nwk, profile, cluster, src_ep, dst_ep, sequence, data, e cluster, profile, 0, - 0x20, + tx_opts, data, ) @@ -151,7 +160,7 @@ async def request(self, nwk, profile, cluster, src_ep, dst_ep, sequence, data, e cluster)) if expect_reply: try: - return await asyncio.wait_for(reply_fut, timeout) + return await asyncio.wait_for(reply_fut, rx_timeout) except asyncio.TimeoutError as ex: LOGGER.debug("[0x%04x:%s:0x%04x]: no reply: %s", nwk, dst_ep, cluster, ex)