Migrate to generic discovery method (#2271)

* Migrate to generic discovery method

* Add tests for discovery
This commit is contained in:
Paulus Schoutsen 2016-06-11 17:43:13 -07:00 committed by GitHub
parent c9756c40e2
commit 30f74bb3ca
32 changed files with 322 additions and 540 deletions

View File

@ -9,7 +9,6 @@ import os
import voluptuous as vol
from homeassistant.components import verisure
from homeassistant.const import (
ATTR_CODE, ATTR_CODE_FORMAT, ATTR_ENTITY_ID, SERVICE_ALARM_TRIGGER,
SERVICE_ALARM_DISARM, SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_AWAY)
@ -24,11 +23,6 @@ SCAN_INTERVAL = 30
ENTITY_ID_FORMAT = DOMAIN + '.{}'
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {
verisure.DISCOVER_ALARMS: 'verisure'
}
SERVICE_TO_METHOD = {
SERVICE_ALARM_DISARM: 'alarm_disarm',
SERVICE_ALARM_ARM_HOME: 'alarm_arm_home',
@ -50,8 +44,7 @@ ALARM_SERVICE_SCHEMA = vol.Schema({
def setup(hass, config):
"""Track states and offer events for sensors."""
component = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL,
DISCOVERY_PLATFORMS)
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL)
component.setup(config)

View File

@ -9,8 +9,6 @@ import logging
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity import Entity
from homeassistant.const import (STATE_ON, STATE_OFF)
from homeassistant.components import (
bloomsky, mysensors, zwave, vera, wemo, wink)
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
DOMAIN = 'binary_sensor'
@ -35,22 +33,11 @@ SENSOR_CLASSES = [
'vibration', # On means vibration detected, Off means no vibration
]
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {
bloomsky.DISCOVER_BINARY_SENSORS: 'bloomsky',
mysensors.DISCOVER_BINARY_SENSORS: 'mysensors',
zwave.DISCOVER_BINARY_SENSORS: 'zwave',
vera.DISCOVER_BINARY_SENSORS: 'vera',
wemo.DISCOVER_BINARY_SENSORS: 'wemo',
wink.DISCOVER_BINARY_SENSORS: 'wink'
}
def setup(hass, config):
"""Track states and offer events for binary sensors."""
component = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL,
DISCOVERY_PLATFORMS)
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL)
component.setup(config)

View File

@ -9,9 +9,8 @@ from datetime import timedelta
import requests
from homeassistant.components import discovery
from homeassistant.const import CONF_API_KEY
from homeassistant.helpers import validate_config
from homeassistant.helpers import validate_config, discovery
from homeassistant.util import Throttle
DOMAIN = "bloomsky"
@ -23,10 +22,6 @@ _LOGGER = logging.getLogger(__name__)
# no point in polling the API more frequently
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=300)
DISCOVER_SENSORS = 'bloomsky.sensors'
DISCOVER_BINARY_SENSORS = 'bloomsky.binary_sensor'
DISCOVER_CAMERAS = 'bloomsky.camera'
# pylint: disable=unused-argument,too-few-public-methods
def setup(hass, config):
@ -45,11 +40,8 @@ def setup(hass, config):
except RuntimeError:
return False
for component, discovery_service in (
('camera', DISCOVER_CAMERAS), ('sensor', DISCOVER_SENSORS),
('binary_sensor', DISCOVER_BINARY_SENSORS)):
discovery.discover(hass, discovery_service, component=component,
hass_config=config)
for component in 'camera', 'binary_sensor', 'sensor':
discovery.load_platform(hass, component, DOMAIN, {}, config)
return True

View File

@ -9,7 +9,6 @@ import logging
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.components import bloomsky, netatmo
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
from homeassistant.components.http import HomeAssistantView
@ -18,12 +17,6 @@ DEPENDENCIES = ['http']
SCAN_INTERVAL = 30
ENTITY_ID_FORMAT = DOMAIN + '.{}'
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {
bloomsky.DISCOVER_CAMERAS: 'bloomsky',
netatmo.DISCOVER_CAMERAS: 'netatmo',
}
STATE_RECORDING = 'recording'
STATE_STREAMING = 'streaming'
STATE_IDLE = 'idle'
@ -35,8 +28,7 @@ ENTITY_IMAGE_URL = '/api/camera_proxy/{0}?token={1}'
def setup(hass, config):
"""Setup the camera component."""
component = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL,
DISCOVERY_PLATFORMS)
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL)
hass.wsgi.register_view(CameraImageView(hass, component.entities))
hass.wsgi.register_view(CameraMjpegStream(hass, component.entities))

View File

@ -12,10 +12,11 @@ import os
import threading
from homeassistant.bootstrap import prepare_setup_platform
from homeassistant.components import discovery, group, zone
from homeassistant.components import group, zone
from homeassistant.components.discovery import SERVICE_NETGEAR
from homeassistant.config import load_yaml_config_file
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_per_platform
from homeassistant.helpers import config_per_platform, discovery
from homeassistant.helpers.entity import Entity
import homeassistant.helpers.config_validation as cv
import homeassistant.util as util
@ -62,7 +63,7 @@ ATTR_GPS = 'gps'
ATTR_BATTERY = 'battery'
DISCOVERY_PLATFORMS = {
discovery.SERVICE_NETGEAR: 'netgear',
SERVICE_NETGEAR: 'netgear',
}
_LOGGER = logging.getLogger(__name__)
@ -95,8 +96,11 @@ def setup(hass, config):
yaml_path = hass.config.path(YAML_DEVICES)
conf = config.get(DOMAIN, {})
if isinstance(conf, list) and len(conf) > 0:
conf = conf[0]
# Config can be an empty list. In that case, substitute a dict
if isinstance(conf, list):
conf = conf[0] if len(conf) > 0 else {}
consider_home = timedelta(
seconds=util.convert(conf.get(CONF_CONSIDER_HOME), int,
DEFAULT_CONSIDER_HOME))

View File

