Beispiel #1
0
    def set_group(self,
                  id,
                  name=None,
                  source_id=None,
                  zones=None,
                  mute=None,
                  vol_delta=None):
        """Configures an existing group
        parameters will be used to configure each sone in the group's zones
        all parameters besides the group id, @id, are optional

        Args:
          id: group id (a guid)
          name: group name
          source_id: group source
          zones: zones that belong to the group
          mute: group mute setting (muted=True)
          vol_delta: volume adjustment to apply to each zone [-79,79]
        Returns:
            'None' on success, otherwise error (dict)
    """
        _, g = self.get_group(id)
        if g is None:
            return utils.error(
                'set group failed, group {} not found'.format(id))
        if type(zones) is str:
            try:
                zones = eval(zones)
            except Exception as e:
                return utils.error(
                    'failed to configure group, error parsing zones: {}'.
                    format(e))
        try:
            name, _ = utils.updated_val(name, g['name'])
            zones, _ = utils.updated_val(zones, g['zones'])
            vol_delta, vol_updated = utils.updated_val(vol_delta,
                                                       g['vol_delta'])
            if vol_updated:
                vol_change = vol_delta - g['vol_delta']
            else:
                vol_change = 0
        except Exception as e:
            return utils.error(
                'failed to configure group, error getting current state: {}'.
                format(e))

        g['name'] = name
        g['zones'] = zones

        for z in [self.status['zones'][zone] for zone in zones]:
            if vol_change != 0:
                # TODO: make this use volume delta adjustment, for now its a fixed group volume
                vol = vol_delta  # vol = z['vol'] + vol_change
            else:
                vol = None
            self.set_zone(z['id'], None, source_id, mute, vol)
        g['vol_delta'] = vol_delta

        # update the group stats
        self._update_groups()
Beispiel #2
0
  def set_source(self, sid: int, update: models.SourceUpdate, force_update: bool = False, internal: bool = False) -> ApiResponse:
    """Modifes the configuration of one of the 4 system sources

      Args:
        id (int): source id [0,3]
        update: changes to source
        force_update: bool, update source even if no changes have been made (for hw startup)
        internal: called by a higher-level ctrl function:

      Returns:
        'None' on success, otherwise error (dict)
    """
    idx, src = utils.find(self.status.sources, sid)
    if idx is not None and src is not None:
      name, _ = utils.updated_val(update.name, src.name)
      input_, input_updated = utils.updated_val(update.input, src.input)
      try:
        # update the name
        src.name = str(name)
        if input_updated or force_update:
          # shutdown old stream
          old_stream = self.get_stream(src)
          if old_stream:
            old_stream.disconnect()
          # start new stream
          last_input = src.input
          src.input = input_ # reconfigure the input so get_stream knows which stream to get
          stream = self.get_stream(src)
          if stream:
            # update the streams last connected source to have no input, since we have stolen its input
            if stream.src is not None and stream.src != idx:
              other_src = self.status.sources[stream.src]
              print('stealing {} from source {}'.format(stream.name, other_src.name))
              other_src.input = ''
            stream.disconnect()
            stream.connect(idx)
          rt_needs_update = self._is_digital(input_) != self._is_digital(last_input)
          if rt_needs_update or force_update:
            # get the current underlying type of each of the sources, for configuration of the runtime
            src_cfg = [self._is_digital(self.status.sources[s].input) for s in range(4)]
            # update this source
            src_cfg[idx] = self._is_digital(input_)
            if not self._rt.update_sources(src_cfg):
              return ApiResponse.error('failed to set source')
          self._update_src_info(src) # synchronize the source's info
        if not internal:
          self.mark_changes()
        return ApiResponse.ok()
      except Exception as exc:
        return ApiResponse.error('failed to set source: ' + str(exc))
    else:
      return ApiResponse.error('failed to set source: index {} out of bounds'.format(idx))
