"""Remote control agent for Mediaroom Set up Boxes (STB).""" import logging import asyncio from enum import Enum from async_timeout import timeout from .commands import COMMANDS from .notify import (install_mediaroom_protocol, GEN_ID_FORMAT) from .error import PyMediaroomError _LOGGER = logging.getLogger(__name__) REMOTE_CONTROL_PORT = 8082 GET_STATE_TIMEOUT = 10 OPEN_CONTROL_TIMEOUT = 5 class State(Enum): """Available States.""" OFF = 0 STANDBY = 1 PLAYING_LIVE_TV = 2 PLAYING_RECORDED_TV = 3 PLAYING_TIMESHIFT_TV = 4 STOPPED = 5 UNKNOWN = -1 class Remote(): """This class represents a MediaRoom Set-up-Box Remote Control.""" def __init__(self, ip): self.stb_ip = ip self._state = State.UNKNOWN self.lock = asyncio.Lock() self.tune_src = None self.current_channel = None @property def device_id(self): """Generate pseudo device ID from fixed IP assigned by operator.""" return GEN_ID_FORMAT.format(self.stb_ip) @property def state(self): return self._state def resolv(self, tune_src): """Return the channel name. TODO: implement remote webservice to resolv tune_src""" self.tune_src = tune_src return self.current_channel def __repr__(self): return self.stb_ip async def send_cmd(self, cmd): """Send remote command to STB.""" _LOGGER.info("Send cmd = %s", cmd) if cmd not in COMMANDS and cmd not in range(0, 999): _LOGGER.error("Unknown command") raise PyMediaroomError("Unknown commands") keys = [] if cmd in range(0, 999): for character in str(cmd): keys.append(COMMANDS["Number"+str(character)]) if len(keys) < 3: keys.append(COMMANDS["OK"]) self.current_channel = cmd else: keys = [COMMANDS[cmd]] try: async with timeout(OPEN_CONTROL_TIMEOUT): async with self.lock: _LOGGER.debug("Connecting to %s:%s", self.stb_ip, REMOTE_CONTROL_PORT) stb_recv, stb_send = await asyncio.open_connection(self.stb_ip, REMOTE_CONTROL_PORT, ) await stb_recv.read(6) _LOGGER.info("Connected to %s:%s", self.stb_ip, REMOTE_CONTROL_PORT) for key in keys: _LOGGER.debug("%s key=%s", cmd, key) stb_send.write("key={}\n".format(key).encode('UTF-8')) _ = await stb_recv.read(3) await asyncio.sleep(0.300) except asyncio.TimeoutError as error: _LOGGER.warning(error) raise PyMediaroomError("Timeout connecting to {}".format(self.stb_ip)) except ConnectionRefusedError as error: _LOGGER.warning(error) raise PyMediaroomError("Connection refused to {}".format(self.stb_ip)) async def turn_on(self): """Turn off media player.""" _LOGGER.debug("turn_on while %s", self._state) if self._state in [State.STANDBY, State.OFF, State.UNKNOWN]: await self.send_cmd('Power') self._state = State.PLAYING_LIVE_TV return self._state async def turn_off(self): """Turn off media player.""" _LOGGER.debug("turn_off while %s", self._state) if self._state in [State.PLAYING_LIVE_TV, State.PLAYING_RECORDED_TV, State.PLAYING_TIMESHIFT_TV, State.UNKNOWN]: await self.send_cmd('Power') self._state = State.OFF return self._state def notify_callback(self, notify): """Process State from NOTIFY message.""" _LOGGER.debug(notify) if notify.ip_address != self.stb_ip: return if notify.tune: self._state = State.PLAYING_LIVE_TV self.tune_src = notify.tune['@src'] try: if notify.stopped: self._state = State.STOPPED elif notify.timeshift: self._state = State.PLAYING_TIMESHIFT_TV elif notify.recorded: self._state = State.PLAYING_RECORDED_TV except PyMediaroomError as e: _LOGGER.debug("%s please report at https://github.com/dgomes/pymediaroom/issues", e) else: self._state = State.STANDBY _LOGGER.debug("%s is %s", self.stb_ip, self._state) return self._state async def discover(ignore_list=[], max_wait=30): """List STB in the network.""" stbs = [] try: async with timeout(max_wait): def responses_callback(notify): """Queue notify messages.""" _LOGGER.debug("Found: %s", notify.ip_address) stbs.append(notify.ip_address) mr_protocol = await install_mediaroom_protocol(responses_callback=responses_callback) await asyncio.sleep(max_wait) except asyncio.TimeoutError: mr_protocol.close() _LOGGER.debug("discover() timeout!") return list(set([stb for stb in stbs if stb not in ignore_list]))