def get_device_by_name(self, player_name, raise_on_notfound=False): try: devices = list(soco.discover()) except TypeError as err: self.error( f"soco.discover returned None for player: {player_name} err: {err}" ) return None for device in devices: if device.player_name == player_name.strip(): return device if raise_on_notfound: raise adplus.ConfigException( f"Could not find Sonos Device named: {player_name}") return None
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 test_discover(self, monkeypatch): # Create a fake socket, whose data is always a certain string monkeypatch.setattr('socket.socket', Mock()) sock = socket.socket.return_value sock.recvfrom.return_value = ( b'SERVER: Linux UPnP/1.0 Sonos/26.1-76230 (ZPS3)', [IP_ADDR] ) # (data, # address) monkeypatch.setattr( 'ifaddr.get_adapters', Mock( side_effect=lambda: [MockAdapter(['192.168.1.15']), MockAdapter(['192.168.1.16'])])) # Fast-forward through timeouts with all_households=True monkeypatch.setattr('time.monotonic', Mock(side_effect=monotonic_time)) # prevent creation of soco instances monkeypatch.setattr('pysonos.config.SOCO_CLASS', Mock(return_value=ZoneMock())) # Fake return value for select monkeypatch.setattr('select.select', Mock(return_value=([sock], [], []))) # set timeout TIMEOUT = 2 discover(timeout=TIMEOUT) # Two packets for 192.168.1.15 and two for 192.168.1.16 assert sock.sendto.call_count == 4 # select called with the relevant timeout select.select.assert_called_with([sock, sock], [], [], 1.6) # SoCo should be created with the IP address received config.SOCO_CLASS.assert_called_with(IP_ADDR) # Now test include_visible parameter. include_invisible=True should # result in calling SoCo.all_zones etc # Reset interfaces to always return the same values monkeypatch.setattr( 'ifaddr.get_adapters', Mock(side_effect=lambda: [MockAdapter('192.168.1.19')])) assert discover(include_invisible=True) == {'ALL'} assert discover(include_invisible=False) == {'VISIBLE'} # if select does not return within timeout SoCo should not be called # at all # simulate no data being returned within timeout monkeypatch.setattr('threading.Thread.join', Mock()) select.select.return_value = ([], [], []) discover(timeout=1) # Check no SoCo instance created config.SOCO_CLASS.assert_not_called
def describe_sonos_system(self, kwargs): devices = list(soco.discover()) favorites = list( soco.music_library.MusicLibrary().get_sonos_favorites()) devices_simple = {} def clip(line): MAX_LENGTH = 75 line = str(line) if len(line) < MAX_LENGTH: return line else: return line[:MAX_LENGTH - 3] + "..." def clipdict(indict): return {k: clip(v) for k, v in indict.items()} for dev in devices: devices_simple[dev.player_name] = { "player_name": dev.player_name, "ip_address": dev.ip_address, "group": str(dev.group), "volume": dev.volume, "play_mode": dev.play_mode, "transport_info": dev.get_current_transport_info(), "current_media_info": (dev.get_current_media_info()), "current_track_info": (dev.get_current_track_info()), # "speaker_info": dev.get_speaker_info() } data = { "devices": devices_simple, "favorites": [clipdict(favorite.to_dict()) for favorite in favorites], } logfile = Path(self.config_dir) / "../logs/sonos_data.json" with logfile.open("wt") as fp: json.dump(data, fp, indent=4)
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)