@ -9,100 +9,30 @@ loaded before the EVENT_PLATFORM_DISCOVERED is fired.
import logging
import threading
from homeassistant import bootstrap
from homeassistant.const import (
ATTR_DISCOVERED, ATTR_SERVICE, EVENT_HOMEASSISTANT_START,
EVENT_PLATFORM_DISCOVERED)
from homeassistant.const import EVENT_HOMEASSISTANT_START
from homeassistant.helpers.discovery import load_platform, discover
DOMAIN = "discovery"
REQUIREMENTS = ['netdisco==0.6.7']
SCAN_INTERVAL = 300 # seconds
LOAD_PLATFORM = 'load_platform'
SERVICE_WEMO = 'belkin_wemo'
SERVICE_HUE = 'philips_hue'
SERVICE_CAST = 'google_cast'
SERVICE_NETGEAR = 'netgear_router'
SERVICE_SONOS = 'sonos'
SERVICE_PLEX = 'plex_mediaserver'
SERVICE_SQUEEZEBOX = 'logitech_mediaserver'
SERVICE_PANASONIC_VIERA = 'panasonic_viera'
SERVICE_ROKU = 'roku'
SERVICE_HANDLERS = {
SERVICE_WEMO: "wemo",
SERVICE_CAST: "media_player",
SERVICE_HUE: "light",
SERVICE_NETGEAR: 'device_tracker',
SERVICE_SONOS: 'media_player',
SERVICE_PLEX: 'media_player',
SERVICE_SQUEEZEBOX: 'media_player',
SERVICE_PANASONIC_VIERA: 'media_player',
SERVICE_ROKU: 'media_player',
SERVICE_NETGEAR: ('device_tracker', None),
SERVICE_WEMO: ('wemo', None),
'philips_hue': ('light', 'hue'),
'google_cast': ('media_player', 'cast'),
'panasonic_viera': ('media_player', 'panasonic_viera'),
'plex_mediaserver': ('media_player', 'plex'),
'roku': ('media_player', 'roku'),
'sonos': ('media_player', 'sonos'),
'logitech_mediaserver': ('media_player', 'squeezebox'),
}
def listen(hass, service, callback):
"""Setup listener for discovery of specific service.
Service can be a string or a list/tuple.
"""
if isinstance(service, str):
service = (service,)
else:
service = tuple(service)
def discovery_event_listener(event):
"""Listen for discovery events."""
if ATTR_SERVICE in event.data and event.data[ATTR_SERVICE] in service:
callback(event.data[ATTR_SERVICE], event.data.get(ATTR_DISCOVERED))
hass.bus.listen(EVENT_PLATFORM_DISCOVERED, discovery_event_listener)
def discover(hass, service, discovered=None, component=None, hass_config=None):
"""Fire discovery event. Can ensure a component is loaded."""
if component is not None:
bootstrap.setup_component(hass, component, hass_config)
data = {
ATTR_SERVICE: service
}
if discovered is not None:
data[ATTR_DISCOVERED] = discovered
hass.bus.fire(EVENT_PLATFORM_DISCOVERED, data)
def load_platform(hass, component, platform, info=None, hass_config=None):
"""Helper method for generic platform loading.
This method allows a platform to be loaded dynamically without it being
known at runtime (in the DISCOVERY_PLATFORMS list of the component).
Advantages of using this method:
- Any component & platforms combination can be dynamically added
- A component (i.e. light) does not have to import every component
that can dynamically add a platform (e.g. wemo, wink, insteon_hub)
- Custom user components can take advantage of discovery/loading
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_DISCOVERED = {LOAD_PLATFORM: <<platform>>} }
* dev note: This listener can be found in entity_component.py
"""
if info is None:
info = {LOAD_PLATFORM: platform}
else:
info[LOAD_PLATFORM] = platform
discover(hass, LOAD_PLATFORM + '.' + component, info, component,
hass_config)
def setup(hass, config):
"""Start a discovery service."""
logger = logging.getLogger(__name__)
@ -119,20 +49,18 @@ def setup(hass, config):
with lock:
logger.info("Found new service: %s %s", service, info)
component = SERVICE_HANDLERS.get(service)
comp_plat = SERVICE_HANDLERS.get(service)
# We do not know how to handle this service.
if not component:
if not comp_plat:
return
# This component cannot be setup.
if not bootstrap.setup_component(hass, component, config):
return
component, platform = comp_plat
hass.bus.fire(EVENT_PLATFORM_DISCOVERED, {
ATTR_SERVICE: service,
ATTR_DISCOVERED: info
})
if platform is None:
discover(hass, service, info, component, config)
else:
load_platform(hass, component, platform, info, config)
# pylint: disable=unused-argument
def start_discovery(event):

View File

@ -8,15 +8,12 @@ import logging
import os
from datetime import timedelta
from homeassistant import bootstrap
from homeassistant.const import (
ATTR_DISCOVERED, ATTR_SERVICE, CONF_API_KEY, EVENT_PLATFORM_DISCOVERED)
from homeassistant.helpers import discovery
from homeassistant.const import CONF_API_KEY
from homeassistant.loader import get_component
from homeassistant.util import Throttle
DOMAIN = "ecobee"
DISCOVER_THERMOSTAT = "ecobee.thermostat"
DISCOVER_SENSORS = "ecobee.sensor"
NETWORK = None
HOLD_TEMP = 'hold_temp'
@ -70,23 +67,11 @@ def setup_ecobee(hass, network, config):
configurator = get_component('configurator')
configurator.request_done(_CONFIGURING.pop('ecobee'))
# Ensure component is loaded
bootstrap.setup_component(hass, 'thermostat', config)
bootstrap.setup_component(hass, 'sensor', config)
hold_temp = config[DOMAIN].get(HOLD_TEMP, False)
# Fire thermostat discovery event
hass.bus.fire(EVENT_PLATFORM_DISCOVERED, {
ATTR_SERVICE: DISCOVER_THERMOSTAT,
ATTR_DISCOVERED: {'hold_temp': hold_temp}
})
# Fire sensor discovery event
hass.bus.fire(EVENT_PLATFORM_DISCOVERED, {
ATTR_SERVICE: DISCOVER_SENSORS,
ATTR_DISCOVERED: {}
})
discovery.load_platform(hass, 'thermostat', DOMAIN,
{'hold_temp': hold_temp}, config)
discovery.load_platform(hass, 'sensor', DOMAIN, None, config)
# pylint: disable=too-few-public-methods

View File

@ -17,7 +17,7 @@ import homeassistant.helpers.config_validation as cv
from homeassistant.const import (
STATE_CLOSED, STATE_OPEN, STATE_UNKNOWN, SERVICE_CLOSE, SERVICE_OPEN,
ATTR_ENTITY_ID)
from homeassistant.components import (group, wink)
from homeassistant.components import group
DOMAIN = 'garage_door'
SCAN_INTERVAL = 30
@ -27,11 +27,6 @@ ENTITY_ID_ALL_GARAGE_DOORS = group.ENTITY_ID_FORMAT.format('all_garage_doors')
ENTITY_ID_FORMAT = DOMAIN + '.{}'
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {
wink.DISCOVER_GARAGE_DOORS: 'wink'
}
GARAGE_DOOR_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
})
@ -60,8 +55,7 @@ def open_door(hass, entity_id=None):
def setup(hass, config):
"""Track states and offer events for garage door."""
component = EntityComponent(
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS,
GROUP_NAME_ALL_GARAGE_DOORS)
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_GARAGE_DOORS)
component.setup(config)
def handle_garage_door_service(service):

View File

