176 lines
5.3 KiB
Python
176 lines
5.3 KiB
Python
"""Support for WeMo device discovery."""
|
|
import logging
|
|
|
|
import requests
|
|
import voluptuous as vol
|
|
|
|
from homeassistant.components.discovery import SERVICE_WEMO
|
|
from homeassistant.helpers import config_validation as cv
|
|
from homeassistant.helpers import discovery
|
|
|
|
from homeassistant.const import (
|
|
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
|
|
|
|
REQUIREMENTS = ['pywemo==0.4.34']
|
|
|
|
DOMAIN = 'wemo'
|
|
|
|
# Mapping from Wemo model_name to component.
|
|
WEMO_MODEL_DISPATCH = {
|
|
'Bridge': 'light',
|
|
'CoffeeMaker': 'switch',
|
|
'Dimmer': 'light',
|
|
'Humidifier': 'fan',
|
|
'Insight': 'switch',
|
|
'LightSwitch': 'switch',
|
|
'Maker': 'switch',
|
|
'Motion': 'binary_sensor',
|
|
'Sensor': 'binary_sensor',
|
|
'Socket': 'switch',
|
|
}
|
|
|
|
SUBSCRIPTION_REGISTRY = None
|
|
KNOWN_DEVICES = []
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
def coerce_host_port(value):
|
|
"""Validate that provided value is either just host or host:port.
|
|
|
|
Returns (host, None) or (host, port) respectively.
|
|
"""
|
|
host, _, port = value.partition(':')
|
|
|
|
if not host:
|
|
raise vol.Invalid('host cannot be empty')
|
|
|
|
if port:
|
|
port = cv.port(port)
|
|
else:
|
|
port = None
|
|
|
|
return host, port
|
|
|
|
|
|
CONF_STATIC = 'static'
|
|
CONF_DISCOVERY = 'discovery'
|
|
|
|
DEFAULT_DISCOVERY = True
|
|
|
|
CONFIG_SCHEMA = vol.Schema({
|
|
DOMAIN: vol.Schema({
|
|
vol.Optional(CONF_STATIC, default=[]): vol.Schema([
|
|
vol.All(cv.string, coerce_host_port)
|
|
]),
|
|
vol.Optional(CONF_DISCOVERY, default=DEFAULT_DISCOVERY): cv.boolean,
|
|
}),
|
|
}, extra=vol.ALLOW_EXTRA)
|
|
|
|
|
|
def setup(hass, config):
|
|
"""Set up for WeMo devices."""
|
|
import pywemo
|
|
|
|
# Keep track of WeMo devices
|
|
devices = []
|
|
|
|
# Keep track of WeMo device subscriptions for push updates
|
|
global SUBSCRIPTION_REGISTRY
|
|
SUBSCRIPTION_REGISTRY = pywemo.SubscriptionRegistry()
|
|
SUBSCRIPTION_REGISTRY.start()
|
|
|
|
def stop_wemo(event):
|
|
"""Shutdown Wemo subscriptions and subscription thread on exit."""
|
|
_LOGGER.debug("Shutting down WeMo event subscriptions")
|
|
SUBSCRIPTION_REGISTRY.stop()
|
|
|
|
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_wemo)
|
|
|
|
def setup_url_for_device(device):
|
|
"""Determine setup.xml url for given device."""
|
|
return 'http://{}:{}/setup.xml'.format(device.host, device.port)
|
|
|
|
def setup_url_for_address(host, port):
|
|
"""Determine setup.xml url for given host and port pair."""
|
|
if not port:
|
|
port = pywemo.ouimeaux_device.probe_wemo(host)
|
|
|
|
if not port:
|
|
return None
|
|
|
|
return 'http://{}:{}/setup.xml'.format(host, port)
|
|
|
|
def discovery_dispatch(service, discovery_info):
|
|
"""Dispatcher for incoming WeMo discovery events."""
|
|
# name, model, location, mac
|
|
model_name = discovery_info.get('model_name')
|
|
serial = discovery_info.get('serial')
|
|
|
|
# Only register a device once
|
|
if serial in KNOWN_DEVICES:
|
|
_LOGGER.debug(
|
|
"Ignoring known device %s %s", service, discovery_info)
|
|
return
|
|
|
|
_LOGGER.debug("Discovered unique WeMo device: %s", serial)
|
|
KNOWN_DEVICES.append(serial)
|
|
|
|
component = WEMO_MODEL_DISPATCH.get(model_name, 'switch')
|
|
|
|
discovery.load_platform(
|
|
hass, component, DOMAIN, discovery_info, config)
|
|
|
|
discovery.listen(hass, SERVICE_WEMO, discovery_dispatch)
|
|
|
|
def discover_wemo_devices(now):
|
|
"""Run discovery for WeMo devices."""
|
|
_LOGGER.debug("Beginning WeMo device discovery...")
|
|
_LOGGER.debug("Adding statically configured WeMo devices...")
|
|
for host, port in config.get(DOMAIN, {}).get(CONF_STATIC, []):
|
|
url = setup_url_for_address(host, port)
|
|
|
|
if not url:
|
|
_LOGGER.error(
|
|
'Unable to get description url for WeMo at: %s',
|
|
'{}:{}'.format(host, port) if port else host)
|
|
continue
|
|
|
|
try:
|
|
device = pywemo.discovery.device_from_description(url, None)
|
|
except (requests.exceptions.ConnectionError,
|
|
requests.exceptions.Timeout) as err:
|
|
_LOGGER.error("Unable to access WeMo at %s (%s)", url, err)
|
|
continue
|
|
|
|
if not [d[1] for d in devices
|
|
if d[1].serialnumber == device.serialnumber]:
|
|
devices.append((url, device))
|
|
|
|
if config.get(DOMAIN, {}).get(CONF_DISCOVERY):
|
|
_LOGGER.debug("Scanning network for WeMo devices...")
|
|
for device in pywemo.discover_devices():
|
|
if not [d[1] for d in devices
|
|
if d[1].serialnumber == device.serialnumber]:
|
|
devices.append((setup_url_for_device(device),
|
|
device))
|
|
|
|
for url, device in devices:
|
|
_LOGGER.debug(
|
|
"Adding WeMo device at %s:%i", device.host, device.port)
|
|
|
|
discovery_info = {
|
|
'model_name': device.model_name,
|
|
'serial': device.serialnumber,
|
|
'mac_address': device.mac,
|
|
'ssdp_description': url,
|
|
}
|
|
|
|
discovery.discover(hass, SERVICE_WEMO, discovery_info)
|
|
|
|
_LOGGER.debug("WeMo device discovery has finished")
|
|
|
|
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, discover_wemo_devices)
|
|
|
|
return True
|