def create_app(mock_ctrl=False, mock_streams=False, config_file='config/house.json'): if mock_ctrl: app.api = ctrl.Api(rt.Mock(), mock_streams=mock_streams, config_file=config_file) else: app.api = ctrl.Api(rt.Rpi(), mock_streams=mock_streams, config_file=config_file) return app
def create_app(mock=False, config_file='config/house.json'): if mock: app.api = ctrl.Api(rt.Mock(), config_file) else: app.api = ctrl.Api(rt.Rpi(), config_file) return app
def reinit(self, settings: models.AppSettings = models.AppSettings(), change_notifier: Optional[Callable[[models.Status], None]] = None, config: Optional[models.Status] = None): """ Initialize or Reinitialize the controller Intitializes the system to to base configuration """ self._change_notifier = change_notifier self._mock_hw = settings.mock_ctrl self._mock_streams = settings.mock_streams self._save_timer = None self._delay_saves = settings.delay_saves self._settings = settings # Create firmware interface. If one already exists delete then re-init. if self._initialized: # we need to make sure to mute every zone before resetting the fw zones_update = models.MultiZoneUpdate(zones=[z.id for z in self.status.zones], update=models.ZoneUpdate(mute=True)) self.set_zones(zones_update, force_update=True, internal=True) try: del self._rt # remove the low level hardware connection except AttributeError: pass self._rt = rt.Mock() if settings.mock_ctrl else rt.Rpi() # reset the fw # test open the config file, this will throw an exception if there are issues writing to the file with open(settings.config_file, 'a'): # use append more to make sure we have read and write permissions, but won't overrite the file pass self.config_file = settings.config_file self.backup_config_file = settings.config_file + '.bak' self.config_file_valid = True # initially we assume the config file is valid errors = [] if config: self.status = config loaded_config = True else: # try to load the config file or its backup config_paths = [self.config_file, self.backup_config_file] loaded_config = False for cfg_path in config_paths: try: if os.path.exists(cfg_path): self.status = models.Status.parse_file(cfg_path) loaded_config = True break errors.append('config file "{}" does not exist'.format(cfg_path)) except Exception as exc: self.config_file_valid = False # mark the config file as invalid so we don't try to back it up errors.append('error loading config file: {}'.format(exc)) if not loaded_config: print(errors[0]) print('using default config') self.status = models.Status.parse_obj(self.DEFAULT_CONFIG) self.save() self.status.info = models.Info( mock_ctrl=self._mock_hw, mock_streams=self._mock_streams, config_file=self.config_file, version=utils.detect_version() ) # TODO: detect missing sources # detect missing zones if self._mock_hw: # only allow 6 zones when mocked to simplify testing # add more if needed by specifying them in the config potential_zones = range(6) else: potential_zones = range(rt.MAX_ZONES) added_zone = False for zid in potential_zones: _, zone = utils.find(self.status.zones, zid) if zone is None and self._rt.exists(zid): added_zone = True self.status.zones.append(models.Zone(id=zid, name=f'Zone {zid+1}')) # save new config if zones were added if added_zone: self.save() # configure all streams into a known state self.streams: Dict[int, amplipi.streams.AnyStream] = {} failed_streams: List[int] = [] for stream in self.status.streams: if stream.id: try: self.streams[stream.id] = amplipi.streams.build_stream(stream, self._mock_streams) except Exception as exc: print(f"Failed to create '{stream.name}' stream: {exc}") failed_streams.append(stream.id) # only keep the successful streams, this fixes a common problem of loading a stream that doesn't exist in the current developement # [:] does an in-place modification to the list suggested by https://stackoverflow.com/a/1208792/1110730 self.status.streams[:] = [s for s in self.status.streams if s.id not in failed_streams] # configure all sources so that they are in a known state for src in self.status.sources: if src.id is not None: update = models.SourceUpdate(input=src.input) self.set_source(src.id, update, force_update=True, internal=True) # configure all of the zones so that they are in a known state # we mute all zones on startup to keep audio from playing immediately at startup for zone in self.status.zones: # TODO: disable zones that are not found # we likely need an additional field for this, maybe auto-disabled? zone_update = models.ZoneUpdate(source_id=zone.source_id, mute=True, vol=zone.vol) self.set_zone(zone.id, zone_update, force_update=True, internal=True) # configure all of the groups (some fields may need to be updated) self._update_groups()