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)