@ -14,7 +14,6 @@ import homeassistant.util as util
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.temperature import convert
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
from homeassistant.components import zwave
from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_TEMPERATURE, STATE_ON, STATE_OFF, STATE_UNKNOWN,
TEMP_CELCIUS)
@ -57,10 +56,6 @@ ATTR_SWING_LIST = "swing_list"
_LOGGER = logging.getLogger(__name__)
DISCOVERY_PLATFORMS = {
zwave.DISCOVER_HVAC: 'zwave'
}
def set_away_mode(hass, away_mode, entity_id=None):
"""Turn all or specified hvac away mode on."""
@ -139,8 +134,7 @@ def set_swing_mode(hass, swing_mode, entity_id=None):
# pylint: disable=too-many-branches
def setup(hass, config):
"""Setup hvacs."""
component = EntityComponent(_LOGGER, DOMAIN, hass,
SCAN_INTERVAL, DISCOVERY_PLATFORMS)
component = EntityComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL)
component.setup(config)
descriptions = load_yaml_config_file(

View File

@ -6,17 +6,12 @@ https://home-assistant.io/components/insteon_hub/
"""
import logging
import homeassistant.bootstrap as bootstrap
from homeassistant.const import (
ATTR_DISCOVERED, ATTR_SERVICE, CONF_API_KEY, CONF_PASSWORD, CONF_USERNAME,
EVENT_PLATFORM_DISCOVERED)
from homeassistant.helpers import validate_config
from homeassistant.loader import get_component
from homeassistant.const import CONF_API_KEY, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config, discovery
DOMAIN = "insteon_hub"
REQUIREMENTS = ['insteon_hub==0.4.5']
INSTEON = None
DISCOVER_LIGHTS = "insteon_hub.lights"
_LOGGER = logging.getLogger(__name__)
@ -44,11 +39,7 @@ def setup(hass, config):
_LOGGER.error("Could not connect to Insteon service.")
return
comp_name = 'light'
discovery = DISCOVER_LIGHTS
component = get_component(comp_name)
bootstrap.setup_component(hass, component.DOMAIN, config)
hass.bus.fire(
EVENT_PLATFORM_DISCOVERED,
{ATTR_SERVICE: discovery, ATTR_DISCOVERED: {}})
for component in 'light':
discovery.load_platform(hass, component, DOMAIN, None, config)
return True

View File

@ -7,19 +7,15 @@ https://home-assistant.io/components/isy994/
import logging
from urllib.parse import urlparse
from homeassistant import bootstrap
from homeassistant.const import (
ATTR_DISCOVERED, ATTR_SERVICE, CONF_HOST, CONF_PASSWORD, CONF_USERNAME,
EVENT_HOMEASSISTANT_STOP, EVENT_PLATFORM_DISCOVERED)
from homeassistant.helpers import validate_config
CONF_HOST, CONF_PASSWORD, CONF_USERNAME,
EVENT_HOMEASSISTANT_STOP)
from homeassistant.helpers import validate_config, discovery
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.loader import get_component
DOMAIN = "isy994"
REQUIREMENTS = ['PyISY==1.0.6']
DISCOVER_LIGHTS = "isy994.lights"
DISCOVER_SWITCHES = "isy994.switches"
DISCOVER_SENSORS = "isy994.sensors"
ISY = None
SENSOR_STRING = 'Sensor'
HIDDEN_STRING = '{HIDE ME}'
@ -76,15 +72,9 @@ def setup(hass, config):
# Listen for HA stop to disconnect.
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop)
# Load components for the devices in the ISY controller that we support.
for comp_name, discovery in ((('sensor', DISCOVER_SENSORS),
('light', DISCOVER_LIGHTS),
('switch', DISCOVER_SWITCHES))):
component = get_component(comp_name)
bootstrap.setup_component(hass, component.DOMAIN, config)
hass.bus.fire(EVENT_PLATFORM_DISCOVERED,
{ATTR_SERVICE: discovery,
ATTR_DISCOVERED: {}})
# Load platforms for the devices in the ISY controller that we support.
for component in ('sensor', 'light', 'switch'):
discovery.load_platform(hass, component, DOMAIN, None, config)
ISY.auto_update = True
return True

View File

@ -10,9 +10,7 @@ import csv
import voluptuous as vol
from homeassistant.components import (
group, discovery, wemo, wink, isy994,
zwave, insteon_hub, mysensors, tellstick, vera)
from homeassistant.components import group
from homeassistant.config import load_yaml_config_file
from homeassistant.const import (
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE,
@ -60,19 +58,6 @@ EFFECT_WHITE = "white"
LIGHT_PROFILES_FILE = "light_profiles.csv"
# Maps discovered services to their platforms.
DISCOVERY_PLATFORMS = {
wemo.DISCOVER_LIGHTS: 'wemo',
wink.DISCOVER_LIGHTS: 'wink',
insteon_hub.DISCOVER_LIGHTS: 'insteon_hub',
isy994.DISCOVER_LIGHTS: 'isy994',
discovery.SERVICE_HUE: 'hue',
zwave.DISCOVER_LIGHTS: 'zwave',
mysensors.DISCOVER_LIGHTS: 'mysensors',
tellstick.DISCOVER_LIGHTS: 'tellstick',
vera.DISCOVER_LIGHTS: 'vera',
}
PROP_TO_ATTR = {
'brightness': ATTR_BRIGHTNESS,
'color_temp': ATTR_COLOR_TEMP,
@ -172,8 +157,7 @@ def toggle(hass, entity_id=None, transition=None):
def setup(hass, config):
"""Expose light control via statemachine and services."""
component = EntityComponent(
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS,
GROUP_NAME_ALL_LIGHTS)
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_LIGHTS)
component.setup(config)
# Load built-in profiles and custom profiles

View File

@ -18,7 +18,7 @@ import homeassistant.helpers.config_validation as cv
from homeassistant.const import (
ATTR_CODE, ATTR_CODE_FORMAT, ATTR_ENTITY_ID, STATE_LOCKED, STATE_UNLOCKED,
STATE_UNKNOWN, SERVICE_LOCK, SERVICE_UNLOCK)
from homeassistant.components import (group, verisure, wink, zwave)
from homeassistant.components import group
DOMAIN = 'lock'
SCAN_INTERVAL = 30
@ -30,13 +30,6 @@ ENTITY_ID_FORMAT = DOMAIN + '.{}'
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {
wink.DISCOVER_LOCKS: 'wink',
verisure.DISCOVER_LOCKS: 'verisure',
zwave.DISCOVER_LOCKS: 'zwave',
}
LOCK_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_CODE): cv.string,
@ -76,8 +69,7 @@ def unlock(hass, entity_id=None, code=None):
def setup(hass, config):
"""Track states and offer events for locks."""
component = EntityComponent(
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS,
GROUP_NAME_ALL_LOCKS)
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_LOCKS)
component.setup(config)
def handle_lock_service(service):

View File

@ -9,7 +9,6 @@ import os
import voluptuous as vol
from homeassistant.components import discovery
from homeassistant.config import load_yaml_config_file
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
@ -30,15 +29,6 @@ SCAN_INTERVAL = 10
ENTITY_ID_FORMAT = DOMAIN + '.{}'
DISCOVERY_PLATFORMS = {
discovery.SERVICE_CAST: 'cast',
discovery.SERVICE_SONOS: 'sonos',
discovery.SERVICE_PLEX: 'plex',
discovery.SERVICE_SQUEEZEBOX: 'squeezebox',
discovery.SERVICE_PANASONIC_VIERA: 'panasonic_viera',
discovery.SERVICE_ROKU: 'roku',
}
SERVICE_PLAY_MEDIA = 'play_media'
SERVICE_SELECT_SOURCE = 'select_source'
@ -285,8 +275,7 @@ def select_source(hass, source, entity_id=None):
def setup(hass, config):
"""Track states and offer events for media_players."""
component = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL,
DISCOVERY_PLATFORMS)
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL)
component.setup(config)

View File

@ -7,14 +7,11 @@ https://home-assistant.io/components/sensor.mysensors/
import logging
import socket
import homeassistant.bootstrap as bootstrap
from homeassistant.const import (ATTR_BATTERY_LEVEL, ATTR_DISCOVERED,
ATTR_SERVICE, CONF_OPTIMISTIC,
from homeassistant.const import (ATTR_BATTERY_LEVEL, CONF_OPTIMISTIC,
EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP,
EVENT_PLATFORM_DISCOVERED, STATE_OFF,
STATE_ON, TEMP_CELSIUS)
from homeassistant.helpers import validate_config
STATE_OFF, STATE_ON, TEMP_CELSIUS)
from homeassistant.helpers import validate_config, discovery
CONF_GATEWAYS = 'gateways'
CONF_DEVICE = 'device'
@ -40,19 +37,6 @@ ATTR_DEVICE = 'device'
GATEWAYS = None
DISCOVER_SENSORS = 'mysensors.sensors'
DISCOVER_SWITCHES = 'mysensors.switches'
DISCOVER_LIGHTS = 'mysensors.lights'
DISCOVER_BINARY_SENSORS = 'mysensors.binary_sensor'
# Maps discovered services to their platforms
DISCOVERY_COMPONENTS = [
('sensor', DISCOVER_SENSORS),
('switch', DISCOVER_SWITCHES),
('light', DISCOVER_LIGHTS),
('binary_sensor', DISCOVER_BINARY_SENSORS),
]
def setup(hass, config): # pylint: disable=too-many-locals
"""Setup the MySensors component."""
@ -124,14 +108,8 @@ def setup(hass, config): # pylint: disable=too-many-locals
GATEWAYS[device] = setup_gateway(
device, persistence_file, baud_rate, tcp_port)
for (component, discovery_service) in DISCOVERY_COMPONENTS:
# Ensure component is loaded
if not bootstrap.setup_component(hass, component, config):
return False
# Fire discovery event
hass.bus.fire(EVENT_PLATFORM_DISCOVERED, {
ATTR_SERVICE: discovery_service,
ATTR_DISCOVERED: {}})
for component in 'sensor', 'switch', 'light', 'binary_sensor':
discovery.load_platform(hass, component, DOMAIN, None, config)
return True

View File

@ -6,10 +6,9 @@ https://home-assistant.io/components/netatmo/
"""
import logging
from urllib.error import HTTPError
from homeassistant.components import discovery
from homeassistant.const import (
CONF_API_KEY, CONF_PASSWORD, CONF_USERNAME)
from homeassistant.helpers import validate_config
from homeassistant.helpers import validate_config, discovery
REQUIREMENTS = [
'https://github.com/jabesq/netatmo-api-python/archive/'
@ -24,9 +23,6 @@ NETATMO_AUTH = None
_LOGGER = logging.getLogger(__name__)
DISCOVER_SENSORS = 'netatmo.sensors'
DISCOVER_CAMERAS = 'netatmo.cameras'
def setup(hass, config):
"""Setup the Netatmo devices."""
@ -54,9 +50,7 @@ def setup(hass, config):
"Please check your settings for NatAtmo API.")
return False
for component, discovery_service in (
('camera', DISCOVER_CAMERAS), ('sensor', DISCOVER_SENSORS)):
discovery.discover(hass, discovery_service, component=component,
hass_config=config)
for component in 'camera', 'sensor':
discovery.load_platform(hass, component, DOMAIN, None, config)
return True

View File

@ -9,9 +9,8 @@ import logging
import time
import requests
from homeassistant.components import discovery
from homeassistant.const import CONF_API_KEY, CONF_HOST
from homeassistant.helpers import validate_config
from homeassistant.helpers import validate_config, discovery
DOMAIN = "octoprint"
OCTOPRINT = None

View File

@ -29,9 +29,6 @@ ENTITY_ID_ALL_ROLLERSHUTTERS = group.ENTITY_ID_FORMAT.format(
ENTITY_ID_FORMAT = DOMAIN + '.{}'
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {}
_LOGGER = logging.getLogger(__name__)
ATTR_CURRENT_POSITION = 'current_position'
@ -68,8 +65,7 @@ def stop(hass, entity_id=None):
def setup(hass, config):
"""Track states and offer events for roller shutters."""
component = EntityComponent(
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS,
GROUP_NAME_ALL_ROLLERSHUTTERS)
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_ROLLERSHUTTERS)
component.setup(config)
def handle_rollershutter_service(service):

View File

@ -8,35 +8,17 @@ import logging
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
from homeassistant.components import (
wink, zwave, isy994, verisure, ecobee, tellduslive, mysensors,
bloomsky, vera, netatmo)
DOMAIN = 'sensor'
SCAN_INTERVAL = 30
ENTITY_ID_FORMAT = DOMAIN + '.{}'
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {
bloomsky.DISCOVER_SENSORS: 'bloomsky',
wink.DISCOVER_SENSORS: 'wink',
zwave.DISCOVER_SENSORS: 'zwave',
isy994.DISCOVER_SENSORS: 'isy994',
verisure.DISCOVER_SENSORS: 'verisure',
ecobee.DISCOVER_SENSORS: 'ecobee',
tellduslive.DISCOVER_SENSORS: 'tellduslive',
mysensors.DISCOVER_SENSORS: 'mysensors',
vera.DISCOVER_SENSORS: 'vera',
netatmo.DISCOVER_SENSORS: 'netatmo',
}
def setup(hass, config):
"""Track states and offer events for sensors."""
component = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL,
DISCOVERY_PLATFORMS)
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL)
component.setup(config)

