diff --git a/esphome/components/light/light_call.cpp b/esphome/components/light/light_call.cpp index ad2ca89a98..9c504996b5 100644 --- a/esphome/components/light/light_call.cpp +++ b/esphome/components/light/light_call.cpp @@ -66,11 +66,17 @@ static const LogString *color_mode_to_human(ColorMode color_mode) { return LOG_STR(""); } +// Helper to log percentage values +static inline void log_percent(const char *name, const char *param, float value) { + ESP_LOGD(TAG, " %s: %.0f%%", param, value * 100.0f); +} + void LightCall::perform() { const char *name = this->parent_->get_name().c_str(); LightColorValues v = this->validate_(); + const bool publish = this->get_publish_(); - if (this->get_publish_()) { + if (publish) { ESP_LOGD(TAG, "'%s' Setting:", name); // Only print color mode when it's being changed @@ -88,11 +94,11 @@ void LightCall::perform() { } if (this->has_brightness()) { - ESP_LOGD(TAG, " Brightness: %.0f%%", v.get_brightness() * 100.0f); + log_percent(name, "Brightness", v.get_brightness()); } if (this->has_color_brightness()) { - ESP_LOGD(TAG, " Color brightness: %.0f%%", v.get_color_brightness() * 100.0f); + log_percent(name, "Color brightness", v.get_color_brightness()); } if (this->has_red() || this->has_green() || this->has_blue()) { ESP_LOGD(TAG, " Red: %.0f%%, Green: %.0f%%, Blue: %.0f%%", v.get_red() * 100.0f, v.get_green() * 100.0f, @@ -100,7 +106,7 @@ void LightCall::perform() { } if (this->has_white()) { - ESP_LOGD(TAG, " White: %.0f%%", v.get_white() * 100.0f); + log_percent(name, "White", v.get_white()); } if (this->has_color_temperature()) { ESP_LOGD(TAG, " Color temperature: %.1f mireds", v.get_color_temperature()); @@ -114,26 +120,26 @@ void LightCall::perform() { if (this->has_flash_()) { // FLASH - if (this->get_publish_()) { + if (publish) { ESP_LOGD(TAG, " Flash length: %.1fs", this->flash_length_ / 1e3f); } - this->parent_->start_flash_(v, this->flash_length_, this->get_publish_()); + this->parent_->start_flash_(v, this->flash_length_, publish); } else if (this->has_transition_()) { // TRANSITION - if (this->get_publish_()) { + if (publish) { ESP_LOGD(TAG, " Transition length: %.1fs", this->transition_length_ / 1e3f); } // Special case: Transition and effect can be set when turning off if (this->has_effect_()) { - if (this->get_publish_()) { + if (publish) { ESP_LOGD(TAG, " Effect: 'None'"); } this->parent_->stop_effect_(); } - this->parent_->start_transition_(v, this->transition_length_, this->get_publish_()); + this->parent_->start_transition_(v, this->transition_length_, publish); } else if (this->has_effect_()) { // EFFECT @@ -144,7 +150,7 @@ void LightCall::perform() { effect_s = this->parent_->effects_[this->effect_ - 1]->get_name().c_str(); } - if (this->get_publish_()) { + if (publish) { ESP_LOGD(TAG, " Effect: '%s'", effect_s); } @@ -155,13 +161,13 @@ void LightCall::perform() { this->parent_->set_immediately_(v, true); } else { // INSTANT CHANGE - this->parent_->set_immediately_(v, this->get_publish_()); + this->parent_->set_immediately_(v, publish); } if (!this->has_transition_()) { this->parent_->target_state_reached_callback_.call(); } - if (this->get_publish_()) { + if (publish) { this->parent_->publish_state(); } if (this->get_save_()) { diff --git a/esphome/components/light/light_json_schema.cpp b/esphome/components/light/light_json_schema.cpp index 26615bae5c..84e1ee9f1d 100644 --- a/esphome/components/light/light_json_schema.cpp +++ b/esphome/components/light/light_json_schema.cpp @@ -8,6 +8,46 @@ namespace light { // See https://www.home-assistant.io/integrations/light.mqtt/#json-schema for documentation on the schema +// Helper to convert float 0-1 to uint8_t 0-255 +static inline uint8_t to_uint8_scaled(float value) { return uint8_t(value * 255); } + +// Helper to parse color component from JSON +static float parse_color_component(JsonObject &color, const char *key, LightCall &call, + LightCall &(LightCall::*setter)(float) ) { + if (color[key].is()) { + float val = float(color[key]) / 255.0f; + (call.*setter)(val); + return val; + } + return 0.0f; +} + +// Lookup table for color mode strings +static const char *get_color_mode_json_str(ColorMode mode) { + switch (mode) { + case ColorMode::ON_OFF: + return "onoff"; + case ColorMode::BRIGHTNESS: + return "brightness"; + case ColorMode::WHITE: + return "white"; // not supported by HA in MQTT + case ColorMode::COLOR_TEMPERATURE: + return "color_temp"; + case ColorMode::COLD_WARM_WHITE: + return "cwww"; // not supported by HA + case ColorMode::RGB: + return "rgb"; + case ColorMode::RGB_WHITE: + return "rgbw"; + case ColorMode::RGB_COLOR_TEMPERATURE: + return "rgbct"; // not supported by HA + case ColorMode::RGB_COLD_WARM_WHITE: + return "rgbww"; + default: + return nullptr; + } +} + void LightJSONSchema::dump_json(LightState &state, JsonObject root) { // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson if (state.supports_effects()) @@ -16,60 +56,36 @@ void LightJSONSchema::dump_json(LightState &state, JsonObject root) { auto values = state.remote_values; auto traits = state.get_output()->get_traits(); - switch (values.get_color_mode()) { - case ColorMode::UNKNOWN: // don't need to set color mode if we don't know it - break; - case ColorMode::ON_OFF: - root["color_mode"] = "onoff"; - break; - case ColorMode::BRIGHTNESS: - root["color_mode"] = "brightness"; - break; - case ColorMode::WHITE: // not supported by HA in MQTT - root["color_mode"] = "white"; - break; - case ColorMode::COLOR_TEMPERATURE: - root["color_mode"] = "color_temp"; - break; - case ColorMode::COLD_WARM_WHITE: // not supported by HA - root["color_mode"] = "cwww"; - break; - case ColorMode::RGB: - root["color_mode"] = "rgb"; - break; - case ColorMode::RGB_WHITE: - root["color_mode"] = "rgbw"; - break; - case ColorMode::RGB_COLOR_TEMPERATURE: // not supported by HA - root["color_mode"] = "rgbct"; - break; - case ColorMode::RGB_COLD_WARM_WHITE: - root["color_mode"] = "rgbww"; - break; + const auto color_mode = values.get_color_mode(); + const char *mode_str = get_color_mode_json_str(color_mode); + if (mode_str != nullptr) { + root["color_mode"] = mode_str; } - if (values.get_color_mode() & ColorCapability::ON_OFF) + if (color_mode & ColorCapability::ON_OFF) root["state"] = (values.get_state() != 0.0f) ? "ON" : "OFF"; - if (values.get_color_mode() & ColorCapability::BRIGHTNESS) - root["brightness"] = uint8_t(values.get_brightness() * 255); + if (color_mode & ColorCapability::BRIGHTNESS) + root["brightness"] = to_uint8_scaled(values.get_brightness()); JsonObject color = root["color"].to(); - if (values.get_color_mode() & ColorCapability::RGB) { - color["r"] = uint8_t(values.get_color_brightness() * values.get_red() * 255); - color["g"] = uint8_t(values.get_color_brightness() * values.get_green() * 255); - color["b"] = uint8_t(values.get_color_brightness() * values.get_blue() * 255); + if (color_mode & ColorCapability::RGB) { + float color_brightness = values.get_color_brightness(); + color["r"] = to_uint8_scaled(color_brightness * values.get_red()); + color["g"] = to_uint8_scaled(color_brightness * values.get_green()); + color["b"] = to_uint8_scaled(color_brightness * values.get_blue()); } - if (values.get_color_mode() & ColorCapability::WHITE) { - color["w"] = uint8_t(values.get_white() * 255); - root["white_value"] = uint8_t(values.get_white() * 255); // legacy API + if (color_mode & ColorCapability::WHITE) { + uint8_t white_val = to_uint8_scaled(values.get_white()); + color["w"] = white_val; + root["white_value"] = white_val; // legacy API } - if (values.get_color_mode() & ColorCapability::COLOR_TEMPERATURE) { + if (color_mode & ColorCapability::COLOR_TEMPERATURE) { // this one isn't under the color subkey for some reason root["color_temp"] = uint32_t(values.get_color_temperature()); } - if (values.get_color_mode() & ColorCapability::COLD_WARM_WHITE) { - color["c"] = uint8_t(values.get_cold_white() * 255); - color["w"] = uint8_t(values.get_warm_white() * 255); + if (color_mode & ColorCapability::COLD_WARM_WHITE) { + color["c"] = to_uint8_scaled(values.get_cold_white()); + color["w"] = to_uint8_scaled(values.get_warm_white()); } } @@ -99,22 +115,16 @@ void LightJSONSchema::parse_color_json(LightState &state, LightCall &call, JsonO JsonObject color = root["color"]; // HA also encodes brightness information in the r, g, b values, so extract that and set it as color brightness. float max_rgb = 0.0f; - if (color["r"].is()) { - float r = float(color["r"]) / 255.0f; - max_rgb = fmaxf(max_rgb, r); - call.set_red(r); - } - if (color["g"].is()) { - float g = float(color["g"]) / 255.0f; - max_rgb = fmaxf(max_rgb, g); - call.set_green(g); - } - if (color["b"].is()) { - float b = float(color["b"]) / 255.0f; - max_rgb = fmaxf(max_rgb, b); - call.set_blue(b); - } - if (color["r"].is() || color["g"].is() || color["b"].is()) { + + float r = parse_color_component(color, "r", call, &LightCall::set_red); + float g = parse_color_component(color, "g", call, &LightCall::set_green); + float b = parse_color_component(color, "b", call, &LightCall::set_blue); + + max_rgb = fmaxf(max_rgb, r); + max_rgb = fmaxf(max_rgb, g); + max_rgb = fmaxf(max_rgb, b); + + if (max_rgb > 0.0f) { call.set_color_brightness(max_rgb); }