diff --git a/tests/test_application.py b/tests/test_application.py index 123e88f..7c75e03 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -152,14 +152,16 @@ async def _version(): app._api.version = mock.MagicMock( side_effect=_version) - await app.startup(auto_form=False) - assert app.form_network.call_count == 0 - await app.startup(auto_form=True) - assert app.form_network.call_count == 1 + with mock.patch('zigpy_deconz.zigbee.application.ConBeeDevice') as con: + con.new.side_effect = asyncio.coroutine(mock.MagicMock()) + await app.startup(auto_form=False) + assert app.form_network.call_count == 0 + await app.startup(auto_form=True) + assert app.form_network.call_count == 1 @pytest.mark.asyncio -async def test_permit(app): +async def test_permit(app, nwk): app._api.write_parameter = mock.MagicMock( side_effect=asyncio.coroutine(mock.MagicMock())) time_s = 30 @@ -187,7 +189,7 @@ def aps_data_request(req_id, dst_addr_ep, profile, cluster, src_ep, data): app.get_device = mock.MagicMock( return_value=zigpy.device.Device(app, mock.sentinel.ieee, - mock.sentinel.nwk)) + nwk)) return await app.request(nwk, 0x0260, 1, 2, 3, seq, b'\x01\x02\x03', expect_reply=expect_reply, **kwargs) @@ -306,3 +308,54 @@ def test_rx_device_annce(app, addr_ieee, addr_nwk): assert app.handle_join.call_args[0][0] == addr_nwk.address assert app.handle_join.call_args[0][1] == addr_ieee.address assert app.handle_join.call_args[0][2] == 0 + + +@pytest.mark.asyncio +async def test_conbee_dev_add_to_group(app, nwk): + group = mock.MagicMock() + app._groups = mock.MagicMock() + app._groups.add_group.return_value = group + + conbee = application.ConBeeDevice(app, mock.sentinel.ieee, nwk) + + await conbee.add_to_group(mock.sentinel.grp_id, mock.sentinel.grp_name) + assert group.add_member.call_count == 1 + + assert app.groups.add_group.call_count == 1 + assert app.groups.add_group.call_args[0][0] is mock.sentinel.grp_id + assert app.groups.add_group.call_args[0][1] is mock.sentinel.grp_name + + +@pytest.mark.asyncio +async def test_conbee_dev_remove_from_group(app, nwk): + group = mock.MagicMock() + app.groups[mock.sentinel.grp_id] = group + conbee = application.ConBeeDevice(app, + mock.sentinel.ieee, nwk) + + await conbee.remove_from_group(mock.sentinel.grp_id) + assert group.remove_member.call_count == 1 + + +def test_conbee_props(nwk): + conbee = application.ConBeeDevice(app, mock.sentinel.ieee, nwk) + assert conbee.manufacturer is not None + assert conbee.model is not None + + +@pytest.mark.asyncio +async def test_conbee_new(app, nwk, monkeypatch): + mock_init = mock.MagicMock( + side_effect=asyncio.coroutine(mock.MagicMock()) + ) + monkeypatch.setattr(zigpy.device.Device, '_initialize', mock_init) + + conbee = await application.ConBeeDevice.new(app, mock.sentinel.ieee, nwk) + assert isinstance(conbee, zigpy_deconz.zigbee.application.ConBeeDevice) + assert mock_init.call_count == 1 + mock_init.reset_mock() + + app.devices[mock.sentinel.ieee] = mock.MagicMock() + conbee = await application.ConBeeDevice.new(app, mock.sentinel.ieee, nwk) + assert isinstance(conbee, zigpy_deconz.zigbee.application.ConBeeDevice) + assert mock_init.call_count == 0 diff --git a/zigpy_deconz/zigbee/application.py b/zigpy_deconz/zigbee/application.py index a74e39c..c5daaf2 100644 --- a/zigpy_deconz/zigbee/application.py +++ b/zigpy_deconz/zigbee/application.py @@ -7,6 +7,7 @@ import zigpy.application import zigpy.exceptions +import zigpy.endpoint import zigpy.types import zigpy.util import zigpy.device @@ -65,6 +66,8 @@ async def startup(self, auto_form=False): if auto_form: await self.form_network() + self.devices[self.ieee] = await ConBeeDevice.new(self, + self.ieee, self.nwk) async def force_remove(self, dev): """Forcibly remove device from NCP.""" @@ -283,3 +286,48 @@ def __exit__(self, exc_type, exc_value, exc_traceback): self.sequence, exc_type.__name__) return False + + +class ConBeeDevice(zigpy.device.Device): + """Zigpy Device representing Coordinator.""" + + async def add_to_group(self, grp_id: int, + name: str = None) -> None: + group = self.application.groups.add_group(grp_id, name) + group.add_member(self) + return + + async def remove_from_group(self, grp_id: int) -> None: + self.application.groups[grp_id].remove_member(self) + return + + @property + def manufacturer(self): + return "dresden elektronik" + + @property + def model(self): + return 'ConBee' + + @classmethod + async def new(cls, application, ieee, nwk): + """Create or replace zigpy device.""" + dev = cls(application, ieee, nwk) + + if ieee in application.devices: + from_dev = application.get_device(ieee=ieee) + dev.status = from_dev.status + dev.node_desc = from_dev.node_desc + for ep_id, from_ep in from_dev.endpoints.items(): + if not ep_id: + continue # Skip ZDO + ep = dev.add_endpoint(ep_id) + ep.profile_id = from_ep.profile_id + ep.device_type = from_ep.device_type + ep.status = from_ep.status + ep.in_clusters = from_ep.in_clusters + ep.out_clusters = from_ep.out_clusters + else: + await dev._initialize() + + return dev