View File

@ -18,9 +18,7 @@ import homeassistant.helpers.config_validation as cv
from homeassistant.const import (
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE,
ATTR_ENTITY_ID)
from homeassistant.components import (
group, wemo, wink, isy994, verisure,
zwave, tellduslive, tellstick, mysensors, vera)
from homeassistant.components import group
DOMAIN = 'switch'
SCAN_INTERVAL = 30
@ -35,19 +33,6 @@ ATTR_CURRENT_POWER_MWH = "current_power_mwh"
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {
wemo.DISCOVER_SWITCHES: 'wemo',
wink.DISCOVER_SWITCHES: 'wink',
isy994.DISCOVER_SWITCHES: 'isy994',
verisure.DISCOVER_SWITCHES: 'verisure',
zwave.DISCOVER_SWITCHES: 'zwave',
tellduslive.DISCOVER_SWITCHES: 'tellduslive',
mysensors.DISCOVER_SWITCHES: 'mysensors',
tellstick.DISCOVER_SWITCHES: 'tellstick',
vera.DISCOVER_SWITCHES: 'vera',
}
PROP_TO_ATTR = {
'current_power_mwh': ATTR_CURRENT_POWER_MWH,
'today_power_mw': ATTR_TODAY_MWH,
@ -87,8 +72,7 @@ def toggle(hass, entity_id=None):
def setup(hass, config):
"""Track states and offer events for switches."""
component = EntityComponent(
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS,
GROUP_NAME_ALL_SWITCHES)
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_SWITCHES)
component.setup(config)
def handle_switch_service(service):

