Beispiel #1
0
def soco(request):
    """Set up and tear down the soco fixture used by all tests."""
    # Get the ip address from the command line, and create the soco object
    # Only one is used per test session, hence the decorator
    ip = request.config.option.IP
    if ip is None:
        pytest.fail("No ip address specified. Use the --ip option.")
    soco_instance = pysonos_module.SoCo(ip)
    # Check the device is playing and has items in the queue
    if len(soco_instance.get_queue()) == 0:
        pytest.fail("Integration tests on the SoCo class must be run "
                    "with at least 1 item in the playlist.")

    transport_info = soco_instance.get_current_transport_info()
    if transport_info["current_transport_state"] != "PLAYING":
        pytest.fail("Integration tests on the SoCo class must be run "
                    "with the Sonos unit playing.")
    # Save the device's state
    state = {
        "queue": soco_instance.get_queue(0, 1000),
        "current_track_info": soco_instance.get_current_track_info(),
    }

    # Yield the device to the test function
    yield soco_instance

    # Tear down. Restore state
    soco_instance.stop()
    soco_instance.clear_queue()
    for track in state["queue"]:
        soco_instance.add_to_queue(track)
    soco_instance.play_from_queue(
        int(state["current_track_info"]["playlist_position"]) - 1)
    soco_instance.seek(state["current_track_info"]["position"])
    soco_instance.play()
Beispiel #2
0
    def _discovery(now=None):
        """Discover players from network or configuration."""
        hosts = config.get(CONF_HOSTS)

        def _discovered_player(soco):
            """Handle a (re)discovered player."""
            try:
                entity = _get_entity_from_soco_uid(hass, soco.uid)

                if not entity:
                    hass.add_job(async_add_entities, [SonosEntity(soco)])
                else:
                    hass.add_job(entity.async_seen())
            except SoCoException:
                pass

        if hosts:
            for host in hosts:
                try:
                    player = pysonos.SoCo(socket.gethostbyname(host))
                    if player.is_visible:
                        # Make sure that the player is available
                        _ = player.volume

                        _discovered_player(player)
                except (OSError, SoCoException):
                    if now is None:
                        _LOGGER.warning("Failed to initialize '%s'", host)

            hass.helpers.event.call_later(DISCOVERY_INTERVAL, _discovery)
        else:
            pysonos.discover_thread(
                _discovered_player,
                interval=DISCOVERY_INTERVAL,
                interface_addr=config.get(CONF_INTERFACE_ADDR))
Beispiel #3
0
 def _discovered_ip(ip_address):
     try:
         player = pysonos.SoCo(ip_address)
     except (OSError, SoCoException):
         _LOGGER.debug("Failed to connect to discovered player '%s'",
                       ip_address)
         return
     if player.is_visible:
         _discovered_player(player)
Beispiel #4
0
    def _discovery(now: datetime.datetime | None = None) -> None:
        """Discover players from network or configuration."""
        hosts = config.get(CONF_HOSTS)

        def _discovered_player(soco: SoCo) -> None:
            """Handle a (re)discovered player."""
            try:
                _LOGGER.debug("Reached _discovered_player, soco=%s", soco)

                data = hass.data[DATA_SONOS]

                if soco.uid not in data.discovered:
                    speaker_info = soco.get_speaker_info(True)
                    _LOGGER.debug("Adding new speaker: %s", speaker_info)
                    speaker = SonosSpeaker(hass, soco, speaker_info)
                    data.discovered[soco.uid] = speaker
                    if soco.household_id not in data.favorites:
                        data.favorites[soco.household_id] = SonosFavorites(
                            hass, soco.household_id
                        )
                        data.favorites[soco.household_id].update()
                    speaker.setup()
                else:
                    dispatcher_send(hass, f"{SONOS_SEEN}-{soco.uid}", soco)

            except SoCoException as ex:
                _LOGGER.debug("SoCoException, ex=%s", ex)

        if hosts:
            for host in hosts:
                try:
                    _LOGGER.debug("Testing %s", host)
                    player = pysonos.SoCo(socket.gethostbyname(host))
                    if player.is_visible:
                        # Make sure that the player is available
                        _ = player.volume

                        _discovered_player(player)
                except (OSError, SoCoException) as ex:
                    _LOGGER.debug("Exception %s", ex)
                    if now is None:
                        _LOGGER.warning("Failed to initialize '%s'", host)

            _LOGGER.debug("Tested all hosts")
            hass.data[DATA_SONOS].hosts_heartbeat = hass.helpers.event.call_later(
                DISCOVERY_INTERVAL.total_seconds(), _discovery
            )
        else:
            _LOGGER.debug("Starting discovery thread")
            hass.data[DATA_SONOS].discovery_thread = pysonos.discover_thread(
                _discovered_player,
                interval=DISCOVERY_INTERVAL.total_seconds(),
                interface_addr=config.get(CONF_INTERFACE_ADDR),
            )
            hass.data[DATA_SONOS].discovery_thread.name = "Sonos-Discovery"
