diff --git a/esphome/components/light/addressable_light_wrapper.h b/esphome/components/light/addressable_light_wrapper.h new file mode 100644 index 0000000000..813dd43313 --- /dev/null +++ b/esphome/components/light/addressable_light_wrapper.h @@ -0,0 +1,56 @@ +#pragma once + +#include "esphome/core/component.h" +#include "addressable_light.h" + +namespace esphome { +namespace light { + +class AddressableLightWrapper : public light::AddressableLight { + public: + explicit AddressableLightWrapper(light::LightState *light_state) : light_state_(light_state) { + this->wrapper_state_ = new uint8_t[5]; + } + + int32_t size() const override { return 1; } + + void clear_effect_data() override { this->wrapper_state_[4] = 0; } + + light::LightTraits get_traits() override { return this->light_state_->get_traits(); } + + void write_state(light::LightState *state) override { + float gamma = this->light_state_->get_gamma_correct(); + float r = gamma_uncorrect(this->wrapper_state_[0] / 255.0f, gamma); + float g = gamma_uncorrect(this->wrapper_state_[1] / 255.0f, gamma); + float b = gamma_uncorrect(this->wrapper_state_[2] / 255.0f, gamma); + float w = gamma_uncorrect(this->wrapper_state_[3] / 255.0f, gamma); + float brightness = fmaxf(r, fmaxf(g, b)); + + auto call = this->light_state_->make_call(); + call.set_state(true); + call.set_brightness_if_supported(1.0f); + call.set_color_brightness_if_supported(brightness); + call.set_red_if_supported(r); + call.set_green_if_supported(g); + call.set_blue_if_supported(b); + call.set_white_if_supported(w); + call.set_transition_length_if_supported(0); + call.set_publish(false); + call.set_save(false); + call.perform(); + + this->mark_shown_(); + } + + protected: + light::ESPColorView get_view_internal(int32_t index) const override { + return {&this->wrapper_state_[0], &this->wrapper_state_[1], &this->wrapper_state_[2], + &this->wrapper_state_[3], &this->wrapper_state_[4], &this->correction_}; + } + + light::LightState *light_state_; + uint8_t *wrapper_state_; +}; + +} // namespace light +} // namespace esphome diff --git a/esphome/components/partition/light.py b/esphome/components/partition/light.py index 06bee2143e..ada83a123e 100644 --- a/esphome/components/partition/light.py +++ b/esphome/components/partition/light.py @@ -2,9 +2,12 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import light from esphome.const import ( + CONF_ADDRESSABLE_LIGHT_ID, CONF_FROM, CONF_ID, + CONF_LIGHT_ID, CONF_SEGMENTS, + CONF_SINGLE_LIGHT_ID, CONF_TO, CONF_OUTPUT_ID, CONF_REVERSED, @@ -12,30 +15,47 @@ from esphome.const import ( partitions_ns = cg.esphome_ns.namespace("partition") AddressableSegment = partitions_ns.class_("AddressableSegment") +AddressableLightWrapper = cg.esphome_ns.namespace("light").class_( + "AddressableLightWrapper" +) PartitionLightOutput = partitions_ns.class_( "PartitionLightOutput", light.AddressableLight ) def validate_from_to(value): - if value[CONF_FROM] > value[CONF_TO]: + if CONF_ID in value and value[CONF_FROM] > value[CONF_TO]: raise cv.Invalid( f"From ({value[CONF_FROM]}) must not be larger than to ({value[CONF_TO]})" ) return value +ADDRESSABLE_SEGMENT_SCHEMA = cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(light.AddressableLightState), + cv.Required(CONF_FROM): cv.positive_int, + cv.Required(CONF_TO): cv.positive_int, + cv.Optional(CONF_REVERSED, default=False): cv.boolean, + } +) + +NONADDRESSABLE_SEGMENT_SCHEMA = cv.COMPONENT_SCHEMA.extend( + { + cv.Required(CONF_SINGLE_LIGHT_ID): cv.use_id(light.LightState), + cv.GenerateID(CONF_ADDRESSABLE_LIGHT_ID): cv.declare_id( + AddressableLightWrapper + ), + cv.GenerateID(CONF_LIGHT_ID): cv.declare_id(light.types.LightState), + } +) + CONFIG_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend( { cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(PartitionLightOutput), cv.Required(CONF_SEGMENTS): cv.All( cv.ensure_list( - { - cv.Required(CONF_ID): cv.use_id(light.AddressableLightState), - cv.Required(CONF_FROM): cv.positive_int, - cv.Required(CONF_TO): cv.positive_int, - cv.Optional(CONF_REVERSED, default=False): cv.boolean, - }, + cv.Any(ADDRESSABLE_SEGMENT_SCHEMA, NONADDRESSABLE_SEGMENT_SCHEMA), validate_from_to, ), cv.Length(min=1), @@ -47,15 +67,25 @@ CONFIG_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend( async def to_code(config): segments = [] for conf in config[CONF_SEGMENTS]: - var = await cg.get_variable(conf[CONF_ID]) - segments.append( - AddressableSegment( - var, - conf[CONF_FROM], - conf[CONF_TO] - conf[CONF_FROM] + 1, - conf[CONF_REVERSED], + if CONF_SINGLE_LIGHT_ID in conf: + wrapper = cg.new_Pvariable( + conf[CONF_ADDRESSABLE_LIGHT_ID], + await cg.get_variable(conf[CONF_SINGLE_LIGHT_ID]), + ) + light_state = cg.new_Pvariable(conf[CONF_LIGHT_ID], "", wrapper) + await cg.register_component(light_state, conf) + cg.add(cg.App.register_light(light_state)) + segments.append(AddressableSegment(light_state, 0, 1, False)) + + else: + segments.append( + AddressableSegment( + await cg.get_variable(conf[CONF_ID]), + conf[CONF_FROM], + conf[CONF_TO] - conf[CONF_FROM] + 1, + conf[CONF_REVERSED], + ) ) - ) var = cg.new_Pvariable(config[CONF_OUTPUT_ID], segments) await cg.register_component(var, config) diff --git a/esphome/const.py b/esphome/const.py index 6a3296bcea..f032cf0fc3 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -320,6 +320,7 @@ CONF_LEVEL = "level" CONF_LG = "lg" CONF_LIBRARIES = "libraries" CONF_LIGHT = "light" +CONF_LIGHT_ID = "light_id" CONF_LIGHTNING_ENERGY = "lightning_energy" CONF_LIGHTNING_THRESHOLD = "lightning_threshold" CONF_LINE_THICKNESS = "line_thickness" @@ -586,6 +587,7 @@ CONF_SHOW_VALUES = "show_values" CONF_SHUNT_RESISTANCE = "shunt_resistance" CONF_SHUNT_VOLTAGE = "shunt_voltage" CONF_SHUTDOWN_MESSAGE = "shutdown_message" +CONF_SINGLE_LIGHT_ID = "single_light_id" CONF_SIZE = "size" CONF_SLEEP_DURATION = "sleep_duration" CONF_SLEEP_PIN = "sleep_pin" diff --git a/tests/test1.yaml b/tests/test1.yaml index ab089dbc15..c1ddf26488 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1615,6 +1615,7 @@ light: - id: addr2 from: 20 to: 25 + - single_light_id: ${roomname}_lights remote_transmitter: - pin: 32