"""Support for LED lights that can be controlled using PWM.""" import logging import voluptuous as vol from homeassistant.const import CONF_NAME, CONF_TYPE, STATE_ON, CONF_ADDRESS from homeassistant.components.light import ( Light, ATTR_BRIGHTNESS, ATTR_HS_COLOR, ATTR_TRANSITION, SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_TRANSITION, PLATFORM_SCHEMA, ) import homeassistant.helpers.config_validation as cv import homeassistant.util.color as color_util from homeassistant.helpers.restore_state import RestoreEntity _LOGGER = logging.getLogger(__name__) CONF_LEDS = "leds" CONF_DRIVER = "driver" CONF_PINS = "pins" CONF_FREQUENCY = "frequency" CONF_DRIVER_GPIO = "gpio" CONF_DRIVER_PCA9685 = "pca9685" CONF_DRIVER_TYPES = [CONF_DRIVER_GPIO, CONF_DRIVER_PCA9685] CONF_LED_TYPE_SIMPLE = "simple" CONF_LED_TYPE_RGB = "rgb" CONF_LED_TYPE_RGBW = "rgbw" CONF_LED_TYPES = [CONF_LED_TYPE_SIMPLE, CONF_LED_TYPE_RGB, CONF_LED_TYPE_RGBW] DEFAULT_BRIGHTNESS = 255 DEFAULT_COLOR = [0, 0] SUPPORT_SIMPLE_LED = SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION SUPPORT_RGB_LED = SUPPORT_BRIGHTNESS | SUPPORT_COLOR | SUPPORT_TRANSITION PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { vol.Required(CONF_LEDS): vol.All( cv.ensure_list, [ { vol.Required(CONF_NAME): cv.string, vol.Required(CONF_DRIVER): vol.In(CONF_DRIVER_TYPES), vol.Required(CONF_PINS): vol.All(cv.ensure_list, [cv.positive_int]), vol.Required(CONF_TYPE): vol.In(CONF_LED_TYPES), vol.Optional(CONF_FREQUENCY): cv.positive_int, vol.Optional(CONF_ADDRESS): cv.byte, } ], ) } ) def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the PWM LED lights.""" from pwmled.led import SimpleLed from pwmled.led.rgb import RgbLed from pwmled.led.rgbw import RgbwLed from pwmled.driver.gpio import GpioDriver from pwmled.driver.pca9685 import Pca9685Driver leds = [] for led_conf in config[CONF_LEDS]: driver_type = led_conf[CONF_DRIVER] pins = led_conf[CONF_PINS] opt_args = {} if CONF_FREQUENCY in led_conf: opt_args["freq"] = led_conf[CONF_FREQUENCY] if driver_type == CONF_DRIVER_GPIO: driver = GpioDriver(pins, **opt_args) elif driver_type == CONF_DRIVER_PCA9685: if CONF_ADDRESS in led_conf: opt_args["address"] = led_conf[CONF_ADDRESS] driver = Pca9685Driver(pins, **opt_args) else: _LOGGER.error("Invalid driver type") return name = led_conf[CONF_NAME] led_type = led_conf[CONF_TYPE] if led_type == CONF_LED_TYPE_SIMPLE: led = PwmSimpleLed(SimpleLed(driver), name) elif led_type == CONF_LED_TYPE_RGB: led = PwmRgbLed(RgbLed(driver), name) elif led_type == CONF_LED_TYPE_RGBW: led = PwmRgbLed(RgbwLed(driver), name) else: _LOGGER.error("Invalid led type") return leds.append(led) add_entities(leds) class PwmSimpleLed(Light, RestoreEntity): """Representation of a simple one-color PWM LED.""" def __init__(self, led, name): """Initialize one-color PWM LED.""" self._led = led self._name = name self._is_on = False self._brightness = DEFAULT_BRIGHTNESS async def async_added_to_hass(self): """Handle entity about to be added to hass event.""" await super().async_added_to_hass() last_state = await self.async_get_last_state() if last_state: self._is_on = last_state.state == STATE_ON self._brightness = last_state.attributes.get( "brightness", DEFAULT_BRIGHTNESS ) self._led.set( is_on=self._is_on, brightness=_from_hass_brightness(self._brightness) ) @property def should_poll(self): """No polling needed.""" return False @property def name(self): """Return the name of the group.""" return self._name @property def is_on(self): """Return true if device is on.""" return self._is_on @property def brightness(self): """Return the brightness property.""" return self._brightness @property def supported_features(self): """Flag supported features.""" return SUPPORT_SIMPLE_LED def turn_on(self, **kwargs): """Turn on a led.""" if ATTR_BRIGHTNESS in kwargs: self._brightness = kwargs[ATTR_BRIGHTNESS] if ATTR_TRANSITION in kwargs: transition_time = kwargs[ATTR_TRANSITION] self._led.transition( transition_time, is_on=True, brightness=_from_hass_brightness(self._brightness), ) else: self._led.set( is_on=True, brightness=_from_hass_brightness(self._brightness) ) self._is_on = True self.schedule_update_ha_state() def turn_off(self, **kwargs): """Turn off a LED.""" if self.is_on: if ATTR_TRANSITION in kwargs: transition_time = kwargs[ATTR_TRANSITION] self._led.transition(transition_time, is_on=False) else: self._led.off() self._is_on = False self.schedule_update_ha_state() class PwmRgbLed(PwmSimpleLed): """Representation of a RGB(W) PWM LED.""" def __init__(self, led, name): """Initialize a RGB(W) PWM LED.""" super().__init__(led, name) self._color = DEFAULT_COLOR async def async_added_to_hass(self): """Handle entity about to be added to hass event.""" await super().async_added_to_hass() last_state = await self.async_get_last_state() if last_state: self._color = last_state.attributes.get("hs_color", DEFAULT_COLOR) self._led.set(color=_from_hass_color(self._color)) @property def hs_color(self): """Return the color property.""" return self._color @property def supported_features(self): """Flag supported features.""" return SUPPORT_RGB_LED def turn_on(self, **kwargs): """Turn on a LED.""" if ATTR_HS_COLOR in kwargs: self._color = kwargs[ATTR_HS_COLOR] if ATTR_BRIGHTNESS in kwargs: self._brightness = kwargs[ATTR_BRIGHTNESS] if ATTR_TRANSITION in kwargs: transition_time = kwargs[ATTR_TRANSITION] self._led.transition( transition_time, is_on=True, brightness=_from_hass_brightness(self._brightness), color=_from_hass_color(self._color), ) else: self._led.set( is_on=True, brightness=_from_hass_brightness(self._brightness), color=_from_hass_color(self._color), ) self._is_on = True self.schedule_update_ha_state() def _from_hass_brightness(brightness): """Convert Home Assistant brightness units to percentage.""" return brightness / 255 def _from_hass_color(color): """Convert Home Assistant RGB list to Color tuple.""" from pwmled import Color rgb = color_util.color_hs_to_RGB(*color) return Color(*tuple(rgb))