View File

@ -7,11 +7,7 @@ https://home-assistant.io/components/tellduslive/
import logging
from datetime import timedelta
from homeassistant import bootstrap
from homeassistant.const import (
ATTR_DISCOVERED, ATTR_SERVICE, EVENT_PLATFORM_DISCOVERED)
from homeassistant.helpers import validate_config
from homeassistant.loader import get_component
from homeassistant.helpers import validate_config, discovery
from homeassistant.util import Throttle
DOMAIN = "tellduslive"
@ -20,12 +16,6 @@ REQUIREMENTS = ['tellive-py==0.5.2']
_LOGGER = logging.getLogger(__name__)
DISCOVER_SENSORS = "tellduslive.sensors"
DISCOVER_SWITCHES = "tellduslive.switches"
DISCOVERY_TYPES = {"sensor": DISCOVER_SENSORS,
"switch": DISCOVER_SWITCHES}
CONF_PUBLIC_KEY = "public_key"
CONF_PRIVATE_KEY = "private_key"
CONF_TOKEN = "token"
@ -101,16 +91,8 @@ class TelldusLiveData(object):
_LOGGER.info("discovered %d new %s devices",
len(found_devices), component_name)
component = get_component(component_name)
bootstrap.setup_component(self._hass,
component.DOMAIN,
self._config)
discovery_type = DISCOVERY_TYPES[component_name]
self._hass.bus.fire(EVENT_PLATFORM_DISCOVERED,
{ATTR_SERVICE: discovery_type,
ATTR_DISCOVERED: found_devices})
discovery.load_platform(self._hass, component_name, DOMAIN,
found_devices, self._config)
def request(self, what, **params):
"""Send a request to the Tellstick Live API."""

View File

@ -8,11 +8,8 @@ import logging
import threading
import voluptuous as vol
from homeassistant import bootstrap
from homeassistant.const import (
ATTR_DISCOVERED, ATTR_SERVICE,
EVENT_PLATFORM_DISCOVERED, EVENT_HOMEASSISTANT_STOP)
from homeassistant.loader import get_component
from homeassistant.helpers import discovery
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.helpers.entity import Entity
DOMAIN = "tellstick"
@ -24,11 +21,6 @@ _LOGGER = logging.getLogger(__name__)
ATTR_SIGNAL_REPETITIONS = "signal_repetitions"
DEFAULT_SIGNAL_REPETITIONS = 1
DISCOVER_SWITCHES = "tellstick.switches"
DISCOVER_LIGHTS = "tellstick.lights"
DISCOVERY_TYPES = {"switch": DISCOVER_SWITCHES,
"light": DISCOVER_LIGHTS}
ATTR_DISCOVER_DEVICES = "devices"
ATTR_DISCOVER_CONFIG = "config"
@ -57,17 +49,11 @@ def _discover(hass, config, found_devices, component_name):
_LOGGER.info("discovered %d new %s devices",
len(found_devices), component_name)
component = get_component(component_name)
bootstrap.setup_component(hass, component.DOMAIN,
config)
signal_repetitions = config[DOMAIN].get(ATTR_SIGNAL_REPETITIONS)
hass.bus.fire(EVENT_PLATFORM_DISCOVERED,
{ATTR_SERVICE: DISCOVERY_TYPES[component_name],
ATTR_DISCOVERED: {ATTR_DISCOVER_DEVICES: found_devices,
ATTR_DISCOVER_CONFIG:
signal_repetitions}})
discovery.load_platform(hass, component_name, DOMAIN, {
ATTR_DISCOVER_DEVICES: found_devices,
ATTR_DISCOVER_CONFIG: signal_repetitions}, config)
def setup(hass, config):

View File

