Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RGB color support #260

Merged
merged 4 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 93 additions & 12 deletions src/fauxmoESP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,59 @@ bool fauxmoESP::_onTCPList(AsyncClient *client, String url, String body) {

}

bool fauxmoESP::_onTCPControl(AsyncClient *client, String url, String body) {
byte* fauxmoESP::_hs2rgb(uint16_t hue, uint8_t sat) {
byte *rgb = new byte[3]{0, 0, 0};

float h = ((float)hue)/65535.0;
float s = ((float)sat)/255.0;

byte i = floor(h*6);
float f = h * 6-i;
float p = 255 * (1-s);
float q = 255 * (1-f*s);
float t = 255 * (1-(1-f)*s);
switch (i%6) {
case 0: rgb[0]=255,rgb[1]=t,rgb[2]=p;break;
case 1: rgb[0]=q,rgb[1]=255,rgb[2]=p;break;
case 2: rgb[0]=p,rgb[1]=255,rgb[2]=t;break;
case 3: rgb[0]=p,rgb[1]=q,rgb[2]=255;break;
case 4: rgb[0]=t,rgb[1]=p,rgb[2]=255;break;
case 5: rgb[0]=255,rgb[1]=p,rgb[2]=q;
}
return rgb;
}

byte* fauxmoESP::_ct2rgb(uint16_t ct) {
byte *rgb = new byte[3]{0, 0, 0};
float temp = 10000/ ct; //kelvins = 1,000,000/mired (and that /100)
float r, g, b;

if (temp <= 66) {
r = 255;
g = temp;
g = 99.470802 * log(g) - 161.119568;
if (temp <= 19) {
b = 0;
} else {
b = temp-10;
b = 138.517731 * log(b) - 305.044793;
}
} else {
r = temp - 60;
r = 329.698727 * pow(r, -0.13320476);
g = temp - 60;
g = 288.12217 * pow(g, -0.07551485 );
b = 255;
}

rgb[0] = (byte)constrain(r,0.1,255.1);
rgb[1] = (byte)constrain(g,0.1,255.1);
rgb[2] = (byte)constrain(b,0.1,255.1);

return rgb;
}

bool fauxmoESP::_onTCPControl(AsyncClient *client, String url, String body) {
// "devicetype" request
if (body.indexOf("devicetype") > 0) {
DEBUG_MSG_FAUXMO("[FAUXMO] Handling devicetype request\n");
Expand All @@ -258,11 +309,27 @@ bool fauxmoESP::_onTCPControl(AsyncClient *client, String url, String body) {
--id;

// Brightness
pos = body.indexOf("bri");
if (pos > 0) {
if ((pos = body.indexOf("bri")) > 0) {
unsigned char value = body.substring(pos+5).toInt();
_devices[id].value = value;
_devices[id].state = (value > 0);
} else if ((pos = body.indexOf("hue")) > 0) {
_devices[id].state = true;
unsigned int pos_comma = body.indexOf(",", pos);
uint16_t hue = body.substring(pos+5, pos_comma).toInt();
pos = body.indexOf("sat", pos_comma);
uint8_t sat = body.substring(pos+5).toInt();
byte* rgb = _hs2rgb(hue, sat);
_devices[id].rgb[0] = rgb[0];
_devices[id].rgb[1] = rgb[1];
_devices[id].rgb[2] = rgb[2];
} else if ((pos = body.indexOf("ct")) > 0) {
_devices[id].state = true;
uint16_t ct = body.substring(pos+4).toInt();
byte* rgb = _ct2rgb(ct);
_devices[id].rgb[0] = rgb[0];
_devices[id].rgb[1] = rgb[1];
_devices[id].rgb[2] = rgb[2];
} else if (body.indexOf("false") > 0) {
_devices[id].state = false;
} else {
Expand All @@ -274,12 +341,15 @@ bool fauxmoESP::_onTCPControl(AsyncClient *client, String url, String body) {
snprintf_P(
response, sizeof(response),
FAUXMO_TCP_STATE_RESPONSE,
id+1, _devices[id].state ? "true" : "false", id+1, _devices[id].value
id+1, _devices[id].state ? "true" : "false"
);
_sendTCPResponse(client, "200 OK", response, "text/xml");

if (_setCallback) {
_setCallback(id, _devices[id].name, _devices[id].state, _devices[id].value);
if (_setStateCallback) {
_setStateCallback(id, _devices[id].name, _devices[id].state, _devices[id].value);
}
if (_setStateWithColorCallback) {
_setStateWithColorCallback(id, _devices[id].name, _devices[id].state, _devices[id].value, _devices[id].rgb);
}

return true;
Expand All @@ -293,7 +363,6 @@ bool fauxmoESP::_onTCPControl(AsyncClient *client, String url, String body) {
}

bool fauxmoESP::_onTCPRequest(AsyncClient *client, bool isGet, String url, String body) {

if (!_enabled) return false;

#if DEBUG_FAUXMO_VERBOSE_TCP
Expand Down Expand Up @@ -456,6 +525,7 @@ unsigned char fauxmoESP::addDevice(const char * device_name) {

snprintf(device.uniqueid, FAUXMO_DEVICE_UNIQUE_ID_LENGTH, "%s:%s-%02X", mac.c_str(), "00:00", device_id);


// Attach
_devices.push_back(device);

Expand Down Expand Up @@ -523,11 +593,22 @@ bool fauxmoESP::setState(unsigned char id, bool state, unsigned char value) {
}

bool fauxmoESP::setState(const char * device_name, bool state, unsigned char value) {
int id = getDeviceId(device_name);
if (id < 0) return false;
_devices[id].state = state;
_devices[id].value = value;
return true;
return setState(getDeviceId(device_name), state, value);
}

bool fauxmoESP::setState(unsigned char id, bool state, unsigned char value, byte* rgb){
if (id >= _devices.size()) return false;
bool success = setState(id, state, value);
if (success) {
_devices[id].rgb[0] = rgb[0];
_devices[id].rgb[1] = rgb[1];
_devices[id].rgb[2] = rgb[2];
}
return success;
}

bool fauxmoESP::setState(const char * device_name, bool state, unsigned char value, byte* rgb){
return setState(getDeviceId(device_name), state, value, rgb);
}

// -----------------------------------------------------------------------------
Expand Down
12 changes: 10 additions & 2 deletions src/fauxmoESP.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,13 @@ THE SOFTWARE.
#include "templates.h"

typedef std::function<void(unsigned char, const char *, bool, unsigned char)> TSetStateCallback;
typedef std::function<void(unsigned char, const char *, bool, unsigned char, byte *)> TSetStateWithColorCallback;

typedef struct {
char * name;
bool state;
unsigned char value;
byte rgb[3] = {255, 255, 255};
char uniqueid[FAUXMO_DEVICE_UNIQUE_ID_LENGTH];
} fauxmoesp_device_t;

Expand All @@ -97,9 +99,12 @@ class fauxmoESP {
char * getDeviceName(unsigned char id, char * buffer, size_t len);
int getDeviceId(const char * device_name);
void setDeviceUniqueId(unsigned char id, const char *uniqueid);
void onSetState(TSetStateCallback fn) { _setCallback = fn; }
void onSetState(TSetStateCallback fn) { _setStateCallback = fn; }
void onSetState(TSetStateWithColorCallback fn) { _setStateWithColorCallback = fn; }
bool setState(unsigned char id, bool state, unsigned char value);
bool setState(const char * device_name, bool state, unsigned char value);
bool setState(unsigned char id, bool state, unsigned char value, byte* rgb);
bool setState(const char * device_name, bool state, unsigned char value, byte* rgb);
bool process(AsyncClient *client, bool isGet, String url, String body);
void enable(bool enable);
void createServer(bool internal) { _internal = internal; }
Expand All @@ -118,7 +123,8 @@ class fauxmoESP {
#endif
WiFiUDP _udp;
AsyncClient * _tcpClients[FAUXMO_TCP_MAX_CLIENTS];
TSetStateCallback _setCallback = NULL;
TSetStateCallback _setStateCallback = NULL;
TSetStateWithColorCallback _setStateWithColorCallback = NULL;

String _deviceJson(unsigned char id, bool all); // all = true means we are listing all devices so use full description template

Expand All @@ -136,4 +142,6 @@ class fauxmoESP {

String _byte2hex(uint8_t zahl);
String _makeMD5(String text);
byte* _hs2rgb(uint16_t hue, uint8_t sat);
byte* _ct2rgb(uint16_t ct);
};
3 changes: 1 addition & 2 deletions src/templates.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ PROGMEM const char FAUXMO_TCP_HEADERS[] =
"Connection: close\r\n\r\n";

PROGMEM const char FAUXMO_TCP_STATE_RESPONSE[] = "["
"{\"success\":{\"/lights/%d/state/on\":%s}},"
"{\"success\":{\"/lights/%d/state/bri\":%d}}" // not needed?
"{\"success\":{\"/lights/%d/state/on\":%s}}"
"]";

// Working with gen1 and gen3, ON/OFF/%, gen3 requires TCP port 80
Expand Down