From b9109cf3d58d30550ccd017a90d8e87d58ac28f7 Mon Sep 17 00:00:00 2001 From: Florent Thoumie Date: Fri, 6 Sep 2019 13:51:28 -0700 Subject: [PATCH 1/3] Add light platform to iaqualink component. --- .coveragerc | 1 + .../components/iaqualink/__init__.py | 16 ++- homeassistant/components/iaqualink/light.py | 111 ++++++++++++++++++ 3 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/iaqualink/light.py diff --git a/.coveragerc b/.coveragerc index c30c78ddf65bec..7a51a591780878 100644 --- a/.coveragerc +++ b/.coveragerc @@ -288,6 +288,7 @@ omit = homeassistant/components/hyperion/light.py homeassistant/components/ialarm/alarm_control_panel.py homeassistant/components/iaqualink/climate.py + homeassistant/components/iaqualink/light.py homeassistant/components/icloud/device_tracker.py homeassistant/components/idteck_prox/* homeassistant/components/ifttt/* diff --git a/homeassistant/components/iaqualink/__init__.py b/homeassistant/components/iaqualink/__init__.py index 95c6f6895fccd1..c7b7bc472dd544 100644 --- a/homeassistant/components/iaqualink/__init__.py +++ b/homeassistant/components/iaqualink/__init__.py @@ -6,10 +6,16 @@ from aiohttp import CookieJar import voluptuous as vol -from iaqualink import AqualinkClient, AqualinkLoginException, AqualinkThermostat +from iaqualink import ( + AqualinkClient, + AqualinkLight, + AqualinkLoginException, + AqualinkThermostat, +) from homeassistant import config_entries from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN +from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.core import callback @@ -67,6 +73,7 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> None # These will contain the initialized devices climates = hass.data[DOMAIN][CLIMATE_DOMAIN] = [] + lights = hass.data[DOMAIN][LIGHT_DOMAIN] = [] session = async_create_clientsession(hass, cookie_jar=CookieJar(unsafe=True)) aqualink = AqualinkClient(username, password, session) @@ -88,11 +95,16 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> None for dev in devices.values(): if isinstance(dev, AqualinkThermostat): climates += [dev] + elif isinstance(dev, AqualinkLight): + lights += [dev] forward_setup = hass.config_entries.async_forward_entry_setup if climates: _LOGGER.debug("Got %s climates: %s", len(climates), climates) hass.async_create_task(forward_setup(entry, CLIMATE_DOMAIN)) + if lights: + _LOGGER.debug("Got %s lights: %s", len(lights), lights) + hass.async_create_task(forward_setup(entry, LIGHT_DOMAIN)) async def _async_systems_update(now): """Refresh internal state for all systems.""" @@ -112,6 +124,8 @@ async def async_unload_entry(hass: HomeAssistantType, entry: ConfigEntry) -> boo if hass.data[DOMAIN][CLIMATE_DOMAIN]: tasks += [forward_unload(entry, CLIMATE_DOMAIN)] + if hass.data[DOMAIN][LIGHT_DOMAIN]: + tasks += [forward_unload(entry, LIGHT_DOMAIN)] hass.data[DOMAIN].clear() diff --git a/homeassistant/components/iaqualink/light.py b/homeassistant/components/iaqualink/light.py new file mode 100644 index 00000000000000..bb4e5f3413183d --- /dev/null +++ b/homeassistant/components/iaqualink/light.py @@ -0,0 +1,111 @@ +"""Support for Aqualink pool lights.""" +import logging + +from iaqualink import AqualinkLight, AqualinkLightEffect + +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, + ATTR_EFFECT, + DOMAIN, + SUPPORT_BRIGHTNESS, + SUPPORT_EFFECT, + Light, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.helpers.typing import HomeAssistantType + +from .const import DOMAIN as AQUALINK_DOMAIN + +_LOGGER = logging.getLogger(__name__) + +PARALLEL_UPDATES = 0 + + +async def async_setup_entry( + hass: HomeAssistantType, config_entry: ConfigEntry, async_add_entities +) -> None: + """Set up discovered switches.""" + devs = [] + for dev in hass.data[AQUALINK_DOMAIN][DOMAIN]: + devs.append(HassAqualinkLight(dev)) + async_add_entities(devs, True) + + +class HassAqualinkLight(Light): + """Representation of a light.""" + + def __init__(self, dev: AqualinkLight): + """Initialize the light.""" + Light.__init__(self) + self.dev = dev + + @property + def name(self) -> str: + """Return the name of the light.""" + return self.dev.label + + @property + def is_on(self) -> bool: + """Return whether the light is on or off.""" + return self.dev.is_on + + async def async_turn_on(self, **kwargs) -> None: + """Turn on the light. + + This handles brightness and light effects for lights that do support + them. + """ + brightness = kwargs.get(ATTR_BRIGHTNESS, None) + effect = kwargs.get(ATTR_EFFECT, None) + + # For now I'm assuming lights support either effects or brightness. + if effect: + effect = AqualinkLightEffect[effect].value + await self.dev.set_effect(effect) + elif brightness: + # Aqualink supports percentages in 25% increments. + pct = int(round(brightness * 4.0 / 255)) * 25 + await self.dev.set_brightness(pct) + else: + await self.dev.turn_on() + + async def async_turn_off(self, **kwargs) -> None: + """Turn off the light.""" + await self.dev.turn_off() + + @property + def brightness(self) -> int: + """Return current brightness of the light. + + The scale needs converting between 0-100 and 0-255. + """ + return self.dev.brightness * 255 / 100 + + @property + def effect(self) -> str: + """Return the current light effect if supported.""" + return AqualinkLightEffect(self.dev.effect).name + + @property + def effect_list(self) -> list: + """Return supported light effects.""" + return list(AqualinkLightEffect.__members__.keys()) + + async def async_update(self) -> None: + """Update the internal state of the light. + + This is currently a no-op since all devices get refreshed during the + main thermostat update. + """ + return None + + @property + def supported_features(self) -> int: + """Return the list of features supported by the light.""" + if self.dev.is_dimmer: + return SUPPORT_BRIGHTNESS + + if self.dev.is_color: + return SUPPORT_EFFECT + + return 0 From 5086bd1a5192b2f53117245da9856c0e91bfca5a Mon Sep 17 00:00:00 2001 From: Florent Thoumie Date: Sat, 7 Sep 2019 02:26:10 +0000 Subject: [PATCH 2/3] Style changes. --- homeassistant/components/iaqualink/light.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/iaqualink/light.py b/homeassistant/components/iaqualink/light.py index bb4e5f3413183d..160d87ee1b13b5 100644 --- a/homeassistant/components/iaqualink/light.py +++ b/homeassistant/components/iaqualink/light.py @@ -24,7 +24,7 @@ async def async_setup_entry( hass: HomeAssistantType, config_entry: ConfigEntry, async_add_entities ) -> None: - """Set up discovered switches.""" + """Set up discovered lights.""" devs = [] for dev in hass.data[AQUALINK_DOMAIN][DOMAIN]: devs.append(HassAqualinkLight(dev)) @@ -36,7 +36,6 @@ class HassAqualinkLight(Light): def __init__(self, dev: AqualinkLight): """Initialize the light.""" - Light.__init__(self) self.dev = dev @property @@ -55,8 +54,8 @@ async def async_turn_on(self, **kwargs) -> None: This handles brightness and light effects for lights that do support them. """ - brightness = kwargs.get(ATTR_BRIGHTNESS, None) - effect = kwargs.get(ATTR_EFFECT, None) + brightness = kwargs.get(ATTR_BRIGHTNESS) + effect = kwargs.get(ATTR_EFFECT) # For now I'm assuming lights support either effects or brightness. if effect: @@ -89,7 +88,7 @@ def effect(self) -> str: @property def effect_list(self) -> list: """Return supported light effects.""" - return list(AqualinkLightEffect.__members__.keys()) + return list(AqualinkLightEffect.__members__) async def async_update(self) -> None: """Update the internal state of the light. From ed1ce0cd4914a29089b995d3705af5fdf8f650f6 Mon Sep 17 00:00:00 2001 From: Florent Thoumie Date: Sun, 8 Sep 2019 01:44:29 +0000 Subject: [PATCH 3/3] Polling moved to timer in the component --- homeassistant/components/iaqualink/light.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/iaqualink/light.py b/homeassistant/components/iaqualink/light.py index 160d87ee1b13b5..fbfb10783ee4a1 100644 --- a/homeassistant/components/iaqualink/light.py +++ b/homeassistant/components/iaqualink/light.py @@ -14,6 +14,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.helpers.typing import HomeAssistantType +from . import AqualinkEntity, refresh_system from .const import DOMAIN as AQUALINK_DOMAIN _LOGGER = logging.getLogger(__name__) @@ -31,7 +32,7 @@ async def async_setup_entry( async_add_entities(devs, True) -class HassAqualinkLight(Light): +class HassAqualinkLight(Light, AqualinkEntity): """Representation of a light.""" def __init__(self, dev: AqualinkLight): @@ -48,6 +49,7 @@ def is_on(self) -> bool: """Return whether the light is on or off.""" return self.dev.is_on + @refresh_system async def async_turn_on(self, **kwargs) -> None: """Turn on the light. @@ -68,6 +70,7 @@ async def async_turn_on(self, **kwargs) -> None: else: await self.dev.turn_on() + @refresh_system async def async_turn_off(self, **kwargs) -> None: """Turn off the light.""" await self.dev.turn_off() @@ -90,14 +93,6 @@ def effect_list(self) -> list: """Return supported light effects.""" return list(AqualinkLightEffect.__members__) - async def async_update(self) -> None: - """Update the internal state of the light. - - This is currently a no-op since all devices get refreshed during the - main thermostat update. - """ - return None - @property def supported_features(self) -> int: """Return the list of features supported by the light."""