@ -15,7 +15,6 @@ from homeassistant.config import load_yaml_config_file
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.temperature import convert
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
from homeassistant.components import (ecobee, zwave)
import homeassistant.helpers.config_validation as cv
from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_TEMPERATURE, STATE_ON, STATE_OFF, STATE_UNKNOWN,
@ -45,11 +44,6 @@ ATTR_OPERATION = "current_operation"
_LOGGER = logging.getLogger(__name__)
DISCOVERY_PLATFORMS = {
ecobee.DISCOVER_THERMOSTAT: 'ecobee',
zwave.DISCOVER_THERMOSTATS: 'zwave'
}
SET_AWAY_MODE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Required(ATTR_AWAY_MODE): cv.boolean,
@ -101,8 +95,7 @@ def set_fan_mode(hass, fan_mode, entity_id=None):
# pylint: disable=too-many-branches
def setup(hass, config):
"""Setup thermostats."""
component = EntityComponent(_LOGGER, DOMAIN, hass,
SCAN_INTERVAL, DISCOVERY_PLATFORMS)
component = EntityComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL)
component.setup(config)
descriptions = load_yaml_config_file(

View File

@ -5,16 +5,13 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/vera/
"""
import logging
from collections import defaultdict
from requests.exceptions import RequestException
from homeassistant import bootstrap
from homeassistant.const import (
ATTR_SERVICE, ATTR_DISCOVERED,
EVENT_HOMEASSISTANT_STOP, EVENT_PLATFORM_DISCOVERED)
from homeassistant.helpers import discovery
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.helpers.entity import Entity
from homeassistant.loader import get_component
REQUIREMENTS = ['pyvera==0.2.10']
@ -27,28 +24,18 @@ VERA_CONTROLLER = None
CONF_EXCLUDE = 'exclude'
CONF_LIGHTS = 'lights'
BINARY_SENSOR = 'binary_sensor'
SENSOR = 'sensor'
LIGHT = 'light'
SWITCH = 'switch'
DEVICE_CATEGORIES = {
'Sensor': BINARY_SENSOR,
'Temperature Sensor': SENSOR,
'Light Sensor': SENSOR,
'Humidity Sensor': SENSOR,
'Dimmable Switch': LIGHT,
'Switch': SWITCH,
'Armable Sensor': SWITCH,
'On/Off Switch': SWITCH,
'Sensor': 'binary_sensor',
'Temperature Sensor': 'sensor',
'Light Sensor': 'sensor',
'Humidity Sensor': 'sensor',
'Dimmable Switch': 'light',
'Switch': 'switch',
'Armable Sensor': 'switch',
'On/Off Switch': 'switch',
# 'Window Covering': NOT SUPPORTED YET
}
DISCOVER_BINARY_SENSORS = 'vera.binary_sensors'
DISCOVER_SENSORS = 'vera.sensors'
DISCOVER_LIGHTS = 'vera.lights'
DISCOVER_SWITCHES = 'vera.switchs'
VERA_DEVICES = defaultdict(list)
@ -100,19 +87,13 @@ def setup(hass, base_config):
dev_type = DEVICE_CATEGORIES.get(device.category)
if dev_type is None:
continue
if dev_type == SWITCH and device.device_id in lights_ids:
dev_type = LIGHT
if dev_type == 'switch' and device.device_id in lights_ids:
dev_type = 'light'
VERA_DEVICES[dev_type].append(device)
for comp_name, discovery in (((BINARY_SENSOR, DISCOVER_BINARY_SENSORS),
(SENSOR, DISCOVER_SENSORS),
(LIGHT, DISCOVER_LIGHTS),
(SWITCH, DISCOVER_SWITCHES))):
component = get_component(comp_name)
bootstrap.setup_component(hass, component.DOMAIN, base_config)
hass.bus.fire(EVENT_PLATFORM_DISCOVERED,
{ATTR_SERVICE: discovery,
ATTR_DISCOVERED: {}})
for component in 'binary_sensor', 'sensor', 'light', 'switch':
discovery.load_platform(hass, component, DOMAIN, None, base_config)
return True

View File

@ -9,19 +9,11 @@ import threading
import time
from datetime import timedelta
from homeassistant import bootstrap
from homeassistant.const import (
ATTR_DISCOVERED, ATTR_SERVICE, CONF_PASSWORD, CONF_USERNAME,
EVENT_PLATFORM_DISCOVERED)
from homeassistant.helpers import validate_config
from homeassistant.loader import get_component
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config, discovery
from homeassistant.util import Throttle
DOMAIN = "verisure"
DISCOVER_SENSORS = 'verisure.sensors'
DISCOVER_SWITCHES = 'verisure.switches'
DISCOVER_ALARMS = 'verisure.alarm_control_panel'
DISCOVER_LOCKS = 'verisure.lock'
REQUIREMENTS = ['vsure==0.8.1']
@ -43,15 +35,8 @@ def setup(hass, config):
if not HUB.login():
return False
for comp_name, discovery in ((('sensor', DISCOVER_SENSORS),
('switch', DISCOVER_SWITCHES),
('alarm_control_panel', DISCOVER_ALARMS),
('lock', DISCOVER_LOCKS))):
component = get_component(comp_name)
bootstrap.setup_component(hass, component.DOMAIN, config)
hass.bus.fire(EVENT_PLATFORM_DISCOVERED,
{ATTR_SERVICE: discovery,
ATTR_DISCOVERED: {}})
for component in ('sensor', 'switch', 'alarm_control_panel', 'lock'):
discovery.load_platform(hass, component, DOMAIN, None, config)
return True

View File

@ -6,29 +6,22 @@ https://home-assistant.io/components/wemo/
"""
import logging
from homeassistant.components import discovery
from homeassistant.components.discovery import SERVICE_WEMO
from homeassistant.helpers import discovery
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
REQUIREMENTS = ['pywemo==0.4.3']
DOMAIN = 'wemo'
DISCOVER_LIGHTS = 'wemo.light'
DISCOVER_BINARY_SENSORS = 'wemo.binary_sensor'
DISCOVER_SWITCHES = 'wemo.switch'
# Mapping from Wemo model_name to service.
# Mapping from Wemo model_name to component.
WEMO_MODEL_DISPATCH = {
'Bridge': DISCOVER_LIGHTS,
'Insight': DISCOVER_SWITCHES,
'Maker': DISCOVER_SWITCHES,
'Sensor': DISCOVER_BINARY_SENSORS,
'Socket': DISCOVER_SWITCHES,
'LightSwitch': DISCOVER_SWITCHES
}
WEMO_SERVICE_DISPATCH = {
DISCOVER_LIGHTS: 'light',
DISCOVER_BINARY_SENSORS: 'binary_sensor',
DISCOVER_SWITCHES: 'switch',
'Bridge': 'light',
'Insight': 'switch',
'Maker': 'switch',
'Sensor': 'binary_sensor',
'Socket': 'switch',
'LightSwitch': 'switch'
}
SUBSCRIPTION_REGISTRY = None
@ -64,13 +57,12 @@ def setup(hass, config):
_LOGGER.debug('Discovered unique device %s', serial)
KNOWN_DEVICES.append(serial)
service = WEMO_MODEL_DISPATCH.get(model_name) or DISCOVER_SWITCHES
component = WEMO_SERVICE_DISPATCH.get(service)
component = WEMO_MODEL_DISPATCH.get(model_name, 'switch')
discovery.discover(hass, service, discovery_info,
component, config)
discovery.load_platform(hass, component, DOMAIN, discovery_info,
config)
discovery.listen(hass, discovery.SERVICE_WEMO, discovery_dispatch)
discovery.listen(hass, SERVICE_WEMO, discovery_dispatch)
_LOGGER.info("Scanning for WeMo devices.")
devices = [(device.host, device) for device in pywemo.discover_devices()]
@ -92,5 +84,5 @@ def setup(hass, config):
discovery_info = (device.name, device.model_name, url, device.mac,
device.serialnumber)
discovery.discover(hass, discovery.SERVICE_WEMO, discovery_info)
discovery.discover(hass, SERVICE_WEMO, discovery_info)
return True

View File

@ -6,24 +6,13 @@ https://home-assistant.io/components/wink/
"""
import logging
from homeassistant import bootstrap
from homeassistant.const import (
ATTR_DISCOVERED, ATTR_SERVICE, CONF_ACCESS_TOKEN,
EVENT_PLATFORM_DISCOVERED, ATTR_BATTERY_LEVEL)
from homeassistant.helpers import validate_config
from homeassistant.const import CONF_ACCESS_TOKEN, ATTR_BATTERY_LEVEL
from homeassistant.helpers import validate_config, discovery
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.loader import get_component
DOMAIN = "wink"
REQUIREMENTS = ['python-wink==0.7.6']
DISCOVER_LIGHTS = "wink.lights"
DISCOVER_SWITCHES = "wink.switches"
DISCOVER_SENSORS = "wink.sensors"
DISCOVER_BINARY_SENSORS = "wink.binary_sensors"
DISCOVER_LOCKS = "wink.locks"
DISCOVER_GARAGE_DOORS = "wink.garage_doors"
def setup(hass, config):
"""Setup the Wink component."""
@ -36,28 +25,17 @@ def setup(hass, config):
pywink.set_bearer_token(config[DOMAIN][CONF_ACCESS_TOKEN])
# Load components for the devices in the Wink that we support
for component_name, func_exists, discovery_type in (
('light', pywink.get_bulbs, DISCOVER_LIGHTS),
('switch', lambda: pywink.get_switches or
pywink.get_sirens or
pywink.get_powerstrip_outlets, DISCOVER_SWITCHES),
('binary_sensor', pywink.get_sensors, DISCOVER_BINARY_SENSORS),
('sensor', lambda: pywink.get_sensors or
pywink.get_eggtrays, DISCOVER_SENSORS),
('lock', pywink.get_locks, DISCOVER_LOCKS),
('garage_door', pywink.get_garage_doors, DISCOVER_GARAGE_DOORS)):
for component_name, func_exists in (
('light', pywink.get_bulbs),
('switch', lambda: pywink.get_switches or pywink.get_sirens or
pywink.get_powerstrip_outlets),
('binary_sensor', pywink.get_sensors),
('sensor', lambda: pywink.get_sensors or pywink.get_eggtrays),
('lock', pywink.get_locks),
('garage_door', pywink.get_garage_doors)):
if func_exists():
component = get_component(component_name)
# Ensure component is loaded
bootstrap.setup_component(hass, component.DOMAIN, config)
# Fire discovery event
hass.bus.fire(EVENT_PLATFORM_DISCOVERED, {
ATTR_SERVICE: discovery_type,
ATTR_DISCOVERED: {}
})
discovery.load_platform(hass, component_name, DOMAIN, None, config)
return True

View File

@ -9,11 +9,11 @@ import os.path
import time
from pprint import pprint
from homeassistant import bootstrap
from homeassistant.helpers import discovery
from homeassistant.const import (
ATTR_BATTERY_LEVEL, ATTR_DISCOVERED, ATTR_ENTITY_ID, ATTR_LOCATION,
ATTR_SERVICE, CONF_CUSTOMIZE, EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP, EVENT_PLATFORM_DISCOVERED)
ATTR_BATTERY_LEVEL, ATTR_ENTITY_ID, ATTR_LOCATION,
CONF_CUSTOMIZE, EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP)
from homeassistant.helpers.event import track_time_change
from homeassistant.util import convert, slugify
@ -37,14 +37,6 @@ SERVICE_HEAL_NETWORK = "heal_network"
SERVICE_SOFT_RESET = "soft_reset"
SERVICE_TEST_NETWORK = "test_network"
DISCOVER_SENSORS = "zwave.sensors"
DISCOVER_SWITCHES = "zwave.switch"
DISCOVER_LIGHTS = "zwave.light"
DISCOVER_BINARY_SENSORS = 'zwave.binary_sensor'
DISCOVER_THERMOSTATS = 'zwave.thermostat'
DISCOVER_HVAC = 'zwave.hvac'
DISCOVER_LOCKS = 'zwave.lock'
EVENT_SCENE_ACTIVATED = "zwave.scene_activated"
COMMAND_CLASS_SWITCH_MULTILEVEL = 38
@ -71,39 +63,32 @@ TYPE_DECIMAL = "Decimal"
# value type).
DISCOVERY_COMPONENTS = [
('sensor',
DISCOVER_SENSORS,
[COMMAND_CLASS_SENSOR_MULTILEVEL,
COMMAND_CLASS_METER,
COMMAND_CLASS_ALARM],
TYPE_WHATEVER,
GENRE_USER),
('light',
DISCOVER_LIGHTS,
[COMMAND_CLASS_SWITCH_MULTILEVEL],
TYPE_BYTE,
GENRE_USER),
('switch',
DISCOVER_SWITCHES,
[COMMAND_CLASS_SWITCH_BINARY],
TYPE_BOOL,
GENRE_USER),
('binary_sensor',
DISCOVER_BINARY_SENSORS,
[COMMAND_CLASS_SENSOR_BINARY],
TYPE_BOOL,
GENRE_USER),
('thermostat',
DISCOVER_THERMOSTATS,
[COMMAND_CLASS_THERMOSTAT_SETPOINT],
TYPE_WHATEVER,
GENRE_WHATEVER),
('hvac',
DISCOVER_HVAC,
[COMMAND_CLASS_THERMOSTAT_FAN_MODE],
TYPE_WHATEVER,
GENRE_WHATEVER),
('lock',
DISCOVER_LOCKS,
[COMMAND_CLASS_DOOR_LOCK],
TYPE_BOOL,
GENRE_USER),
@ -235,7 +220,6 @@ def setup(hass, config):
def value_added(node, value):
"""Called when a value is added to a node on the network."""
for (component,
discovery_service,
command_ids,
value_type,
value_genre) in DISCOVERY_COMPONENTS:
@ -247,9 +231,6 @@ def setup(hass, config):
if value_genre is not None and value_genre != value.genre:
continue
# Ensure component is loaded
bootstrap.setup_component(hass, component, config)
# Configure node
name = "{}.{}".format(component, _object_id(value))
@ -261,14 +242,10 @@ def setup(hass, config):
else:
value.disable_poll()
# Fire discovery event
hass.bus.fire(EVENT_PLATFORM_DISCOVERED, {
ATTR_SERVICE: discovery_service,
ATTR_DISCOVERED: {
ATTR_NODE_ID: node.node_id,
ATTR_VALUE_ID: value.value_id,
}
})
discovery.load_platform(hass, component, DOMAIN, {
ATTR_NODE_ID: node.node_id,
ATTR_VALUE_ID: value.value_id,
}, config)
def scene_activated(node, scene_id):
"""Called when a scene is activated on any node in the network."""

View File

@ -0,0 +1,86 @@
"""Helper methods to help with platform discovery."""
from homeassistant import bootstrap
from homeassistant.const import (
ATTR_DISCOVERED, ATTR_SERVICE, EVENT_PLATFORM_DISCOVERED)
EVENT_LOAD_PLATFORM = 'load_platform.{}'
ATTR_PLATFORM = 'platform'
def listen(hass, service, callback):
"""Setup listener for discovery of specific service.
Service can be a string or a list/tuple.
"""
if isinstance(service, str):
service = (service,)
else:
service = tuple(service)
def discovery_event_listener(event):
"""Listen for discovery events."""
if ATTR_SERVICE in event.data and event.data[ATTR_SERVICE] in service:
callback(event.data[ATTR_SERVICE], event.data.get(ATTR_DISCOVERED))
hass.bus.listen(EVENT_PLATFORM_DISCOVERED, discovery_event_listener)
def discover(hass, service, discovered=None, component=None, hass_config=None):
"""Fire discovery event. Can ensure a component is loaded."""
if component is not None:
bootstrap.setup_component(hass, component, hass_config)
data = {
ATTR_SERVICE: service
}
if discovered is not None:
data[ATTR_DISCOVERED] = discovered
hass.bus.fire(EVENT_PLATFORM_DISCOVERED, data)
def listen_platform(hass, component, callback):
"""Register a platform loader listener."""
service = EVENT_LOAD_PLATFORM.format(component)
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
callback(platform, event.data.get(ATTR_DISCOVERED))
hass.bus.listen(EVENT_PLATFORM_DISCOVERED, discovery_platform_listener)
def load_platform(hass, component, platform, discovered=None,
hass_config=None):
"""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.
"""
if component is not None:
bootstrap.setup_component(hass, component, hass_config)
data = {
ATTR_SERVICE: EVENT_LOAD_PLATFORM.format(component),
ATTR_PLATFORM: platform,
}
if discovered is not None:
data[ATTR_DISCOVERED] = discovered
hass.bus.fire(EVENT_PLATFORM_DISCOVERED, data)

View File

@ -2,11 +2,11 @@
from threading import Lock
from homeassistant.bootstrap import prepare_setup_platform
from homeassistant.components import discovery, group
from homeassistant.components import group
from homeassistant.const import (
ATTR_ENTITY_ID, CONF_SCAN_INTERVAL, CONF_ENTITY_NAMESPACE,
DEVICE_DEFAULT_NAME)
from homeassistant.helpers import config_per_platform
from homeassistant.helpers import config_per_platform, discovery
from homeassistant.helpers.entity import generate_entity_id
from homeassistant.helpers.event import track_utc_time_change
from homeassistant.helpers.service import extract_entity_ids
@ -20,8 +20,7 @@ class EntityComponent(object):
# pylint: disable=too-many-instance-attributes
# pylint: disable=too-many-arguments
def __init__(self, logger, domain, hass,
scan_interval=DEFAULT_SCAN_INTERVAL,
discovery_platforms=None, group_name=None):
scan_interval=DEFAULT_SCAN_INTERVAL, group_name=None):
"""Initialize an entity component."""
self.logger = logger
self.hass = hass
@ -29,7 +28,6 @@ class EntityComponent(object):
self.domain = domain
self.entity_id_format = domain + '.{}'
self.scan_interval = scan_interval
self.discovery_platforms = discovery_platforms
self.group_name = group_name
self.entities = {}
@ -54,23 +52,14 @@ class EntityComponent(object):
for p_type, p_config in config_per_platform(config, self.domain):
self._setup_platform(p_type, p_config)
if self.discovery_platforms:
# Discovery listener for all items in discovery_platforms array
# passed from a component's setup method (e.g. light/__init__.py)
discovery.listen(
self.hass, self.discovery_platforms.keys(),
lambda service, info:
self._setup_platform(self.discovery_platforms[service], {},
info))
# Generic discovery listener for loading platform dynamically
# Refer to: homeassistant.components.discovery.load_platform()
def load_platform_callback(service, info):
def component_platform_discovered(platform, info):
"""Callback to load a platform."""
platform = info.pop(discovery.LOAD_PLATFORM)
self._setup_platform(platform, {}, info if info else None)
discovery.listen(self.hass, discovery.LOAD_PLATFORM + '.' +
self.domain, load_platform_callback)
self._setup_platform(platform, {}, info)
discovery.listen_platform(self.hass, self.domain,
component_platform_discovered)
def extract_from_service(self, service):
"""Extract all known entities from a service call.

View File

@ -0,0 +1,90 @@
"""Test discovery helpers."""
from unittest.mock import patch
from homeassistant.helpers import discovery
from tests.common import get_test_home_assistant
class TestHelpersDiscovery:
"""Tests for discovery helper methods."""
def setup_method(self, method):
"""Setup things to be run when tests are started."""
self.hass = get_test_home_assistant()
def teardown_method(self, method):
"""Stop everything that was started."""
self.hass.stop()
@patch('homeassistant.bootstrap.setup_component')
def test_listen(self, mock_setup_component):
"""Test discovery listen/discover combo."""
calls_single = []
calls_multi = []
def callback_single(service, info):
"""Service discovered callback."""
calls_single.append((service, info))
def callback_multi(service, info):
"""Service discovered callback."""
calls_multi.append((service, info))
discovery.listen(self.hass, 'test service', callback_single)
discovery.listen(self.hass, ['test service', 'another service'],
callback_multi)
discovery.discover(self.hass, 'test service', 'discovery info',
'test_component')
self.hass.pool.block_till_done()
discovery.discover(self.hass, 'another service', 'discovery info',
'test_component')
self.hass.pool.block_till_done()
assert mock_setup_component.called
assert mock_setup_component.call_args[0] == \
(self.hass, 'test_component', None)
assert len(calls_single) == 1
assert calls_single[0] == ('test service', 'discovery info')
assert len(calls_single) == 1
assert len(calls_multi) == 2
assert ['test service', 'another service'] == [info[0] for info
in calls_multi]
@patch('homeassistant.bootstrap.setup_component')
def test_platform(self, mock_setup_component):
"""Test discover platform method."""
calls = []
def platform_callback(platform, info):
"""Platform callback method."""
calls.append((platform, info))
discovery.listen_platform(self.hass, 'test_component',
platform_callback)
discovery.load_platform(self.hass, 'test_component', 'test_platform',
'discovery info')
assert mock_setup_component.called
assert mock_setup_component.call_args[0] == \
(self.hass, 'test_component', None)
self.hass.pool.block_till_done()
discovery.load_platform(self.hass, 'test_component_2', 'test_platform',
'discovery info')
self.hass.pool.block_till_done()
assert len(calls) == 1
assert calls[0] == ('test_platform', 'discovery info')
self.hass.bus.fire(discovery.EVENT_PLATFORM_DISCOVERED, {
discovery.ATTR_SERVICE:
discovery.EVENT_LOAD_PLATFORM.format('test_component')
})
self.hass.pool.block_till_done()
assert len(calls) == 1

View File

@ -9,7 +9,7 @@ import homeassistant.core as ha
import homeassistant.loader as loader
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.components import discovery
from homeassistant.helpers import discovery
import homeassistant.util.dt as dt_util
from tests.common import (
@ -228,22 +228,17 @@ class TestHelpersEntityComponent(unittest.TestCase):
'._setup_platform')
def test_setup_does_discovery(self, mock_setup):
"""Test setup for discovery."""
component = EntityComponent(
_LOGGER, DOMAIN, self.hass, discovery_platforms={
'discovery.test': 'platform_test',
})
component = EntityComponent(_LOGGER, DOMAIN, self.hass)
component.setup({})
self.hass.bus.fire(discovery.EVENT_PLATFORM_DISCOVERED, {
discovery.ATTR_SERVICE: 'discovery.test',
discovery.ATTR_DISCOVERED: 'discovery_info',
})
discovery.load_platform(self.hass, DOMAIN, 'platform_test',
{'msg': 'discovery_info'})
self.hass.pool.block_till_done()
assert mock_setup.called
assert ('platform_test', {}, 'discovery_info') == \
assert ('platform_test', {}, {'msg': 'discovery_info'}) == \
mock_setup.call_args[0]
@patch('homeassistant.helpers.entity_component.track_utc_time_change')