Beispiel #1
0
    def get_queue(self, start=0, max_items=100):
        """ Get information about the queue.

        Returns:
        A list containing a dictionary for each track in the queue. The track dictionary
        contains the following information about the track: title, artist, album, album_art, uri

        If we're unable to return data for a field, we'll return an empty
        list. This can happen for all kinds of reasons so be sure to check
        values.

        This method is heavly based on Sam Soffes (aka soffes) ruby implementation

        """
        queue = []

        body = GET_QUEUE_BODY_TEMPLATE.format(start, max_items)

        response = self.__send_command(CONTENT_DIRECTORY_ENDPOINT,
                                       BROWSE_ACTION, body)

        try:
            dom = XML.fromstring(really_utf8(response))
            resultText = dom.findtext('.//Result')
            if not resultText: return queue

            resultDom = XML.fromstring(really_utf8(resultText))
            for element in resultDom.findall(
                    './/{urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/}item'):
                try:
                    item = {
                        'title': None,
                        'artist': None,
                        'album': None,
                        'album_art': None,
                        'uri': None
                    }

                    item['title'] = element.findtext(
                        '{http://purl.org/dc/elements/1.1/}title')
                    item['artist'] = element.findtext(
                        '{http://purl.org/dc/elements/1.1/}creator')
                    item['album'] = element.findtext(
                        '{urn:schemas-upnp-org:metadata-1-0/upnp/}album')
                    item['album_art'] = element.findtext(
                        '{urn:schemas-upnp-org:metadata-1-0/upnp/}albumArtURI')
                    item['uri'] = element.findtext(
                        '{urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/}res')

                    queue.append(item)
                except:
                    logger.warning('Could not handle item: %s', element)
                    logger.error(traceback.format_exc())

        except:
            logger.error('Could not handle result from Sonos')
            logger.error(traceback.format_exc())

        return queue
Beispiel #2
0
    def get_queue(self, start = 0, max_items = 100):
        """ Get information about the queue.

        Returns:
        A list containing a dictionary for each track in the queue. The track dictionary
        contains the following information about the track: title, artist, album, album_art, uri

        If we're unable to return data for a field, we'll return an empty
        list. This can happen for all kinds of reasons so be sure to check
        values.

        This method is heavly based on Sam Soffes (aka soffes) ruby implementation

        """
        queue = []

        body = GET_QUEUE_BODY_TEMPLATE.format(start, max_items)

        response = self.__send_command(CONTENT_DIRECTORY_ENDPOINT, BROWSE_ACTION, body)

        try:
            dom = XML.fromstring(really_utf8(response))
            resultText = dom.findtext('.//Result')
            if not resultText: return queue

            resultDom  = XML.fromstring(really_utf8(resultText))
            for element in resultDom.findall('.//{urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/}item'):
                try:
                    item = {
                        'title': None,
                        'artist': None,
                        'album': None,
                        'album_art': None,
                        'uri': None
                        }

                    item['title'] =     element.findtext('{http://purl.org/dc/elements/1.1/}title')
                    item['artist'] =    element.findtext('{http://purl.org/dc/elements/1.1/}creator')
                    item['album'] =     element.findtext('{urn:schemas-upnp-org:metadata-1-0/upnp/}album')
                    item['album_art'] = element.findtext('{urn:schemas-upnp-org:metadata-1-0/upnp/}albumArtURI')
                    item['uri'] =       element.findtext('{urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/}res')

                    queue.append(item)
                except:
                    logger.warning('Could not handle item: %s', element)
                    logger.error(traceback.format_exc())

        except:
            logger.error('Could not handle result from Sonos')
            logger.error(traceback.format_exc())

        return queue
