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
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
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.')))
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.')))
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
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
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
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
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