Beispiel #5
0
 def _create_soco(ip_address: str,
                  source: SoCoCreationSource) -> SoCo | None:
     """Create a soco instance and return if successful."""
     try:
         soco = pysonos.SoCo(ip_address)
         # Ensure that the player is available and UID is cached
         _ = soco.uid
         _ = soco.volume
         return soco
     except (OSError, SoCoException) as ex:
         _LOGGER.warning("Failed to connect to %s player '%s': %s",
                         source.value, ip_address, ex)
     return None
Beispiel #6
0
def init(**kwargs):
    """Initialize variables for the unittests that are only known at run
    time."""
    global SOCO  # pylint: disable-msg=W0603
    SOCO = pysonos.SoCo(kwargs['ip'])

    if len(SOCO.get_queue()) == 0:
        raise SoCoUnitTestInitError('Unit tests on the SoCo class must be run '
                                    'with at least 1 item in the playlist')

    transport_info = SOCO.get_current_transport_info()
    if transport_info['current_transport_state'] != 'PLAYING':
        raise SoCoUnitTestInitError('Unit tests on the SoCo class must be run '
                                    'with the sonos unit playing')
Beispiel #7
0
    def _discovery(now=None):
        """Discover players from network or configuration."""
        hosts = config.get(CONF_HOSTS)
        _LOGGER.debug(hosts)

        def _discovered_alarm(soco):
            """Handle a (re)discovered player."""
            try:
                _LOGGER.debug("Reached _discovered_player, soco=%s", soco)
                for one_alarm in alarms.get_alarms(soco):
                    if one_alarm.zone == soco and one_alarm._alarm_id not in hass.data[DATA_SONOS].discovered:
                        _LOGGER.debug("Adding new alarm")
                        hass.data[DATA_SONOS].discovered.append(one_alarm._alarm_id)
                        hass.add_job(
                            async_add_entities, [SonosAlarmSwitch(one_alarm)],
                        )
                    else:
                        entity = _get_entity_from_alarm_id(hass, one_alarm._alarm_id)
                        if entity and (entity.soco == soco or not entity.available):
                            _LOGGER.debug("Seen %s", entity)
                            hass.add_job(entity.async_seen(soco))  # type: ignore
            except SoCoUPnPException as ex:
                _LOGGER.debug("SoCoException, ex=%s", ex)

        if hosts:
            for host in hosts:
                try:
                    _LOGGER.debug("Testing %s", host)
                    player = pysonos.SoCo(socket.gethostbyname(host))
                    if player.is_visible:
                        # Make sure that the player is available
                        _ = player.volume

                        _discovered_alarm(player)
                except (OSError, SoCoUPnPException) as ex:
                    _LOGGER.debug("Exception %s", ex)
                    if now is None:
                        _LOGGER.warning("Failed to initialize '%s'", host)

            _LOGGER.debug("Tested all hosts")
            hass.data[DATA_SONOS].hosts_heartbeat = hass.helpers.event.call_later(
                DISCOVERY_INTERVAL, _discovery)
        else:
            _LOGGER.debug("Starting discovery thread")
            hass.data[DATA_SONOS].discovery_thread = pysonos.discover_thread(
                _discovered_alarm,
                interval=DISCOVERY_INTERVAL,
                interface_addr=config.get(CONF_INTERFACE_ADDR),
            )
            hass.data[DATA_SONOS].discovery_thread.name = "Sonos-Discovery"
    def _discovery(now=None):
        """Discover players from network or configuration."""
        hosts = config.get(CONF_HOSTS)

        def _discovered_player(soco):
            """Handle a (re)discovered player."""
            try:
                _LOGGER.debug("Reached _discovered_player, soco=%s", soco)

                if soco not in hass.data[DATA_SONOS].discovered:
                    _LOGGER.debug("Adding new entity")
                    hass.data[DATA_SONOS].discovered.append(soco)
                    hass.add_job(async_add_entities, [SonosEntity(soco)])
                else:
                    entity = _get_entity_from_soco_uid(hass, soco.uid)
                    if entity:
                        _LOGGER.debug("Seen %s", entity)
                        hass.add_job(entity.async_seen())
            except SoCoException as ex:
                _LOGGER.debug("SoCoException, ex=%s", ex)

        if hosts:
            for host in hosts:
                try:
                    _LOGGER.debug("Testing %s", host)
                    player = pysonos.SoCo(socket.gethostbyname(host))
                    if player.is_visible:
                        # Make sure that the player is available
                        _ = player.volume

                        _discovered_player(player)
                except (OSError, SoCoException) as ex:
                    _LOGGER.debug("Exception %s", ex)
                    if now is None:
                        _LOGGER.warning("Failed to initialize '%s'", host)

            _LOGGER.debug("Tested all hosts")
            hass.data[
                DATA_SONOS].hosts_heartbeat = hass.helpers.event.call_later(
                    DISCOVERY_INTERVAL, _discovery)
        else:
            _LOGGER.debug("Starting discovery thread")
            hass.data[DATA_SONOS].discovery_thread = pysonos.discover_thread(
                _discovered_player,
                interval=DISCOVERY_INTERVAL,
                interface_addr=config.get(CONF_INTERFACE_ADDR),
            )
    def _discovery(now=None):
        """Discover players from network or configuration."""
        hosts = config.get(CONF_HOSTS)
        _LOGGER.debug(hosts)
        alarm_list = []

        def _discovered_alarm(soco):
            """Handle a (re)discovered player."""
            try:
                _LOGGER.debug("Reached _discovered_player, soco=%s", soco)
                for one_alarm in alarms.get_alarms(soco):
                    if one_alarm.zone == soco and one_alarm not in alarm_list:
                        _LOGGER.debug("Adding new alarm")
                        alarm_list.append(one_alarm)
                        hass.add_job(
                            async_add_entities,
                            [SonosAlarmSwitch(soco, one_alarm)],
                        )
            except SoCoException as ex:
                _LOGGER.debug("SoCoException, ex=%s", ex)

        if hosts:
            for host in hosts:
                try:
                    _LOGGER.debug("Testing %s", host)
                    player = pysonos.SoCo(socket.gethostbyname(host))
                    if player.is_visible:
                        # Make sure that the player is available
                        _ = player.volume

                        _discovered_alarm(player)
                except (OSError, SoCoException) as ex:
                    _LOGGER.debug("Exception %s", ex)
                    if now is None:
                        _LOGGER.warning("Failed to initialize '%s'", host)

            _LOGGER.debug("Tested all hosts")
            hass.helpers.event.call_later(DISCOVERY_INTERVAL, _discovery)
        else:
            _LOGGER.debug("Starting discovery thread")
            pysonos.discover_thread(
                _discovered_alarm,
                interval=DISCOVERY_INTERVAL,
                interface_addr=config.get(CONF_INTERFACE_ADDR),
            )
