"""A pretend HEOS network, for `python3 app.py --demo`. Lets you work on the interface on a laptop, with no speakers on the network -- and lets the panel be tested without waking the house up. """ import time from controller import stepped_level class _FakeAvr: # Codes already in HEOS's own "inputs/" shape (see GET # /api/avr/inputs against the real AVR), not the demo's own invention -- # the panel's input icon is keyed on this exact format. INPUTS = [ {"code": "inputs/mediaplayer", "name": "Apple TV"}, {"code": "inputs/game", "name": "PlayStation"}, {"code": "inputs/tvaudio", "name": "TV Box"}, {"code": "inputs/bluray", "name": "Blu-ray"}, {"code": "inputs/tuner", "name": "Radio"}, {"code": "inputs/phono", "name": "Turntable"}, ] def __init__(self): self.connected = True self._code = "inputs/mediaplayer" def inputs(self, refresh=False): return list(self.INPUTS) def name_for(self, code): return next((s["name"] for s in self.INPUTS if s["code"] == code), code) def current_input(self): return {"code": self._code, "name": self.name_for(self._code)} def select_input(self, code): time.sleep(0.15) # the real AVR is not instant either self._code = code return self.current_input() class DemoController: def __init__(self, cfg): self.cfg = cfg self.avr = _FakeAvr() self.heos = None self._volume = {key: 22 + 7 * i for i, key in enumerate(cfg.TARGETS)} self._play = {key: "play" for key in cfg.TARGETS} self._joined = set() # One room on Spotify and one not, so both kinds of card show up. self._spotify = {key: i == 0 for i, key in enumerate(cfg.ROOM_KEYS)} # No cover, since the demo has no network to fetch one from. self._track = {"song": "Harvest Moon", "artist": "Neil Young", "image": None} # Its play button only shows for a stream one of your accounts plays. self._account = next(iter(cfg.SPOTIFY_ACCOUNTS), None) # -- what the UI uses --------------------------------------------- def state(self): return { "host": {"key": self.cfg.HOST_KEY, "label": self.cfg.TARGETS[self.cfg.HOST_KEY]["label"]}, "rooms": [ {"key": key, "label": self.cfg.TARGETS[key]["label"], "available": True, "grouped": key in self._joined, "volume": self._volume[key], "play_state": self._play[key], "spotify": self._spotify[key], "spotify_account": self._account if self._spotify[key] else None, "now_playing": self._track if self._spotify[key] and self._play[key] != "stop" else None, "error": None} for key in self.cfg.ROOM_KEYS ], "avr": { "connected": True, "inputs": self.avr.inputs(), "input": self.avr.current_input(), # Standing in for a Zidoo plugged into this input, so the # AVR card's now-playing block has something to show here too. "now_playing": {"song": "Big Buck Bunny", "artist": "2008", "image": None, "position_ms": 241000, "duration_ms": 596000} if self.avr.current_input()["code"] == self.cfg.ZIDOO_INPUT_CODE else None, }, "heos_ok": True, "errors": [], "demo": True, } def volume(self, key): return self._volume[key] def set_volume(self, key, level): self._volume[key] = max(0, min(100, int(level))) return self._volume[key] def nudge_volume(self, key, delta): return self.set_volume(key, self._volume[key] + int(delta)) def step_volume(self, key, steps): return self.set_volume(key, stepped_level(self._volume[key], int(steps), self.cfg.VOLUME_STEP)) def toggle_mute(self, key): return None def get_play_state(self, key): return self._play[key] def toggle_play(self, key, state=None): if state is None: state = "pause" if self._play[key] == "play" else "play" self._play[key] = state return state def join(self, key): self._joined.add(key) return self.joined_keys() def leave(self, key): self._joined.discard(key) return self.joined_keys() def set_membership(self, joined): self._joined = {k for k in self.cfg.ROOM_KEYS if k in joined} return self.joined_keys() def joined_keys(self): return [k for k in self.cfg.ROOM_KEYS if k in self._joined] # -- enough of the rest to keep the legacy routes answering -------- def scan(self): return { "players": [{"name": t["heos_name"], "pid": 1000 + i, "model": "Demo"} for i, t in enumerate(self.cfg.TARGETS.values())], "groups": [], } def group_targets(self, host_key, member_keys): return self.set_membership(member_keys) def ungroup(self, key): return self.leave(key) def play_state(self, key, state): return None def skip(self, key, direction): return None def heos_inputs(self, key): return [{"name": s["name"], "input_id": s["code"]} for s in self.avr.inputs()] def play_heos_input(self, key, input_id, source_key=None): return None # -- the AVR: app.py calls these directly, same as the real Controller def avr_inputs(self): return self.avr.inputs() def avr_current_input(self): return self.avr.current_input() def avr_select_input(self, code): return self.avr.select_input(code)