Esempio n. 1
0
    def _init_device(self):
        ''' Initialise the device within its own thread.

        Raises:
            CommsError, ConfigError.
        '''

        self.conn = musiccastComm(self._host, self._api_port, self._listenport)

        # Retrieve the device info and load them
        self._dev_info = self.conn.mcrequest('system', 'getDeviceInfo')
        try:
            device_id = self._dev_info['device_id'].upper()
            self._serial_num = self._dev_info['system_id']
            self._model = self._dev_info['model_name']
        except KeyError:
            raise mcc.CommsError('getDeviceInfo does not have the expected structure.')
        if self._device_mcid != device_id: # sanity check
            raise mcc.ConfigError('getDeviceInfo does not yield the expected device_id.')

        # Retrieve the device features
        self._features = self.conn.mcrequest('system', 'getFeatures')

        # Create the zones
        for zone_data in self.get_feature('zone'):
            zone = Zone(zone_data, self)
            if zone.zone_mcid == 'main': # ensure first in the list is 'main'
                self.zones.insert(0, zone)
            else:
                self.zones.append(zone)
            self.zone_d[zone.zone_mcid] = zone # update the helper dictionary
        if not self.zones:
            raise mcc.ConfigError(''.join(('Device <', self.name(), '> has no zones.')))
        self.zones = tuple(self.zones) # 'freeze' the list in a tuple

        # Create the inputs, sources and feeds
        for input_data in self.get_feature(('system', 'input_list')):
            try:
                play_info_type = input_data['play_info_type']
            except KeyError:
                mcc.CommsError('getFeatures does not have the expected structure.')
            if play_info_type == 'none':
                feed = Feed(input_data, self)
                self.feeds.append(feed)
            else:
                source = Source(input_data, self)
                self.sources.append(source)
        self.feeds = tuple(self.feeds)
        self.sources = tuple(self.sources)
        self.inputs = self.feeds + self.sources
        if not self.inputs:
            raise mcc.ConfigError(''.join(('Device <', self.name(), '> has no inputs.')))
        self._inputs_d = {inp.input_mcid:inp for inp in self.inputs}

        # success
        self._ready = True
        self._load_time = time.time() # time of initialisation for future reference
        LOG.debug(''.join(('Initialisation of device <', self.name(), '> successful.')))

        return
Esempio n. 2
0
    def get_input(self, input_mcid, raises=False):
        ''' Returns the :class:`Input` object from its mcid.
        Args:
            input_mcid (string): the MusicCast id of the input searched
            raises (boolean): if True, raises an exception instead of returning ``False``

        Returns:
            :class:`Input` object if found, or ``None``

        Raises:
            ConfigError or LogicError.
        '''
        if input_mcid is None:
            if raises:
                raise mcc.ConfigError(''.join(('No valid input id argument on device <',
                                               self.name(), '>.')))
            else:
                return None
        else:
            try:
                inp = self._inputs_d[input_mcid]
            except KeyError:
                if raises:
                    raise mcc.ConfigError(''.join(('MusicCast input <', input_mcid,
                                                   '> not found in device <', self.name(), '>.')))
                else: return None
        return inp
Esempio n. 3
0
    def init_infotype(self, play_info_type):
        ''' Returns a new or an existing instance of PlayInfoType.

        For each type there is only one instance of the corresponding class.

        Args:
            play_info_type (string): one of **tuner**, **cd**, or **netusb**.

        Raises:
            ConfigError: if the play_info_type is not recognised.
        '''
        if play_info_type in self._infotype_d:
            return self._infotype_d[play_info_type]
        if play_info_type == 'tuner':
            self._infotype_d[play_info_type] = Tuner(self)
            return self._infotype_d[play_info_type]
        elif play_info_type == 'cd':
            self._infotype_d[play_info_type] = CD(self)
            return self._infotype_d[play_info_type]
        elif play_info_type == 'netusb':
            self._infotype_d[play_info_type] = NetUSB(self)
            return self._infotype_d[play_info_type]
        elif play_info_type == 'none':
            return None
        else:
            raise mcc.ConfigError(''.join(('PlayInfoType <', play_info_type, '> does not exist.')))
Esempio n. 4
0
    def get_infotype(self, play_info_type):
        ''' Retrieves the info_type instance.

        Only used (for now) by the event lambdas.
        '''
        try: return self._infotype_d[play_info_type]
        except KeyError:
            raise mcc.ConfigError(''.join(('InfoType <', play_info_type, '> not found.')))
Esempio n. 5
0
    def get_zone(self, zone_mcid, raises=False):
        ''' Returns the :class:`Zone` object from its identification.

        Args:
            zone_mcid (string): the MusicCast id of the zone searched
            raises (boolean): if True, raises an exception instead of returning ``False``
        '''
        if zone_mcid is None:
            if raises: raise mcc.ConfigError('Zone id cannot be None.')
            else: return None
        try:
            return self.zone_d[zone_mcid]
        except KeyError:
            if raises: raise mcc.ConfigError(''.join(('Zone id <', zone_mcid,
                                                      '> not found in device <',
                                                      self.name(), '>.')))
            else: return None