Beispiel #3
0
    def get_current_transport_info(self):
        """ Get the current playback state

        Returns:
        A dictionary containing the following information about the speakers playing state
        current_transport_state (PLAYING, PAUSED_PLAYBACK, STOPPED),
        current_trasnport_status (OK, ?), current_speed(1,?)

        This allows us to know if speaker is playing or not. Don't know other states of
        CurrentTransportStatus and CurrentSpeed.

        """
        response = self.__send_command(TRANSPORT_ENDPOINT, GET_CUR_TRANSPORT_ACTION, GET_CUR_TRANSPORT_BODY)
        dom = XML.fromstring(really_utf8(response))

        playstate = {
            'current_transport_status': '',
            'current_transport_state': '',
            'current_transport_speed': ''
        }

        playstate['current_transport_state'] = dom.findtext('.//CurrentTransportState')
        playstate['current_transport_status'] = dom.findtext('.//CurrentTransportStatus')
        playstate['current_transport_speed'] = dom.findtext('.//CurrentSpeed')

        return playstate
Beispiel #4
0
    def get_speaker_info(self, refresh=False):
        """ Get information about the Sonos speaker.

        Arguments:
        refresh -- Refresh the speaker info cache.

        Returns:
        Information about the Sonos speaker, such as the UID, MAC Address, and
        Zone Name.

        """
        if self.speaker_info and refresh is False:
            return self.speaker_info
        else:
            response = requests.get('http://' + self.speaker_ip + ':1400/status/zp')

            dom = XML.fromstring(response.content)

            self.speaker_info['zone_name'] = really_utf8(dom.findtext('.//ZoneName'))
            self.speaker_info['zone_icon'] = dom.findtext('.//ZoneIcon')
            self.speaker_info['uid'] = dom.findtext('.//LocalUID')
            self.speaker_info['serial_number'] = dom.findtext('.//SerialNumber')
            self.speaker_info['software_version'] = dom.findtext('.//SoftwareVersion')
            self.speaker_info['hardware_version'] = dom.findtext('.//HardwareVersion')
            self.speaker_info['mac_address'] = dom.findtext('.//MACAddress')

            return self.speaker_info
Beispiel #5
0
    def get_current_transport_info(self):
        """ Get the current playback state

        Returns:
        A dictionary containing the following information about the speakers playing state
        current_transport_state (PLAYING, PAUSED_PLAYBACK, STOPPED),
        current_trasnport_status (OK, ?), current_speed(1,?)

        This allows us to know if speaker is playing or not. Don't know other states of
        CurrentTransportStatus and CurrentSpeed.

        """
        response = self.__send_command(TRANSPORT_ENDPOINT,
                                       GET_CUR_TRANSPORT_ACTION,
                                       GET_CUR_TRANSPORT_BODY)
        dom = XML.fromstring(really_utf8(response))

        playstate = {
            'current_transport_status': '',
            'current_transport_state': '',
            'current_transport_speed': ''
        }

        playstate['current_transport_state'] = dom.findtext(
            './/CurrentTransportState')
        playstate['current_transport_status'] = dom.findtext(
            './/CurrentTransportStatus')
        playstate['current_transport_speed'] = dom.findtext('.//CurrentSpeed')

        return playstate
Beispiel #6
0
    def get_speaker_info(self, refresh=False):
        """ Get information about the Sonos speaker.

        Arguments:
        refresh -- Refresh the speaker info cache.

        Returns:
        Information about the Sonos speaker, such as the UID, MAC Address, and
        Zone Name.

        """
        if self.speaker_info and refresh is False:
            return self.speaker_info
        else:
            response = requests.get('http://' + self.speaker_ip +
                                    ':1400/status/zp')

            dom = XML.fromstring(response.content)

            self.speaker_info['zone_name'] = really_utf8(
                dom.findtext('.//ZoneName'))
            self.speaker_info['zone_icon'] = dom.findtext('.//ZoneIcon')
            self.speaker_info['uid'] = dom.findtext('.//LocalUID')
            self.speaker_info['serial_number'] = dom.findtext(
                './/SerialNumber')
            self.speaker_info['software_version'] = dom.findtext(
                './/SoftwareVersion')
            self.speaker_info['hardware_version'] = dom.findtext(
                './/HardwareVersion')
            self.speaker_info['mac_address'] = dom.findtext('.//MACAddress')

            return self.speaker_info
