Brightness control fixes (#1)
* refactor light value controls method * fix brightness debug output * partial fixes for brightness control * try to fix white brightness * fix payload debug logging * fix debug output logging * fix light_off message * fix brightness
This commit is contained in:
parent
4417c77875
commit
d20f78ddfe
5 changed files with 106 additions and 57 deletions
|
|
@ -1,5 +1,6 @@
|
|||
#include "esphome/core/component_iterator.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/components/light/color_mode.h"
|
||||
#include "fastcon_controller.h"
|
||||
#include "protocol.h"
|
||||
|
||||
|
|
@ -130,51 +131,91 @@ namespace esphome
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> FastconController::get_advertisement(uint32_t light_id_, bool is_on, float brightness, float red, float green, float blue)
|
||||
std::vector<uint8_t> FastconController::get_light_data(light::LightState *state)
|
||||
{
|
||||
std::vector<uint8_t> light_data;
|
||||
std::vector<uint8_t> light_data = {
|
||||
0, // 0 - On/Off Bit + 7-bit Brightness
|
||||
0, // 1 - Blue byte
|
||||
0, // 2 - Red byte
|
||||
0, // 3 - Green byte
|
||||
0, // 4 - Warm byte
|
||||
0 // 5 - Cold byte
|
||||
};
|
||||
|
||||
// Convert brightness to 0-127 range
|
||||
uint8_t bright = static_cast<uint8_t>(std::min(brightness * 127.0f, 127.0f));
|
||||
// TODO: need to figure out when esphome is changing to white vs setting brightness
|
||||
|
||||
auto values = state->current_values;
|
||||
|
||||
bool is_on = values.is_on();
|
||||
if (!is_on)
|
||||
{
|
||||
// Off state
|
||||
light_data = {static_cast<uint8_t>(0)}; // Just the off command
|
||||
}
|
||||
else if (red == 0 && green == 0 && blue == 0)
|
||||
{
|
||||
// Warm white mode
|
||||
light_data = std::vector<uint8_t>{
|
||||
static_cast<uint8_t>(128 + bright), // On bit (128) + brightness
|
||||
0, 0, 0, // RGB values
|
||||
127, 127 // Warm/cold values
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// RGB mode
|
||||
uint8_t r = static_cast<uint8_t>(red * 255.0f);
|
||||
uint8_t g = static_cast<uint8_t>(green * 255.0f);
|
||||
uint8_t b = static_cast<uint8_t>(blue * 255.0f);
|
||||
|
||||
light_data = std::vector<uint8_t>{
|
||||
static_cast<uint8_t>(128 + bright), // On bit (128) + brightness
|
||||
b, r, g, // RGB values (in BRG order per protocol)
|
||||
0, 0 // No warm/cold values in RGB mode
|
||||
};
|
||||
return std::vector<uint8_t>({0x00});
|
||||
}
|
||||
|
||||
return this->single_control(light_id_, light_data);
|
||||
auto color_mode = values.get_color_mode();
|
||||
bool has_white = (static_cast<uint8_t>(color_mode) & static_cast<uint8_t>(light::ColorCapability::WHITE)) != 0;
|
||||
float brightness = std::min(values.get_brightness() * 127.0f, 127.0f); // clamp the value to at most 127
|
||||
light_data[0] = 0x80 + static_cast<uint8_t>(brightness);
|
||||
|
||||
if (has_white)
|
||||
{
|
||||
return std::vector<uint8_t>({static_cast<uint8_t>(brightness)});
|
||||
// DEBUG: when changing to white mode, this should be the payload:
|
||||
// ff0000007f7f
|
||||
}
|
||||
|
||||
bool has_rgb = (static_cast<uint8_t>(color_mode) & static_cast<uint8_t>(light::ColorCapability::RGB)) != 0;
|
||||
if (has_rgb)
|
||||
{
|
||||
light_data[1] = static_cast<uint8_t>(values.get_blue() * 255.0f);
|
||||
light_data[2] = static_cast<uint8_t>(values.get_red() * 255.0f);
|
||||
light_data[3] = static_cast<uint8_t>(values.get_green() * 255.0f);
|
||||
}
|
||||
|
||||
bool has_cold_warm = (static_cast<uint8_t>(color_mode) & static_cast<uint8_t>(light::ColorCapability::COLD_WARM_WHITE)) != 0;
|
||||
if (has_cold_warm)
|
||||
{
|
||||
light_data[4] = static_cast<uint8_t>(values.get_warm_white() * 255.0f);
|
||||
light_data[5] = static_cast<uint8_t>(values.get_cold_white() * 255.0f);
|
||||
}
|
||||
|
||||
// TODO figure out if we can use these, and how
|
||||
bool has_temp = (static_cast<uint8_t>(color_mode) & static_cast<uint8_t>(light::ColorCapability::COLOR_TEMPERATURE)) != 0;
|
||||
if (has_temp)
|
||||
{
|
||||
float temperature = values.get_color_temperature();
|
||||
if (temperature < 153)
|
||||
{
|
||||
light_data[4] = 0xff;
|
||||
light_data[5] = 0x00;
|
||||
}
|
||||
else if (temperature > 500)
|
||||
{
|
||||
light_data[4] = 0x00;
|
||||
light_data[5] = 0xff;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Linear interpolation between (153, 0xff) and (500, 0x00)
|
||||
light_data[4] = (uint8_t)(((500 - temperature) * 255.0f + (temperature - 153) * 0x00) / (500 - 153));
|
||||
light_data[5] = (uint8_t)(((temperature - 153) * 255.0f + (500 - temperature) * 0x00) / (500 - 153));
|
||||
}
|
||||
}
|
||||
|
||||
return light_data;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> FastconController::single_control(uint32_t light_id_, const std::vector<uint8_t> &data)
|
||||
std::vector<uint8_t> FastconController::single_control(uint32_t light_id_, const std::vector<uint8_t> &light_data)
|
||||
{
|
||||
std::vector<uint8_t> result_data(12);
|
||||
|
||||
result_data[0] = 2 | (((0xfffffff & (data.size() + 1)) << 4));
|
||||
result_data[0] = 2 | (((0xfffffff & (light_data.size() + 1)) << 4));
|
||||
result_data[1] = light_id_;
|
||||
std::copy(data.begin(), data.end(), result_data.begin() + 2);
|
||||
std::copy(light_data.begin(), light_data.end(), result_data.begin() + 2);
|
||||
|
||||
// Debug output - print payload as hex
|
||||
auto hex_str = vector_to_hex_string(result_data).data();
|
||||
ESP_LOGD(TAG, "Inner Payload (%d bytes): %s", result_data.size(), hex_str);
|
||||
|
||||
return this->generate_command(5, light_id_, result_data, true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,10 @@ namespace esphome
|
|||
void setup() override;
|
||||
void loop() override;
|
||||
|
||||
std::vector<uint8_t> get_light_data(light::LightState *state);
|
||||
std::vector<uint8_t> single_control(uint32_t addr, const std::vector<uint8_t> &light_data);
|
||||
|
||||
void queueCommand(uint32_t light_id_, const std::vector<uint8_t> &data);
|
||||
std::vector<uint8_t> get_advertisement(uint32_t light_id_, bool is_on, float brightness, float red, float green, float blue);
|
||||
|
||||
void clear_queue();
|
||||
bool is_queue_empty() const
|
||||
|
|
@ -73,7 +75,6 @@ namespace esphome
|
|||
|
||||
// Protocol implementation
|
||||
std::vector<uint8_t> generate_command(uint8_t n, uint32_t light_id_, const std::vector<uint8_t> &data, bool forward = true);
|
||||
std::vector<uint8_t> single_control(uint32_t addr, const std::vector<uint8_t> &data);
|
||||
|
||||
std::array<uint8_t, 4> mesh_key_{};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "esphome/core/log.h"
|
||||
#include "fastcon_light.h"
|
||||
#include "fastcon_controller.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace esphome
|
||||
{
|
||||
|
|
@ -28,7 +29,7 @@ namespace esphome
|
|||
light::LightTraits FastconLight::get_traits()
|
||||
{
|
||||
auto traits = light::LightTraits();
|
||||
traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::WHITE, light::ColorMode::BRIGHTNESS});
|
||||
traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::WHITE, light::ColorMode::BRIGHTNESS, light::ColorMode::COLD_WARM_WHITE});
|
||||
traits.set_min_mireds(153);
|
||||
traits.set_max_mireds(500);
|
||||
return traits;
|
||||
|
|
@ -36,40 +37,31 @@ namespace esphome
|
|||
|
||||
void FastconLight::write_state(light::LightState *state)
|
||||
{
|
||||
float red = 0.0f, green = 0.0f, blue = 0.0f;
|
||||
// Get the light data bits from the state
|
||||
auto light_data = this->controller_->get_light_data(state);
|
||||
|
||||
// Get the light state values
|
||||
float brightness = state->current_values.get_brightness() * 127.0f; // Scale to 0-127
|
||||
bool is_on = state->current_values.is_on();
|
||||
auto color_mode = state->current_values.get_color_mode();
|
||||
|
||||
if (!is_on)
|
||||
// Debug output - print the light state values
|
||||
bool is_on = (light_data[0] & 0x80) != 0;
|
||||
float brightness = ((light_data[0] & 0x7F) / 127.0f) * 100.0f;
|
||||
if (light_data.size() == 1)
|
||||
{
|
||||
brightness = 0.0f;
|
||||
ESP_LOGD(TAG, "Writing state: light_id=%d, on=%d, brightness=%.1f%%", light_id_, is_on, brightness);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto r = light_data[2];
|
||||
auto g = light_data[3];
|
||||
auto b = light_data[1];
|
||||
auto warm = light_data[4];
|
||||
auto cold = light_data[5];
|
||||
ESP_LOGD(TAG, "Writing state: light_id=%d, on=%d, brightness=%.1f%%, rgb=(%d,%d,%d), warm=%d, cold=%d", light_id_, is_on, brightness, r, g, b, warm, cold);
|
||||
}
|
||||
|
||||
if (color_mode == light::ColorMode::RGB)
|
||||
{
|
||||
state->current_values_as_rgb(&red, &green, &blue);
|
||||
}
|
||||
|
||||
// Convert to protocol values
|
||||
auto r = static_cast<uint8_t>(red * 255.0f);
|
||||
auto g = static_cast<uint8_t>(green * 255.0f);
|
||||
auto b = static_cast<uint8_t>(blue * 255.0f);
|
||||
|
||||
ESP_LOGD(TAG, "Writing state: light_id=%d, on=%d, brightness=%.1f%%, rgb=(%d,%d,%d)", light_id_, is_on, (brightness / 127.0f) * 100.0f, r, g, b);
|
||||
|
||||
// Get the advertisement data
|
||||
auto adv_data = this->controller_->get_advertisement(this->light_id_, is_on, brightness, red, green, blue);
|
||||
// Generate the advertisement payload
|
||||
auto adv_data = this->controller_->single_control(this->light_id_, light_data);
|
||||
|
||||
// Debug output - print payload as hex
|
||||
char hex_str[adv_data.size() * 2 + 1]; // Each byte needs 2 chars + null terminator
|
||||
for (size_t i = 0; i < adv_data.size(); i++)
|
||||
{
|
||||
sprintf(hex_str + (i * 2), "%02X", adv_data[i]);
|
||||
}
|
||||
hex_str[adv_data.size() * 2] = '\0'; // Ensure null termination
|
||||
auto hex_str = vector_to_hex_string(adv_data).data();
|
||||
ESP_LOGD(TAG, "Advertisement Payload (%d bytes): %s", adv_data.size(), hex_str);
|
||||
|
||||
// Send the advertisement
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
#include <vector>
|
||||
#include <cstdio>
|
||||
#include "esphome/core/log.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
|
@ -109,5 +111,16 @@ namespace esphome
|
|||
ctx.f_0x4 = var0;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<char> vector_to_hex_string(std::vector<uint8_t> &data)
|
||||
{
|
||||
std::vector<char> hex_str(data.size() * 2 + 1); // Allocate the vector with the required size
|
||||
for (size_t i = 0; i < data.size(); i++)
|
||||
{
|
||||
sprintf(hex_str.data() + (i * 2), "%02X", data[i]);
|
||||
}
|
||||
hex_str[data.size() * 2] = '\0'; // Ensure null termination
|
||||
return hex_str;
|
||||
}
|
||||
} // namespace fastcon
|
||||
} // namespace esphome
|
||||
|
|
@ -30,5 +30,7 @@ namespace esphome
|
|||
|
||||
void whitening_init(uint32_t val, WhiteningContext &ctx);
|
||||
void whitening_encode(std::vector<uint8_t> &data, WhiteningContext &ctx);
|
||||
|
||||
std::vector<char> vector_to_hex_string(std::vector<uint8_t> &data);
|
||||
} // namespace fastcon
|
||||
} // namespace esphome
|
||||
Loading…
Add table
Add a link
Reference in a new issue