Beispiel #10
0
    def _manual_hosts(now: datetime.datetime | None = None) -> None:
        """Players from network configuration."""
        for host in hosts:
            try:
                _LOGGER.debug("Testing %s", host)
                player = pysonos.SoCo(socket.gethostbyname(host))
                if player.is_visible:
                    # Make sure that the player is available
                    _ = player.volume
                _discovered_player(player)
            except (OSError, SoCoException) as ex:
                _LOGGER.debug("Issue connecting to '%s': %s", host, ex)
                if now is None:
                    _LOGGER.warning("Failed to initialize '%s'", host)

        _LOGGER.debug("Tested all hosts")
        data.hosts_heartbeat = hass.helpers.event.call_later(
            DISCOVERY_INTERVAL.total_seconds(), _manual_hosts)
Beispiel #11
0
    def _create_sonos_entities():
        """Discover players and return a list of SonosEntity objects."""
        players = []
        hosts = config.get(CONF_HOSTS)

        if hosts:
            for host in hosts:
                try:
                    players.append(pysonos.SoCo(socket.gethostbyname(host)))
                except (OSError, SoCoException):
                    _LOGGER.warning("Failed to initialize '%s'", host)
        else:
            players = pysonos.discover(
                interface_addr=config.get(CONF_INTERFACE_ADDR),
                all_households=True)

        if not players:
            _LOGGER.warning("No Sonos speakers found")

        return [SonosEntity(p) for p in players]
