Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Support for local control of entities by emulating a Philips Hue bridge."""
2 
3 from __future__ import annotations
4 
5 import logging
6 
7 from aiohttp import web
8 import voluptuous as vol
9 
10 from homeassistant.components.http import KEY_HASS
11 from homeassistant.components.network import async_get_source_ip
12 from homeassistant.const import (
13  CONF_ENTITIES,
14  CONF_TYPE,
15  EVENT_HOMEASSISTANT_STARTED,
16  EVENT_HOMEASSISTANT_STOP,
17 )
18 from homeassistant.core import Event, HomeAssistant
20 from homeassistant.helpers.typing import ConfigType
21 
22 from .config import (
23  CONF_ADVERTISE_IP,
24  CONF_ADVERTISE_PORT,
25  CONF_ENTITY_HIDDEN,
26  CONF_ENTITY_NAME,
27  CONF_EXPOSE_BY_DEFAULT,
28  CONF_EXPOSED_DOMAINS,
29  CONF_HOST_IP,
30  CONF_LIGHTS_ALL_DIMMABLE,
31  CONF_LISTEN_PORT,
32  CONF_OFF_MAPS_TO_ON_DOMAINS,
33  CONF_UPNP_BIND_MULTICAST,
34  DEFAULT_LIGHTS_ALL_DIMMABLE,
35  DEFAULT_LISTEN_PORT,
36  DEFAULT_TYPE,
37  TYPE_ALEXA,
38  TYPE_GOOGLE,
39  Config,
40 )
41 from .const import DOMAIN
42 from .hue_api import (
43  HueAllGroupsStateView,
44  HueAllLightsStateView,
45  HueConfigView,
46  HueFullStateView,
47  HueGroupView,
48  HueOneLightChangeView,
49  HueOneLightStateView,
50  HueUnauthorizedUser,
51  HueUsernameView,
52 )
53 from .upnp import DescriptionXmlView, async_create_upnp_datagram_endpoint
54 
55 _LOGGER = logging.getLogger(__name__)
56 
57 
58 CONFIG_ENTITY_SCHEMA = vol.Schema(
59  {
60  vol.Optional(CONF_ENTITY_NAME): cv.string,
61  vol.Optional(CONF_ENTITY_HIDDEN): cv.boolean,
62  }
63 )
64 
65 
66 CONFIG_SCHEMA = vol.Schema(
67  {
68  DOMAIN: vol.Schema(
69  {
70  vol.Optional(CONF_HOST_IP): cv.string,
71  vol.Optional(CONF_LISTEN_PORT, default=DEFAULT_LISTEN_PORT): cv.port,
72  vol.Optional(CONF_ADVERTISE_IP): cv.string,
73  vol.Optional(CONF_ADVERTISE_PORT): cv.port,
74  vol.Optional(CONF_UPNP_BIND_MULTICAST): cv.boolean,
75  vol.Optional(CONF_OFF_MAPS_TO_ON_DOMAINS): cv.ensure_list,
76  vol.Optional(CONF_EXPOSE_BY_DEFAULT): cv.boolean,
77  vol.Optional(CONF_EXPOSED_DOMAINS): cv.ensure_list,
78  vol.Optional(CONF_TYPE, default=DEFAULT_TYPE): vol.Any(
79  TYPE_ALEXA, TYPE_GOOGLE
80  ),
81  vol.Optional(CONF_ENTITIES): vol.Schema(
82  {cv.entity_id: CONFIG_ENTITY_SCHEMA}
83  ),
84  vol.Optional(
85  CONF_LIGHTS_ALL_DIMMABLE, default=DEFAULT_LIGHTS_ALL_DIMMABLE
86  ): cv.boolean,
87  }
88  )
89  },
90  extra=vol.ALLOW_EXTRA,
91 )
92 
93 
95  hass: HomeAssistant, config: Config, app: web.Application
96 ) -> None:
97  """Start the emulated hue bridge."""
98  protocol = await async_create_upnp_datagram_endpoint(
99  config.host_ip_addr,
100  config.upnp_bind_multicast,
101  config.advertise_ip,
102  config.advertise_port or config.listen_port,
103  )
104 
105  runner = web.AppRunner(app)
106  await runner.setup()
107 
108  site = web.TCPSite(runner, config.host_ip_addr, config.listen_port)
109 
110  try:
111  await site.start()
112  except OSError as error:
113  _LOGGER.error(
114  "Failed to create HTTP server at port %d: %s", config.listen_port, error
115  )
116  protocol.close()
117  return
118 
119  async def stop_emulated_hue_bridge(event: Event) -> None:
120  """Stop the emulated hue bridge."""
121  protocol.close()
122  await site.stop()
123  await runner.cleanup()
124 
125  hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_emulated_hue_bridge)
126 
127 
128 async def async_setup(hass: HomeAssistant, yaml_config: ConfigType) -> bool:
129  """Activate the emulated_hue component."""
130  local_ip = await async_get_source_ip(hass)
131  config = Config(hass, yaml_config.get(DOMAIN, {}), local_ip)
132  await config.async_setup()
133 
134  app = web.Application()
135  app[KEY_HASS] = hass
136 
137  # We misunderstood the startup signal. You're not allowed to change
138  # anything during startup. Temp workaround.
139  app._on_startup.freeze() # noqa: SLF001
140  await app.startup()
141 
142  DescriptionXmlView(config).register(hass, app, app.router)
143  HueUsernameView().register(hass, app, app.router)
144  HueConfigView(config).register(hass, app, app.router)
145  HueUnauthorizedUser().register(hass, app, app.router)
146  HueAllLightsStateView(config).register(hass, app, app.router)
147  HueOneLightStateView(config).register(hass, app, app.router)
148  HueOneLightChangeView(config).register(hass, app, app.router)
149  HueAllGroupsStateView(config).register(hass, app, app.router)
150  HueGroupView(config).register(hass, app, app.router)
151  HueFullStateView(config).register(hass, app, app.router)
152 
153  async def _start(event: Event) -> None:
154  """Start the bridge."""
155  await start_emulated_hue_bridge(hass, config, app)
156 
157  hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, _start)
158 
159  return True
UPNPResponderProtocol async_create_upnp_datagram_endpoint(str host_ip_addr, bool upnp_bind_multicast, str advertise_ip, int advertise_port)
Definition: upnp.py:148
None start_emulated_hue_bridge(HomeAssistant hass, Config config, web.Application app)
Definition: __init__.py:96
bool async_setup(HomeAssistant hass, ConfigType yaml_config)
Definition: __init__.py:128
def register(HomeAssistant hass, Heos controller)
Definition: services.py:29
str async_get_source_ip(HomeAssistant hass, str|UndefinedType target_ip=UNDEFINED)
Definition: __init__.py:40