def scan(timeout=DISCOVER_TIMEOUT): """Send a message over the network to discover uPnP devices. Inspired by Crimsdings https://github.com/crimsdings/ChromeCast/blob/master/cc_discovery.py Protocol explanation: https://embeddedinn.wordpress.com/tutorials/upnp-device-architecture/ """ ssdp_requests = ssdp_request(ST_ALL), ssdp_request(ST_ROOTDEVICE) stop_wait = datetime.now() + timedelta(seconds=timeout) sockets = [] for addr in interface_addresses(): try: sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Set the time-to-live for messages for local network sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, SSDP_MX) sock.bind((addr, 0)) sockets.append(sock) except socket.error: pass entries = {} for sock in [s for s in sockets]: try: for req in ssdp_requests: sock.sendto(req, SSDP_TARGET) sock.setblocking(False) except socket.error: sockets.remove(sock) sock.close() try: while sockets: time_diff = stop_wait - datetime.now() seconds_left = time_diff.total_seconds() if seconds_left <= 0: break ready = select.select(sockets, [], [], seconds_left)[0] for sock in ready: try: response = sock.recv(1024).decode("utf-8") except socket.error: logging.getLogger(__name__).exception( "Socket error while discovering SSDP devices") sockets.remove(sock) sock.close() continue entry = UPNPEntry.from_response(response) entries[(entry.st, entry.location)] = entry finally: for s in sockets: s.close() return sorted(entries.values(), key=lambda entry: entry.location)
def scan(st=None, timeout=DISCOVER_TIMEOUT, max_entries=None): """Send a message over the network to discover uPnP devices. Inspired by Crimsdings https://github.com/crimsdings/ChromeCast/blob/master/cc_discovery.py Protocol explanation: https://embeddedinn.wordpress.com/tutorials/upnp-device-architecture/ """ # pylint: disable=too-many-nested-blocks,too-many-branches ssdp_st = st or ST_ALL ssdp_request = "\r\n".join([ 'M-SEARCH * HTTP/1.1', 'HOST: 239.255.255.250:1900', 'MAN: "ssdp:discover"', 'MX: {:d}'.format(SSDP_MX), 'ST: {}'.format(ssdp_st), '', '']).encode('utf-8') if st is None: # Wemo does not respond to a query for all devices+services # but only to a query for just root devices (ST_ROOTDEVICE). Use that # as well for the default case. alt_ssdp_request = "\r\n".join([ 'M-SEARCH * HTTP/1.1', 'HOST: 239.255.255.250:1900', 'MAN: "ssdp:discover"', 'MX: {:d}'.format(SSDP_MX), 'ST: {}'.format(ST_ROOTDEVICE), '', '']).encode('utf-8') stop_wait = datetime.now() + timedelta(0, timeout) sockets = [] for addr in interface_addresses(): try: sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Set the time-to-live for messages for local network sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 1) sock.bind((addr, 0)) sockets.append(sock) except socket.error: pass entries = [] for sock in [s for s in sockets]: try: sock.sendto(ssdp_request, SSDP_TARGET) if alt_ssdp_request: sock.sendto(alt_ssdp_request, SSDP_TARGET) sock.setblocking(False) except socket.error: sockets.remove(sock) sock.close() try: while sockets: time_diff = stop_wait - datetime.now() seconds_left = time_diff.total_seconds() if seconds_left <= 0: break ready = select.select(sockets, [], [], seconds_left)[0] for sock in ready: try: response = sock.recv(1024).decode("utf-8") except socket.error: logging.getLogger(__name__).exception( "Socket error while discovering SSDP devices") sockets.remove(sock) sock.close() continue entry = UPNPEntry.from_response(response) if (st is None or entry.st == st) and entry not in entries: entries.append(entry) if max_entries and len(entries) == max_entries: raise StopIteration except StopIteration: pass finally: for s in sockets: s.close() return entries
def scan(st=None, timeout=DISCOVER_TIMEOUT, max_entries=None): """ Sends a message over the network to discover upnp devices. Inspired by Crimsdings https://github.com/crimsdings/ChromeCast/blob/master/cc_discovery.py Protocol explanation: https://embeddedinn.wordpress.com/tutorials/upnp-device-architecture/ """ # pylint: disable=too-many-nested-blocks,too-many-branches ssdp_st = st or ST_ALL ssdp_request = "\r\n".join([ 'M-SEARCH * HTTP/1.1', 'HOST: 239.255.255.250:1900', 'MAN: "ssdp:discover"', 'MX: {:d}'.format(SSDP_MX), 'ST: {}'.format(ssdp_st), '', '']).encode('utf-8') stop_wait = datetime.now() + timedelta(0, timeout) sockets = [] for addr in interface_addresses(): try: sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Set the time-to-live for messages for local network sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 1) sock.bind((addr, 0)) sockets.append(sock) except socket.error: pass entries = [] for sock in [s for s in sockets]: try: sock.sendto(ssdp_request, SSDP_TARGET) sock.setblocking(False) except socket.error: sockets.remove(sock) sock.close() try: while sockets: time_diff = stop_wait - datetime.now() seconds_left = time_diff.total_seconds() if seconds_left <= 0: break ready = select.select(sockets, [], [], seconds_left)[0] for sock in ready: try: response = sock.recv(1024).decode("utf-8") except socket.error: logging.getLogger(__name__).exception( "Socket error while discovering SSDP devices") sockets.remove(sock) sock.close() continue entry = UPNPEntry.from_response(response) if (st is None or entry.st == st) and entry not in entries: entries.append(entry) if max_entries and len(entries) == max_entries: raise StopIteration except StopIteration: pass finally: for s in sockets: s.close() return entries