2016-11-19 16:18:33 +00:00
|
|
|
"""Helper methods to help with platform discovery.
|
|
|
|
|
|
|
|
There are two different types of discoveries that can be fired/listened for.
|
2017-06-08 13:53:12 +00:00
|
|
|
- listen/discover is for services. These are targeted at a component.
|
2016-11-19 16:18:33 +00:00
|
|
|
- listen_platform/discover_platform is for platforms. These are used by
|
2016-11-20 00:05:33 +00:00
|
|
|
components to allow discovery of their platforms.
|
2016-11-19 16:18:33 +00:00
|
|
|
"""
|
2017-03-05 09:41:54 +00:00
|
|
|
from homeassistant import setup, core
|
2017-10-08 15:17:54 +00:00
|
|
|
from homeassistant.loader import bind_hass
|
2016-06-12 00:43:13 +00:00
|
|
|
from homeassistant.const import (
|
|
|
|
ATTR_DISCOVERED, ATTR_SERVICE, EVENT_PLATFORM_DISCOVERED)
|
2017-02-18 19:31:37 +00:00
|
|
|
from homeassistant.exceptions import HomeAssistantError
|
|
|
|
from homeassistant.loader import DEPENDENCY_BLACKLIST
|
2018-03-11 17:01:12 +00:00
|
|
|
from homeassistant.util.async_ import run_callback_threadsafe
|
2016-06-12 00:43:13 +00:00
|
|
|
|
|
|
|
EVENT_LOAD_PLATFORM = 'load_platform.{}'
|
|
|
|
ATTR_PLATFORM = 'platform'
|
|
|
|
|
|
|
|
|
2017-10-08 15:17:54 +00:00
|
|
|
@bind_hass
|
2016-06-12 00:43:13 +00:00
|
|
|
def listen(hass, service, callback):
|
2017-05-02 16:18:47 +00:00
|
|
|
"""Set up listener for discovery of specific service.
|
2016-06-12 00:43:13 +00:00
|
|
|
|
2016-11-18 22:35:08 +00:00
|
|
|
Service can be a string or a list/tuple.
|
|
|
|
"""
|
|
|
|
run_callback_threadsafe(
|
|
|
|
hass.loop, async_listen, hass, service, callback).result()
|
|
|
|
|
|
|
|
|
|
|
|
@core.callback
|
2017-10-08 15:17:54 +00:00
|
|
|
@bind_hass
|
2016-11-18 22:35:08 +00:00
|
|
|
def async_listen(hass, service, callback):
|
2017-05-02 16:18:47 +00:00
|
|
|
"""Set up listener for discovery of specific service.
|
2016-11-18 22:35:08 +00:00
|
|
|
|
2016-06-12 00:43:13 +00:00
|
|
|
Service can be a string or a list/tuple.
|
|
|
|
"""
|
|
|
|
if isinstance(service, str):
|
|
|
|
service = (service,)
|
|
|
|
else:
|
|
|
|
service = tuple(service)
|
|
|
|
|
2016-11-18 22:35:08 +00:00
|
|
|
@core.callback
|
2016-06-12 00:43:13 +00:00
|
|
|
def discovery_event_listener(event):
|
|
|
|
"""Listen for discovery events."""
|
|
|
|
if ATTR_SERVICE in event.data and event.data[ATTR_SERVICE] in service:
|
2016-11-18 22:35:08 +00:00
|
|
|
hass.async_add_job(callback, event.data[ATTR_SERVICE],
|
|
|
|
event.data.get(ATTR_DISCOVERED))
|
2016-06-12 00:43:13 +00:00
|
|
|
|
2016-11-18 22:35:08 +00:00
|
|
|
hass.bus.async_listen(EVENT_PLATFORM_DISCOVERED, discovery_event_listener)
|
2016-06-12 00:43:13 +00:00
|
|
|
|
|
|
|
|
2017-10-08 15:17:54 +00:00
|
|
|
@bind_hass
|
2016-06-12 00:43:13 +00:00
|
|
|
def discover(hass, service, discovered=None, component=None, hass_config=None):
|
|
|
|
"""Fire discovery event. Can ensure a component is loaded."""
|
2016-11-22 03:33:08 +00:00
|
|
|
hass.add_job(
|
2016-11-19 16:18:33 +00:00
|
|
|
async_discover(hass, service, discovered, component, hass_config))
|
|
|
|
|
|
|
|
|
2017-10-08 15:17:54 +00:00
|
|
|
@bind_hass
|
2018-03-17 03:27:05 +00:00
|
|
|
async def async_discover(hass, service, discovered=None, component=None,
|
|
|
|
hass_config=None):
|
2016-11-19 16:18:33 +00:00
|
|
|
"""Fire discovery event. Can ensure a component is loaded."""
|
2017-02-18 19:31:37 +00:00
|
|
|
if component in DEPENDENCY_BLACKLIST:
|
|
|
|
raise HomeAssistantError(
|
|
|
|
'Cannot discover the {} component.'.format(component))
|
|
|
|
|
2016-11-19 16:18:33 +00:00
|
|
|
if component is not None and component not in hass.config.components:
|
2018-03-17 03:27:05 +00:00
|
|
|
await setup.async_setup_component(
|
2017-03-01 04:33:19 +00:00
|
|
|
hass, component, hass_config)
|
2016-06-12 00:43:13 +00:00
|
|
|
|
|
|
|
data = {
|
|
|
|
ATTR_SERVICE: service
|
|
|
|
}
|
|
|
|
|
|
|
|
if discovered is not None:
|
|
|
|
data[ATTR_DISCOVERED] = discovered
|
|
|
|
|
2016-11-19 16:18:33 +00:00
|
|
|
hass.bus.async_fire(EVENT_PLATFORM_DISCOVERED, data)
|
2016-06-12 00:43:13 +00:00
|
|
|
|
|
|
|
|
2017-10-08 15:17:54 +00:00
|
|
|
@bind_hass
|
2016-06-12 00:43:13 +00:00
|
|
|
def listen_platform(hass, component, callback):
|
|
|
|
"""Register a platform loader listener."""
|
2016-10-16 16:35:46 +00:00
|
|
|
run_callback_threadsafe(
|
|
|
|
hass.loop, async_listen_platform, hass, component, callback
|
|
|
|
).result()
|
|
|
|
|
|
|
|
|
2017-10-08 15:17:54 +00:00
|
|
|
@bind_hass
|
2016-10-16 16:35:46 +00:00
|
|
|
def async_listen_platform(hass, component, callback):
|
|
|
|
"""Register a platform loader listener.
|
|
|
|
|
|
|
|
This method must be run in the event loop.
|
|
|
|
"""
|
2016-06-12 00:43:13 +00:00
|
|
|
service = EVENT_LOAD_PLATFORM.format(component)
|
|
|
|
|
2016-10-16 16:35:46 +00:00
|
|
|
@core.callback
|
2016-06-12 00:43:13 +00:00
|
|
|
def discovery_platform_listener(event):
|
|
|
|
"""Listen for platform discovery events."""
|
|
|
|
if event.data.get(ATTR_SERVICE) != service:
|
|
|
|
return
|
|
|
|
|
|
|
|
platform = event.data.get(ATTR_PLATFORM)
|
|
|
|
|
|
|
|
if not platform:
|
|
|
|
return
|
|
|
|
|
2016-10-16 16:35:46 +00:00
|
|
|
hass.async_run_job(
|
|
|
|
callback, platform, event.data.get(ATTR_DISCOVERED)
|
|
|
|
)
|
2016-06-12 00:43:13 +00:00
|
|
|
|
2016-10-16 16:35:46 +00:00
|
|
|
hass.bus.async_listen(
|
|
|
|
EVENT_PLATFORM_DISCOVERED, discovery_platform_listener)
|
2016-06-12 00:43:13 +00:00
|
|
|
|
|
|
|
|
2017-10-08 15:17:54 +00:00
|
|
|
@bind_hass
|
2018-10-29 18:21:21 +00:00
|
|
|
def load_platform(hass, component, platform, discovered, hass_config):
|
2016-06-12 00:43:13 +00:00
|
|
|
"""Load a component and platform dynamically.
|
|
|
|
|
|
|
|
Target components will be loaded and an EVENT_PLATFORM_DISCOVERED will be
|
|
|
|
fired to load the platform. The event will contain:
|
|
|
|
{ ATTR_SERVICE = LOAD_PLATFORM + '.' + <<component>>
|
|
|
|
ATTR_PLATFORM = <<platform>>
|
|
|
|
ATTR_DISCOVERED = <<discovery info>> }
|
|
|
|
|
|
|
|
Use `listen_platform` to register a callback for these events.
|
|
|
|
"""
|
2016-11-22 03:33:08 +00:00
|
|
|
hass.add_job(
|
2016-11-19 16:18:33 +00:00
|
|
|
async_load_platform(hass, component, platform, discovered,
|
|
|
|
hass_config))
|
2016-10-29 19:54:47 +00:00
|
|
|
|
|
|
|
|
2017-10-08 15:17:54 +00:00
|
|
|
@bind_hass
|
2018-10-29 18:21:21 +00:00
|
|
|
async def async_load_platform(hass, component, platform, discovered,
|
|
|
|
hass_config):
|
2016-10-29 19:54:47 +00:00
|
|
|
"""Load a component and platform dynamically.
|
|
|
|
|
|
|
|
Target components will be loaded and an EVENT_PLATFORM_DISCOVERED will be
|
|
|
|
fired to load the platform. The event will contain:
|
|
|
|
{ ATTR_SERVICE = LOAD_PLATFORM + '.' + <<component>>
|
|
|
|
ATTR_PLATFORM = <<platform>>
|
|
|
|
ATTR_DISCOVERED = <<discovery info>> }
|
|
|
|
|
|
|
|
Use `listen_platform` to register a callback for these events.
|
|
|
|
|
2018-03-17 03:27:05 +00:00
|
|
|
Warning: Do not await this inside a setup method to avoid a dead lock.
|
2018-07-23 12:05:38 +00:00
|
|
|
Use `hass.async_create_task(async_load_platform(..))` instead.
|
2016-10-29 19:54:47 +00:00
|
|
|
|
|
|
|
This method is a coroutine.
|
|
|
|
"""
|
2018-10-29 18:21:21 +00:00
|
|
|
assert hass_config, 'You need to pass in the real hass config'
|
|
|
|
|
2017-02-18 19:31:37 +00:00
|
|
|
if component in DEPENDENCY_BLACKLIST:
|
|
|
|
raise HomeAssistantError(
|
|
|
|
'Cannot discover the {} component.'.format(component))
|
|
|
|
|
2016-11-19 16:18:33 +00:00
|
|
|
setup_success = True
|
2016-10-29 19:54:47 +00:00
|
|
|
|
2017-03-01 04:33:19 +00:00
|
|
|
if component not in hass.config.components:
|
2018-03-17 03:27:05 +00:00
|
|
|
setup_success = await setup.async_setup_component(
|
2017-03-01 04:33:19 +00:00
|
|
|
hass, component, hass_config)
|
2016-06-12 00:43:13 +00:00
|
|
|
|
2018-08-19 20:29:08 +00:00
|
|
|
# No need to fire event if we could not set up component
|
2016-11-19 16:18:33 +00:00
|
|
|
if not setup_success:
|
2016-10-29 19:54:47 +00:00
|
|
|
return
|
2016-06-12 00:43:13 +00:00
|
|
|
|
2016-10-29 19:54:47 +00:00
|
|
|
data = {
|
|
|
|
ATTR_SERVICE: EVENT_LOAD_PLATFORM.format(component),
|
|
|
|
ATTR_PLATFORM: platform,
|
|
|
|
}
|
2016-06-12 00:43:13 +00:00
|
|
|
|
2016-10-29 19:54:47 +00:00
|
|
|
if discovered is not None:
|
|
|
|
data[ATTR_DISCOVERED] = discovered
|
2016-07-26 05:49:10 +00:00
|
|
|
|
2016-10-29 19:54:47 +00:00
|
|
|
hass.bus.async_fire(EVENT_PLATFORM_DISCOVERED, data)
|