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()
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))
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)
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"
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
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')
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), )
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)
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]
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)