Beispiel #12
0
def _setup_platform(hass, config, add_entities, discovery_info):
    """Set up the Sonos platform."""
    import pysonos

    if DATA_SONOS not in hass.data:
        hass.data[DATA_SONOS] = SonosData(hass)

    advertise_addr = config.get(CONF_ADVERTISE_ADDR)
    if advertise_addr:
        pysonos.config.EVENT_ADVERTISE_IP = advertise_addr

    players = []
    if discovery_info:
        player = pysonos.SoCo(discovery_info.get('host'))

        # If host already exists by config
        if player.uid in hass.data[DATA_SONOS].uids:
            return

        # If invisible, such as a stereo slave
        if not player.is_visible:
            return

        players.append(player)
    else:
        hosts = config.get(CONF_HOSTS)
        if hosts:
            # Support retro compatibility with comma separated list of hosts
            # from config
            hosts = hosts[0] if len(hosts) == 1 else hosts
            hosts = hosts.split(',') if isinstance(hosts, str) else hosts
            for host in hosts:
                try:
                    players.append(pysonos.SoCo(socket.gethostbyname(host)))
                except OSError:
                    _LOGGER.warning("Failed to initialize '%s'", host)
        else:
            players = pysonos.discover(
                interface_addr=config.get(CONF_INTERFACE_ADDR),
                all_households=True)

        if not players:
            _LOGGER.warning("No Sonos speakers found")
            return

    hass.data[DATA_SONOS].uids.update(p.uid for p in players)
    add_entities(SonosEntity(p) for p in players)
    _LOGGER.debug("Added %s Sonos speakers", len(players))

    def _service_to_entities(service):
        """Extract and return entities from service call."""
        entity_ids = service.data.get('entity_id')

        entities = hass.data[DATA_SONOS].entities
        if entity_ids:
            entities = [e for e in entities if e.entity_id in entity_ids]

        return entities

    async def async_service_handle(service):
        """Handle async services."""
        entities = _service_to_entities(service)

        if service.service == SERVICE_JOIN:
            master = [e for e in hass.data[DATA_SONOS].entities
                      if e.entity_id == service.data[ATTR_MASTER]]
            if master:
                await SonosEntity.join_multi(hass, master[0], entities)
        elif service.service == SERVICE_UNJOIN:
            await SonosEntity.unjoin_multi(hass, entities)
        elif service.service == SERVICE_SNAPSHOT:
            await SonosEntity.snapshot_multi(
                hass, entities, service.data[ATTR_WITH_GROUP])
        elif service.service == SERVICE_RESTORE:
            await SonosEntity.restore_multi(
                hass, entities, service.data[ATTR_WITH_GROUP])

    hass.services.register(
        DOMAIN, SERVICE_JOIN, async_service_handle,
        schema=SONOS_JOIN_SCHEMA)

    hass.services.register(
        DOMAIN, SERVICE_UNJOIN, async_service_handle,
        schema=SONOS_SCHEMA)

    hass.services.register(
        DOMAIN, SERVICE_SNAPSHOT, async_service_handle,
        schema=SONOS_STATES_SCHEMA)

    hass.services.register(
        DOMAIN, SERVICE_RESTORE, async_service_handle,
        schema=SONOS_STATES_SCHEMA)

    def service_handle(service):
        """Handle sync services."""
        for entity in _service_to_entities(service):
            if service.service == SERVICE_SET_TIMER:
                entity.set_sleep_timer(service.data[ATTR_SLEEP_TIME])
            elif service.service == SERVICE_CLEAR_TIMER:
                entity.clear_sleep_timer()
            elif service.service == SERVICE_UPDATE_ALARM:
                entity.set_alarm(**service.data)
            elif service.service == SERVICE_SET_OPTION:
                entity.set_option(**service.data)

    hass.services.register(
        DOMAIN, SERVICE_SET_TIMER, service_handle,
        schema=SONOS_SET_TIMER_SCHEMA)

    hass.services.register(
        DOMAIN, SERVICE_CLEAR_TIMER, service_handle,
        schema=SONOS_SCHEMA)

    hass.services.register(
        DOMAIN, SERVICE_UPDATE_ALARM, service_handle,
        schema=SONOS_UPDATE_ALARM_SCHEMA)

    hass.services.register(
        DOMAIN, SERVICE_SET_OPTION, service_handle,
        schema=SONOS_SET_OPTION_SCHEMA)