Beispiel #3
0
  def set_group(self, gid, update: models.GroupUpdate, internal: bool = False) -> ApiResponse:
    """Configures an existing group
        parameters will be used to configure each sone in the group's zones
        all parameters besides the group id, @id, are optional

        Args:
          gid: group id (a guid)
          update: changes to group
          internal: called by a higher-level ctrl function
        Returns:
          'None' on success, otherwise error (dict)
    """
    _, group = utils.find(self.status.groups, gid)
    if group is None:
      return ApiResponse.error('set group failed, group {} not found'.format(gid))
    name, _ = utils.updated_val(update.name, group.name)
    zones, _ = utils.updated_val(update.zones, group.zones)
    vol_delta, vol_updated = utils.updated_val(update.vol_delta, group.vol_delta)
    if vol_updated and (group.vol_delta is not None and vol_delta is not None):
      vol_change = vol_delta - group.vol_delta
    else:
      vol_change = 0

    group.name = name
    group.zones = zones

    # update each of the member zones
    zone_update = models.ZoneUpdate(source_id=update.source_id, mute=update.mute)
    if vol_change != 0:
      # TODO: make this use volume delta adjustment, for now its a fixed group volume
      zone_update.vol = vol_delta # vol = z.vol + vol_change
    for zone in [self.status.zones[zone] for zone in zones]:
      self.set_zone(zone.id, zone_update, internal=True)

    # save the volume
    group.vol_delta = vol_delta

    if not internal:
      # update the group stats
      self._update_groups()
      self.mark_changes()

    return ApiResponse.ok()
Beispiel #4
0
    def set_zone(self,
                 id,
                 name=None,
                 source_id=None,
                 mute=None,
                 vol=None,
                 disabled=None,
                 force_update=False):
        """Configures a zone

      Args:
        id (int): any valid zone [0,p*6-1] (6 zones per preamp)
        name(str): friendly name for the zone, ie "bathroom" or "kitchen 1"
        source_id (int): source to connect to [0,4]
        mute (bool): mute the zone regardless of set volume
        vol (int): attenuation [-79,0] 0 is max volume, -79 is min volume
        disabled (bool): disable zone, for when the zone is not connected to any speakers and not in use
        force_update: bool, update source even if no changes have been made (for hw startup)
      Returns:
        'None' on success, otherwise error (dict)
    """
        idx = None
        for i, s in enumerate(self.status['zones']):
            if s['id'] == int(id):
                idx = i
        if idx is not None:
            try:
                z = self.status['zones'][idx]
                # TODO: use updated? value
                name, _ = utils.updated_val(name, z['name'])
                source_id, update_source_id = utils.updated_val(
                    source_id, z['source_id'])
                mute, update_mutes = utils.updated_val(mute, z['mute'])
                vol, update_vol = utils.updated_val(vol, z['vol'])
                disabled, _ = utils.updated_val(disabled, z['disabled'])
            except Exception as e:
                return utils.error(
                    'failed to set zone, error getting current state: {}'.
                    format(e))
            try:
                sid = utils.parse_int(source_id, [0, 1, 2, 3, 4])
                vol = utils.parse_int(
                    vol, range(-79, 79)
                )  # hold additional state for group delta volume adjustments, output volume will be saturated to 0dB
                zones = self.status['zones']
                # update non hw state
                z['name'] = name
                z['disabled'] = disabled
                # TODO: figure out an order of operations here, like does mute need to be done before changing sources?
                if update_source_id or force_update:
                    zone_sources = [zone['source_id'] for zone in zones]
                    zone_sources[idx] = sid
                    if self._rt.update_zone_sources(idx, zone_sources):
                        z['source_id'] = sid
                    else:
                        return utils.error(
                            'set zone failed: unable to update zone source')
                if update_mutes or force_update:
                    mutes = [zone['mute'] for zone in zones]
                    mutes[idx] = mute
                    if self._rt.update_zone_mutes(idx, mutes):
                        z['mute'] = mute
                    else:
                        return utils.error(
                            'set zone failed: unable to update zone mute')
                if update_vol or force_update:
                    real_vol = utils.clamp(vol, -79, 0)
                    if self._rt.update_zone_vol(idx, real_vol):
                        z['vol'] = vol
                    else:
                        return utils.error(
                            'set zone failed: unable to update zone volume')

                # update the group stats (individual zone volumes, sources, and mute configuration can effect a group)
                self._update_groups()

                return None
            except Exception as e:
                return utils.error('set zone: ' + str(e))
        else:
            return utils.error('set zone: index {} out of bounds'.format(idx))
