148 lines
5.0 KiB
Python
148 lines
5.0 KiB
Python
"""Bridge between emulated_roku and Home Assistant."""
|
|
import logging
|
|
|
|
from homeassistant.const import (
|
|
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
|
|
from homeassistant.core import CoreState, EventOrigin
|
|
|
|
LOGGER = logging.getLogger('.')
|
|
|
|
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."""
|
|
from emulated_roku import EmulatedRokuServer, \
|
|
EmulatedRokuCommandHandler
|
|
|
|
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("Intializing 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 == 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
|