Home Assistant Unofficial Reference 2024.12.1
binding.py
Go to the documentation of this file.
1 """Bridge between emulated_roku and Home Assistant."""
2 
3 import logging
4 
5 from emulated_roku import EmulatedRokuCommandHandler, EmulatedRokuServer
6 
7 from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP
8 from homeassistant.core import CoreState, EventOrigin
9 
10 LOGGER = logging.getLogger(__package__)
11 
12 EVENT_ROKU_COMMAND = "roku_command"
13 
14 ATTR_COMMAND_TYPE = "type"
15 ATTR_SOURCE_NAME = "source_name"
16 ATTR_KEY = "key"
17 ATTR_APP_ID = "app_id"
18 
19 ROKU_COMMAND_KEYDOWN = "keydown"
20 ROKU_COMMAND_KEYUP = "keyup"
21 ROKU_COMMAND_KEYPRESS = "keypress"
22 ROKU_COMMAND_LAUNCH = "launch"
23 
24 
26  """Manages an emulated_roku server."""
27 
28  def __init__(
29  self,
30  hass,
31  name,
32  host_ip,
33  listen_port,
34  advertise_ip,
35  advertise_port,
36  upnp_bind_multicast,
37  ):
38  """Initialize the properties."""
39  self.hasshass = hass
40 
41  self.roku_usnroku_usn = name
42  self.host_iphost_ip = host_ip
43  self.listen_portlisten_port = listen_port
44 
45  self.advertise_portadvertise_port = advertise_port
46  self.advertise_ipadvertise_ip = advertise_ip
47 
48  self.bind_multicastbind_multicast = upnp_bind_multicast
49 
50  self._api_server_api_server = None
51 
52  self._unsub_start_listener_unsub_start_listener = None
53  self._unsub_stop_listener_unsub_stop_listener = None
54 
55  async def setup(self):
56  """Start the emulated_roku server."""
57 
58  class EventCommandHandler(EmulatedRokuCommandHandler):
59  """emulated_roku command handler to turn commands into events."""
60 
61  def __init__(self, hass):
62  self.hasshass = hass
63 
64  def on_keydown(self, roku_usn, key):
65  """Handle keydown event."""
66  self.hasshass.bus.async_fire(
67  EVENT_ROKU_COMMAND,
68  {
69  ATTR_SOURCE_NAME: roku_usn,
70  ATTR_COMMAND_TYPE: ROKU_COMMAND_KEYDOWN,
71  ATTR_KEY: key,
72  },
73  EventOrigin.local,
74  )
75 
76  def on_keyup(self, roku_usn, key):
77  """Handle keyup event."""
78  self.hasshass.bus.async_fire(
79  EVENT_ROKU_COMMAND,
80  {
81  ATTR_SOURCE_NAME: roku_usn,
82  ATTR_COMMAND_TYPE: ROKU_COMMAND_KEYUP,
83  ATTR_KEY: key,
84  },
85  EventOrigin.local,
86  )
87 
88  def on_keypress(self, roku_usn, key):
89  """Handle keypress event."""
90  self.hasshass.bus.async_fire(
91  EVENT_ROKU_COMMAND,
92  {
93  ATTR_SOURCE_NAME: roku_usn,
94  ATTR_COMMAND_TYPE: ROKU_COMMAND_KEYPRESS,
95  ATTR_KEY: key,
96  },
97  EventOrigin.local,
98  )
99 
100  def launch(self, roku_usn, app_id):
101  """Handle launch event."""
102  self.hasshass.bus.async_fire(
103  EVENT_ROKU_COMMAND,
104  {
105  ATTR_SOURCE_NAME: roku_usn,
106  ATTR_COMMAND_TYPE: ROKU_COMMAND_LAUNCH,
107  ATTR_APP_ID: app_id,
108  },
109  EventOrigin.local,
110  )
111 
112  LOGGER.debug(
113  "Initializing emulated_roku %s on %s:%s",
114  self.roku_usnroku_usn,
115  self.host_iphost_ip,
116  self.listen_portlisten_port,
117  )
118 
119  handler = EventCommandHandler(self.hasshass)
120 
121  self._api_server_api_server = EmulatedRokuServer(
122  self.hasshass.loop,
123  handler,
124  self.roku_usnroku_usn,
125  self.host_iphost_ip,
126  self.listen_portlisten_port,
127  advertise_ip=self.advertise_ipadvertise_ip,
128  advertise_port=self.advertise_portadvertise_port,
129  bind_multicast=self.bind_multicastbind_multicast,
130  )
131 
132  async def emulated_roku_stop(event):
133  """Wrap the call to emulated_roku.close."""
134  LOGGER.debug("Stopping emulated_roku %s", self.roku_usnroku_usn)
135  self._unsub_stop_listener_unsub_stop_listener = None
136  await self._api_server_api_server.close()
137 
138  async def emulated_roku_start(event):
139  """Wrap the call to emulated_roku.start."""
140  try:
141  LOGGER.debug("Starting emulated_roku %s", self.roku_usnroku_usn)
142  self._unsub_start_listener_unsub_start_listener = None
143  await self._api_server_api_server.start()
144  except OSError:
145  LOGGER.exception(
146  "Failed to start Emulated Roku %s on %s:%s",
147  self.roku_usnroku_usn,
148  self.host_iphost_ip,
149  self.listen_portlisten_port,
150  )
151  # clean up inconsistent state on errors
152  await emulated_roku_stop(None)
153  else:
154  self._unsub_stop_listener_unsub_stop_listener = self.hasshass.bus.async_listen_once(
155  EVENT_HOMEASSISTANT_STOP, emulated_roku_stop
156  )
157 
158  # start immediately if already running
159  if self.hasshass.state is CoreState.running:
160  await emulated_roku_start(None)
161  else:
162  self._unsub_start_listener_unsub_start_listener = self.hasshass.bus.async_listen_once(
163  EVENT_HOMEASSISTANT_START, emulated_roku_start
164  )
165 
166  return True
167 
168  async def unload(self):
169  """Unload the emulated_roku server."""
170  LOGGER.debug("Unloading emulated_roku %s", self.roku_usnroku_usn)
171 
172  if self._unsub_start_listener_unsub_start_listener:
173  self._unsub_start_listener_unsub_start_listener()
174  self._unsub_start_listener_unsub_start_listener = None
175 
176  if self._unsub_stop_listener_unsub_stop_listener:
177  self._unsub_stop_listener_unsub_stop_listener()
178  self._unsub_stop_listener_unsub_stop_listener = None
179 
180  await self._api_server_api_server.close()
181 
182  return True
def __init__(self, hass, name, host_ip, listen_port, advertise_ip, advertise_port, upnp_bind_multicast)
Definition: binding.py:37