def discover(timeout=5,
             include_invisible=False,
             interface_addr=None,
             all_households=False):
    """ Discover Sonos zones on the local network.

    Return a set of `SoCo` instances for each zone found.
    Include invisible zones (bridges and slave zones in stereo pairs if
    ``include_invisible`` is `True`. Will block for up to ``timeout`` seconds,
     after which return `None` if no zones found.

    Args:
        timeout (int, optional): block for this many seconds, at most.
            Defaults to 5.
        include_invisible (bool, optional): include invisible zones in the
            return set. Defaults to `False`.
        interface_addr (str or None): Discovery operates by sending UDP
            multicast datagrams. ``interface_addr`` is a string (dotted
            quad) representation of the network interface address to use as
            the source of the datagrams (i.e. it is a value for
            `socket.IP_MULTICAST_IF <socket>`). If `None` or not specified,
            all system interfaces will be tried. Defaults to `None`.
        all_households (bool, optional): wait for all replies to discover
            multiple households. If `False` or not specified, return only
            the first household found.
    Returns:
        set: a set of `SoCo` instances, one for each zone found, or else
            `None`.

    """

    found_zones = set()
    first_response = None

    def callback(zone):
        nonlocal first_response

        if first_response is None:
            first_response = time.monotonic()

        if include_invisible:
            found_zones.update(zone.all_zones)
        else:
            found_zones.update(zone.visible_zones)

        if not all_households:
            thread.stop()

    thread = discover_thread(
        callback, timeout, include_invisible, interface_addr)
    while thread.is_alive() and not thread.stopped():
        if first_response is None:
            thread.join(timeout=1)
        else:
            thread.join(timeout=first_response + 1 - time.monotonic())
            thread.stop()

    return found_zones or None
Discover Sonos zones on the local network.

    Return a set of `SoCo` instances for each zone found.
    Include invisible zones (bridges and slave zones in stereo pairs if
    ``include_invisible`` is `True`. Will block for up to ``timeout`` seconds,
     after which return `None` if no zones found.

    Args:
        timeout (int, optional): block for this many seconds, at most.
            Defaults to 5.
        include_invisible (bool, optional): include invisible zones in the
            return set. Defaults to `False`.
        interface_addr (str or None): Discovery operates by sending UDP
            multicast datagrams. ``interface_addr`` is a string (dotted
            quad) representation of the network interface address to use as
            the source of the datagrams (i.e. it is a value for
            `socket.IP_MULTICAST_IF <socket>`). If `None` or not specified,
            all system interfaces will be tried. Defaults to `None`.
        all_households (bool, optional): wait for all replies to discover
            multiple households. If `False` or not specified, return only
            the first household found.
    Returns:
        set: a set of `SoCo` instances, one for each zone found, or else
            `None`.