def _create_visible_speakers(self, ip_address: str) -> None: """Create all visible SonosSpeaker instances with the provided seed IP.""" try: soco = SoCo(ip_address) visible_zones = soco.visible_zones self._known_invisible = soco.all_zones - visible_zones except (OSError, SoCoException) as ex: _LOGGER.warning("Failed to request visible zones from %s: %s", ip_address, ex) return for zone in visible_zones: if zone.uid not in self.data.discovered: self._add_speaker(zone)
def _discovered_player(self, soco: SoCo) -> None: """Handle a (re)discovered player.""" try: speaker_info = soco.get_speaker_info(True) _LOGGER.debug("Adding new speaker: %s", speaker_info) speaker = SonosSpeaker(self.hass, soco, speaker_info) self.data.discovered[soco.uid] = speaker for coordinator, coord_dict in ( (SonosAlarms, self.data.alarms), (SonosFavorites, self.data.favorites), ): if soco.household_id not in coord_dict: new_coordinator = coordinator(self.hass, soco.household_id) new_coordinator.setup(soco) coord_dict[soco.household_id] = new_coordinator speaker.setup() except (OSError, SoCoException): _LOGGER.warning("Failed to add SonosSpeaker using %s", soco, exc_info=True)
def _create_soco(self, ip_address: str, source: SoCoCreationSource) -> SoCo | None: """Create a soco instance and return if successful.""" if ip_address in self.data.discovery_ignored: return None try: soco = SoCo(ip_address) # Ensure that the player is available and UID is cached uid = soco.uid _ = soco.volume return soco except NotSupportedException as exc: _LOGGER.debug("Device %s is not supported, ignoring: %s", uid, exc) self.data.discovery_ignored.add(ip_address) except (OSError, SoCoException) as ex: _LOGGER.warning("Failed to connect to %s player '%s': %s", source.value, ip_address, ex) return None
def _add_speaker(self, soco: SoCo) -> None: """Create and set up a new SonosSpeaker instance.""" try: speaker_info = soco.get_speaker_info(True) if soco.uid not in self.data.boot_counts: self.data.boot_counts[soco.uid] = soco.boot_seqnum _LOGGER.debug("Adding new speaker: %s", speaker_info) speaker = SonosSpeaker(self.hass, soco, speaker_info) self.data.discovered[soco.uid] = speaker for coordinator, coord_dict in ( (SonosAlarms, self.data.alarms), (SonosFavorites, self.data.favorites), ): if soco.household_id not in coord_dict: new_coordinator = coordinator(self.hass, soco.household_id) new_coordinator.setup(soco) coord_dict[soco.household_id] = new_coordinator speaker.setup(self.entry) except (OSError, SoCoException): _LOGGER.warning("Failed to add SonosSpeaker using %s", soco, exc_info=True)
def _get_account_xml(soco): """Fetch the account data from a Sonos device. Args: soco (SoCo): a SoCo instance to query. If soco is none, a random device will be used. Returns: (str): a byte string containing the account data xml """ # It is likely that the same information is available over UPnP as well # via a call to # systemProperties.GetStringX([('VariableName','R_SvcAccounts')])) # This returns an encrypted string, and, so far, we cannot decrypt it device = soco or SoCo.any_soco() log.debug("Fetching account data from %s", device) settings_url = "http://{0}:1400/status/accounts".format( device.ip_address) result = requests.get(settings_url).content log.debug("Account data: %s", result) return result
def _get_account_xml(soco): """Fetch the account data from a Sonos device. Args: soco (SoCo): a SoCo instance to query. If soco is none, a random device will be used. Returns: (str): a byte string containing the account data xml """ # It is likely that the same information is available over UPnP as well # via a call to # systemProperties.GetStringX([('VariableName','R_SvcAccounts')])) # This returns an encrypted string, and, so far, we cannot decrypt it device = soco or SoCo.any_soco() log.debug("Fetching account data from %s", device) settings_url = "http://{0}:1400/status/accounts".format( device.ip_address) result = requests.get(settings_url).content log.debug("Account data: %s", result) return result
def _poll_manual_hosts(self, now: datetime.datetime | None = None) -> None: """Add and maintain Sonos devices from a manual configuration.""" for host in self.hosts: ip_addr = socket.gethostbyname(host) soco = SoCo(ip_addr) try: visible_zones = soco.visible_zones except OSError: _LOGGER.warning("Could not get visible Sonos devices from %s", ip_addr) else: if new_hosts := { x.ip_address for x in visible_zones if x.ip_address not in self.hosts }: _LOGGER.debug("Adding to manual hosts: %s", new_hosts) self.hosts.update(new_hosts) dispatcher_send( self.hass, f"{SONOS_SPEAKER_ACTIVITY}-{soco.uid}", "manual zone scan", ) break
async def _async_handle_discovery_message(self, uid: str, discovered_ip: str, boot_seqnum: int) -> None: """Handle discovered player creation and activity.""" async with self.discovery_lock: if not self.data.discovered: # Initial discovery, attempt to add all visible zones await self.hass.async_add_executor_job( self._create_visible_speakers, discovered_ip, ) elif uid not in self.data.discovered: if self.is_device_invisible(discovered_ip): return await self.hass.async_add_executor_job(self._add_speaker, SoCo(discovered_ip)) elif boot_seqnum and boot_seqnum > self.data.boot_counts[uid]: self.data.boot_counts[uid] = boot_seqnum async_dispatcher_send(self.hass, f"{SONOS_REBOOTED}-{uid}") else: async_dispatcher_send(self.hass, f"{SONOS_SPEAKER_ACTIVITY}-{uid}", "discovery")
#!/usr/bin/env python import status_wrapper from soco.core import SoCo sonos = SoCo("10.0.0.11") def wrap(j): # Truncate music titles j[1]["short_text"] = j[1]["full_text"][:160] + "..." try: block = {"name": "sonos"} volume = sonos.volume mute = sonos.mute icon = "♫" if mute: icon = "✕" block["color"] = status_wrapper.color_degraded block["full_text"] = "{} {}%".format(icon, volume) j.insert(0, block) except: pass return j status_wrapper.mainloop(wrap)
def _set_device_volume(self, device: SoCo, volume: int): device.volume = volume logger.info('Volume of Sonos device with name \'%s\' changed to %d.', device.player_name, device.volume)
#!/usr/bin/env python3 from soco.core import SoCo from sys import argv sonos = SoCo("10.0.0.11") vol_step = 5 if argv[1] == "line_in": sonos.switch_to_line_in() sonos.play() if argv[1] == "+": sonos.volume = (int(sonos.volume / vol_step) + 1) * vol_step if argv[1] == "-": sonos.volume = (int(sonos.volume / vol_step) - 1) * vol_step if argv[1] == "set": sonos.volume = int(argv[2]) if argv[1] == "mute": sonos.mute = not (sonos.mute)