Beispiel #7
0
    def __get_radio_favorites(self, favorite_type, start=0, max_items=100):
        """ Helper method for `get_favorite_radio_*` methods.

        Arguments:
        favorite_type -- Specify either `RADIO_STATIONS` or `RADIO_SHOWS`.
        start -- Which number to start the retrieval from. Used for paging.
        max_items -- The total number of results to return.

        """
        if favorite_type != RADIO_SHOWS or RADIO_STATIONS:
            favorite_type = RADIO_STATIONS

        body = GET_RADIO_FAVORITES_BODY_TEMPLATE.format(
            favorite_type, start, max_items)

        response = self.__send_command(CONTENT_DIRECTORY_ENDPOINT,
                                       BROWSE_ACTION, body)

        dom = XML.fromstring(really_utf8(response))

        result = {}
        favorites = []

        d = dom.findtext('.//Result')

        if d != '':
            # Favorites are returned in DIDL-Lite format
            metadata = XML.fromstring(really_utf8(d))

            for item in metadata.findall(
                    './/{urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/}item'):
                favorite = {}

                favorite['title'] = really_utf8(
                    item.findtext(
                        './/{http://purl.org/dc/elements/1.1/}title'))
                favorite['uri'] = item.findtext(
                    './/{urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/}res')

                favorites.append(favorite)

        result['total'] = dom.findtext('.//TotalMatches', 0)
        result['returned'] = len(favorites)
        result['favorites'] = favorites

        return result
Beispiel #8
0
    def __get_radio_favorites(self, favorite_type, start=0, max_items=100):
        """ Helper method for `get_favorite_radio_*` methods.

        Arguments:
        favorite_type -- Specify either `RADIO_STATIONS` or `RADIO_SHOWS`.
        start -- Which number to start the retrieval from. Used for paging.
        max_items -- The total number of results to return.

        """
        if favorite_type != RADIO_SHOWS or RADIO_STATIONS:
            favorite_type = RADIO_STATIONS

        body = GET_RADIO_FAVORITES_BODY_TEMPLATE.format(favorite_type, start, max_items)

        response = self.__send_command(CONTENT_DIRECTORY_ENDPOINT, BROWSE_ACTION, body)

        dom = XML.fromstring(really_utf8(response))

        result = {}
        favorites = []

        d = dom.findtext('.//Result')

        if d != '':
            # Favorites are returned in DIDL-Lite format
            metadata = XML.fromstring(really_utf8(d))

            for item in metadata.findall('.//{urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/}item'):
                favorite = {}

                favorite['title'] = really_utf8(item.findtext('.//{http://purl.org/dc/elements/1.1/}title'))
                favorite['uri'] = item.findtext('.//{urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/}res')

                favorites.append(favorite)

        result['total'] = dom.findtext('.//TotalMatches', 0)
        result['returned'] = len(favorites)
        result['favorites'] = favorites

        return result
Beispiel #9
0
    def __get_topology(self, refresh=False):
        """ Gets the topology if it is not already available or if refresh=True

        Code contributed by Aaron Daubman ([email protected])

        Arguments:
        refresh -- Refresh the topology cache
        """
        if not self.topology or refresh:
            self.topology = {}
            response = requests.get('http://' + self.speaker_ip + ':1400/status/topology')
            dom = XML.fromstring(really_utf8(response.content))
            for player in dom.find('ZonePlayers'):
                if player.text not in self.topology:
                    self.topology[player.text] = {}
                self.topology[player.text]['group'] = player.attrib.get('group')
                self.topology[player.text]['uuid'] = player.attrib.get('uuid')
                self.topology[player.text]['coordinator'] = (player.attrib.get('coordinator') == 'true')
                # Split the IP out of the URL returned in location - e.g. return '10.1.1.1' from 'http://10.1.1.1:1400/...'
                self.topology[player.text]['ip'] = player.attrib.get('location').split('//')[1].split(':')[0]
