mirror of https://github.com/home-assistant/core
183 lines
5.6 KiB
Python
183 lines
5.6 KiB
Python
"""Bridge between emulated_roku and Home Assistant."""
|
|
|
|
import logging
|
|
|
|
from emulated_roku import EmulatedRokuCommandHandler, EmulatedRokuServer
|
|
|
|
from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP
|
|
from homeassistant.core import CoreState, EventOrigin
|
|
|
|
LOGGER = logging.getLogger(__package__)
|
|
|
|
EVENT_ROKU_COMMAND = "roku_command"
|
|
|
|
ATTR_COMMAND_TYPE = "type"
|
|
ATTR_SOURCE_NAME = "source_name"
|
|
ATTR_KEY = "key"
|
|
ATTR_APP_ID = "app_id"
|
|
|
|
ROKU_COMMAND_KEYDOWN = "keydown"
|
|
ROKU_COMMAND_KEYUP = "keyup"
|
|
ROKU_COMMAND_KEYPRESS = "keypress"
|
|
ROKU_COMMAND_LAUNCH = "launch"
|
|
|
|
|
|
class EmulatedRoku:
|
|
"""Manages an emulated_roku server."""
|
|
|
|
def __init__(
|
|
self,
|
|
hass,
|
|
name,
|
|
host_ip,
|
|
listen_port,
|
|
advertise_ip,
|
|
advertise_port,
|
|
upnp_bind_multicast,
|
|
):
|
|
"""Initialize the properties."""
|
|
self.hass = hass
|
|
|
|
self.roku_usn = name
|
|
self.host_ip = host_ip
|
|
self.listen_port = listen_port
|
|
|
|
self.advertise_port = advertise_port
|
|
self.advertise_ip = advertise_ip
|
|
|
|
self.bind_multicast = upnp_bind_multicast
|
|
|
|
self._api_server = None
|
|
|
|
self._unsub_start_listener = None
|
|
self._unsub_stop_listener = None
|
|
|
|
async def setup(self):
|
|
"""Start the emulated_roku server."""
|
|
|
|
class EventCommandHandler(EmulatedRokuCommandHandler):
|
|
"""emulated_roku command handler to turn commands into events."""
|
|
|
|
def __init__(self, hass):
|
|
self.hass = hass
|
|
|
|
def on_keydown(self, roku_usn, key):
|
|
"""Handle keydown event."""
|
|
self.hass.bus.async_fire(
|
|
EVENT_ROKU_COMMAND,
|
|
{
|
|
ATTR_SOURCE_NAME: roku_usn,
|
|
ATTR_COMMAND_TYPE: ROKU_COMMAND_KEYDOWN,
|
|
ATTR_KEY: key,
|
|
},
|
|
EventOrigin.local,
|
|
)
|
|
|
|
def on_keyup(self, roku_usn, key):
|
|
"""Handle keyup event."""
|
|
self.hass.bus.async_fire(
|
|
EVENT_ROKU_COMMAND,
|
|
{
|
|
ATTR_SOURCE_NAME: roku_usn,
|
|
ATTR_COMMAND_TYPE: ROKU_COMMAND_KEYUP,
|
|
ATTR_KEY: key,
|
|
},
|
|
EventOrigin.local,
|
|
)
|
|
|
|
def on_keypress(self, roku_usn, key):
|
|
"""Handle keypress event."""
|
|
self.hass.bus.async_fire(
|
|
EVENT_ROKU_COMMAND,
|
|
{
|
|
ATTR_SOURCE_NAME: roku_usn,
|
|
ATTR_COMMAND_TYPE: ROKU_COMMAND_KEYPRESS,
|
|
ATTR_KEY: key,
|
|
},
|
|
EventOrigin.local,
|
|
)
|
|
|
|
def launch(self, roku_usn, app_id):
|
|
"""Handle launch event."""
|
|
self.hass.bus.async_fire(
|
|
EVENT_ROKU_COMMAND,
|
|
{
|
|
ATTR_SOURCE_NAME: roku_usn,
|
|
ATTR_COMMAND_TYPE: ROKU_COMMAND_LAUNCH,
|
|
ATTR_APP_ID: app_id,
|
|
},
|
|
EventOrigin.local,
|
|
)
|
|
|
|
LOGGER.debug(
|
|
"Initializing emulated_roku %s on %s:%s",
|
|
self.roku_usn,
|
|
self.host_ip,
|
|
self.listen_port,
|
|
)
|
|
|
|
handler = EventCommandHandler(self.hass)
|
|
|
|
self._api_server = EmulatedRokuServer(
|
|
self.hass.loop,
|
|
handler,
|
|
self.roku_usn,
|
|
self.host_ip,
|
|
self.listen_port,
|
|
advertise_ip=self.advertise_ip,
|
|
advertise_port=self.advertise_port,
|
|
bind_multicast=self.bind_multicast,
|
|
)
|
|
|
|
async def emulated_roku_stop(event):
|
|
"""Wrap the call to emulated_roku.close."""
|
|
LOGGER.debug("Stopping emulated_roku %s", self.roku_usn)
|
|
self._unsub_stop_listener = None
|
|
await self._api_server.close()
|
|
|
|
async def emulated_roku_start(event):
|
|
"""Wrap the call to emulated_roku.start."""
|
|
try:
|
|
LOGGER.debug("Starting emulated_roku %s", self.roku_usn)
|
|
self._unsub_start_listener = None
|
|
await self._api_server.start()
|
|
except OSError:
|
|
LOGGER.exception(
|
|
"Failed to start Emulated Roku %s on %s:%s",
|
|
self.roku_usn,
|
|
self.host_ip,
|
|
self.listen_port,
|
|
)
|
|
# clean up inconsistent state on errors
|
|
await emulated_roku_stop(None)
|
|
else:
|
|
self._unsub_stop_listener = self.hass.bus.async_listen_once(
|
|
EVENT_HOMEASSISTANT_STOP, emulated_roku_stop
|
|
)
|
|
|
|
# start immediately if already running
|
|
if self.hass.state is CoreState.running:
|
|
await emulated_roku_start(None)
|
|
else:
|
|
self._unsub_start_listener = self.hass.bus.async_listen_once(
|
|
EVENT_HOMEASSISTANT_START, emulated_roku_start
|
|
)
|
|
|
|
return True
|
|
|
|
async def unload(self):
|
|
"""Unload the emulated_roku server."""
|
|
LOGGER.debug("Unloading emulated_roku %s", self.roku_usn)
|
|
|
|
if self._unsub_start_listener:
|
|
self._unsub_start_listener()
|
|
self._unsub_start_listener = None
|
|
|
|
if self._unsub_stop_listener:
|
|
self._unsub_stop_listener()
|
|
self._unsub_stop_listener = None
|
|
|
|
await self._api_server.close()
|
|
|
|
return True
|