Clean up Nanoleaf (#56732)

This commit is contained in:
Milan Meulemans 2021-09-28 22:39:54 +02:00 committed by GitHub
parent bc59387437
commit db30c27455
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 51 additions and 110 deletions

View file

@ -7,7 +7,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DEVICE, DOMAIN, NAME, SERIAL_NO from .const import DOMAIN
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
@ -22,11 +22,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
except InvalidToken as err: except InvalidToken as err:
raise ConfigEntryAuthFailed from err raise ConfigEntryAuthFailed from err
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = { hass.data.setdefault(DOMAIN, {})[entry.entry_id] = nanoleaf
DEVICE: nanoleaf,
NAME: nanoleaf.name,
SERIAL_NO: nanoleaf.serial_no,
}
hass.async_create_task( hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, "light") hass.config_entries.async_forward_entry_setup(entry, "light")

View file

@ -1,7 +1,3 @@
"""Constants for Nanoleaf integration.""" """Constants for Nanoleaf integration."""
DOMAIN = "nanoleaf" DOMAIN = "nanoleaf"
DEVICE = "device"
SERIAL_NO = "serial_no"
NAME = "name"

View file

@ -1,10 +1,8 @@
"""Support for Nanoleaf Lights.""" """Support for Nanoleaf Lights."""
from __future__ import annotations from __future__ import annotations
import logging
from aiohttp import ServerDisconnectedError from aiohttp import ServerDisconnectedError
from aionanoleaf import Unavailable from aionanoleaf import Nanoleaf, Unavailable
import voluptuous as vol import voluptuous as vol
from homeassistant.components.light import ( from homeassistant.components.light import (
@ -32,22 +30,11 @@ from homeassistant.util.color import (
color_temperature_mired_to_kelvin as mired_to_kelvin, color_temperature_mired_to_kelvin as mired_to_kelvin,
) )
from .const import DEVICE, DOMAIN, NAME, SERIAL_NO from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
RESERVED_EFFECTS = ("*Solid*", "*Static*", "*Dynamic*")
DEFAULT_NAME = "Nanoleaf" DEFAULT_NAME = "Nanoleaf"
ICON = "mdi:triangle-outline"
SUPPORT_NANOLEAF = (
SUPPORT_BRIGHTNESS
| SUPPORT_COLOR_TEMP
| SUPPORT_EFFECT
| SUPPORT_COLOR
| SUPPORT_TRANSITION
)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{ {
vol.Required(CONF_HOST): cv.string, vol.Required(CONF_HOST): cv.string,
@ -77,94 +64,74 @@ async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None: ) -> None:
"""Set up the Nanoleaf light.""" """Set up the Nanoleaf light."""
data = hass.data[DOMAIN][entry.entry_id] nanoleaf: Nanoleaf = hass.data[DOMAIN][entry.entry_id]
async_add_entities([NanoleafLight(data[DEVICE], data[NAME], data[SERIAL_NO])], True) async_add_entities([NanoleafLight(nanoleaf)])
class NanoleafLight(LightEntity): class NanoleafLight(LightEntity):
"""Representation of a Nanoleaf Light.""" """Representation of a Nanoleaf Light."""
def __init__(self, light, name, unique_id): def __init__(self, nanoleaf: Nanoleaf) -> None:
"""Initialize an Nanoleaf light.""" """Initialize an Nanoleaf light."""
self._unique_id = unique_id self._nanoleaf = nanoleaf
self._available = True self._attr_unique_id = self._nanoleaf.serial_no
self._brightness = None self._attr_name = self._nanoleaf.name
self._color_temp = None self._attr_min_mireds = 154
self._effect = None self._attr_max_mireds = 833
self._effects_list = None
self._light = light
self._name = name
self._hs_color = None
self._state = None
@property
def available(self):
"""Return availability."""
return self._available
@property @property
def brightness(self): def brightness(self):
"""Return the brightness of the light.""" """Return the brightness of the light."""
if self._brightness is not None: return int(self._nanoleaf.brightness * 2.55)
return int(self._brightness * 2.55)
return None
@property @property
def color_temp(self): def color_temp(self):
"""Return the current color temperature.""" """Return the current color temperature."""
if self._color_temp is not None: return color_util.color_temperature_kelvin_to_mired(
return color_util.color_temperature_kelvin_to_mired(self._color_temp) self._nanoleaf.color_temperature
return None )
@property @property
def effect(self): def effect(self):
"""Return the current effect.""" """Return the current effect."""
return self._effect # The API returns the *Solid* effect if the Nanoleaf is in HS or CT mode.
# The effects *Static* and *Dynamic* are not supported by Home Assistant.
# These reserved effects are implicitly set and are not in the effect_list.
# https://forum.nanoleaf.me/docs/openapi#_byoot0bams8f
return (
None if self._nanoleaf.effect in RESERVED_EFFECTS else self._nanoleaf.effect
)
@property @property
def effect_list(self): def effect_list(self):
"""Return the list of supported effects.""" """Return the list of supported effects."""
return self._effects_list return self._nanoleaf.effects_list
@property
def min_mireds(self):
"""Return the coldest color_temp that this light supports."""
return 154
@property
def max_mireds(self):
"""Return the warmest color_temp that this light supports."""
return 833
@property
def unique_id(self):
"""Return a unique ID."""
return self._unique_id
@property
def name(self):
"""Return the display name of this light."""
return self._name
@property @property
def icon(self): def icon(self):
"""Return the icon to use in the frontend, if any.""" """Return the icon to use in the frontend, if any."""
return ICON return "mdi:triangle-outline"
@property @property
def is_on(self): def is_on(self):
"""Return true if light is on.""" """Return true if light is on."""
return self._light.is_on return self._nanoleaf.is_on
@property @property
def hs_color(self): def hs_color(self):
"""Return the color in HS.""" """Return the color in HS."""
return self._hs_color return self._nanoleaf.hue, self._nanoleaf.saturation
@property @property
def supported_features(self): def supported_features(self):
"""Flag supported features.""" """Flag supported features."""
return SUPPORT_NANOLEAF return (
SUPPORT_BRIGHTNESS
| SUPPORT_COLOR_TEMP
| SUPPORT_EFFECT
| SUPPORT_COLOR
| SUPPORT_TRANSITION
)
async def async_turn_on(self, **kwargs): async def async_turn_on(self, **kwargs):
"""Instruct the light to turn on.""" """Instruct the light to turn on."""
@ -176,61 +143,43 @@ class NanoleafLight(LightEntity):
if hs_color: if hs_color:
hue, saturation = hs_color hue, saturation = hs_color
await self._light.set_hue(int(hue)) await self._nanoleaf.set_hue(int(hue))
await self._light.set_saturation(int(saturation)) await self._nanoleaf.set_saturation(int(saturation))
if color_temp_mired: if color_temp_mired:
await self._light.set_color_temperature(mired_to_kelvin(color_temp_mired)) await self._nanoleaf.set_color_temperature(
mired_to_kelvin(color_temp_mired)
)
if transition: if transition:
if brightness: # tune to the required brightness in n seconds if brightness: # tune to the required brightness in n seconds
await self._light.set_brightness( await self._nanoleaf.set_brightness(
int(brightness / 2.55), transition=int(kwargs[ATTR_TRANSITION]) int(brightness / 2.55), transition=int(kwargs[ATTR_TRANSITION])
) )
else: # If brightness is not specified, assume full brightness else: # If brightness is not specified, assume full brightness
await self._light.set_brightness( await self._nanoleaf.set_brightness(100, transition=int(transition))
100, transition=int(kwargs[ATTR_TRANSITION])
)
else: # If no transition is occurring, turn on the light else: # If no transition is occurring, turn on the light
await self._light.turn_on() await self._nanoleaf.turn_on()
if brightness: if brightness:
await self._light.set_brightness(int(brightness / 2.55)) await self._nanoleaf.set_brightness(int(brightness / 2.55))
if effect: if effect:
if effect not in self._effects_list: if effect not in self.effect_list:
raise ValueError( raise ValueError(
f"Attempting to apply effect not in the effect list: '{effect}'" f"Attempting to apply effect not in the effect list: '{effect}'"
) )
await self._light.set_effect(effect) await self._nanoleaf.set_effect(effect)
async def async_turn_off(self, **kwargs): async def async_turn_off(self, **kwargs):
"""Instruct the light to turn off.""" """Instruct the light to turn off."""
transition = kwargs.get(ATTR_TRANSITION) transition = kwargs.get(ATTR_TRANSITION)
if transition: await self._nanoleaf.turn_off(transition)
await self._light.set_brightness(0, transition=int(transition))
else:
await self._light.turn_off()
async def async_update(self) -> None: async def async_update(self) -> None:
"""Fetch new state data for this light.""" """Fetch new state data for this light."""
try: try:
await self._light.get_info() await self._nanoleaf.get_info()
except ServerDisconnectedError: except ServerDisconnectedError:
# Retry the request once if the device disconnected # Retry the request once if the device disconnected
await self._light.get_info() await self._nanoleaf.get_info()
except Unavailable: except Unavailable:
self._available = False self._attr_available = False
return return
self._available = True self._attr_available = True
self._brightness = self._light.brightness
self._effects_list = self._light.effects_list
# Nanoleaf api returns non-existent effect named "*Solid*" when light set to solid color.
# This causes various issues with scening (see https://github.com/home-assistant/core/issues/36359).
# Until fixed at the library level, we should ensure the effect exists before saving to light properties
self._effect = (
self._light.effect if self._light.effect in self._effects_list else None
)
if self._effect is None:
self._color_temp = self._light.color_temperature
self._hs_color = self._light.hue, self._light.saturation
else:
self._color_temp = None
self._hs_color = None
self._state = self._light.is_on