Beispiel #10
0
 def _set_topology(self, zp):
     '''
     improved (!)version of SoCo get_toplogy:
     - uses uPnP call to one zone player
     - excludes invisible zones (e.g. in stereo pair)
     - builds dictionary with key of ip and a dictionary of zone details
     Note: uses master = True v.s. SoCo Coordinator = True!
     '''
     # get group state from one zone via direct uPnP call using SoCo
     zgroups = zp.zoneGroupTopology.GetZoneGroupState()['ZoneGroupState']  
     tree = XML.fromstring(really_utf8(zgroups))
     for grp in tree:
         for zp in grp:
             # ignore zones marked as Invisible (e.g. one of a stereo pair)
             if 'Invisible' in zp.attrib:  # Invisible info not always there
                 if zp.attrib['Invisible']:  # Invisible ==1 (True)
                     continue
             ip_key = zp.attrib['Location'].split('//')[1].split(':')[0]
             self.topology[ip_key] = {}
             self.topology[ip_key]['uuid'] = zp.attrib['UUID']
             self.topology[ip_key]['zone_name'] = zp.attrib['ZoneName']  #not needed if this is the key!!!!!!!
             self.topology[ip_key]['master'] = (zp.attrib['UUID'] == grp.attrib['Coordinator'])
             self.topology[ip_key]['group'] = grp.attrib['Coordinator']
     return len(self.topology)
Beispiel #11
0
    def __get_topology(self, refresh=False):
        """ Gets the topology if it is not already available or if refresh=True

        Code contributed by Aaron Daubman ([email protected])

        Arguments:
        refresh -- Refresh the topology cache
        """
        if not self.topology or refresh:
            self.topology = {}
            response = requests.get('http://' + self.speaker_ip +
                                    ':1400/status/topology')
            dom = XML.fromstring(really_utf8(response.content))
            for player in dom.find('ZonePlayers'):
                if player.text not in self.topology:
                    self.topology[player.text] = {}
                self.topology[player.text]['group'] = player.attrib.get(
                    'group')
                self.topology[player.text]['uuid'] = player.attrib.get('uuid')
                self.topology[player.text]['coordinator'] = (
                    player.attrib.get('coordinator') == 'true')
                # Split the IP out of the URL returned in location - e.g. return '10.1.1.1' from 'http://10.1.1.1:1400/...'
                self.topology[player.text]['ip'] = player.attrib.get(
                    'location').split('//')[1].split(':')[0]
Beispiel #12
0
    def get_current_track_info(self):
        """ Get information about the currently playing track.

        Returns:
        A dictionary containing the following information about the currently
        playing track: playlist_position, duration, title, artist, album,
        position and a link to the album art.

        If we're unable to return data for a field, we'll return an empty
        string. This can happen for all kinds of reasons so be sure to check
        values. For example, a track may not have complete metadata and be
        missing an album name. In this case track['album'] will be an empty string.

        """
        response = self.__send_command(TRANSPORT_ENDPOINT, GET_CUR_TRACK_ACTION, GET_CUR_TRACK_BODY)

        dom = XML.fromstring(really_utf8(response))

        track = {'title': '', 'artist': '', 'album': '', 'album_art': '',
            'position': ''}

        track['playlist_position'] = dom.findtext('.//Track')
        track['duration'] = dom.findtext('.//TrackDuration')
        track['uri'] = dom.findtext('.//TrackURI')
        track['position'] = dom.findtext('.//RelTime')

        d = dom.findtext('.//TrackMetaData')

        # Duration seems to be '0:00:00' when listening to radio
        if d != '' and track['duration'] == '0:00:00':
            metadata = XML.fromstring(really_utf8(d))

            #Try parse trackinfo
            trackinfo = metadata.findtext('.//{urn:schemas-rinconnetworks-com:metadata-1-0/}streamContent')

            index = trackinfo.find(' - ')

            if index > -1:
                track['artist'] = trackinfo[:index]
                track['title'] = trackinfo[index+3:]
            else:
                logger.warning('Could not handle track info: "%s"', trackinfo)
                logger.warning(traceback.format_exc())
                track['title'] = really_utf8(trackinfo)

        # If the speaker is playing from the line-in source, querying for track
        # metadata will return "NOT_IMPLEMENTED".
        elif d != '' and d != 'NOT_IMPLEMENTED':
            # Track metadata is returned in DIDL-Lite format
            metadata  = XML.fromstring(really_utf8(d))
            md_title  = metadata.findtext('.//{http://purl.org/dc/elements/1.1/}title')
            md_artist = metadata.findtext('.//{http://purl.org/dc/elements/1.1/}creator')
            md_album  = metadata.findtext('.//{urn:schemas-upnp-org:metadata-1-0/upnp/}album')

            track['title'] = ""
            if (md_title):
                track['title'] = really_utf8(md_title)

            track['artist'] = ""
            if (md_artist):
                track['artist'] = really_utf8(md_artist)

            track['album'] = ""
            if (md_album):
                track['album'] = really_utf8(md_album)

            album_art = metadata.findtext('.//{urn:schemas-upnp-org:metadata-1-0/upnp/}albumArtURI')

            if album_art is not None:
                track['album_art'] = 'http://' + self.speaker_ip + ':1400' + metadata.findtext('.//{urn:schemas-upnp-org:metadata-1-0/upnp/}albumArtURI')

        return track
