wip with light and mqtt working
This commit is contained in:
186
lib/light/light.cpp
Normal file
186
lib/light/light.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
#include <ArduinoJson.h>
|
||||
#include "light.h"
|
||||
#include "mqtt.h"
|
||||
|
||||
constexpr uint16_t CONFIG_MSG_SIZE = 1024;
|
||||
constexpr uint16_t STATUS_MSG_SIZE = 128;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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);
|
||||
gSetpoint = static_cast<uint32_t>(g * brightnessFactor);
|
||||
bSetpoint = static_cast<uint32_t>(b * brightnessFactor);
|
||||
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);
|
||||
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
|
||||
}
|
||||
if (lightType == LightType::rgb)
|
||||
{
|
||||
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);
|
||||
}
|
||||
94
lib/light/light.h
Normal file
94
lib/light/light.h
Normal file
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "pin.h"
|
||||
#include "mqtt.h"
|
||||
|
||||
struct LightInfo {
|
||||
std::string uniqueId;
|
||||
const std::string name = "Smart RGB Light";
|
||||
const std::string discoveryTopic = "homeassistant/light/smart_rgb_light/light/config";
|
||||
const std::string baseTopic = "studiotj/smart-rgb/light";
|
||||
const std::string availabilityTopic = "studiotj/smart-rgb/light/status";
|
||||
const std::string stateTopic = "studiotj/smart-rgb/light/state";
|
||||
const std::string stateValueTemplate = "{{ value_json.state }}";
|
||||
const std::string commandTopic = "studiotj/smart-rgb/light/state/set";
|
||||
const std::string brightnessCommandTopic = "studiotj/smart-rgb/light/brightness/set";
|
||||
const std::string brightnessValueTemplate = "{{ value_json.brightness }}";
|
||||
const std::string colorTempCommandTopic = "studiotj/smart-rgb/light/color_temp/set";
|
||||
const std::string colorTempKelvinTopic = "studiotj/smart-rgb/light/color_temp_kelvin/set";
|
||||
const std::string colorTempStateTopic = "studiotj/smart-rgb/light/color_temp/state";
|
||||
const std::string colorTempValueTemplate = "{{ value_json.color_temp }}";
|
||||
const std::string hsCommandTopic = "studiotj/smart-rgb/light/hs/set";
|
||||
const std::string hsCommandTemplate = "{{ value_json.hs_cmd }}";
|
||||
const std::string hsStateTopic = "studiotj/smart-rgb/light/hs/state";
|
||||
const std::string hsValueTemplate = " {{ value_json.hs_value }}";
|
||||
const std::string rgbCommandTopic = "studiotj/smart-rgb/light/rgb/set";
|
||||
const std::string rgbCommandTemplate = "{{ {'rgb': [red, green, blue]} | to_json }}";
|
||||
const std::string rgbStateTopic = "studiotj/smart-rgb/light/rgb/state";
|
||||
const std::string rgbValueTemplate = "{{ value_json.rgb | join(',') }}";
|
||||
const std::string rgbwCommandTopic = "studiotj/smart-rgb/light/rgbw/set";
|
||||
const std::string rgbwCommandTemplate = "{{ value_json.rgbw_cmd }}";
|
||||
const std::string rgbwStateTopic = "studiotj/smart-rgb/light/rgbw/state";
|
||||
const std::string rgbwValueTemplate = "{{ value_json.rgbw_value }}";
|
||||
const std::string rgbwwCommandTopic = "studiotj/smart-rgb/light/rgbww/set";
|
||||
const std::string rgbwwCommandTemplate = "{{ value_json.rgbww_cmd }}";
|
||||
const std::string rgbwwStateTopic = "studiotj/smart-rgb/light/rgbww/state";
|
||||
const std::string rgbwwValueTemplate = "{{ value_json.rgbww_value }}";
|
||||
const std::string supportedColorModesTopic = "studiotj/smart-rgb/light/supported_color_modes";
|
||||
const std::string supportedColorModesValue = "['rgb', 'brightness']";
|
||||
const std::string availabilityTemplate = "{{ value_json.availability }}";
|
||||
};
|
||||
|
||||
struct DeviceInfo {
|
||||
std::string name = "Smart RGB Light";
|
||||
std::string model = "smart_rgb_light";
|
||||
std::string identifier = "smart_rgb_light_";
|
||||
std::string swVersion = "1.0"; // TODO: version will be generated.
|
||||
std::string manufacturer = "Studio TJ";
|
||||
};
|
||||
|
||||
enum LightType {
|
||||
onOff,
|
||||
brightness,
|
||||
colorTemperature,
|
||||
rgb,
|
||||
rgbw,
|
||||
rgbww,
|
||||
};
|
||||
|
||||
class Light {
|
||||
public:
|
||||
Light(Pin* pinR, Pin* pinG, Pin* pinB, Mqtt* mqttClient, std::string uniqueId);
|
||||
Light(Pin* pinR, Pin* pinG, Pin* pinB, Pin* pinCW, Mqtt* mqttClient, std::string uniqueId);
|
||||
Light(Pin* pinR, Pin* pinG, Pin* pinB, Pin* pinCW, Pin* pinWW, Mqtt* mqttClient, std::string uniqueId);
|
||||
void subscribeToMqttTopics();
|
||||
void publishInitialState();
|
||||
void publishCurrentState();
|
||||
void setHsl(uint8_t h, uint8_t s, uint8_t l);
|
||||
void setColorTemperature(uint16_t temperature);
|
||||
void setBrightness(uint8_t brightness);
|
||||
void turnOn();
|
||||
void turnOff();
|
||||
|
||||
private:
|
||||
void handleCommand(const std::string& command);
|
||||
void operatePin();
|
||||
uint8_t r = 255; // Default to white
|
||||
uint8_t g = 255; // Default to white
|
||||
uint8_t b = 255; // Default to white
|
||||
uint8_t cw = 255; // Default to white
|
||||
uint8_t ww = 255; // Default to white
|
||||
uint16_t colorTemperature;
|
||||
uint8_t brightness;
|
||||
bool isOn = false;
|
||||
Pin* pinR;
|
||||
Pin* pinG;
|
||||
Pin* pinB;
|
||||
Pin* pinCW;
|
||||
Pin* pinWW;
|
||||
Mqtt* mqttClient;
|
||||
LightInfo lightInfo;
|
||||
DeviceInfo deviceInfo;
|
||||
LightType lightType = onOff; // Default light type
|
||||
};
|
||||
0
lib/light/lightinfo.h
Normal file
0
lib/light/lightinfo.h
Normal file
Reference in New Issue
Block a user