def discover(service="ssdp:all", timeout=1, retries=2, ipAddress="239.255.255.250", port=1900):
        """Discovers UPnP devices in the local network.

        Try to discover all devices in the local network which do support UPnP. The discovery process can fail
        for various reasons and it is recommended to do at least two discoveries, which you can specify with the
        ``retries`` parameter.

        The default ``service`` parameter tries to address all devices also if you know which kind of service type
        you are looking for you should set it as some devices do not respond or respond differently otherwise.

        :param service: the service type or list of service types of devices you look for
        :type service: str or list[str]
        :param float timeout: the socket timeout for each try
        :param int retries: how often should be a discovery request send
        :param str ipAddress: the multicast ip address to use
        :param int port: the port to use
        :return: a list of DiscoveryResponse objects or empty if no device was found
        :rtype: list[DiscoveryResponse]

        Example:
          ::

            results = discover()
            for result in results:
                print("Host: " + result.locationHost + " Port: " + result.locationPort + " Device definitions: " + \\
                    result.location)

        .. seealso::

            :class:`~simpletr64.DiscoveryResponse`, :meth:`~simpletr64.Discover.discoverParticularHost`
        """

        socket.setdefaulttimeout(timeout)

        messages = []

        if isinstance(service, str):
            services = [service]
        elif isinstance(service, list):
            services = service

        for service in services:
            message = 'M-SEARCH * HTTP/1.1\r\nMX: 5\r\nMAN: "ssdp:discover"\r\nHOST: ' + \
                      ipAddress + ':' + str(port) + '\r\n'
            message += "ST: " + service + "\r\n\r\n"
            messages.append(message)

        responses = {}

        for _ in range(retries):

            # setup the socket
            sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)

            # noinspection PyAssignmentToLoopOrWithParameter
            for _ in range(2):

                # send the messages with different service types
                for message in messages:
                    # send message more often to make sure all devices will get it
                    sock.sendto(message.encode('utf-8'), (ipAddress, port))

            while True:
                try:
                    # read the message until timeout
                    data = sock.recv(1024)
                except socket.timeout:
                    break
                else:
                    # no time out, read the response data and create response object
                    response = DiscoveryResponse(data)
                    # filter duplicated responses
                    responses[response.location] = response

        # return a list of all responses
        return list(responses.values())
Discovers UPnP devices in the local network.

        Try to discover all devices in the local network which do support UPnP. The discovery process can fail
        for various reasons and it is recommended to do at least two discoveries, which you can specify with the
        ``retries`` parameter.

        The default ``service`` parameter tries to address all devices also if you know which kind of service type
        you are looking for you should set it as some devices do not respond or respond differently otherwise.

        :param service: the service type or list of service types of devices you look for
        :type service: str or list[str]
        :param float timeout: the socket timeout for each try
        :param int retries: how often should be a discovery request send
        :param str ipAddress: the multicast ip address to use
        :param int port: the port to use
        :return: a list of DiscoveryResponse objects or empty if no device was found
        :rtype: list[DiscoveryResponse]

        Example:
          ::

            results = discover()
            for result in results:
                print("Host: " + result.locationHost + " Port: " + result.locationPort + " Device definitions: " + \\
                    result.location)

        .. seealso::

            :class:`~simpletr64.DiscoveryResponse`, :meth:`~simpletr64.Discover.discoverParticularHost`