Beispiel #13
0
    def get_current_track_info(self):
        """ Get information about the currently playing track.

        Returns:
        A dictionary containing the following information about the currently
        playing track: playlist_position, duration, title, artist, album,
        position and a link to the album art.

        If we're unable to return data for a field, we'll return an empty
        string. This can happen for all kinds of reasons so be sure to check
        values. For example, a track may not have complete metadata and be
        missing an album name. In this case track['album'] will be an empty string.

        """
        response = self.__send_command(TRANSPORT_ENDPOINT,
                                       GET_CUR_TRACK_ACTION,
                                       GET_CUR_TRACK_BODY)

        dom = XML.fromstring(really_utf8(response))

        track = {
            'title': '',
            'artist': '',
            'album': '',
            'album_art': '',
            'position': ''
        }

        track['playlist_position'] = dom.findtext('.//Track')
        track['duration'] = dom.findtext('.//TrackDuration')
        track['uri'] = dom.findtext('.//TrackURI')
        track['position'] = dom.findtext('.//RelTime')

        d = dom.findtext('.//TrackMetaData')

        # Duration seems to be '0:00:00' when listening to radio
        if d != '' and track['duration'] == '0:00:00':
            metadata = XML.fromstring(really_utf8(d))

            #Try parse trackinfo
            trackinfo = metadata.findtext(
                './/{urn:schemas-rinconnetworks-com:metadata-1-0/}streamContent'
            )

            index = trackinfo.find(' - ')

            if index > -1:
                track['artist'] = trackinfo[:index]
                track['title'] = trackinfo[index + 3:]
            else:
                logger.warning('Could not handle track info: "%s"', trackinfo)
                logger.warning(traceback.format_exc())
                track['title'] = really_utf8(trackinfo)

        # If the speaker is playing from the line-in source, querying for track
        # metadata will return "NOT_IMPLEMENTED".
        elif d != '' and d != 'NOT_IMPLEMENTED':
            # Track metadata is returned in DIDL-Lite format
            metadata = XML.fromstring(really_utf8(d))
            md_title = metadata.findtext(
                './/{http://purl.org/dc/elements/1.1/}title')
            md_artist = metadata.findtext(
                './/{http://purl.org/dc/elements/1.1/}creator')
            md_album = metadata.findtext(
                './/{urn:schemas-upnp-org:metadata-1-0/upnp/}album')

            track['title'] = ""
            if (md_title):
                track['title'] = really_utf8(md_title)

            track['artist'] = ""
            if (md_artist):
                track['artist'] = really_utf8(md_artist)

            track['album'] = ""
            if (md_album):
                track['album'] = really_utf8(md_album)

            album_art = metadata.findtext(
                './/{urn:schemas-upnp-org:metadata-1-0/upnp/}albumArtURI')

            if album_art is not None:
                track[
                    'album_art'] = 'http://' + self.speaker_ip + ':1400' + metadata.findtext(
                        './/{urn:schemas-upnp-org:metadata-1-0/upnp/}albumArtURI'
                    )

        return track