core/homeassistant/components/discovery.py

159 lines
4.8 KiB
Python
Raw Normal View History

"""
Starts a service to scan in intervals for new devices.
Will emit EVENT_PLATFORM_DISCOVERED whenever a new service has been discovered.
Knows which components handle certain types, will make sure they are
loaded before the EVENT_PLATFORM_DISCOVERED is fired.
"""
import asyncio
import json
from datetime import timedelta
import logging
import os
import voluptuous as vol
from homeassistant.core import callback
from homeassistant.const import EVENT_HOMEASSISTANT_START
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import async_track_point_in_utc_time
from homeassistant.helpers.discovery import async_load_platform, async_discover
import homeassistant.util.dt as dt_util
REQUIREMENTS = ['netdisco==1.1.0']
DOMAIN = 'discovery'
SCAN_INTERVAL = timedelta(seconds=300)
2015-08-24 00:20:09 +00:00
SERVICE_NETGEAR = 'netgear_router'
SERVICE_WEMO = 'belkin_wemo'
SERVICE_HASS_IOS_APP = 'hass_ios'
SERVICE_IKEA_TRADFRI = 'ikea_tradfri'
SERVICE_HASSIO = 'hassio'
Axis component (#7381) * Added Axis hub, binary sensors and camera * Added Axis logo to static images * Added Axis logo to configurator Added Axis mdns discovery * Fixed flake8 and pylint comments * Missed a change from list to function call V5 of axis py * Added dependencies to requirements_all.txt * Clean up * Added files to coveragerc * Guide lines says to import function when needed, this makes Tox pass * Removed storing hass in config until at the end where I send it to axisdevice * Don't call update in the constructor * Don't keep hass private * Unnecessary lint ignore, following Baloobs suggestion of using NotImplementedError * Axis package not in pypi yet * Do not catch bare excepts. Device schema validations raise vol.Invalid. * setup_device still adds hass object to the config, so the need to remove it prior to writing config file still remains * Don't expect axis.conf contains correct values * Improved configuration validation * Trigger time better explains functionality than scan interval * Forgot to remove this earlier * Guideline says double qoutes for sentences * Return false from discovery if config file contains bad data * Keys in AXIS_DEVICES are serialnumber * Ordered imports in alphabetical order * Moved requirement to pypi * Moved update callback that handles trigger time to axis binary sensor * Renamed configurator instance to request_id since that is what it really is * Removed unnecessary configurator steps * Changed link in configurator to platform documentation * Add not-context-manager (#7523) * Add not-context-manager * Add missing comma * Threadsafe configurator (#7536) * Make Configurator thread safe, get_instance timing issues breaking configurator working on multiple devices * No blank lines allowed after function docstring * Fix comment Tox * Added Axis hub, binary sensors and camera * Added Axis logo to static images * Added Axis logo to configurator Added Axis mdns discovery * Fixed flake8 and pylint comments * Missed a change from list to function call V5 of axis py * Added dependencies to requirements_all.txt * Clean up * Added files to coveragerc * Guide lines says to import function when needed, this makes Tox pass * Removed storing hass in config until at the end where I send it to axisdevice * Don't call update in the constructor * Don't keep hass private * Unnecessary lint ignore, following Baloobs suggestion of using NotImplementedError * Axis package not in pypi yet * Do not catch bare excepts. Device schema validations raise vol.Invalid. * setup_device still adds hass object to the config, so the need to remove it prior to writing config file still remains * Don't expect axis.conf contains correct values * Improved configuration validation * Trigger time better explains functionality than scan interval * Forgot to remove this earlier * Guideline says double qoutes for sentences * Return false from discovery if config file contains bad data * Keys in AXIS_DEVICES are serialnumber * Ordered imports in alphabetical order * Moved requirement to pypi * Moved update callback that handles trigger time to axis binary sensor * Renamed configurator instance to request_id since that is what it really is * Removed unnecessary configurator steps * Changed link in configurator to platform documentation * No blank lines allowed after function docstring * No blank lines allowed after function docstring * Changed discovery to use axis instead of axis_mdns * Travis CI requested rerun of script/gen_requirements_all.py
2017-05-12 15:51:54 +00:00
SERVICE_AXIS = 'axis'
SERVICE_APPLE_TV = 'apple_tv'
SERVICE_WINK = 'wink'
SERVICE_HANDLERS = {
SERVICE_HASS_IOS_APP: ('ios', None),
SERVICE_NETGEAR: ('device_tracker', None),
SERVICE_WEMO: ('wemo', None),
SERVICE_IKEA_TRADFRI: ('tradfri', None),
SERVICE_HASSIO: ('hassio', None),
Axis component (#7381) * Added Axis hub, binary sensors and camera * Added Axis logo to static images * Added Axis logo to configurator Added Axis mdns discovery * Fixed flake8 and pylint comments * Missed a change from list to function call V5 of axis py * Added dependencies to requirements_all.txt * Clean up * Added files to coveragerc * Guide lines says to import function when needed, this makes Tox pass * Removed storing hass in config until at the end where I send it to axisdevice * Don't call update in the constructor * Don't keep hass private * Unnecessary lint ignore, following Baloobs suggestion of using NotImplementedError * Axis package not in pypi yet * Do not catch bare excepts. Device schema validations raise vol.Invalid. * setup_device still adds hass object to the config, so the need to remove it prior to writing config file still remains * Don't expect axis.conf contains correct values * Improved configuration validation * Trigger time better explains functionality than scan interval * Forgot to remove this earlier * Guideline says double qoutes for sentences * Return false from discovery if config file contains bad data * Keys in AXIS_DEVICES are serialnumber * Ordered imports in alphabetical order * Moved requirement to pypi * Moved update callback that handles trigger time to axis binary sensor * Renamed configurator instance to request_id since that is what it really is * Removed unnecessary configurator steps * Changed link in configurator to platform documentation * Add not-context-manager (#7523) * Add not-context-manager * Add missing comma * Threadsafe configurator (#7536) * Make Configurator thread safe, get_instance timing issues breaking configurator working on multiple devices * No blank lines allowed after function docstring * Fix comment Tox * Added Axis hub, binary sensors and camera * Added Axis logo to static images * Added Axis logo to configurator Added Axis mdns discovery * Fixed flake8 and pylint comments * Missed a change from list to function call V5 of axis py * Added dependencies to requirements_all.txt * Clean up * Added files to coveragerc * Guide lines says to import function when needed, this makes Tox pass * Removed storing hass in config until at the end where I send it to axisdevice * Don't call update in the constructor * Don't keep hass private * Unnecessary lint ignore, following Baloobs suggestion of using NotImplementedError * Axis package not in pypi yet * Do not catch bare excepts. Device schema validations raise vol.Invalid. * setup_device still adds hass object to the config, so the need to remove it prior to writing config file still remains * Don't expect axis.conf contains correct values * Improved configuration validation * Trigger time better explains functionality than scan interval * Forgot to remove this earlier * Guideline says double qoutes for sentences * Return false from discovery if config file contains bad data * Keys in AXIS_DEVICES are serialnumber * Ordered imports in alphabetical order * Moved requirement to pypi * Moved update callback that handles trigger time to axis binary sensor * Renamed configurator instance to request_id since that is what it really is * Removed unnecessary configurator steps * Changed link in configurator to platform documentation * No blank lines allowed after function docstring * No blank lines allowed after function docstring * Changed discovery to use axis instead of axis_mdns * Travis CI requested rerun of script/gen_requirements_all.py
2017-05-12 15:51:54 +00:00
SERVICE_AXIS: ('axis', None),
SERVICE_APPLE_TV: ('apple_tv', None),
SERVICE_WINK: ('wink', 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'),
'yamaha': ('media_player', 'yamaha'),
'logitech_mediaserver': ('media_player', 'squeezebox'),
'directv': ('media_player', 'directv'),
'denonavr': ('media_player', 'denonavr'),
'samsung_tv': ('media_player', 'samsungtv'),
'yeelight': ('light', 'yeelight'),
Frontier silicon (#6131) * added frontier_silicon constant * added the frontier_silicon component * cleaning up according to travis * trying to satisfy pylint * trying to satisfy pylint * fsapi version 0.0.6 * with fsapi version 0.0.7 * added fsapi dependency * yielding the FSAPI * Removing white space from docstring * Removing white space from an empty line * Switching to sync * clean up white spaces and rename device to FSAPIDevice * added frontier_silicon constant * added the frontier_silicon component * cleaning up according to travis * trying to satisfy pylint * trying to satisfy pylint * fsapi version 0.0.6 * with fsapi version 0.0.7 * added fsapi dependency * yielding the FSAPI * Removing white space from docstring * Removing white space from an empty line * Switching to sync * clean up white spaces and rename device to FSAPIDevice * changed info to debug * added frontier_silicon constant * added the frontier_silicon component * cleaning up according to travis * trying to satisfy pylint * trying to satisfy pylint * fsapi version 0.0.6 * with fsapi version 0.0.7 * added fsapi dependency * yielding the FSAPI * Removing white space from docstring * Removing white space from an empty line * Switching to sync * clean up white spaces and rename device to FSAPIDevice * added the frontier_silicon component * trying to satisfy pylint * fsapi version 0.0.6 * remove white space * generated requirements * added the frontier_silicon component * cleaning up according to travis * trying to satisfy pylint * trying to satisfy pylint * fsapi version 0.0.6 * with fsapi version 0.0.7 * added fsapi dependency * yielding the FSAPI * Removing white space from docstring * Removing white space from an empty line * Switching to sync * clean up white spaces and rename device to FSAPIDevice * trying to satisfy pylint * changed info to debug * added the frontier_silicon component * fsapi version 0.0.6 * generated requirements * pylint * moved import requests to the method where it is being used * add a basic unit test * cleaned up source code * added frontier_silicon constant * added the frontier_silicon component * added basic test * added fsapi to requirements_all.txt * added coverage omit, though a basic test was included * added MEDIA_TYPE_MUSIC for artist and album * removed duplicate cons * switched fsapi call to a property, removed unecessary comment * detailed docstring for fs_device * added a space for the info_name - info_text separator * reduced proeprty (fsapi) access for volume down/up
2017-02-28 14:23:07 +00:00
'frontier_silicon': ('media_player', 'frontier_silicon'),
'openhome': ('media_player', 'openhome'),
'harmony': ('remote', 'harmony'),
'sabnzbd': ('sensor', 'sabnzbd'),
'bose_soundtouch': ('media_player', 'soundtouch'),
'bluesound': ('media_player', 'bluesound'),
}
CONF_IGNORE = 'ignore'
CONFIG_SCHEMA = vol.Schema({
vol.Required(DOMAIN): vol.Schema({
vol.Optional(CONF_IGNORE, default=[]):
vol.All(cv.ensure_list, [vol.In(SERVICE_HANDLERS)])
}),
}, extra=vol.ALLOW_EXTRA)
@asyncio.coroutine
def async_setup(hass, config):
2016-03-08 16:55:57 +00:00
"""Start a discovery service."""
from netdisco.discovery import NetworkDiscovery
logger = logging.getLogger(__name__)
netdisco = NetworkDiscovery()
already_discovered = set()
# Disable zeroconf logging, it spams
logging.getLogger('zeroconf').setLevel(logging.CRITICAL)
# Platforms ignore by config
ignored_platforms = config[DOMAIN][CONF_IGNORE]
@asyncio.coroutine
def new_service_found(service, info):
"""Handle a new service if one is found."""
if service in ignored_platforms:
logger.info("Ignoring service: %s %s", service, info)
return
comp_plat = SERVICE_HANDLERS.get(service)
# We do not know how to handle this service.
if not comp_plat:
return
discovery_hash = json.dumps([service, info], sort_keys=True)
if discovery_hash in already_discovered:
return
already_discovered.add(discovery_hash)
logger.info("Found new service: %s %s", service, info)
component, platform = comp_plat
if platform is None:
yield from async_discover(hass, service, info, component, config)
else:
yield from async_load_platform(
hass, component, platform, info, config)
@asyncio.coroutine
def scan_devices(now):
"""Scan for devices."""
results = yield from hass.async_add_job(_discover, netdisco)
for result in results:
hass.async_add_job(new_service_found(*result))
async_track_point_in_utc_time(hass, scan_devices,
dt_util.utcnow() + SCAN_INTERVAL)
@callback
def schedule_first(event):
"""Schedule the first discovery when Home Assistant starts up."""
async_track_point_in_utc_time(hass, scan_devices, dt_util.utcnow())
# discovery local services
if 'HASSIO' in os.environ:
hass.async_add_job(new_service_found(SERVICE_HASSIO, {}))
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, schedule_first)
return True
def _discover(netdisco):
"""Discover devices."""
results = []
try:
netdisco.scan()
for disc in netdisco.discover():
for service in netdisco.get_info(disc):
results.append((disc, service))
finally:
netdisco.stop()
return results