Add EverLights light component (#19817)
* EverLights light integration. Supports single color (with color and brightness parameters) or saved pattern (with effect parameter). * Fix pylint parameter name warning. * Code review feedback. * Add tests for the two helper functions of EverLights component. * Fixes for review feedback. * Change test style. * Style fixes for hound.
This commit is contained in:
parent
5999df1953
commit
9482a6303d
4 changed files with 197 additions and 0 deletions
|
@ -581,6 +581,7 @@ omit =
|
|||
homeassistant/components/light/blinkt.py
|
||||
homeassistant/components/light/decora_wifi.py
|
||||
homeassistant/components/light/decora.py
|
||||
homeassistant/components/light/everlights.py
|
||||
homeassistant/components/light/flux_led.py
|
||||
homeassistant/components/light/futurenow.py
|
||||
homeassistant/components/light/greenwave.py
|
||||
|
|
177
homeassistant/components/light/everlights.py
Normal file
177
homeassistant/components/light/everlights.py
Normal file
|
@ -0,0 +1,177 @@
|
|||
"""
|
||||
Support for EverLights lights.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/light.everlights/
|
||||
"""
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
from typing import Tuple
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import CONF_HOSTS
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS, ATTR_HS_COLOR, ATTR_EFFECT,
|
||||
SUPPORT_BRIGHTNESS, SUPPORT_EFFECT, SUPPORT_COLOR,
|
||||
Light, PLATFORM_SCHEMA)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
import homeassistant.util.color as color_util
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.exceptions import PlatformNotReady
|
||||
|
||||
REQUIREMENTS = ['pyeverlights==0.1.0']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SUPPORT_EVERLIGHTS = (SUPPORT_EFFECT | SUPPORT_BRIGHTNESS | SUPPORT_COLOR)
|
||||
|
||||
SCAN_INTERVAL = timedelta(minutes=1)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_HOSTS): vol.All(cv.ensure_list, [cv.string]),
|
||||
})
|
||||
|
||||
NAME_FORMAT = "EverLights {} Zone {}"
|
||||
|
||||
|
||||
def color_rgb_to_int(red: int, green: int, blue: int) -> int:
|
||||
"""Return a RGB color as an integer."""
|
||||
return red*256*256+green*256+blue
|
||||
|
||||
|
||||
def color_int_to_rgb(value: int) -> Tuple[int, int, int]:
|
||||
"""Return an RGB tuple from an integer."""
|
||||
return (value >> 16, (value >> 8) & 0xff, value & 0xff)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities,
|
||||
discovery_info=None):
|
||||
"""Set up the EverLights lights from configuration.yaml."""
|
||||
import pyeverlights
|
||||
lights = []
|
||||
|
||||
for ipaddr in config[CONF_HOSTS]:
|
||||
api = pyeverlights.EverLights(ipaddr,
|
||||
async_get_clientsession(hass))
|
||||
|
||||
try:
|
||||
status = await api.get_status()
|
||||
|
||||
effects = await api.get_all_patterns()
|
||||
|
||||
except pyeverlights.ConnectionError:
|
||||
raise PlatformNotReady
|
||||
|
||||
else:
|
||||
lights.append(EverLightsLight(api, pyeverlights.ZONE_1,
|
||||
status, effects))
|
||||
lights.append(EverLightsLight(api, pyeverlights.ZONE_2,
|
||||
status, effects))
|
||||
|
||||
async_add_entities(lights)
|
||||
|
||||
|
||||
class EverLightsLight(Light):
|
||||
"""Representation of a Flux light."""
|
||||
|
||||
def __init__(self, api, channel, status, effects):
|
||||
"""Initialize the light."""
|
||||
self._api = api
|
||||
self._channel = channel
|
||||
self._status = status
|
||||
self._effects = effects
|
||||
self._mac = status['mac']
|
||||
self._error_reported = False
|
||||
self._hs_color = [255, 255]
|
||||
self._brightness = 255
|
||||
self._effect = None
|
||||
self._available = True
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Return a unique ID."""
|
||||
return '{}-{}'.format(self._mac, self._channel)
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return True if entity is available."""
|
||||
return self._available
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the device."""
|
||||
return NAME_FORMAT.format(self._mac, self._channel)
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return true if device is on."""
|
||||
return self._status['ch{}Active'.format(self._channel)] == 1
|
||||
|
||||
@property
|
||||
def brightness(self):
|
||||
"""Return the brightness of this light between 0..255."""
|
||||
return self._brightness
|
||||
|
||||
@property
|
||||
def hs_color(self):
|
||||
"""Return the color property."""
|
||||
return self._hs_color
|
||||
|
||||
@property
|
||||
def effect(self):
|
||||
"""Return the effect property."""
|
||||
return self._effect
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
"""Flag supported features."""
|
||||
return SUPPORT_EVERLIGHTS
|
||||
|
||||
@property
|
||||
def effect_list(self):
|
||||
"""Return the list of supported effects."""
|
||||
return self._effects
|
||||
|
||||
async def async_turn_on(self, **kwargs):
|
||||
"""Turn the light on."""
|
||||
hs_color = kwargs.get(ATTR_HS_COLOR, self._hs_color)
|
||||
brightness = kwargs.get(ATTR_BRIGHTNESS, self._brightness)
|
||||
effect = kwargs.get(ATTR_EFFECT)
|
||||
|
||||
if effect is not None:
|
||||
colors = await self._api.set_pattern_by_id(self._channel, effect)
|
||||
|
||||
rgb = color_int_to_rgb(colors[0])
|
||||
hsv = color_util.color_RGB_to_hsv(*rgb)
|
||||
hs_color = hsv[:2]
|
||||
brightness = hsv[2] / 100 * 255
|
||||
|
||||
else:
|
||||
rgb = color_util.color_hsv_to_RGB(*hs_color, brightness/255*100)
|
||||
colors = [color_rgb_to_int(*rgb)]
|
||||
|
||||
await self._api.set_pattern(self._channel, colors)
|
||||
|
||||
self._hs_color = hs_color
|
||||
self._brightness = brightness
|
||||
self._effect = effect
|
||||
|
||||
async def async_turn_off(self, **kwargs):
|
||||
"""Turn the light off."""
|
||||
await self._api.clear_pattern(self._channel)
|
||||
|
||||
async def async_update(self):
|
||||
"""Synchronize state with control box."""
|
||||
import pyeverlights
|
||||
|
||||
try:
|
||||
self._status = await self._api.get_status()
|
||||
except pyeverlights.ConnectionError:
|
||||
if self._available:
|
||||
_LOGGER.warning("EverLights control box connection lost.")
|
||||
self._available = False
|
||||
else:
|
||||
if not self._available:
|
||||
_LOGGER.warning("EverLights control box connection restored.")
|
||||
self._available = True
|
|
@ -981,6 +981,9 @@ pyenvisalink==3.8
|
|||
# homeassistant.components.climate.ephember
|
||||
pyephember==0.2.0
|
||||
|
||||
# homeassistant.components.light.everlights
|
||||
pyeverlights==0.1.0
|
||||
|
||||
# homeassistant.components.sensor.fido
|
||||
pyfido==2.1.1
|
||||
|
||||
|
|
16
tests/components/light/test_everlights.py
Normal file
16
tests/components/light/test_everlights.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
"""The tests for the everlights component."""
|
||||
from homeassistant.components.light import everlights
|
||||
|
||||
|
||||
def test_color_rgb_to_int():
|
||||
"""Test RGB to integer conversion."""
|
||||
assert everlights.color_rgb_to_int(0x00, 0x00, 0x00) == 0x000000
|
||||
assert everlights.color_rgb_to_int(0xff, 0xff, 0xff) == 0xffffff
|
||||
assert everlights.color_rgb_to_int(0x12, 0x34, 0x56) == 0x123456
|
||||
|
||||
|
||||
def test_int_to_rgb():
|
||||
"""Test integer to RGB conversion."""
|
||||
assert everlights.color_int_to_rgb(0x000000) == (0x00, 0x00, 0x00)
|
||||
assert everlights.color_int_to_rgb(0xffffff) == (0xff, 0xff, 0xff)
|
||||
assert everlights.color_int_to_rgb(0x123456) == (0x12, 0x34, 0x56)
|
Loading…
Add table
Reference in a new issue