Files
smart-rgb-esp32/lib/light/light.cpp

227 lines
8.7 KiB
C++
Raw Normal View History

2025-08-03 00:34:05 +02:00
#include <ArduinoJson.h>
#include "light.h"
#include "mqtt.h"
2025-08-27 10:40:39 +02:00
constexpr uint16_t configMsgSize = 1024;
constexpr uint16_t statusMsgSize = 128;
constexpr uint8_t minPwmValue = 0;
constexpr float gammaCorrection = 2.2f;
constexpr uint8_t maxPwm = 255;
2025-08-03 00:34:05 +02:00
const struct {
String available = "online";
String notAvailable = "offline";
} Availability;
Light::Light(Pin* pinR, Pin* pinG, Pin* pinB, Mqtt* mqttClient, std::string uniqueId)
: pinR(pinR), pinG(pinG), pinB(pinB), pinCW(nullptr), pinWW(nullptr), mqttClient(mqttClient) {
lightInfo.uniqueId = uniqueId;
lightType = LightType::rgb;
publishInitialState();
subscribeToMqttTopics();
}
2025-08-27 10:40:39 +02:00
Light::Light(Pin* pinR, Pin* pinG, Pin* pinB, Pin* pinCW, Pin* pinWW, Mqtt* mqttClient, std::string uniqueId)
: pinR(pinR), pinG(pinG), pinB(pinB), pinCW(pinCW), pinWW(pinWW), mqttClient(mqttClient) {
lightInfo.uniqueId = uniqueId;
lightType = LightType::rgbww;
publishInitialState();
subscribeToMqttTopics();
}
2025-08-03 00:34:05 +02:00
void Light::publishInitialState() {
// Publish the initial state of the light
JsonDocument configInfo;
JsonObject deviceInfo = configInfo["device"].to<JsonObject>();
deviceInfo["name"] = this->deviceInfo.name;
deviceInfo["model"] = this->deviceInfo.model;
JsonArray identifiers = deviceInfo["identifiers"].to<JsonArray>();
identifiers.add(this->deviceInfo.identifier + this->lightInfo.uniqueId);
deviceInfo["sw_version"] = this->deviceInfo.swVersion;
deviceInfo["manufacturer"] = this->deviceInfo.manufacturer;
configInfo["unique_id"] = this->lightInfo.uniqueId;
configInfo["name"] = this->lightInfo.name;
configInfo["schema"] = "json";
// configInfo["json_attributes_topic"] = this->lightInfo.statusTopic;
configInfo["command_topic"] = this->lightInfo.commandTopic;
JsonArray availabilityInfo = configInfo["availability"].to<JsonArray>();
JsonObject availabilityItem = availabilityInfo.add<JsonObject>();
availabilityItem["topic"] = this->lightInfo.availabilityTopic;
availabilityItem["value_template"] = this->lightInfo.availabilityTemplate;
JsonArray supportedColorModes = configInfo["supported_color_modes"].to<JsonArray>();
if (lightType == LightType::rgb) {
supportedColorModes.add("rgb");
} else if (lightType == LightType::rgbw) {
supportedColorModes.add("rgbw");
} else if (lightType == LightType::rgbww) {
supportedColorModes.add("rgbww");
supportedColorModes.add("color_temp");
} else if (lightType == LightType::colorTemperature) {
supportedColorModes.add("color_temp");
} else if (lightType == LightType::brightness) {
supportedColorModes.add("brightness");
} else {
supportedColorModes.add("onoff");
}
configInfo["state_topic"] = this->lightInfo.stateTopic;
// configInfo["state_value_template"] = this->lightInfo.stateValueTemplate;
std::string configJson;
serializeJson(configInfo, configJson);
mqttClient->publish(lightInfo.discoveryTopic, configJson);
std::string stateJson;
JsonDocument stateInfo;
stateInfo["state"] = "OFF"; // Initial state is OFF
// stateInfo["availability"] = Availability.available; // Initial availability
stateInfo["brightness"] = 0; // Initial brightness
JsonObject rgbValue = stateInfo["rgb_value"].to<JsonObject>();
rgbValue["r"] = 255;
rgbValue["g"] = 255;
rgbValue["b"] = 255;
serializeJson(stateInfo, stateJson);
std::string availabilityJson;
JsonDocument availabilityInfoDoc;
availabilityInfoDoc["availability"] = Availability.available; // Initial availability
serializeJson(availabilityInfoDoc, availabilityJson);
mqttClient->publish(lightInfo.stateTopic, stateJson);
mqttClient->publish(lightInfo.availabilityTopic, availabilityJson);
}
void Light::operatePin() {
uint32_t rSetpoint = r;
uint32_t gSetpoint = g;
uint32_t bSetpoint = b;
if (!isOn) {
turnOff();
return;
}
float brightnessFactor = brightness / 255.0f;
rSetpoint = static_cast<uint32_t>(r * brightnessFactor);
2025-08-27 10:40:39 +02:00
rSetpoint = correctGamma(rSetpoint);
// if (rSetpoint < minPwmValue && rSetpoint > 0) {
// rSetpoint = minPwmValue;
// }
2025-08-03 00:34:05 +02:00
gSetpoint = static_cast<uint32_t>(g * brightnessFactor);
2025-08-27 10:40:39 +02:00
gSetpoint = correctGamma(gSetpoint);
// if (gSetpoint < minPwmValue && gSetpoint > 0) {
// gSetpoint = minPwmValue;
// }
2025-08-03 00:34:05 +02:00
bSetpoint = static_cast<uint32_t>(b * brightnessFactor);
2025-08-27 10:40:39 +02:00
bSetpoint = correctGamma(bSetpoint);
// if (bSetpoint < minPwmValue && bSetpoint > 0) {
// bSetpoint = minPwmValue;
// }
2025-08-03 00:34:05 +02:00
Serial.printf("Setting RGB: R=%d, G=%d, B=%d with brightness factor: %.2f\n", rSetpoint, gSetpoint, bSetpoint, brightnessFactor);
pinR->setLedLevel(rSetpoint);
pinG->setLedLevel(gSetpoint);
pinB->setLedLevel(bSetpoint);
2025-08-27 10:40:39 +02:00
if (pinCW != nullptr) {
pinCW->setLedLevel(cw);
}
if (pinWW != nullptr) {
pinWW->setLedLevel(ww);
}
2025-08-03 00:34:05 +02:00
Serial.printf("Set RGB: R=%d, G=%d, B=%d\n", r, g, b);
}
void Light::subscribeToMqttTopics() {
mqttClient->subscribe(lightInfo.commandTopic, [this](uint8_t* payload, int length) {
std::string command(reinterpret_cast<char*>(payload), length);
handleCommand(command);
});
}
void Light::handleCommand(const std::string& command) {
Serial.println("Received command: " + String(command.c_str()));
JsonDocument commandJson;
deserializeJson(commandJson, command);
if (commandJson.isNull()) {
Serial.println("Invalid command JSON");
return;
}
if (commandJson["state"].is<String>()) {
std::string state = commandJson["state"].as<std::string>();
if (state == "ON") {
isOn = true;
} else if (state == "OFF") {
isOn = false;
}
}
if (commandJson["brightness"].is<int>()) {
brightness = commandJson["brightness"].as<int>();
}
if (commandJson["color"].is<JsonObject>()) {
JsonObject color = commandJson["color"];
r = color["r"] | 255; // Default to 255 if not provided
g = color["g"] | 255; // Default to 255 if not provided
b = color["b"] | 255; // Default to 255 if not provided
2025-08-27 10:40:39 +02:00
if (lightType == LightType::rgbw || lightType == LightType::rgbww) {
ww = color["w"] | 255; // Default to 255 if not provided
}
if (lightType == LightType::rgbww) {
cw = color["cw"] | 255; // Default to 255 if not provided
}
2025-08-03 00:34:05 +02:00
}
2025-08-27 10:40:39 +02:00
if (lightType == LightType::rgb || lightType == LightType::rgbw || lightType == LightType::rgbww)
2025-08-03 00:34:05 +02:00
{
operatePin();
}
publishCurrentState();
}
void Light::turnOn() {
isOn = true;
if (pinR != nullptr) pinR->setLedLevel(r);
if (pinG != nullptr) pinG->setLedLevel(g);
if (pinB != nullptr) pinB->setLedLevel(b);
if (pinCW != nullptr) pinCW->setLedLevel(cw);
if (pinWW != nullptr) pinWW->setLedLevel(ww);
}
void Light::turnOff() {
isOn = false;
if (pinR != nullptr) pinR->setLedLevel(0);
if (pinG != nullptr) pinG->setLedLevel(0);
if (pinB != nullptr) pinB->setLedLevel(0);
if (pinCW != nullptr) pinCW->setLedLevel(0);
if (pinWW != nullptr) pinWW->setLedLevel(0);
}
void Light::publishCurrentState() {
// Publish the current state of the light
JsonDocument stateInfo;
stateInfo["state"] = isOn ? "ON" : "OFF";
stateInfo["availability"] = Availability.available; // Current availability
stateInfo["brightness"] = brightness;
if (lightType == LightType::rgb || lightType == LightType::rgbw || lightType == LightType::rgbww) {
JsonObject rgbValue = stateInfo["color"].to<JsonObject>();
rgbValue["r"] = r;
rgbValue["g"] = g;
rgbValue["b"] = b;
if (lightType == LightType::rgb) {
stateInfo["color_mode"] = "rgb";
}
if (lightType == LightType::rgbw) {
stateInfo["color_mode"] = "rgbw";
rgbValue["w"] = ww;
} else if (lightType == LightType::rgbww) {
stateInfo["color_mode"] = "rgbww";
rgbValue["cw"] = cw;
rgbValue["ww"] = ww;
}
}
std::string stateJson;
serializeJson(stateInfo, stateJson);
Serial.println("Publishing current state: " + String(stateJson.c_str()));
mqttClient->publish(lightInfo.stateTopic, stateJson);
2025-08-27 10:40:39 +02:00
}
uint32_t Light::correctGamma(uint32_t originalPwm) {
// Apply gamma correction to the PWM value
float pwmPercentage = originalPwm / 255.0f;
return static_cast<uint32_t>(pow(pwmPercentage, 1 / gammaCorrection) * 255);
2025-08-03 00:34:05 +02:00
}