def __init__(self, searchTarget, maxWaitTime, upnp, ignoreTimeout=True, ignoreError=True, verbose=False, loggerName="SSDP Client"): """SSDP Constructor Args: :param searchTarget - String contaning ST tag according to UPnP documentation. :param maxWaitTime - Int contaning max wait time according to UPnP version. :param upnp - float protocol version, 1.0 or 1.1 :param ignoreTimeout - bool contaning surpressing status on socket.timeout :param ignoreError - bool contaning surpressing status on socket.error :param verbose - bool to set verbose on actions. :param loggerName - String logger name, default: "SSDP Client". """ # Validating upnp version with their own maximum timeout assert (upnp == 1.0 and (maxWaitTime >= 1 and maxWaitTime <= 120)) \ or (upnp == 1.1 and (maxWaitTime >= 1 and maxWaitTime <= 3)) # Results will be appended on this list. self.result = ResponseHandler() # Give 1 more sec for timeout. self.maxWait = maxWaitTime + 1 self.logging = logging.getLogger(loggerName) # Starting Multicast Class. self.transport = Multicast(multicastAddress="239.255.255.250", multicastPort=1900, socketTimeout=self.maxWait, multicastTTL=socketTTL.UPnPTTL, loggerName=loggerName, verbose=verbose, payloadParser=SSDPResponseParser) self.ignoreError = ignoreError self.ignoreTimeout = ignoreTimeout # if verbose, Log all. self.verbose = verbose # Build protocol self.transport.buildProtocol() # Multicast SSDP Package over the subnet, if the firewall is not blocking cross # hops over subnet, this may reach another subnet. self.transport.multicastDatagram(mSearch(searchTarget=searchTarget, maxWaitTime=maxWaitTime)) threading.Thread.__init__(self)
class SSDP(threading.Thread): """Simple Service Discovery Protocol for services and devices discovery over the LAN. This protocol basically sends a multicast UDP package containing a HTTP based SOAP message for the group 239.255.255.250 and waits for a unicast HTTP SOAP message response from the device/service if any of the searched over the net answers. Note: Its pretty common to send a UPnP 1.1 protocol scan and receive an UPnP 1.0 response, since they're pretty equal. The searchTarget supports a series of payload Flow: Multicast Group(239.255.255.250) |L| ______________________ |A| ____________________ | Control Point | Multicast |N| | root device | | | (request) | | | --------------- | | |===============|=|====>| |device | | | | | | | | ------------- | | | |<--------------|-|-----| | | service | | | | | Unicast | | | | |___________| | | | | (response) | | | |_______________| | |_____________________| |___________________| Response on devices list: UdpPackage(data=<SSDPResponseParser(http://10.35.107.202:2869/upnphost/udhisapi.dll?content=uuid:30c0fe1b-528d-4870-a0c0-73dbc2c14a10, urn:dmc-samsung-com:device:SyncServer:1, uuid:30c0fe1b-528d-4870-a0c0-73dbc2c14a10::urn:dmc-samsung-com:device:SyncServer:1, Microsoft-Windows-NT/5.1 UPnP/1.0 UPnP-Device-Host/1.0, 3200, "http://schemas.upnp.org/upnp/1/0/"; ns=01)>, host='10.35.107.202', port=1900, status=1) """ def __init__(self, searchTarget, maxWaitTime, upnp, ignoreTimeout=True, ignoreError=True, verbose=False, loggerName="SSDP Client"): """SSDP Constructor Args: :param searchTarget - String contaning ST tag according to UPnP documentation. :param maxWaitTime - Int contaning max wait time according to UPnP version. :param upnp - float protocol version, 1.0 or 1.1 :param ignoreTimeout - bool contaning surpressing status on socket.timeout :param ignoreError - bool contaning surpressing status on socket.error :param verbose - bool to set verbose on actions. :param loggerName - String logger name, default: "SSDP Client". """ # Validating upnp version with their own maximum timeout assert (upnp == 1.0 and (maxWaitTime >= 1 and maxWaitTime <= 120)) \ or (upnp == 1.1 and (maxWaitTime >= 1 and maxWaitTime <= 3)) # Results will be appended on this list. self.result = ResponseHandler() # Give 1 more sec for timeout. self.maxWait = maxWaitTime + 1 self.logging = logging.getLogger(loggerName) # Starting Multicast Class. self.transport = Multicast(multicastAddress="239.255.255.250", multicastPort=1900, socketTimeout=self.maxWait, multicastTTL=socketTTL.UPnPTTL, loggerName=loggerName, verbose=verbose, payloadParser=SSDPResponseParser) self.ignoreError = ignoreError self.ignoreTimeout = ignoreTimeout # if verbose, Log all. self.verbose = verbose # Build protocol self.transport.buildProtocol() # Multicast SSDP Package over the subnet, if the firewall is not blocking cross # hops over subnet, this may reach another subnet. self.transport.multicastDatagram(mSearch(searchTarget=searchTarget, maxWaitTime=maxWaitTime)) threading.Thread.__init__(self) def run(self): """Thread task runner to multicast SSDP and wait for unicast responses""" start = time.time() while (time.time() - start) < self.maxWait: res = self.transport.receiveDatagram() # Surpress Errors and timeouts on recvfrom() # if surpressing is set on constructor. if (self.ignoreError == False and res.status == NetworkStatus.error) or \ (self.ignoreTimeout == False and res.status == NetworkStatus.timeout): self.result.add(res) else: self.result.add(res) if self.verbose: self.logging.info('Search done') def join(self, timeout=None): super(SSDP, self).join(timeout)