def datagram_received(self, data, addr) -> None: """Handle discovery responses.""" ip, port = addr if ip in self.discovered_devices: return if port == 9999: info = json.loads(TPLinkSmartHomeProtocol.decrypt(data)) device_class = Discover._get_device_class(info) device = device_class(ip) else: info = json.loads(data[16:]) device_class = Discover._get_new_device_class(info) owner = Discover._get_new_owner(info) if owner is not None: owner_bin = bytes.fromhex(owner) _LOGGER.debug( "[DISCOVERY] Device owner is %s, empty owner is %s", owner_bin, self.emptyUser, ) if owner is None or owner == "" or owner_bin == self.emptyUser: _LOGGER.debug("[DISCOVERY] Device %s has no owner", ip) device = device_class(ip, Auth()) elif (self.authentication is not None and owner_bin == self.authentication.owner()): _LOGGER.debug("[DISCOVERY] Device %s has authenticated owner", ip) device = device_class(ip, self.authentication) else: _LOGGER.debug("[DISCOVERY] Found %s with unknown owner %s", ip, owner) return _LOGGER.debug("[DISCOVERY] %s << %s", ip, info) asyncio.ensure_future(device.update()) 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 read_payloads_from_file(file): """Read the given pcap file and yield json payloads.""" pcap = dpkt.pcap.Reader(file) for ts, pkt in pcap: eth = Ethernet(pkt) if eth.type != ETH_TYPE_IP: continue ip = eth.ip if ip.p == 6: transport = ip.tcp elif ip == 17: transport = ip.udp else: continue if transport.sport != 9999 and transport.dport != 9999: continue data = transport.data try: decrypted = TPLinkSmartHomeProtocol.decrypt(data[4:]) except Exception as ex: click.echo( click.style(f"Unable to decrypt the data, ignoring: {ex}", fg="red") ) continue try: json_payload = json.loads(decrypted) except Exception as ex: click.echo( click.style(f"Unable to parse payload, ignoring: {ex}", fg="red") ) continue if not json_payload: # ignore empty payloads click.echo(click.style("Got empty payload, ignoring", fg="red")) continue yield json_payload
def datagram_received(self, data, addr) -> None: """Handle discovery responses.""" ip, port = addr if ip in self.discovered_devices: return info = json.loads(TPLinkSmartHomeProtocol.decrypt(data)) _LOGGER.debug("[DISCOVERY] %s << %s", ip, info) try: device = Discover._create_device_from_discovery_info(ip, info) except SmartDeviceException as ex: _LOGGER.debug("Unable to find device type from %s: %s", info, ex) return self.discovered_devices[ip] = device if device 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)
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."""