Various enhancements for WeMo component/platforms (#19419)

* WeMo - Various fixes and improvements

Various fixes & improvements to the WeMo components, including:
-- Fixes to rediscovery
-- New reset filter service for the WeMo Humidifier
-- Switched the remainder of the WeMo components to async IO
-- Removed any remaining IO in entity properties and moved them to the polling/subscription update process

* WeMo - Fix pywemo version and remove test code from WeMo fan component

* WeMo Humidifier - Add services.yaml entry for reset filter life service

* WeMo - Update binary_sensor component to use asyncio

* WeMo - Add available property to binary_sensor component

* WeMo - Fixed line length issue

* WeMo - Fix issue with discovering the same device multiple times

* WeMo - Fix for the fix for discovering devices multiple times

* WeMo - Fix long lines

* WeMo - Fixes from code review

* WeMo - Breaking Change - entity_ids is now required on wemo_set_humidity

* WeMo - Code review fixes

* WeMo - Code review fixes

* WeMo - Code review fixes
This commit is contained in:
Adam Belebczuk 2018-12-19 02:12:32 -05:00 committed by Martin Hjelmare
parent ef6c39f911
commit 7f0dd442fd
6 changed files with 256 additions and 100 deletions

View File

@ -4,7 +4,10 @@ Support for WeMo sensors.
For more details about this component, please refer to the documentation at For more details about this component, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.wemo/ https://home-assistant.io/components/binary_sensor.wemo/
""" """
import asyncio
import logging import logging
import async_timeout
import requests import requests
from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.components.binary_sensor import BinarySensorDevice
@ -41,48 +44,90 @@ class WemoBinarySensor(BinarySensorDevice):
"""Initialize the WeMo sensor.""" """Initialize the WeMo sensor."""
self.wemo = device self.wemo = device
self._state = None self._state = None
self._available = True
self._update_lock = None
self._model_name = self.wemo.model_name
self._name = self.wemo.name
self._serialnumber = self.wemo.serialnumber
wemo = hass.components.wemo def _subscription_callback(self, _device, _type, _params):
wemo.SUBSCRIPTION_REGISTRY.register(self.wemo) """Update the state by the Wemo sensor."""
wemo.SUBSCRIPTION_REGISTRY.on(self.wemo, None, self._update_callback) _LOGGER.debug("Subscription update for %s", self.name)
def _update_callback(self, _device, _type, _params):
"""Handle state changes."""
_LOGGER.info("Subscription update for %s", _device)
updated = self.wemo.subscription_update(_type, _params) updated = self.wemo.subscription_update(_type, _params)
self._update(force_update=(not updated)) self.hass.add_job(
self._async_locked_subscription_callback(not updated))
if not hasattr(self, 'hass'): async def _async_locked_subscription_callback(self, force_update):
"""Handle an update from a subscription."""
# If an update is in progress, we don't do anything
if self._update_lock.locked():
return return
self.schedule_update_ha_state()
@property await self._async_locked_update(force_update)
def should_poll(self): self.async_schedule_update_ha_state()
"""No polling needed with subscriptions."""
return False async def async_added_to_hass(self):
"""Wemo sensor added to HASS."""
# Define inside async context so we know our event loop
self._update_lock = asyncio.Lock()
registry = self.hass.components.wemo.SUBSCRIPTION_REGISTRY
await self.hass.async_add_executor_job(registry.register, self.wemo)
registry.on(self.wemo, None, self._subscription_callback)
async def async_update(self):
"""Update WeMo state.
Wemo has an aggressive retry logic that sometimes can take over a
minute to return. If we don't get a state after 5 seconds, assume the
Wemo sensor is unreachable. If update goes through, it will be made
available again.
"""
# If an update is in progress, we don't do anything
if self._update_lock.locked():
return
try:
with async_timeout.timeout(5):
await asyncio.shield(self._async_locked_update(True))
except asyncio.TimeoutError:
_LOGGER.warning('Lost connection to %s', self.name)
self._available = False
async def _async_locked_update(self, force_update):
"""Try updating within an async lock."""
async with self._update_lock:
await self.hass.async_add_executor_job(self._update, force_update)
def _update(self, force_update=True):
"""Update the sensor state."""
try:
self._state = self.wemo.get_state(force_update)
if not self._available:
_LOGGER.info('Reconnected to %s', self.name)
self._available = True
except AttributeError as err:
_LOGGER.warning("Could not update status for %s (%s)",
self.name, err)
self._available = False
@property @property
def unique_id(self): def unique_id(self):
"""Return the id of this WeMo device.""" """Return the id of this WeMo sensor."""
return self.wemo.serialnumber return self._serialnumber
@property @property
def name(self): def name(self):
"""Return the name of the service if any.""" """Return the name of the service if any."""
return self.wemo.name return self._name
@property @property
def is_on(self): def is_on(self):
"""Return true if sensor is on.""" """Return true if sensor is on."""
return self._state return self._state
def update(self): @property
"""Update WeMo state.""" def available(self):
self._update(force_update=True) """Return true if sensor is available."""
return self._available
def _update(self, force_update=True):
try:
self._state = self.wemo.get_state(force_update)
except AttributeError as err:
_LOGGER.warning(
"Could not update status for %s (%s)", self.name, err)

View File

@ -209,8 +209,15 @@ wemo_set_humidity:
description: Set the target humidity of WeMo humidifier devices. description: Set the target humidity of WeMo humidifier devices.
fields: fields:
entity_id: entity_id:
description: Names of the WeMo humidifier entities (0 or more entities, if no entity_id is provided, all WeMo humidifiers will have the target humidity set). description: Names of the WeMo humidifier entities (1 or more entity_ids are required).
example: 'fan.wemo_humidifier' example: 'fan.wemo_humidifier'
target_humidity: target_humidity:
description: Target humidity. This is a float value between 0 and 100, but will be mapped to the humidity levels that WeMo humidifiers support (45, 50, 55, 60, and 100/Max) by rounding the value down to the nearest supported value. description: Target humidity. This is a float value between 0 and 100, but will be mapped to the humidity levels that WeMo humidifiers support (45, 50, 55, 60, and 100/Max) by rounding the value down to the nearest supported value.
example: 56.5 example: 56.5
wemo_reset_filter_life:
description: Reset the WeMo Humidifier's filter life to 100%.
fields:
entity_id:
description: Names of the WeMo humidifier entities (1 or more entity_ids are required).
example: 'fan.wemo_humidifier'

View File

@ -78,11 +78,17 @@ HASS_FAN_SPEED_TO_WEMO = {v: k for (k, v) in WEMO_FAN_SPEED_TO_HASS.items()
SERVICE_SET_HUMIDITY = 'wemo_set_humidity' SERVICE_SET_HUMIDITY = 'wemo_set_humidity'
SET_HUMIDITY_SCHEMA = vol.Schema({ SET_HUMIDITY_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, vol.Required(ATTR_ENTITY_ID): cv.entity_ids,
vol.Required(ATTR_TARGET_HUMIDITY): vol.Required(ATTR_TARGET_HUMIDITY):
vol.All(vol.Coerce(float), vol.Range(min=0, max=100)) vol.All(vol.Coerce(float), vol.Range(min=0, max=100))
}) })
SERVICE_RESET_FILTER_LIFE = 'wemo_reset_filter_life'
RESET_FILTER_LIFE_SCHEMA = vol.Schema({
vol.Required(ATTR_ENTITY_ID): cv.entity_ids
})
def setup_platform(hass, config, add_entities, discovery_info=None): def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up discovered WeMo humidifiers.""" """Set up discovered WeMo humidifiers."""
@ -111,22 +117,29 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
def service_handle(service): def service_handle(service):
"""Handle the WeMo humidifier services.""" """Handle the WeMo humidifier services."""
entity_ids = service.data.get(ATTR_ENTITY_ID) entity_ids = service.data.get(ATTR_ENTITY_ID)
target_humidity = service.data.get(ATTR_TARGET_HUMIDITY)
if entity_ids: humidifiers = [device for device in
humidifiers = [device for device in hass.data[DATA_KEY].values() if hass.data[DATA_KEY].values() if
device.entity_id in entity_ids] device.entity_id in entity_ids]
else:
humidifiers = hass.data[DATA_KEY].values() if service.service == SERVICE_SET_HUMIDITY:
target_humidity = service.data.get(ATTR_TARGET_HUMIDITY)
for humidifier in humidifiers: for humidifier in humidifiers:
humidifier.set_humidity(target_humidity) humidifier.set_humidity(target_humidity)
elif service.service == SERVICE_RESET_FILTER_LIFE:
for humidifier in humidifiers:
humidifier.reset_filter_life()
# Register service(s) # Register service(s)
hass.services.register( hass.services.register(
DOMAIN, SERVICE_SET_HUMIDITY, service_handle, DOMAIN, SERVICE_SET_HUMIDITY, service_handle,
schema=SET_HUMIDITY_SCHEMA) schema=SET_HUMIDITY_SCHEMA)
hass.services.register(
DOMAIN, SERVICE_RESET_FILTER_LIFE, service_handle,
schema=RESET_FILTER_LIFE_SCHEMA)
class WemoHumidifier(FanEntity): class WemoHumidifier(FanEntity):
"""Representation of a WeMo humidifier.""" """Representation of a WeMo humidifier."""
@ -137,7 +150,6 @@ class WemoHumidifier(FanEntity):
self._state = None self._state = None
self._available = True self._available = True
self._update_lock = None self._update_lock = None
self._fan_mode = None self._fan_mode = None
self._target_humidity = None self._target_humidity = None
self._current_humidity = None self._current_humidity = None
@ -145,9 +157,6 @@ class WemoHumidifier(FanEntity):
self._filter_life = None self._filter_life = None
self._filter_expired = None self._filter_expired = None
self._last_fan_on_mode = WEMO_FAN_MEDIUM self._last_fan_on_mode = WEMO_FAN_MEDIUM
# look up model name, name, and serial number
# once as it incurs network traffic
self._model_name = self.wemo.model_name self._model_name = self.wemo.model_name
self._name = self.wemo.name self._name = self.wemo.name
self._serialnumber = self.wemo.serialnumber self._serialnumber = self.wemo.serialnumber
@ -211,12 +220,12 @@ class WemoHumidifier(FanEntity):
return WEMO_FAN_SPEED_TO_HASS.get(self._fan_mode) return WEMO_FAN_SPEED_TO_HASS.get(self._fan_mode)
@property @property
def speed_list(self: FanEntity) -> list: def speed_list(self) -> list:
"""Get the list of available speeds.""" """Get the list of available speeds."""
return SUPPORTED_SPEEDS return SUPPORTED_SPEEDS
@property @property
def supported_features(self: FanEntity) -> int: def supported_features(self) -> int:
"""Flag supported features.""" """Flag supported features."""
return SUPPORTED_FEATURES return SUPPORTED_FEATURES
@ -276,22 +285,22 @@ class WemoHumidifier(FanEntity):
self.name, err) self.name, err)
self._available = False self._available = False
def turn_on(self: FanEntity, speed: str = None, **kwargs) -> None: def turn_on(self, speed: str = None, **kwargs) -> None:
"""Turn the switch on.""" """Turn the switch on."""
if speed is None: if speed is None:
self.wemo.set_state(self._last_fan_on_mode) self.wemo.set_state(self._last_fan_on_mode)
else: else:
self.set_speed(speed) self.set_speed(speed)
def turn_off(self: FanEntity, **kwargs) -> None: def turn_off(self, **kwargs) -> None:
"""Turn the switch off.""" """Turn the switch off."""
self.wemo.set_state(WEMO_FAN_OFF) self.wemo.set_state(WEMO_FAN_OFF)
def set_speed(self: FanEntity, speed: str) -> None: def set_speed(self, speed: str) -> None:
"""Set the fan_mode of the Humidifier.""" """Set the fan_mode of the Humidifier."""
self.wemo.set_state(HASS_FAN_SPEED_TO_WEMO.get(speed)) self.wemo.set_state(HASS_FAN_SPEED_TO_WEMO.get(speed))
def set_humidity(self: FanEntity, humidity: float) -> None: def set_humidity(self, humidity: float) -> None:
"""Set the target humidity level for the Humidifier.""" """Set the target humidity level for the Humidifier."""
if humidity < 50: if humidity < 50:
self.wemo.set_humidity(WEMO_HUMIDITY_45) self.wemo.set_humidity(WEMO_HUMIDITY_45)
@ -303,3 +312,7 @@ class WemoHumidifier(FanEntity):
self.wemo.set_humidity(WEMO_HUMIDITY_60) self.wemo.set_humidity(WEMO_HUMIDITY_60)
elif humidity >= 100: elif humidity >= 100:
self.wemo.set_humidity(WEMO_HUMIDITY_100) self.wemo.set_humidity(WEMO_HUMIDITY_100)
def reset_filter_life(self) -> None:
"""Reset the filter life to 100%."""
self.wemo.reset_filter_life()

View File

@ -4,9 +4,12 @@ Support for Belkin WeMo lights.
For more details about this component, please refer to the documentation at For more details about this component, please refer to the documentation at
https://home-assistant.io/components/light.wemo/ https://home-assistant.io/components/light.wemo/
""" """
import asyncio
import logging import logging
from datetime import timedelta from datetime import timedelta
import requests import requests
import async_timeout
from homeassistant import util from homeassistant import util
from homeassistant.components.light import ( from homeassistant.components.light import (
@ -74,40 +77,52 @@ class WemoLight(Light):
def __init__(self, device, update_lights): def __init__(self, device, update_lights):
"""Initialize the WeMo light.""" """Initialize the WeMo light."""
self.light_id = device.name
self.wemo = device self.wemo = device
self.update_lights = update_lights self._state = None
self._update_lights = update_lights
self._available = True
self._update_lock = None
self._brightness = None
self._hs_color = None
self._color_temp = None
self._is_on = None
self._name = self.wemo.name
self._unique_id = self.wemo.uniqueID
async def async_added_to_hass(self):
"""Wemo light added to HASS."""
# Define inside async context so we know our event loop
self._update_lock = asyncio.Lock()
@property @property
def unique_id(self): def unique_id(self):
"""Return the ID of this light.""" """Return the ID of this light."""
return self.wemo.uniqueID return self._unique_id
@property @property
def name(self): def name(self):
"""Return the name of the light.""" """Return the name of the light."""
return self.wemo.name return self._name
@property @property
def brightness(self): def brightness(self):
"""Return the brightness of this light between 0..255.""" """Return the brightness of this light between 0..255."""
return self.wemo.state.get('level', 255) return self._brightness
@property @property
def hs_color(self): def hs_color(self):
"""Return the hs color values of this light.""" """Return the hs color values of this light."""
xy_color = self.wemo.state.get('color_xy') return self._hs_color
return color_util.color_xy_to_hs(*xy_color) if xy_color else None
@property @property
def color_temp(self): def color_temp(self):
"""Return the color temperature of this light in mireds.""" """Return the color temperature of this light in mireds."""
return self.wemo.state.get('temperature_mireds') return self._color_temp
@property @property
def is_on(self): def is_on(self):
"""Return true if device is on.""" """Return true if device is on."""
return self.wemo.state['onoff'] != 0 return self._is_on
@property @property
def supported_features(self): def supported_features(self):
@ -117,7 +132,7 @@ class WemoLight(Light):
@property @property
def available(self): def available(self):
"""Return if light is available.""" """Return if light is available."""
return self.wemo.state['available'] return self._available
def turn_on(self, **kwargs): def turn_on(self, **kwargs):
"""Turn the light on.""" """Turn the light on."""
@ -145,9 +160,40 @@ class WemoLight(Light):
transitiontime = int(kwargs.get(ATTR_TRANSITION, 0)) transitiontime = int(kwargs.get(ATTR_TRANSITION, 0))
self.wemo.turn_off(transition=transitiontime) self.wemo.turn_off(transition=transitiontime)
def update(self): def _update(self, force_update=True):
"""Synchronize state with bridge.""" """Synchronize state with bridge."""
self.update_lights(no_throttle=True) self._update_lights(no_throttle=force_update)
self._state = self.wemo.state
self._is_on = self._state.get('onoff') != 0
self._brightness = self._state.get('level', 255)
self._color_temp = self._state.get('temperature_mireds')
self._available = True
xy_color = self._state.get('color_xy')
if xy_color:
self._hs_color = color_util.color_xy_to_hs(*xy_color)
else:
self._hs_color = None
async def async_update(self):
"""Synchronize state with bridge."""
# If an update is in progress, we don't do anything
if self._update_lock.locked():
return
try:
with async_timeout.timeout(5):
await asyncio.shield(self._async_locked_update(True))
except asyncio.TimeoutError:
_LOGGER.warning('Lost connection to %s', self.name)
self._available = False
async def _async_locked_update(self, force_update):
"""Try updating within an async lock."""
async with self._update_lock:
await self.hass.async_add_executor_job(self._update, force_update)
class WemoDimmer(Light): class WemoDimmer(Light):
@ -156,46 +202,79 @@ class WemoDimmer(Light):
def __init__(self, device): def __init__(self, device):
"""Initialize the WeMo dimmer.""" """Initialize the WeMo dimmer."""
self.wemo = device self.wemo = device
self._brightness = None
self._state = None self._state = None
self._available = True
self._update_lock = None
self._brightness = None
self._model_name = self.wemo.model_name
self._name = self.wemo.name
self._serialnumber = self.wemo.serialnumber
def _subscription_callback(self, _device, _type, _params):
"""Update the state by the Wemo device."""
_LOGGER.debug("Subscription update for %s", self.name)
updated = self.wemo.subscription_update(_type, _params)
self.hass.add_job(
self._async_locked_subscription_callback(not updated))
async def _async_locked_subscription_callback(self, force_update):
"""Handle an update from a subscription."""
# If an update is in progress, we don't do anything
if self._update_lock.locked():
return
await self._async_locked_update(force_update)
self.async_schedule_update_ha_state()
async def async_added_to_hass(self): async def async_added_to_hass(self):
"""Register update callback.""" """Wemo dimmer added to HASS."""
wemo = self.hass.components.wemo # Define inside async context so we know our event loop
# The register method uses a threading condition, so call via executor. self._update_lock = asyncio.Lock()
# and await to wait until the task is done.
await self.hass.async_add_job(
wemo.SUBSCRIPTION_REGISTRY.register, self.wemo)
# The on method just appends to a defaultdict list.
wemo.SUBSCRIPTION_REGISTRY.on(self.wemo, None, self._update_callback)
def _update_callback(self, _device, _type, _params): registry = self.hass.components.wemo.SUBSCRIPTION_REGISTRY
"""Update the state by the Wemo device.""" await self.hass.async_add_executor_job(registry.register, self.wemo)
_LOGGER.debug("Subscription update for %s", _device) registry.on(self.wemo, None, self._subscription_callback)
updated = self.wemo.subscription_update(_type, _params)
self._update(force_update=(not updated)) async def async_update(self):
self.schedule_update_ha_state() """Update WeMo state.
Wemo has an aggressive retry logic that sometimes can take over a
minute to return. If we don't get a state after 5 seconds, assume the
Wemo dimmer is unreachable. If update goes through, it will be made
available again.
"""
# If an update is in progress, we don't do anything
if self._update_lock.locked():
return
try:
with async_timeout.timeout(5):
await asyncio.shield(self._async_locked_update(True))
except asyncio.TimeoutError:
_LOGGER.warning('Lost connection to %s', self.name)
self._available = False
self.wemo.reconnect_with_device()
async def _async_locked_update(self, force_update):
"""Try updating within an async lock."""
async with self._update_lock:
await self.hass.async_add_executor_job(self._update, force_update)
@property @property
def unique_id(self): def unique_id(self):
"""Return the ID of this WeMo dimmer.""" """Return the ID of this WeMo dimmer."""
return self.wemo.serialnumber return self._serialnumber
@property @property
def name(self): def name(self):
"""Return the name of the dimmer if any.""" """Return the name of the dimmer if any."""
return self.wemo.name return self._name
@property @property
def supported_features(self): def supported_features(self):
"""Flag supported features.""" """Flag supported features."""
return SUPPORT_BRIGHTNESS return SUPPORT_BRIGHTNESS
@property
def should_poll(self):
"""No polling needed with subscriptions."""
return False
@property @property
def brightness(self): def brightness(self):
"""Return the brightness of this light between 1 and 100.""" """Return the brightness of this light between 1 and 100."""
@ -210,11 +289,17 @@ class WemoDimmer(Light):
"""Update the device state.""" """Update the device state."""
try: try:
self._state = self.wemo.get_state(force_update) self._state = self.wemo.get_state(force_update)
wemobrightness = int(self.wemo.get_brightness(force_update)) wemobrightness = int(self.wemo.get_brightness(force_update))
self._brightness = int((wemobrightness * 255) / 100) self._brightness = int((wemobrightness * 255) / 100)
if not self._available:
_LOGGER.info('Reconnected to %s', self.name)
self._available = True
except AttributeError as err: except AttributeError as err:
_LOGGER.warning("Could not update status for %s (%s)", _LOGGER.warning("Could not update status for %s (%s)",
self.name, err) self.name, err)
self._available = False
def turn_on(self, **kwargs): def turn_on(self, **kwargs):
"""Turn the dimmer on.""" """Turn the dimmer on."""
@ -232,3 +317,8 @@ class WemoDimmer(Light):
def turn_off(self, **kwargs): def turn_off(self, **kwargs):
"""Turn the dimmer off.""" """Turn the dimmer off."""
self.wemo.off() self.wemo.off()
@property
def available(self):
"""Return if dimmer is available."""
return self._available

View File

@ -64,10 +64,12 @@ class WemoSwitch(SwitchDevice):
self.maker_params = None self.maker_params = None
self.coffeemaker_mode = None self.coffeemaker_mode = None
self._state = None self._state = None
self._mode_string = None
self._available = True self._available = True
self._update_lock = None self._update_lock = None
# look up model name once as it incurs network traffic
self._model_name = self.wemo.model_name self._model_name = self.wemo.model_name
self._name = self.wemo.name
self._serialnumber = self.wemo.serialnumber
def _subscription_callback(self, _device, _type, _params): def _subscription_callback(self, _device, _type, _params):
"""Update the state by the Wemo device.""" """Update the state by the Wemo device."""
@ -85,24 +87,15 @@ class WemoSwitch(SwitchDevice):
await self._async_locked_update(force_update) await self._async_locked_update(force_update)
self.async_schedule_update_ha_state() self.async_schedule_update_ha_state()
@property
def should_poll(self):
"""Device should poll.
Subscriptions push the state, however it won't detect if a device
is no longer available. Use polling to detect if a device is available.
"""
return True
@property @property
def unique_id(self): def unique_id(self):
"""Return the ID of this WeMo switch.""" """Return the ID of this WeMo switch."""
return self.wemo.serialnumber return self._serialnumber
@property @property
def name(self): def name(self):
"""Return the name of the switch if any.""" """Return the name of the switch if any."""
return self.wemo.name return self._name
@property @property
def device_state_attributes(self): def device_state_attributes(self):
@ -169,7 +162,7 @@ class WemoSwitch(SwitchDevice):
def detail_state(self): def detail_state(self):
"""Return the state of the device.""" """Return the state of the device."""
if self.coffeemaker_mode is not None: if self.coffeemaker_mode is not None:
return self.wemo.mode_string return self._mode_string
if self.insight_params: if self.insight_params:
standby_state = int(self.insight_params['state']) standby_state = int(self.insight_params['state'])
if standby_state == WEMO_ON: if standby_state == WEMO_ON:
@ -242,6 +235,7 @@ class WemoSwitch(SwitchDevice):
"""Update the device state.""" """Update the device state."""
try: try:
self._state = self.wemo.get_state(force_update) self._state = self.wemo.get_state(force_update)
if self._model_name == 'Insight': if self._model_name == 'Insight':
self.insight_params = self.wemo.insight_params self.insight_params = self.wemo.insight_params
self.insight_params['standby_state'] = ( self.insight_params['standby_state'] = (
@ -250,6 +244,7 @@ class WemoSwitch(SwitchDevice):
self.maker_params = self.wemo.maker_params self.maker_params = self.wemo.maker_params
elif self._model_name == 'CoffeeMaker': elif self._model_name == 'CoffeeMaker':
self.coffeemaker_mode = self.wemo.mode self.coffeemaker_mode = self.wemo.mode
self._mode_string = self.wemo.mode_string
if not self._available: if not self._available:
_LOGGER.info('Reconnected to %s', self.name) _LOGGER.info('Reconnected to %s', self.name)

View File

@ -96,6 +96,8 @@ def setup(hass, config):
# Only register a device once # Only register a device once
if serial in KNOWN_DEVICES: if serial in KNOWN_DEVICES:
_LOGGER.debug('Ignoring known device %s %s',
service, discovery_info)
return return
_LOGGER.debug('Discovered unique device %s', serial) _LOGGER.debug('Discovered unique device %s', serial)
KNOWN_DEVICES.append(serial) KNOWN_DEVICES.append(serial)
@ -123,6 +125,7 @@ def setup(hass, config):
devices = [] devices = []
_LOGGER.debug("Scanning statically configured WeMo devices...")
for host, port in config.get(DOMAIN, {}).get(CONF_STATIC, []): for host, port in config.get(DOMAIN, {}).get(CONF_STATIC, []):
url = setup_url_for_address(host, port) url = setup_url_for_address(host, port)
@ -139,16 +142,19 @@ def setup(hass, config):
_LOGGER.error('Unable to access %s (%s)', url, err) _LOGGER.error('Unable to access %s (%s)', url, err)
continue continue
if not [d[1] for d in devices
if d[1].serialnumber == device.serialnumber]:
devices.append((url, device)) devices.append((url, device))
if config.get(DOMAIN, {}).get(CONF_DISCOVERY): if config.get(DOMAIN, {}).get(CONF_DISCOVERY):
_LOGGER.debug("Scanning for WeMo devices.") _LOGGER.debug("Scanning for WeMo devices...")
devices.extend( for device in pywemo.discover_devices():
(setup_url_for_device(device), device) if not [d[1] for d in devices
for device in pywemo.discover_devices()) if d[1].serialnumber == device.serialnumber]:
devices.append((setup_url_for_device(device), device))
for url, device in devices: for url, device in devices:
_LOGGER.debug('Adding wemo at %s:%i', device.host, device.port) _LOGGER.debug('Adding WeMo device at %s:%i', device.host, device.port)
discovery_info = { discovery_info = {
'model_name': device.model_name, 'model_name': device.model_name,