def do_discover(self) -> None: """Send number of discovery datagrams.""" req = json.dumps(Discover.DISCOVERY_QUERY) _LOGGER.debug("[DISCOVERY] %s >> %s", self.target, Discover.DISCOVERY_QUERY) encrypted_req = TPLinkSmartHomeProtocol.encrypt(req) for i in range(self.discovery_packets): self.transport.sendto(encrypted_req[4:], self.target) # type: ignore
class _DiscoverProtocol(asyncio.DatagramProtocol): """Implementation of the discovery protocol handler. This is internal class, use :func:Discover.discover: instead. """ discovered_devices: Dict[str, SmartDevice] discovered_devices_raw: Dict[str, Dict] def __init__( self, *, on_discovered: OnDiscoveredCallable = None, target: str = "255.255.255.255", timeout: int = 5, discovery_packets: int = 3, ): self.transport = None self.tries = discovery_packets self.timeout = timeout self.on_discovered = on_discovered self.protocol = TPLinkSmartHomeProtocol() self.target = (target, Discover.DISCOVERY_PORT) self.discovered_devices = {} self.discovered_devices_raw = {} def connection_made(self, transport) -> None: """Set socket options for broadcasting.""" self.transport = transport sock = transport.get_extra_info("socket") sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.do_discover() def do_discover(self) -> None: """Send number of discovery datagrams.""" req = json.dumps(Discover.DISCOVERY_QUERY) _LOGGER.debug("[DISCOVERY] %s >> %s", self.target, Discover.DISCOVERY_QUERY) encrypted_req = self.protocol.encrypt(req) for i in range(self.tries): self.transport.sendto(encrypted_req[4:], self.target) # type: ignore def datagram_received(self, data, addr) -> None: """Handle discovery responses.""" ip, port = addr if ip in self.discovered_devices: return info = json.loads(self.protocol.decrypt(data)) _LOGGER.debug("[DISCOVERY] %s << %s", ip, info) device_class = Discover._get_device_class(info) device = device_class(ip) self.discovered_devices[ip] = device self.discovered_devices_raw[ip] = info if device_class is not None: if self.on_discovered is not None: asyncio.ensure_future(self.on_discovered(device)) else: _LOGGER.error("Received invalid response: %s", info) def error_received(self, ex): """Handle asyncio.Protocol errors.""" _LOGGER.error("Got error: %s", ex) def connection_lost(self, ex): """NOP implementation of connection lost."""