mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	Make light.addressable_set color parameters behave as documented & consistent with elsewhere (#2009)
This commit is contained in:
		| @@ -25,16 +25,16 @@ void AddressableLight::call_setup() { | |||||||
| } | } | ||||||
|  |  | ||||||
| Color esp_color_from_light_color_values(LightColorValues val) { | Color esp_color_from_light_color_values(LightColorValues val) { | ||||||
|   auto r = static_cast<uint8_t>(roundf(val.get_color_brightness() * val.get_red() * 255.0f)); |   auto r = to_uint8_scale(val.get_color_brightness() * val.get_red()); | ||||||
|   auto g = static_cast<uint8_t>(roundf(val.get_color_brightness() * val.get_green() * 255.0f)); |   auto g = to_uint8_scale(val.get_color_brightness() * val.get_green()); | ||||||
|   auto b = static_cast<uint8_t>(roundf(val.get_color_brightness() * val.get_blue() * 255.0f)); |   auto b = to_uint8_scale(val.get_color_brightness() * val.get_blue()); | ||||||
|   auto w = static_cast<uint8_t>(roundf(val.get_white() * 255.0f)); |   auto w = to_uint8_scale(val.get_white()); | ||||||
|   return Color(r, g, b, w); |   return Color(r, g, b, w); | ||||||
| } | } | ||||||
|  |  | ||||||
| void AddressableLight::write_state(LightState *state) { | void AddressableLight::write_state(LightState *state) { | ||||||
|   auto val = state->current_values; |   auto val = state->current_values; | ||||||
|   auto max_brightness = static_cast<uint8_t>(roundf(val.get_brightness() * val.get_state() * 255.0f)); |   auto max_brightness = to_uint8_scale(val.get_brightness() * val.get_state()); | ||||||
|   this->correction_.set_local_brightness(max_brightness); |   this->correction_.set_local_brightness(max_brightness); | ||||||
|  |  | ||||||
|   this->last_transition_progress_ = 0.0f; |   this->last_transition_progress_ = 0.0f; | ||||||
| @@ -68,7 +68,7 @@ void AddressableLight::write_state(LightState *state) { | |||||||
|  |  | ||||||
|     // our transition will handle brightness, disable brightness in correction. |     // our transition will handle brightness, disable brightness in correction. | ||||||
|     this->correction_.set_local_brightness(255); |     this->correction_.set_local_brightness(255); | ||||||
|     target_color *= static_cast<uint8_t>(roundf(end_values.get_brightness() * end_values.get_state() * 255.0f)); |     target_color *= to_uint8_scale(end_values.get_brightness() * end_values.get_state()); | ||||||
|  |  | ||||||
|     float denom = (1.0f - new_smoothed); |     float denom = (1.0f - new_smoothed); | ||||||
|     float alpha = denom == 0.0f ? 0.0f : (new_smoothed - prev_smoothed) / denom; |     float alpha = denom == 0.0f ? 0.0f : (new_smoothed - prev_smoothed) / denom; | ||||||
|   | |||||||
| @@ -54,8 +54,8 @@ class AddressableLight : public LightOutput, public Component { | |||||||
|   void set_effect_active(bool effect_active) { this->effect_active_ = effect_active; } |   void set_effect_active(bool effect_active) { this->effect_active_ = effect_active; } | ||||||
|   void write_state(LightState *state) override; |   void write_state(LightState *state) override; | ||||||
|   void set_correction(float red, float green, float blue, float white = 1.0f) { |   void set_correction(float red, float green, float blue, float white = 1.0f) { | ||||||
|     this->correction_.set_max_brightness(Color(uint8_t(roundf(red * 255.0f)), uint8_t(roundf(green * 255.0f)), |     this->correction_.set_max_brightness( | ||||||
|                                                uint8_t(roundf(blue * 255.0f)), uint8_t(roundf(white * 255.0f)))); |         Color(to_uint8_scale(red), to_uint8_scale(green), to_uint8_scale(blue), to_uint8_scale(white))); | ||||||
|   } |   } | ||||||
|   void setup_state(LightState *state) override { |   void setup_state(LightState *state) override { | ||||||
|     this->correction_.calculate_gamma_table(state->get_gamma_correct()); |     this->correction_.calculate_gamma_table(state->get_gamma_correct()); | ||||||
|   | |||||||
| @@ -337,7 +337,7 @@ class AddressableFlickerEffect : public AddressableLightEffect { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   void set_update_interval(uint32_t update_interval) { this->update_interval_ = update_interval; } |   void set_update_interval(uint32_t update_interval) { this->update_interval_ = update_interval; } | ||||||
|   void set_intensity(float intensity) { this->intensity_ = static_cast<uint8_t>(roundf(intensity * 255.0f)); } |   void set_intensity(float intensity) { this->intensity_ = to_uint8_scale(intensity); } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   uint32_t update_interval_{16}; |   uint32_t update_interval_{16}; | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								esphome/components/light/automation.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								esphome/components/light/automation.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | #include "automation.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace light { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "light.automation"; | ||||||
|  |  | ||||||
|  | void addressableset_warn_about_scale(const char *field) { | ||||||
|  |   ESP_LOGW(TAG, "Lambda for parameter %s of light.addressable_set should return values in range 0-1 instead of 0-255.", | ||||||
|  |            field); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace light | ||||||
|  | }  // namespace esphome | ||||||
| @@ -135,39 +135,51 @@ class LightTurnOffTrigger : public Trigger<> { | |||||||
|   } |   } | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | // This is slightly ugly, but we can't log in headers, and can't make this a static method on AddressableSet | ||||||
|  | // due to the template. It's just a temporary warning anyway. | ||||||
|  | void addressableset_warn_about_scale(const char *field); | ||||||
|  |  | ||||||
| template<typename... Ts> class AddressableSet : public Action<Ts...> { | template<typename... Ts> class AddressableSet : public Action<Ts...> { | ||||||
|  public: |  public: | ||||||
|   explicit AddressableSet(LightState *parent) : parent_(parent) {} |   explicit AddressableSet(LightState *parent) : parent_(parent) {} | ||||||
|  |  | ||||||
|   TEMPLATABLE_VALUE(int32_t, range_from) |   TEMPLATABLE_VALUE(int32_t, range_from) | ||||||
|   TEMPLATABLE_VALUE(int32_t, range_to) |   TEMPLATABLE_VALUE(int32_t, range_to) | ||||||
|   TEMPLATABLE_VALUE(uint8_t, color_brightness) |   TEMPLATABLE_VALUE(float, color_brightness) | ||||||
|   TEMPLATABLE_VALUE(uint8_t, red) |   TEMPLATABLE_VALUE(float, red) | ||||||
|   TEMPLATABLE_VALUE(uint8_t, green) |   TEMPLATABLE_VALUE(float, green) | ||||||
|   TEMPLATABLE_VALUE(uint8_t, blue) |   TEMPLATABLE_VALUE(float, blue) | ||||||
|   TEMPLATABLE_VALUE(uint8_t, white) |   TEMPLATABLE_VALUE(float, white) | ||||||
|  |  | ||||||
|   void play(Ts... x) override { |   void play(Ts... x) override { | ||||||
|     auto *out = (AddressableLight *) this->parent_->get_output(); |     auto *out = (AddressableLight *) this->parent_->get_output(); | ||||||
|     int32_t range_from = this->range_from_.value_or(x..., 0); |     int32_t range_from = this->range_from_.value_or(x..., 0); | ||||||
|     int32_t range_to = this->range_to_.value_or(x..., out->size() - 1) + 1; |     int32_t range_to = this->range_to_.value_or(x..., out->size() - 1) + 1; | ||||||
|     uint8_t remote_color_brightness = |     uint8_t color_brightness = | ||||||
|         static_cast<uint8_t>(roundf(this->parent_->remote_values.get_color_brightness() * 255.0f)); |         to_uint8_scale(this->color_brightness_.value_or(x..., this->parent_->remote_values.get_color_brightness())); | ||||||
|     uint8_t color_brightness = this->color_brightness_.value_or(x..., remote_color_brightness); |  | ||||||
|     auto range = out->range(range_from, range_to); |     auto range = out->range(range_from, range_to); | ||||||
|     if (this->red_.has_value()) |     if (this->red_.has_value()) | ||||||
|       range.set_red(esp_scale8(this->red_.value(x...), color_brightness)); |       range.set_red(esp_scale8(to_uint8_compat(this->red_.value(x...), "red"), color_brightness)); | ||||||
|     if (this->green_.has_value()) |     if (this->green_.has_value()) | ||||||
|       range.set_green(esp_scale8(this->green_.value(x...), color_brightness)); |       range.set_green(esp_scale8(to_uint8_compat(this->green_.value(x...), "green"), color_brightness)); | ||||||
|     if (this->blue_.has_value()) |     if (this->blue_.has_value()) | ||||||
|       range.set_blue(esp_scale8(this->blue_.value(x...), color_brightness)); |       range.set_blue(esp_scale8(to_uint8_compat(this->blue_.value(x...), "blue"), color_brightness)); | ||||||
|     if (this->white_.has_value()) |     if (this->white_.has_value()) | ||||||
|       range.set_white(this->white_.value(x...)); |       range.set_white(to_uint8_compat(this->white_.value(x...), "white")); | ||||||
|     out->schedule_show(); |     out->schedule_show(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   LightState *parent_; |   LightState *parent_; | ||||||
|  |  | ||||||
|  |   // Historically, this action required uint8_t (0-255) for RGBW values from lambdas. Keep compatibility. | ||||||
|  |   static inline uint8_t to_uint8_compat(float value, const char *field) { | ||||||
|  |     if (value > 1.0f) { | ||||||
|  |       addressableset_warn_about_scale(field); | ||||||
|  |       return static_cast<uint8_t>(value); | ||||||
|  |     } | ||||||
|  |     return to_uint8_scale(value); | ||||||
|  |   } | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace light | }  // namespace light | ||||||
|   | |||||||
| @@ -173,6 +173,7 @@ LIGHT_ADDRESSABLE_SET_ACTION_SCHEMA = cv.Schema( | |||||||
|         cv.Required(CONF_ID): cv.use_id(AddressableLightState), |         cv.Required(CONF_ID): cv.use_id(AddressableLightState), | ||||||
|         cv.Optional(CONF_RANGE_FROM): cv.templatable(cv.positive_int), |         cv.Optional(CONF_RANGE_FROM): cv.templatable(cv.positive_int), | ||||||
|         cv.Optional(CONF_RANGE_TO): cv.templatable(cv.positive_int), |         cv.Optional(CONF_RANGE_TO): cv.templatable(cv.positive_int), | ||||||
|  |         cv.Optional(CONF_COLOR_BRIGHTNESS): cv.templatable(cv.percentage), | ||||||
|         cv.Optional(CONF_RED): cv.templatable(cv.percentage), |         cv.Optional(CONF_RED): cv.templatable(cv.percentage), | ||||||
|         cv.Optional(CONF_GREEN): cv.templatable(cv.percentage), |         cv.Optional(CONF_GREEN): cv.templatable(cv.percentage), | ||||||
|         cv.Optional(CONF_BLUE): cv.templatable(cv.percentage), |         cv.Optional(CONF_BLUE): cv.templatable(cv.percentage), | ||||||
| @@ -194,28 +195,20 @@ async def light_addressable_set_to_code(config, action_id, template_arg, args): | |||||||
|         templ = await cg.templatable(config[CONF_RANGE_TO], args, cg.int32) |         templ = await cg.templatable(config[CONF_RANGE_TO], args, cg.int32) | ||||||
|         cg.add(var.set_range_to(templ)) |         cg.add(var.set_range_to(templ)) | ||||||
|  |  | ||||||
|     def rgbw_to_exp(x): |     if CONF_COLOR_BRIGHTNESS in config: | ||||||
|         return int(round(x * 255)) |         templ = await cg.templatable(config[CONF_COLOR_BRIGHTNESS], args, cg.float_) | ||||||
|  |         cg.add(var.set_color_brightness(templ)) | ||||||
|     if CONF_RED in config: |     if CONF_RED in config: | ||||||
|         templ = await cg.templatable( |         templ = await cg.templatable(config[CONF_RED], args, cg.float_) | ||||||
|             config[CONF_RED], args, cg.uint8, to_exp=rgbw_to_exp |  | ||||||
|         ) |  | ||||||
|         cg.add(var.set_red(templ)) |         cg.add(var.set_red(templ)) | ||||||
|     if CONF_GREEN in config: |     if CONF_GREEN in config: | ||||||
|         templ = await cg.templatable( |         templ = await cg.templatable(config[CONF_GREEN], args, cg.float_) | ||||||
|             config[CONF_GREEN], args, cg.uint8, to_exp=rgbw_to_exp |  | ||||||
|         ) |  | ||||||
|         cg.add(var.set_green(templ)) |         cg.add(var.set_green(templ)) | ||||||
|     if CONF_BLUE in config: |     if CONF_BLUE in config: | ||||||
|         templ = await cg.templatable( |         templ = await cg.templatable(config[CONF_BLUE], args, cg.float_) | ||||||
|             config[CONF_BLUE], args, cg.uint8, to_exp=rgbw_to_exp |  | ||||||
|         ) |  | ||||||
|         cg.add(var.set_blue(templ)) |         cg.add(var.set_blue(templ)) | ||||||
|     if CONF_WHITE in config: |     if CONF_WHITE in config: | ||||||
|         templ = await cg.templatable( |         templ = await cg.templatable(config[CONF_WHITE], args, cg.float_) | ||||||
|             config[CONF_WHITE], args, cg.uint8, to_exp=rgbw_to_exp |  | ||||||
|         ) |  | ||||||
|         cg.add(var.set_white(templ)) |         cg.add(var.set_white(templ)) | ||||||
|     return var |     return var | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| #include "esp_color_correction.h" | #include "esp_color_correction.h" | ||||||
|  | #include "light_color_values.h" | ||||||
| #include "esphome/core/log.h" | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| @@ -7,7 +8,7 @@ namespace light { | |||||||
| void ESPColorCorrection::calculate_gamma_table(float gamma) { | void ESPColorCorrection::calculate_gamma_table(float gamma) { | ||||||
|   for (uint16_t i = 0; i < 256; i++) { |   for (uint16_t i = 0; i < 256; i++) { | ||||||
|     // corrected = val ^ gamma |     // corrected = val ^ gamma | ||||||
|     auto corrected = static_cast<uint8_t>(roundf(255.0f * gamma_correct(i / 255.0f, gamma))); |     auto corrected = to_uint8_scale(gamma_correct(i / 255.0f, gamma)); | ||||||
|     this->gamma_table_[i] = corrected; |     this->gamma_table_[i] = corrected; | ||||||
|   } |   } | ||||||
|   if (gamma == 0.0f) { |   if (gamma == 0.0f) { | ||||||
| @@ -17,7 +18,7 @@ void ESPColorCorrection::calculate_gamma_table(float gamma) { | |||||||
|   } |   } | ||||||
|   for (uint16_t i = 0; i < 256; i++) { |   for (uint16_t i = 0; i < 256; i++) { | ||||||
|     // val = corrected ^ (1/gamma) |     // val = corrected ^ (1/gamma) | ||||||
|     auto uncorrected = static_cast<uint8_t>(roundf(255.0f * powf(i / 255.0f, 1.0f / gamma))); |     auto uncorrected = to_uint8_scale(powf(i / 255.0f, 1.0f / gamma)); | ||||||
|     this->gamma_reverse_table_[i] = uncorrected; |     this->gamma_reverse_table_[i] = uncorrected; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -11,6 +11,8 @@ | |||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace light { | namespace light { | ||||||
|  |  | ||||||
|  | inline static uint8_t to_uint8_scale(float x) { return static_cast<uint8_t>(roundf(x * 255.0f)); } | ||||||
|  |  | ||||||
| /** This class represents the color state for a light object. | /** This class represents the color state for a light object. | ||||||
|  * |  * | ||||||
|  * All values in this class (except color temperature) are represented using floats in the range |  * All values in this class (except color temperature) are represented using floats in the range | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user