class DiscoveryService(Listener): """Discovery event handler for gree devices.""" def __init__(self, hass) -> None: """Initialize discovery service.""" super().__init__() self.hass = hass self.discovery = Discovery(DISCOVERY_TIMEOUT) self.discovery.add_listener(self) hass.data[DOMAIN].setdefault(COORDINATORS, []) async def device_found(self, device_info: DeviceInfo) -> None: """Handle new device found on the network.""" device = Device(device_info) try: await device.bind() except DeviceNotBoundError: _LOGGER.error("Unable to bind to gree device: %s", device_info) except DeviceTimeoutError: _LOGGER.error("Timeout trying to bind to gree device: %s", device_info) _LOGGER.info( "Adding Gree device %s at %s:%i", device.device_info.name, device.device_info.ip, device.device_info.port, ) coordo = DeviceDataUpdateCoordinator(self.hass, device) self.hass.data[DOMAIN][COORDINATORS].append(coordo) await coordo.async_refresh() async_dispatcher_send(self.hass, DISPATCH_DEVICE_DISCOVERED, coordo)
def __init__(self, hass) -> None: """Initialize discovery service.""" super().__init__() self.hass = hass self.discovery = Discovery(DISCOVERY_TIMEOUT) self.discovery.add_listener(self) hass.data[DOMAIN].setdefault(COORDINATORS, [])
async def run_discovery(bind=False): """Run the device discovery process.""" _LOGGER.debug("Scanning network for Gree devices") discovery = Discovery() listener = DiscoveryListener(bind) discovery.add_listener(listener) await discovery.scan(wait_for=10) _LOGGER.info("Done discovering devices")
async def test_add_new_listener(): """Register a listener, test that is registered.""" listener = MagicMock(spec_set=Listener) discovery = Discovery() result = discovery.add_listener(listener) assert result is not None result = discovery.add_listener(listener) assert result is None
async def test_remove_listener(): """Register, remove listener, test results.""" listener = MagicMock(spec_set=Listener) discovery = Discovery() result = discovery.add_listener(listener) assert result is not None result = discovery.remove_listener(listener) assert result is True result = discovery.remove_listener(listener) assert result is False
async def test_add_new_listener_with_devices(): """Register a listener, test that is registered.""" with patch.object(Discovery, "devices", new_callable=PropertyMock) as mock: mock.return_value = [get_mock_device_info()] listener = MagicMock(spec_set=Listener) discovery = Discovery() result = discovery.add_listener(listener) await asyncio.gather(*discovery.tasks) assert result is not None assert len(result) == 1 assert listener.device_found.call_count == 1
async def _async_has_devices(hass: HomeAssistant) -> bool: """Return if there are devices that can be discovered.""" gree_discovery = Discovery(DISCOVERY_TIMEOUT) bcast_addr = list(await async_get_ipv4_broadcast_addresses(hass)) devices = await gree_discovery.scan(wait_for=DISCOVERY_TIMEOUT, bcast_ifaces=bcast_addr) return len(devices) > 0
async def test_discover_devices_bad_data(netifaces, addr, bcast, family): """Create a socket broadcast responder, an async broadcast listener, test discovery responses. """ netifaces.return_value = { 2: [{ "addr": addr[0], "netmask": "255.0.0.0", "peer": bcast }] } with Responder(family, addr[1]) as sock: def responder(s): (d, addr) = s.recvfrom(2048) p = json.loads(d) assert p == DISCOVERY_REQUEST s.sendto("garbage data".encode(), addr) serv = Thread(target=responder, args=(sock, )) serv.start() # Run the listener portion now discovery = Discovery(allow_loopback=True) response = await discovery.scan(wait_for=DEFAULT_TIMEOUT) assert response is not None assert len(response) == 0 sock.close() serv.join(timeout=DEFAULT_TIMEOUT)
async def test_discovery_events(netifaces, addr, bcast, family): netifaces.return_value = { 2: [{ "addr": addr[0], "netmask": "255.0.0.0", "peer": bcast }] } with Responder(family, addr[1]) as sock: def responder(s): (d, addr) = s.recvfrom(2048) p = json.loads(d) assert p == DISCOVERY_REQUEST p = json.dumps(encrypt_payload(DISCOVERY_RESPONSE)) s.sendto(p.encode(), addr) serv = Thread(target=responder, args=(sock, )) serv.start() with patch.object(Discovery, "packet_received", return_value=None) as mock: discovery = Discovery(allow_loopback=True) await discovery.scan() await asyncio.sleep(DEFAULT_TIMEOUT) assert mock.call_count == 1 sock.close() serv.join(timeout=DEFAULT_TIMEOUT)
async def test_discover_deduplicate_multiple_discoveries( netifaces, addr, bcast, family): netifaces.return_value = { 2: [{ "addr": addr[0], "netmask": "255.0.0.0", "peer": bcast }] } devices = [ { "cid": "aabbcc001122", "mac": "aabbcc001122", "name": "MockDevice1" }, { "cid": "aabbcc001123", "mac": "aabbcc001123", "name": "MockDevice2" }, { "cid": "aabbcc001123", "mac": "aabbcc001123", "name": "MockDevice2" }, ] with Responder(family, addr[1]) as sock: def responder(s): (d, addr) = s.recvfrom(2048) p = json.loads(d) assert p == DISCOVERY_REQUEST for d in devices: r = DISCOVERY_RESPONSE.copy() r["pack"].update(d) p = json.dumps(encrypt_payload(r)) s.sendto(p.encode(), addr) serv = Thread(target=responder, args=(sock, )) serv.start() discovery = Discovery(allow_loopback=True) devices = await discovery.scan(wait_for=DEFAULT_TIMEOUT) assert devices is not None assert len(devices) == 2 sock.close() serv.join(timeout=DEFAULT_TIMEOUT)
async def test_discover_no_devices(netifaces): netifaces.return_value = { 2: [{ "addr": "127.0.0.1", "netmask": "255.0.0.0", "peer": "127.255.255.255" }] } discovery = Discovery(allow_loopback=True) devices = await discovery.scan(wait_for=DEFAULT_TIMEOUT) assert devices is not None assert len(devices) == 0
async def _async_has_devices(opp) -> bool: """Return if there are devices that can be discovered.""" gree_discovery = Discovery(DISCOVERY_TIMEOUT) devices = await gree_discovery.scan(wait_for=DISCOVERY_TIMEOUT) return len(devices) > 0