Esempio n. 6
0
 def _get_device_from_id(self, device_id, raises=False):
     ''' Returns the device object if found, None otherwise.'''
     with self._devices_lock:
         try:
             device = self._devices_shared[device_id]
         except KeyError:
             if raises:
                 raise mcc.ConfigError(''.join(
                     ('Device <', device_id, '> not found.')))
             else:
                 device = None
     return device
Esempio n. 7
0
 def parse_event(event):
     ''' Reads event dictionary and returns lambdas for each key match found.'''
     flist = [] # list of all lambdas to call; the elements are pairs (func, arg)
     for key1 in event:
         try: isdict = isinstance(musiccastDevice.EVENTS[key1], dict)
         except KeyError:
             LOG.info(''.join(('Event has an unknown item <', str(key1), '>. Ignore.')))
             continue
         if isdict:
             if not isinstance(event[key1], dict):
                 raise mcc.ConfigError('Unexpected structure of event. Ignore.')
             for key2 in event[key1]:
                 try: func = musiccastDevice.EVENTS[key1][key2]
                 except KeyError:
                     LOG.info(''.join(('Unknown item in event <', str(key2), '>. Ignore.')))
                     continue
                 if func is not None: flist.append((func, event[key1][key2]))
         else:
             func = musiccastDevice.EVENTS[key1]
             if func is not None: flist.append((func, event[key1]))
     return flist
Esempio n. 8
0
    def _transform_arg(self, key, invalue=None, mcvalue=None):
        '''Transforms a message argument from/to internal to/from MusicCast.

        This method goes hand in hand with the TRANSFORM_ARG dictionary.

        Args:
            key (string): internal name of argument.
            invalue (string): the internal value to be transformed; if provided the transformation
              is done from this value to the MusicCast value, which is returned.
            mcvalue (string): the MusicCast value to be transformed; relevant only if ``invalue`` is
              None, in which case the transformation is done from this value to the
              internal value, which is returned.

        Returns:
            string: the transformed representation of the value.
        '''
        try:
            func = TRANSFORM_ARG[key]  # Retrieve the transformation lambdas
        except KeyError:
            raise mcc.LogicError(''.join(
                ('Argument ', str(key), ' has no transformation.')))
        if invalue is not None:  # transform from internal to MusicCast
            value = invalue
            trsfrm = 0
        elif mcvalue is not None:  # transform from MusicCast to internal
            value = mcvalue
            trsfrm = 1
        else:
            raise mcc.ConfigError(''.join(
                ('No valid parameters to transform <', str(key), '> on zone <',
                 self.name(), '> of device <', self.device.name(), '>.')))
        try:
            trsfrm_value = func[trsfrm](self, value)
        except (TypeError,
                ValueError) as err:  # errors to catch in case of bad format
            raise mcc.LogicError(''.join(
                ('Value ', str(value), ' of argument ', str(key),
                 ' seems of the wrong type. Error:\n\t', str(err))))
        return trsfrm_value
Esempio n. 9
0
    def get_feature(self, flist):
        ''' Returns a branch from the getFeatures tree.

        This method retrieves a branch or leaf from a JSON type object.
        The argument is a list made of strings and/or pairs of string.
        For each string, the method expect to find a dictionary as the next branch,
        and selects the value (which is another branch or leaf) returned by that string as key.
        For each single key dictionary {key: value}, it expects to find an array or similar objects,
        and in that case it searches for the array that contains the right 'value' for that 'key'.

        This method helps in reading the responses from Yamaha MusicCast API.

        Args:
            flist: list representing a leaf or a branch from a JSON type of string

        Raises:
            CommsError, ConfigError.

        Example:
            Use get_feature(('zone', {'id': 'main'}, 'range_step', {'id': 'volume'}, 'max'))
            to retrieve
        '''
        branch = self._features
        if isinstance(flist, basestring): # Python 3: isinstance(arg, str)
            flist = (flist,)
        for arg in flist:
            if isinstance(arg, basestring):
                try: branch = branch[arg]
                except KeyError:
                    raise mcc.ConfigError(''.join(('Argument <', str(arg),
                                                   '> not found in current branch: ',
                                                   str(branch))))
                except TypeError:
                    raise mcc.CommsError(''.join(('The current branch is not a dictionary: ',
                                                  str(branch))))

            elif isinstance(arg, dict) and len(arg) == 1: # arg is a single key dictionary
                key, value = arg.items()[0]
                try:
                    _ = iter(branch)
                except TypeError:
                    raise mcc.ConfigError(''.join(('The current branch is not an array: ',
                                                   str(branch))))
                obj = None
                found = False
                for obj in branch:
                    try: found = (obj[key] == value)
                    except KeyError:
                        raise mcc.ConfigError(''.join(('Key <', str(key),
                                                       '> not found in current branch: ',
                                                       str(obj))))
                    except TypeError:
                        raise mcc.ConfigError(''.join(('The current branch is not a dictionary: ',
                                                       str(obj))))
                    if found: break
                if obj is None:
                    raise mcc.ConfigError(''.join(('The current branch has no length: ',
                                                   str(branch))))
                if not found:
                    raise mcc.ConfigError(''.join(('Value <', str(value), '> for key <',
                                                   str(key), '> not found in array.')))
                branch = obj
            else: # unrecognised token
                raise mcc.ConfigError(''.join(('Unrecognised token: ', str(arg))))
        return branch