Beispiel #5
0
    def set_source(self, id, name=None, input=None, force_update=False):
        """Modifes the configuration of one of the 4 system sources

      Args:
        id (int): source id [0,3]
        name (str): user friendly source name, ie. "cd player" or "stream 1"
        input: method of audio input ('local', 'stream=ID')
        force_update: bool, update source even if no changes have been made (for hw startup)

      Returns:
        'None' on success, otherwise error (dict)
    """
        idx = None
        for i, s in enumerate(self.status['sources']):
            if s['id'] == id:
                idx = i
        if idx is not None:
            try:
                src = self.status['sources'][idx]
                name, _ = utils.updated_val(name, src['name'])
                input, input_updated = utils.updated_val(input, src['input'])
            except Exception as e:
                return utils.error(
                    'failed to set source, error getting current state: {}'.
                    format(e))
            try:
                # update the name
                src['name'] = str(name)
                if input_updated or force_update:
                    # shutdown old stream
                    old_stream = self.get_stream(src['input'])
                    if old_stream:
                        old_stream.disconnect()
                    # start new stream
                    stream = self.get_stream(input)
                    if stream:
                        # update the streams last connected source to have no input, since we have stolen its input
                        if stream.src is not None and stream.src != idx:
                            other_src = self.status['sources'][stream.src]
                            print('stealing {} from source {}'.format(
                                stream.name, other_src['name']))
                            other_src['input'] = ''
                        else:
                            print('stream.src={} idx={}'.format(
                                stream.src, idx))
                        stream.disconnect()
                        stream.connect(idx)
                    rt_needs_update = self._is_digital(
                        input) != self._is_digital(src['input'])
                    if rt_needs_update or force_update:
                        # get the current underlying type of each of the sources, for configuration of the runtime
                        src_cfg = [
                            self._is_digital(
                                self.status['sources'][s]['input'])
                            for s in range(4)
                        ]
                        # update this source
                        src_cfg[idx] = self._is_digital(input)
                        if self._rt.update_sources(src_cfg):
                            # update the status
                            src['input'] = input
                            return None
                        else:
                            return utils.error('failed to set source')
                    else:
                        src['input'] = input
            except Exception as e:
                return utils.error('failed to set source: ' + str(e))
        else:
            return utils.error(
                'failed to set source: index {} out of bounds'.format(idx))
Beispiel #6
0
  def set_zone(self, zid, update: models.ZoneUpdate, force_update: bool = False, internal: bool = False) -> ApiResponse:
    """Reconfigures a zone

      Args:
        id: any valid zone [0,p*6-1] (6 zones per preamp)
        update: changes to zone
        force_update: update source even if no changes have been made (for hw startup)
        internal: called by a higher-level ctrl function
      Returns:
        ApiResponse
    """
    idx, zone = utils.find(self.status.zones, zid)
    if idx is not None and zone is not None:
      name, _ = utils.updated_val(update.name, zone.name)
      source_id, update_source_id = utils.updated_val(update.source_id, zone.source_id)
      mute, update_mutes = utils.updated_val(update.mute, zone.mute)
      vol, update_vol = utils.updated_val(update.vol, zone.vol)
      disabled, _ = utils.updated_val(update.disabled, zone.disabled)
      try:
        sid = utils.parse_int(source_id, [0, 1, 2, 3])
        vol = utils.parse_int(vol, range(-79, 79)) # hold additional state for group delta volume adjustments, output volume will be saturated to 0dB
        zones = self.status.zones
        # update non hw state
        zone.name = name
        zone.disabled = disabled
        if update_source_id or force_update:
          zone_sources = [zone.source_id for zone in zones]
          zone_sources[idx] = sid
          if self._rt.update_zone_sources(idx, zone_sources):
            zone.source_id = sid
          else:
            return ApiResponse.error('set zone failed: unable to update zone source')

        def set_mute():
          mutes = [zone.mute for zone in zones]
          mutes[idx] = mute
          if self._rt.update_zone_mutes(idx, mutes):
            zone.mute = mute
          else:
            raise Exception('set zone failed: unable to update zone mute')

        def set_vol():
          real_vol = utils.clamp(vol, -79, 0)
          if self._rt.update_zone_vol(idx, real_vol):
            zone.vol = vol
          else:
            raise Exception('set zone failed: unable to update zone volume')

        # To avoid potential unwanted loud output:
        # If muting, mute before setting volumes
        # If un-muting, set desired volume first
        try:
          if force_update or (update_mutes and update_vol):
            if mute:
              set_mute()
              set_vol()
            else:
              set_vol()
              set_mute()
          elif update_vol:
            set_vol()
          elif update_mutes:
            set_mute()
        except Exception as exc:
          return ApiResponse.error(str(exc))

        if not internal:
          # update the group stats (individual zone volumes, sources, and mute configuration can effect a group)
          self._update_groups()
          self.mark_changes()

        return ApiResponse.ok()
      except Exception as exc:
        return ApiResponse.error('set zone: '  + str(exc))
    else:
        return ApiResponse.error('set zone: index {} out of bounds'.format(idx))