|
|
@ -127,7 +127,7 @@ void _setCCTInputValue(unsigned char warm, unsigned char cold) { |
|
|
|
_setInputValue(1, constrain(cold, Light::VALUE_MIN, Light::VALUE_MAX)); |
|
|
|
} |
|
|
|
|
|
|
|
void _lightApplyBrightness(unsigned char channels = lightChannels()) { |
|
|
|
void _lightApplyBrightness(size_t channels = lightChannels()) { |
|
|
|
|
|
|
|
double brightness = static_cast<double>(_light_brightness) / static_cast<double>(Light::BRIGHTNESS_MAX); |
|
|
|
|
|
|
@ -218,7 +218,7 @@ String lightDesc(unsigned char id) { |
|
|
|
void _fromLong(unsigned long value, bool brightness) { |
|
|
|
if (brightness) { |
|
|
|
_setRGBInputValue((value >> 24) & 0xFF, (value >> 16) & 0xFF, (value >> 8) & 0xFF); |
|
|
|
_light_brightness = (value & 0xFF) * Light::BRIGHTNESS_MAX / 255; |
|
|
|
lightBrightness((value & 0xFF) * Light::BRIGHTNESS_MAX / 255); |
|
|
|
} else { |
|
|
|
_setRGBInputValue((value >> 16) & 0xFF, (value >> 8) & 0xFF, (value) & 0xFF); |
|
|
|
} |
|
|
@ -246,25 +246,21 @@ void _fromRGB(const char * rgb) { |
|
|
|
default: // assume decimal values separated by commas
|
|
|
|
char * tok; |
|
|
|
unsigned char count = 0; |
|
|
|
unsigned char channels = _light_channel.size(); |
|
|
|
|
|
|
|
tok = strtok(p, ","); |
|
|
|
const auto channels = _light_channel.size(); |
|
|
|
char buf[16] = {0}; |
|
|
|
memmove(buf, rgb, sizeof(buf)); |
|
|
|
char *tok = strtok(buf, ","); |
|
|
|
while (tok != NULL) { |
|
|
|
_setInputValue(count, atoi(tok)); |
|
|
|
if (++count == channels) break; |
|
|
|
tok = strtok(NULL, ","); |
|
|
|
} |
|
|
|
|
|
|
|
// RGB but less than 3 values received, assume it is 0
|
|
|
|
if (_light_has_color && (count < 3)) { |
|
|
|
// check channel 1 and 2:
|
|
|
|
for (int i = 1; i <= 2; i++) { |
|
|
|
if (count < (i+1)) { |
|
|
|
_setInputValue(i, 0); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
break; |
|
|
|
// If less than 3 values received, set the rest to 0
|
|
|
|
if (count < 2) _setInputValue(1, 0); |
|
|
|
if (count < 3) _setInputValue(2, 0); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -332,25 +328,39 @@ void _fromHSV(const char * hsv) { |
|
|
|
|
|
|
|
// Thanks to Sacha Telgenhof for sharing this code in his AiLight library
|
|
|
|
// https://github.com/stelgenhof/AiLight
|
|
|
|
void _fromKelvin(unsigned long kelvin) { |
|
|
|
// Color temperature is measured in mireds (kelvin = 1e6/mired)
|
|
|
|
long _toKelvin(const long mireds) { |
|
|
|
return constrain(static_cast<long>(1000000L / mireds), Light::KELVIN_WARMWHITE, Light::KELVIN_COLDWHITE); |
|
|
|
} |
|
|
|
|
|
|
|
if (!_light_has_color) { |
|
|
|
long _toMireds(const long kelvin) { |
|
|
|
return constrain(static_cast<long>(lround(1000000L / kelvin)), Light::MIREDS_COLDWHITE, Light::MIREDS_WARMWHITE); |
|
|
|
} |
|
|
|
|
|
|
|
if(!_light_use_cct) return; |
|
|
|
void _lightMireds(const long kelvin) { |
|
|
|
_light_mireds = _toMireds(kelvin); |
|
|
|
} |
|
|
|
|
|
|
|
_light_mireds = constrain(static_cast<unsigned int>(lround(1000000UL / kelvin)), Light::MIREDS_COLDWHITE, Light::MIREDS_WARMWHITE); |
|
|
|
void _lightMiredsCCT(const long kelvin) { |
|
|
|
_lightMireds(kelvin); |
|
|
|
|
|
|
|
// This change the range from 153-500 to 0-347 so we get a value between 0 and 1 in the end.
|
|
|
|
double factor = ((double) _light_mireds - (double) Light::MIREDS_COLDWHITE)/((double) Light::MIREDS_WARMWHITE - (double) Light::MIREDS_COLDWHITE); |
|
|
|
unsigned char warm = lround(factor * Light::VALUE_MAX); |
|
|
|
unsigned char cold = lround(((double) 1.0 - factor) * Light::VALUE_MAX); |
|
|
|
// This change the range from 153-500 to 0-347 so we get a value between 0 and 1 in the end.
|
|
|
|
const double factor = ((double) _light_mireds - (double) Light::MIREDS_COLDWHITE)/((double) Light::MIREDS_WARMWHITE - (double) Light::MIREDS_COLDWHITE); |
|
|
|
_setCCTInputValue( |
|
|
|
lround(factor * Light::VALUE_MAX), |
|
|
|
lround(((double) 1.0 - factor) * Light::VALUE_MAX) |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
_setCCTInputValue(warm, cold); |
|
|
|
void _fromKelvin(long kelvin) { |
|
|
|
|
|
|
|
return; |
|
|
|
if (!_light_has_color) { |
|
|
|
if (!_light_use_cct) return; |
|
|
|
_lightMiredsCCT(kelvin); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
_light_mireds = constrain(static_cast<unsigned int>(lround(1000000UL / kelvin)), Light::MIREDS_COLDWHITE, Light::MIREDS_WARMWHITE); |
|
|
|
_lightMireds(kelvin); |
|
|
|
|
|
|
|
if (_light_use_cct) { |
|
|
|
_setRGBInputValue(Light::VALUE_MAX, Light::VALUE_MAX, Light::VALUE_MAX); |
|
|
@ -359,13 +369,13 @@ void _fromKelvin(unsigned long kelvin) { |
|
|
|
|
|
|
|
// Calculate colors
|
|
|
|
kelvin /= 100; |
|
|
|
unsigned int red = (kelvin <= 66) |
|
|
|
const unsigned int red = (kelvin <= 66) |
|
|
|
? Light::VALUE_MAX |
|
|
|
: 329.698727446 * fs_pow((double) (kelvin - 60), -0.1332047592); |
|
|
|
unsigned int green = (kelvin <= 66) |
|
|
|
const unsigned int green = (kelvin <= 66) |
|
|
|
? 99.4708025861 * fs_log(kelvin) - 161.1195681661 |
|
|
|
: 288.1221695283 * fs_pow((double) kelvin, -0.0755148492); |
|
|
|
unsigned int blue = (kelvin >= 66) |
|
|
|
const unsigned int blue = (kelvin >= 66) |
|
|
|
? Light::VALUE_MAX |
|
|
|
: ((kelvin <= 19) |
|
|
|
? 0 |
|
|
@ -375,10 +385,8 @@ void _fromKelvin(unsigned long kelvin) { |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Color temperature is measured in mireds (kelvin = 1e6/mired)
|
|
|
|
void _fromMireds(unsigned long mireds) { |
|
|
|
unsigned long kelvin = constrain(static_cast<unsigned int>(1000000UL / mireds), Light::KELVIN_WARMWHITE, Light::KELVIN_COLDWHITE); |
|
|
|
_fromKelvin(kelvin); |
|
|
|
void _fromMireds(const long mireds) { |
|
|
|
_fromKelvin(_toKelvin(mireds)); |
|
|
|
} |
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
@ -480,6 +488,35 @@ template <typename T, typename Tin, typename Tout> T _lightMap(T x, Tin in_min, |
|
|
|
return (x - in_min) * (out_max - out_min) / divisor + out_min; |
|
|
|
} |
|
|
|
|
|
|
|
int _lightAdjustValue(const int& value, const String& operation) { |
|
|
|
if (!operation.length()) return value; |
|
|
|
|
|
|
|
// if prefixed with a sign, treat expression as numerical operation
|
|
|
|
// otherwise, use as the new value
|
|
|
|
int updated = operation.toInt(); |
|
|
|
if (operation[0] == '+' || operation[0] == '-') { |
|
|
|
updated = value + updated; |
|
|
|
} |
|
|
|
|
|
|
|
return updated; |
|
|
|
} |
|
|
|
|
|
|
|
void _lightAdjustBrightness(const char *payload) { |
|
|
|
lightBrightness(_lightAdjustValue(lightBrightness(), payload)); |
|
|
|
} |
|
|
|
|
|
|
|
void _lightAdjustChannel(unsigned char id, const char *payload) { |
|
|
|
lightChannel(id, _lightAdjustValue(lightChannel(id), payload)); |
|
|
|
} |
|
|
|
|
|
|
|
void _lightAdjustKelvin(const char *payload) { |
|
|
|
_fromKelvin(_lightAdjustValue(_toKelvin(_light_mireds), payload)); |
|
|
|
} |
|
|
|
|
|
|
|
void _lightAdjustMireds(const char *payload) { |
|
|
|
_fromMireds(_lightAdjustValue(_light_mireds, payload)); |
|
|
|
} |
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// PROVIDER
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
@ -657,14 +694,14 @@ void _lightMQTTCallback(unsigned int type, const char * topic, const char * payl |
|
|
|
|
|
|
|
// Color temperature in mireds
|
|
|
|
if (t.equals(MQTT_TOPIC_MIRED)) { |
|
|
|
_fromMireds(atol(payload)); |
|
|
|
_lightAdjustMireds(payload); |
|
|
|
lightUpdate(true, mqttForward()); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// Color temperature in kelvins
|
|
|
|
if (t.equals(MQTT_TOPIC_KELVIN)) { |
|
|
|
_fromKelvin(atol(payload)); |
|
|
|
_lightAdjustKelvin(payload); |
|
|
|
lightUpdate(true, mqttForward()); |
|
|
|
return; |
|
|
|
} |
|
|
@ -683,7 +720,7 @@ void _lightMQTTCallback(unsigned int type, const char * topic, const char * payl |
|
|
|
|
|
|
|
// Brightness
|
|
|
|
if (t.equals(MQTT_TOPIC_BRIGHTNESS)) { |
|
|
|
lightBrightness(atoi(payload)); |
|
|
|
_lightAdjustBrightness(payload); |
|
|
|
lightUpdate(true, mqttForward()); |
|
|
|
return; |
|
|
|
} |
|
|
@ -701,7 +738,7 @@ void _lightMQTTCallback(unsigned int type, const char * topic, const char * payl |
|
|
|
DEBUG_MSG_P(PSTR("[LIGHT] Wrong channelID (%d)\n"), channelID); |
|
|
|
return; |
|
|
|
} |
|
|
|
lightChannel(channelID, atoi(payload)); |
|
|
|
_lightAdjustChannel(channelID, payload); |
|
|
|
lightUpdate(true, mqttForward()); |
|
|
|
return; |
|
|
|
} |
|
|
@ -780,7 +817,7 @@ void lightBroker() { |
|
|
|
// API
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
unsigned char lightChannels() { |
|
|
|
size_t lightChannels() { |
|
|
|
return _light_channel.size(); |
|
|
|
} |
|
|
|
|
|
|
@ -910,33 +947,32 @@ String lightColor() { |
|
|
|
return lightColor(true); |
|
|
|
} |
|
|
|
|
|
|
|
unsigned int lightChannel(unsigned char id) { |
|
|
|
long lightChannel(unsigned char id) { |
|
|
|
if (id <= _light_channel.size()) { |
|
|
|
return _light_channel[id].inputValue; |
|
|
|
} |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
void lightChannel(unsigned char id, unsigned char value) { |
|
|
|
if (id <= _light_channel.size()) { |
|
|
|
_setInputValue(id, constrain(value, Light::VALUE_MIN, Light::VALUE_MAX)); |
|
|
|
} |
|
|
|
void lightChannel(unsigned char id, long value) { |
|
|
|
if (id > _light_channel.size()) return; |
|
|
|
_setInputValue(id, constrain(value, Light::VALUE_MIN, Light::VALUE_MAX)); |
|
|
|
} |
|
|
|
|
|
|
|
void lightChannelStep(unsigned char id, int steps) { |
|
|
|
lightChannel(id, lightChannel(id) + steps * LIGHT_STEP); |
|
|
|
void lightChannelStep(unsigned char id, long steps, long multiplier) { |
|
|
|
lightChannel(id, static_cast<int>(lightChannel(id)) + (steps * multiplier)); |
|
|
|
} |
|
|
|
|
|
|
|
unsigned int lightBrightness() { |
|
|
|
long lightBrightness() { |
|
|
|
return _light_brightness; |
|
|
|
} |
|
|
|
|
|
|
|
void lightBrightness(unsigned int brightness) { |
|
|
|
void lightBrightness(long brightness) { |
|
|
|
_light_brightness = constrain(brightness, Light::BRIGHTNESS_MIN, Light::BRIGHTNESS_MAX); |
|
|
|
} |
|
|
|
|
|
|
|
void lightBrightnessStep(int steps) { |
|
|
|
lightBrightness(_light_brightness + steps * LIGHT_STEP); |
|
|
|
void lightBrightnessStep(long steps, long multiplier) { |
|
|
|
lightBrightness(static_cast<int>(_light_brightness) + (steps * multiplier)); |
|
|
|
} |
|
|
|
|
|
|
|
unsigned int lightTransitionTime() { |
|
|
@ -1032,14 +1068,14 @@ void _lightWebSocketOnAction(uint32_t client_id, const char * action, JsonObject |
|
|
|
|
|
|
|
if (strcmp(action, "channel") == 0) { |
|
|
|
if (data.containsKey("id") && data.containsKey("value")) { |
|
|
|
lightChannel(data["id"], data["value"]); |
|
|
|
lightChannel(data["id"].as<unsigned char>(), data["value"].as<int>()); |
|
|
|
lightUpdate(true, true); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (strcmp(action, "brightness") == 0) { |
|
|
|
if (data.containsKey("value")) { |
|
|
|
lightBrightness(data["value"]); |
|
|
|
lightBrightness(data["value"].as<int>()); |
|
|
|
lightUpdate(true, true); |
|
|
|
} |
|
|
|
} |
|
|
@ -1081,7 +1117,7 @@ void _lightAPISetup() { |
|
|
|
apiRegister(MQTT_TOPIC_KELVIN, |
|
|
|
[](char * buffer, size_t len) {}, |
|
|
|
[](const char * payload) { |
|
|
|
_fromKelvin(atol(payload)); |
|
|
|
_lightAdjustKelvin(payload); |
|
|
|
lightUpdate(true, true); |
|
|
|
} |
|
|
|
); |
|
|
@ -1089,7 +1125,7 @@ void _lightAPISetup() { |
|
|
|
apiRegister(MQTT_TOPIC_MIRED, |
|
|
|
[](char * buffer, size_t len) {}, |
|
|
|
[](const char * payload) { |
|
|
|
_fromMireds(atol(payload)); |
|
|
|
_lightAdjustMireds(payload); |
|
|
|
lightUpdate(true, true); |
|
|
|
} |
|
|
|
); |
|
|
@ -1105,7 +1141,7 @@ void _lightAPISetup() { |
|
|
|
snprintf_P(buffer, len, PSTR("%d"), _light_channel[id].target); |
|
|
|
}, |
|
|
|
[id](const char * payload) { |
|
|
|
lightChannel(id, atoi(payload)); |
|
|
|
_lightAdjustChannel(id, payload); |
|
|
|
lightUpdate(true, true); |
|
|
|
} |
|
|
|
); |
|
|
@ -1126,7 +1162,7 @@ void _lightAPISetup() { |
|
|
|
snprintf_P(buffer, len, PSTR("%d"), _light_brightness); |
|
|
|
}, |
|
|
|
[](const char * payload) { |
|
|
|
lightBrightness(atoi(payload)); |
|
|
|
_lightAdjustBrightness(payload); |
|
|
|
lightUpdate(true, true); |
|
|
|
} |
|
|
|
); |
|
|
@ -1141,38 +1177,37 @@ void _lightInitCommands() { |
|
|
|
|
|
|
|
terminalRegisterCommand(F("BRIGHTNESS"), [](Embedis* e) { |
|
|
|
if (e->argc > 1) { |
|
|
|
const String value(e->argv[1]); |
|
|
|
if( value.length() > 0 ) { |
|
|
|
if( value[0] == '+' || value[0] == '-' ) { |
|
|
|
lightBrightness(lightBrightness()+String(e->argv[1]).toInt()); |
|
|
|
} else { |
|
|
|
lightBrightness(String(e->argv[1]).toInt()); |
|
|
|
} |
|
|
|
lightUpdate(true, true); |
|
|
|
} |
|
|
|
_lightAdjustBrightness(e->argv[1]); |
|
|
|
lightUpdate(true, true); |
|
|
|
} |
|
|
|
DEBUG_MSG_P(PSTR("Brightness: %d\n"), lightBrightness()); |
|
|
|
DEBUG_MSG_P(PSTR("Brightness: %u\n"), lightBrightness()); |
|
|
|
terminalOK(); |
|
|
|
}); |
|
|
|
|
|
|
|
terminalRegisterCommand(F("CHANNEL"), [](Embedis* e) { |
|
|
|
if (e->argc < 2) { |
|
|
|
terminalError(F("Wrong arguments")); |
|
|
|
terminalError(F("CHANNEL <ID> [<VALUE>]")); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const int id = String(e->argv[1]).toInt(); |
|
|
|
if (id < 0 || id >= lightChannels()) { |
|
|
|
terminalError(F("Channel value out of range")); |
|
|
|
return; |
|
|
|
} |
|
|
|
int id = String(e->argv[1]).toInt(); |
|
|
|
|
|
|
|
if (e->argc > 2) { |
|
|
|
int value = String(e->argv[2]).toInt(); |
|
|
|
lightChannel(id, value); |
|
|
|
_lightAdjustChannel(id, e->argv[2]); |
|
|
|
lightUpdate(true, true); |
|
|
|
} |
|
|
|
DEBUG_MSG_P(PSTR("Channel #%d (%s): %d\n"), id, lightDesc(id).c_str(), lightChannel(id)); |
|
|
|
|
|
|
|
DEBUG_MSG_P(PSTR("Channel #%u (%s): %d\n"), id, lightDesc(id).c_str(), lightChannel(id)); |
|
|
|
terminalOK(); |
|
|
|
}); |
|
|
|
|
|
|
|
terminalRegisterCommand(F("COLOR"), [](Embedis* e) { |
|
|
|
if (e->argc > 1) { |
|
|
|
String color = String(e->argv[1]); |
|
|
|
lightColor(color.c_str()); |
|
|
|
lightColor(e->argv[1]); |
|
|
|
lightUpdate(true, true); |
|
|
|
} |
|
|
|
DEBUG_MSG_P(PSTR("Color: %s\n"), lightColor().c_str()); |
|
|
@ -1181,8 +1216,7 @@ void _lightInitCommands() { |
|
|
|
|
|
|
|
terminalRegisterCommand(F("KELVIN"), [](Embedis* e) { |
|
|
|
if (e->argc > 1) { |
|
|
|
String color = String("K") + String(e->argv[1]); |
|
|
|
lightColor(color.c_str()); |
|
|
|
_lightAdjustKelvin(e->argv[1]); |
|
|
|
lightUpdate(true, true); |
|
|
|
} |
|
|
|
DEBUG_MSG_P(PSTR("Color: %s\n"), lightColor().c_str()); |
|
|
@ -1191,17 +1225,8 @@ void _lightInitCommands() { |
|
|
|
|
|
|
|
terminalRegisterCommand(F("MIRED"), [](Embedis* e) { |
|
|
|
if (e->argc > 1) { |
|
|
|
const String value(e->argv[1]); |
|
|
|
String color = String("M"); |
|
|
|
if( value.length() > 0 ) { |
|
|
|
if( value[0] == '+' || value[0] == '-' ) { |
|
|
|
color += String(_light_mireds + String(e->argv[1]).toInt()); |
|
|
|
} else { |
|
|
|
color += String(e->argv[1]); |
|
|
|
} |
|
|
|
lightColor(color.c_str()); |
|
|
|
lightUpdate(true, true); |
|
|
|
} |
|
|
|
_lightAdjustMireds(e->argv[1]); |
|
|
|
lightUpdate(true, true); |
|
|
|
} |
|
|
|
DEBUG_MSG_P(PSTR("Color: %s\n"), lightColor().c_str()); |
|
|
|
terminalOK(); |
|
|
|