mirror of https://github.com/arendst/Tasmota.git
Add SetOption68 to enable multi-channel PWM instead of a single light (#6134)
This commit is contained in:
parent
d6e475e73a
commit
81ca44dba2
|
@ -9,6 +9,7 @@
|
|||
* Add define USE_ENERGY_POWER_LIMIT to disable Energy Power Limit detection while Energy Margin detection is active
|
||||
* Add allow repeat/longpress for IRSend raw, introduced IRSend<r> option (#6074)
|
||||
* Change Store AWS IoT Private Key and Certificate in SPI Flash avoiding device-specific compilations
|
||||
* Add SetOption68 to enable multi-channel PWM instead of a single light (#6134)
|
||||
*
|
||||
* 6.6.0.2 20190714
|
||||
* Change commands Var and Mem to show all parameters when no index is given (#6107)
|
||||
|
|
|
@ -81,7 +81,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu
|
|||
uint32_t tuya_show_dimmer : 1; // bit 15 (v6.5.0.15) - SetOption65 - Enable or Disable Dimmer slider control
|
||||
uint32_t tuya_dimmer_range_255 : 1; // bit 16 (v6.6.0.1) - SetOption66 - Enable or Disable Dimmer range 255 slider control
|
||||
uint32_t buzzer_enable : 1; // bit 17 (v6.6.0.1) - SetOption67 - Enable buzzer when available
|
||||
uint32_t spare18 : 1;
|
||||
uint32_t pmw_multi_channels : 1; // bit 18 (v6.6.0.3) - SetOption68 - Enable multi-channels PWM insteas of Color PWM
|
||||
uint32_t spare19 : 1;
|
||||
uint32_t spare20 : 1;
|
||||
uint32_t spare21 : 1;
|
||||
|
|
|
@ -59,6 +59,7 @@ const uint8_t MAX_COUNTERS = 4; // Max number of counter sensors
|
|||
const uint8_t MAX_TIMERS = 16; // Max number of Timers
|
||||
const uint8_t MAX_PULSETIMERS = 8; // Max number of supported pulse timers
|
||||
const uint8_t MAX_FRIENDLYNAMES = 4; // Max number of Friendly names
|
||||
const uint8_t MAX_HUE_DEVICES = 15; // Max number of Philips Hue device per emulation
|
||||
const uint8_t MAX_DOMOTICZ_IDX = 4; // Max number of Domoticz device, key and switch indices
|
||||
const uint8_t MAX_DOMOTICZ_SNS_IDX = 12; // Max number of Domoticz sensors indices
|
||||
const uint8_t MAX_KNX_GA = 10; // Max number of KNX Group Addresses to read that can be set
|
||||
|
@ -264,7 +265,7 @@ const uint8_t kDefaultRfCode[9] PROGMEM = { 0x21, 0x16, 0x01, 0x0E, 0x03, 0x48,
|
|||
\*********************************************************************************************/
|
||||
|
||||
extern uint8_t light_device; // Light device number
|
||||
extern uint8_t light_power; // Light power
|
||||
extern power_t light_power; // Light power
|
||||
extern uint8_t rotary_changed; // Rotary switch changed
|
||||
|
||||
#endif // _SONOFF_H_
|
||||
|
|
|
@ -1451,6 +1451,13 @@ void GpioInit(void)
|
|||
light_type |= LT_SM16716;
|
||||
}
|
||||
#endif // USE_SM16716
|
||||
|
||||
// post-process for lights
|
||||
if (Settings.flag3.pmw_multi_channels) {
|
||||
uint32_t pwm_channels = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7);
|
||||
if (0 == pwm_channels) pwm_channels = 1;
|
||||
devices_present += pwm_channels - 1; // add the pwm channels controls at the end
|
||||
}
|
||||
#endif // USE_LIGHT
|
||||
if (!light_type) {
|
||||
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
|
||||
|
|
|
@ -585,6 +585,9 @@ void CmndSetoption(void)
|
|||
if (10 == pindex) { // SetOption60 enable or disable traditional sleep
|
||||
WiFiSetSleepMode(); // Update WiFi sleep mode accordingly
|
||||
}
|
||||
if (18 == pindex) { // SetOption68 for multi-channel PWM, requires a reboot
|
||||
restart_flag = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // SetOption32 .. 49
|
||||
|
|
|
@ -46,6 +46,8 @@ uint8_t *efm8bb1_update = nullptr;
|
|||
|
||||
enum UploadTypes { UPL_TASMOTA, UPL_SETTINGS, UPL_EFM8BB1 };
|
||||
|
||||
static const char * HEADER_KEYS[] = { "User-Agent", };
|
||||
|
||||
const char HTTP_HEAD[] PROGMEM =
|
||||
"<!DOCTYPE html><html lang=\"" D_HTML_LANGUAGE "\" class=\"\">"
|
||||
"<head>"
|
||||
|
@ -543,6 +545,11 @@ void StartWebserver(int type, IPAddress ipweb)
|
|||
#endif // Not FIRMWARE_MINIMAL
|
||||
}
|
||||
reset_web_log_flag = false;
|
||||
|
||||
// Collect User-Agent for Alexa Hue Emulation
|
||||
// This is used in xdrv_20_hue.ino in function findEchoGeneration()
|
||||
WebServer->collectHeaders(HEADER_KEYS, sizeof(HEADER_KEYS)/sizeof(char*));
|
||||
|
||||
WebServer->begin(); // Web server start
|
||||
}
|
||||
if (webserver_state != type) {
|
||||
|
|
|
@ -126,7 +126,7 @@
|
|||
\*********************************************************************************************/
|
||||
|
||||
#define XDRV_04 4
|
||||
//#define DEBUG_LIGHT
|
||||
// #define DEBUG_LIGHT
|
||||
|
||||
const uint8_t LIGHT_COLOR_SIZE = 25; // Char array scolor size
|
||||
const uint8_t WS2812_SCHEMES = 7; // Number of additional WS2812 schemes supported by xdrv_ws2812.ino
|
||||
|
@ -241,10 +241,11 @@ uint8_t light_new_color[LST_MAX];
|
|||
uint8_t light_last_color[LST_MAX];
|
||||
uint8_t light_color_remap[LST_MAX];
|
||||
|
||||
power_t light_power = 0; // Power<x> for each channel if SetOption68, or boolean if single light
|
||||
uint8_t light_wheel = 0;
|
||||
uint8_t light_subtype = 0; // LST_ subtype
|
||||
bool light_pwm_multi_channels = false; // SetOption68, treat each PWM channel as an independant dimmer
|
||||
uint8_t light_device = 0;
|
||||
uint8_t light_power = 0;
|
||||
uint8_t light_old_power = 1;
|
||||
uint8_t light_update = 1;
|
||||
uint8_t light_wakeup_active = 0;
|
||||
|
@ -772,6 +773,7 @@ private:
|
|||
|
||||
// are RGB and CT linked, i.e. if we set CT then RGB channels are off
|
||||
bool _ct_rgb_linked = true;
|
||||
bool _pwm_multi_channels = false; // treat each channel as independant dimmer
|
||||
|
||||
public:
|
||||
LightControllerClass(LightStateClass& state) {
|
||||
|
@ -784,7 +786,11 @@ public:
|
|||
|
||||
inline bool setCTRGBLinked(bool ct_rgb_linked) {
|
||||
bool prev = _ct_rgb_linked;
|
||||
_ct_rgb_linked = ct_rgb_linked;
|
||||
if (_pwm_multi_channels) {
|
||||
_ct_rgb_linked = false; // force to false if _pwm_multi_channels is set
|
||||
} else {
|
||||
_ct_rgb_linked = ct_rgb_linked;
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
|
||||
|
@ -792,6 +798,17 @@ public:
|
|||
return _ct_rgb_linked;
|
||||
}
|
||||
|
||||
inline bool setPWMMultiChannel(bool pwm_multi_channels) {
|
||||
bool prev = _pwm_multi_channels;
|
||||
_pwm_multi_channels = pwm_multi_channels;
|
||||
if (pwm_multi_channels) setCTRGBLinked(false); // if pwm multi channel, then unlink RGB and CT
|
||||
return prev;
|
||||
}
|
||||
|
||||
inline bool isPWMMultiChannel(void) {
|
||||
return _pwm_multi_channels;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LIGHT
|
||||
void debugLogs() {
|
||||
uint8_t r,g,b,c,w;
|
||||
|
@ -815,12 +832,15 @@ public:
|
|||
// first try setting CW, if zero, it select RGB mode
|
||||
_state->setCW(Settings.light_color[3], Settings.light_color[4], true);
|
||||
_state->setRGB(Settings.light_color[0], Settings.light_color[1], Settings.light_color[2]);
|
||||
// We apply dimmer in priority to RGB
|
||||
uint8_t bri = _state->DimmerToBri(Settings.light_dimmer);
|
||||
if (Settings.light_color[0] + Settings.light_color[1] + Settings.light_color[2] > 0) {
|
||||
_state->setBriRGB(bri);
|
||||
} else {
|
||||
_state->setBriCT(bri);
|
||||
if (!_pwm_multi_channels) {
|
||||
// only if non-multi channel
|
||||
// We apply dimmer in priority to RGB
|
||||
uint8_t bri = _state->DimmerToBri(Settings.light_dimmer);
|
||||
if (Settings.light_color[0] + Settings.light_color[1] + Settings.light_color[2] > 0) {
|
||||
_state->setBriRGB(bri);
|
||||
} else {
|
||||
_state->setBriCT(bri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -864,6 +884,15 @@ public:
|
|||
void calcLevels() {
|
||||
uint8_t r,g,b,c,w,briRGB,briCT;
|
||||
_state->getActualRGBCW(&r,&g,&b,&c,&w);
|
||||
|
||||
if (_pwm_multi_channels) { // if PWM multi channel, no more transformation required
|
||||
light_current_color[0] = r;
|
||||
light_current_color[1] = g;
|
||||
light_current_color[2] = b;
|
||||
light_current_color[3] = c;
|
||||
light_current_color[4] = w;
|
||||
return;
|
||||
}
|
||||
briRGB = _state->getBriRGB();
|
||||
briCT = _state->getBriCT();
|
||||
|
||||
|
@ -907,20 +936,28 @@ public:
|
|||
|
||||
// save the current light state to Settings.
|
||||
void saveSettings() {
|
||||
uint8_t cm = _state->getColorMode();
|
||||
if (light_pwm_multi_channels) {
|
||||
// simply save each channel
|
||||
_state->getActualRGBCW(&Settings.light_color[0], &Settings.light_color[1],
|
||||
&Settings.light_color[2], &Settings.light_color[3],
|
||||
&Settings.light_color[4]);
|
||||
Settings.light_dimmer = 100; // arbitrary value, unused in this mode
|
||||
} else {
|
||||
uint8_t cm = _state->getColorMode();
|
||||
|
||||
memset(&Settings.light_color[0], 0, sizeof(Settings.light_color));
|
||||
if (LCM_RGB & cm) { // can be either LCM_RGB or LCM_BOTH
|
||||
_state->getRGB(&Settings.light_color[0], &Settings.light_color[1], &Settings.light_color[2]);
|
||||
Settings.light_dimmer = _state->BriToDimmer(_state->getBriRGB());
|
||||
// anyways we always store RGB with BrightnessRGB
|
||||
if (LCM_BOTH == cm) {
|
||||
// then store at actual brightness CW/WW if dual mode
|
||||
_state->getActualRGBCW(nullptr, nullptr, nullptr, &Settings.light_color[3], &Settings.light_color[4]);
|
||||
memset(&Settings.light_color[0], 0, sizeof(Settings.light_color));
|
||||
if (LCM_RGB & cm) { // can be either LCM_RGB or LCM_BOTH
|
||||
_state->getRGB(&Settings.light_color[0], &Settings.light_color[1], &Settings.light_color[2]);
|
||||
Settings.light_dimmer = _state->BriToDimmer(_state->getBriRGB());
|
||||
// anyways we always store RGB with BrightnessRGB
|
||||
if (LCM_BOTH == cm) {
|
||||
// then store at actual brightness CW/WW if dual mode
|
||||
_state->getActualRGBCW(nullptr, nullptr, nullptr, &Settings.light_color[3], &Settings.light_color[4]);
|
||||
}
|
||||
} else if (LCM_CT == cm) { // cm can only be LCM_CT
|
||||
_state->getCW(&Settings.light_color[3], &Settings.light_color[4]);
|
||||
Settings.light_dimmer = _state->BriToDimmer(_state->getBriCT());
|
||||
}
|
||||
} else if (LCM_CT == cm) { // cm can only be LCM_CT
|
||||
_state->getCW(&Settings.light_color[3], &Settings.light_color[4]);
|
||||
Settings.light_dimmer = _state->BriToDimmer(_state->getBriCT());
|
||||
}
|
||||
#ifdef DEBUG_LIGHT
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::saveSettings Settings.light_color (%d %d %d %d %d - %d)",
|
||||
|
@ -1322,6 +1359,7 @@ void LightInit(void)
|
|||
|
||||
light_device = devices_present;
|
||||
light_subtype = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); // Always 0 - LST_MAX (5)
|
||||
light_pwm_multi_channels = Settings.flag3.pmw_multi_channels;
|
||||
|
||||
#if defined(USE_WS2812) && (USE_WS2812_CTYPE > NEO_3LED)
|
||||
if (LT_WS2812 == light_type) {
|
||||
|
@ -1329,6 +1367,16 @@ void LightInit(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
if ((LST_SINGLE < light_subtype) && light_pwm_multi_channels) {
|
||||
// we treat each PWM channel as an independant one, hence we switch to
|
||||
light_controller.setPWMMultiChannel(true);
|
||||
light_device = devices_present - light_subtype + 1; // adjust if we also have relays
|
||||
}
|
||||
#ifdef DEBUG_LIGHT
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightInit light_pwm_multi_channels=%d light_subtype=%d light_device=%d devices_present=%d",
|
||||
light_pwm_multi_channels, light_subtype, light_device, devices_present);
|
||||
#endif
|
||||
|
||||
light_controller.setSubType(light_subtype);
|
||||
light_controller.loadSettings();
|
||||
|
||||
|
@ -1456,6 +1504,32 @@ void LightSetDimmer(uint8_t dimmer) {
|
|||
light_controller.changeDimmer(dimmer);
|
||||
}
|
||||
|
||||
// If SetOption68 is set, get the brightness for a specific device
|
||||
uint8_t LightGetBri(uint8_t device) {
|
||||
uint8_t bri = 254; // default value if relay
|
||||
if (light_pwm_multi_channels) {
|
||||
if ((device >= light_device) && (device < light_device + LST_MAX) && (device <= devices_present)) {
|
||||
bri = light_current_color[device - light_device];
|
||||
}
|
||||
} else if (device == light_device) {
|
||||
bri = light_state.getBri();
|
||||
}
|
||||
return bri;
|
||||
}
|
||||
|
||||
// If SetOption68 is set, get the brightness for a specific device
|
||||
|
||||
void LightSetBri(uint8_t device, uint8_t bri) {
|
||||
if (light_pwm_multi_channels) {
|
||||
if ((device >= light_device) && (device < light_device + LST_MAX) && (device <= devices_present)) {
|
||||
light_current_color[device - light_device] = bri;
|
||||
light_controller.changeChannels(light_current_color);
|
||||
}
|
||||
} else if (device == light_device) {
|
||||
light_controller.changeBri(bri);
|
||||
}
|
||||
}
|
||||
|
||||
void LightSetColorTemp(uint16_t ct)
|
||||
{
|
||||
/* Color Temperature (https://developers.meethue.com/documentation/core-concepts)
|
||||
|
@ -1568,19 +1642,52 @@ void LightState(uint8_t append)
|
|||
|
||||
void LightPreparePower(void)
|
||||
{
|
||||
if (light_state.getBri() && !(light_power)) {
|
||||
if (!Settings.flag.not_power_linked) {
|
||||
ExecuteCommandPower(light_device, POWER_ON_NO_STATE, SRC_LIGHT);
|
||||
#ifdef DEBUG_LIGHT
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, "LightPreparePower power=%d light_power=%d", power, light_power);
|
||||
#endif
|
||||
// If multi-channels, then we only switch off channels with a value of zero
|
||||
if (light_pwm_multi_channels) {
|
||||
// for (uint32_t i = 0; i < light_subtype; i++) {
|
||||
// // if channel is non-null, channel is supposed to be on, but it is off, do Power On
|
||||
// if ((light_current_color[i]) && (bitRead(light_power, i)) && (0 == bitRead(power, i + light_device - 1))) {
|
||||
// ExecuteCommandPower(light_device + i, POWER_ON_NO_STATE, SRC_LIGHT);
|
||||
// //bitSet(Settings.power, i + light_device - 1);
|
||||
// #ifdef DEBUG_LIGHT
|
||||
// AddLog_P2(LOG_LEVEL_DEBUG, "ExecuteCommandPower ON device=%d", light_device + i);
|
||||
// #endif
|
||||
// }
|
||||
// // if channel is zero and channel is on, set it off
|
||||
// if ((0 == light_current_color[i]) && bitRead(power, i + light_device - 1)) {
|
||||
// ExecuteCommandPower(light_device + i, POWER_OFF_NO_STATE, SRC_LIGHT);
|
||||
// //bitClear(Settings.power, i + light_device - 1);
|
||||
// #ifdef DEBUG_LIGHT
|
||||
// AddLog_P2(LOG_LEVEL_DEBUG, "ExecuteCommandPower OFF device=%d", light_device + i);
|
||||
// #endif
|
||||
// }
|
||||
// #ifdef USE_DOMOTICZ
|
||||
// DomoticzUpdatePowerState(light_device + i);
|
||||
// #endif // USE_DOMOTICZ
|
||||
// }
|
||||
} else {
|
||||
if (light_state.getBri() && !(light_power)) {
|
||||
if (!Settings.flag.not_power_linked) {
|
||||
ExecuteCommandPower(light_device, POWER_ON_NO_STATE, SRC_LIGHT);
|
||||
}
|
||||
}
|
||||
else if (!light_state.getBri() && light_power) {
|
||||
ExecuteCommandPower(light_device, POWER_OFF_NO_STATE, SRC_LIGHT);
|
||||
}
|
||||
}
|
||||
else if (!light_state.getBri() && light_power) {
|
||||
ExecuteCommandPower(light_device, POWER_OFF_NO_STATE, SRC_LIGHT);
|
||||
}
|
||||
#ifdef USE_DOMOTICZ
|
||||
DomoticzUpdatePowerState(light_device);
|
||||
DomoticzUpdatePowerState(light_device);
|
||||
#endif // USE_DOMOTICZ
|
||||
}
|
||||
|
||||
if (Settings.flag3.hass_tele_on_power) { MqttPublishTeleState(); }
|
||||
|
||||
#ifdef DEBUG_LIGHT
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, "LightPreparePower End power=%d light_power=%d", power, light_power);
|
||||
#endif
|
||||
light_power = power >> (light_device - 1); // reset next state
|
||||
LightState(0);
|
||||
}
|
||||
|
||||
|
@ -1667,11 +1774,26 @@ void LightSetPower(void)
|
|||
{
|
||||
// light_power = XdrvMailbox.index;
|
||||
light_old_power = light_power;
|
||||
light_power = bitRead(XdrvMailbox.index, light_device -1);
|
||||
//light_power = bitRead(XdrvMailbox.index, light_device -1);
|
||||
uint32_t mask = 1; // default mask
|
||||
if (light_pwm_multi_channels) {
|
||||
mask = (1 << light_subtype) - 1; // wider mask
|
||||
}
|
||||
uint32_t shift = light_device - 1;
|
||||
// If PWM multi_channels
|
||||
// Ex: 3 Relays and 4 PWM - devices_present = 7, light_device = 4, light_subtype = 4
|
||||
// Result: mask = 0b00001111 = 0x0F, shift = 3.
|
||||
// Power bits we consider are: 0b01111000 = 0x78
|
||||
// If regular situation: devices_present == light_subtype
|
||||
light_power = (XdrvMailbox.index & (mask << shift)) >> shift;
|
||||
if (light_wakeup_active) {
|
||||
light_wakeup_active--;
|
||||
}
|
||||
if (light_power && !light_old_power) {
|
||||
#ifdef DEBUG_LIGHT
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightSetPower XdrvMailbox.index=%d light_old_power=%d light_power=%d mask=%d shift=%d",
|
||||
XdrvMailbox.index, light_old_power, light_power, mask, shift);
|
||||
#endif
|
||||
if (light_power != light_old_power) {
|
||||
light_update = 1;
|
||||
}
|
||||
LightAnimate();
|
||||
|
@ -1762,6 +1884,22 @@ void LightAnimate(void)
|
|||
}
|
||||
|
||||
if ((Settings.light_scheme < LS_MAX) || !light_power) {
|
||||
|
||||
// If SetOption68, multi_channels
|
||||
if (light_pwm_multi_channels) {
|
||||
// if multi-channels, specifically apply the light_power bits
|
||||
for (uint32_t i = 0; i < LST_MAX; i++) {
|
||||
if (0 == bitRead(light_power,i)) { // if power down bit is zero
|
||||
light_new_color[i] = 0; // shut down this channel
|
||||
}
|
||||
}
|
||||
// #ifdef DEBUG_LIGHT
|
||||
// AddLog_P2(LOG_LEVEL_DEBUG_MORE, "Animate>> light_power=%d light_new_color=[%d,%d,%d,%d,%d]",
|
||||
// light_power, light_new_color[0], light_new_color[1], light_new_color[2],
|
||||
// light_new_color[3], light_new_color[4]);
|
||||
// #endif
|
||||
}
|
||||
|
||||
if (memcmp(light_last_color, light_new_color, light_subtype)) {
|
||||
light_update = 1;
|
||||
}
|
||||
|
@ -1777,62 +1915,11 @@ void LightAnimate(void)
|
|||
}
|
||||
|
||||
if (PHILIPS == my_module_type) {
|
||||
// Xiaomi Philips bulbs follow a different scheme:
|
||||
uint8_t cold; // channel 1 is the color tone, mapped to cold channel (0..255)
|
||||
light_state.getCW(&cold, nullptr);
|
||||
cur_col[1] = cold;
|
||||
cur_col_10bits[1] = changeUIntScale(cur_col[1], 0, 255, 0, 1023);
|
||||
// now set channel 0 to overall brightness
|
||||
uint8_t pxBri = light_state.getBriCT();
|
||||
// channel 0=intensity, channel1=temperature
|
||||
if (Settings.light_correction) { // gamma correction
|
||||
cur_col[0] = ledGamma(pxBri);
|
||||
cur_col_10bits[0] = ledGamma(pxBri, 10); // 10 bits gamma correction
|
||||
} else {
|
||||
cur_col[0] = pxBri;
|
||||
cur_col_10bits[0] = changeUIntScale(pxBri, 0, 255, 0, 1023); // no gamma, extend to 10 bits
|
||||
}
|
||||
calcGammaXiaomiBulbs(cur_col, cur_col_10bits);
|
||||
} else if (light_pwm_multi_channels) {
|
||||
calcGammaMultiChannels(cur_col, cur_col_10bits);
|
||||
} else { // PHILIPS != my_module_type
|
||||
// Apply gamma correction for 8 and 10 bits resolutions, if needed
|
||||
if (Settings.light_correction) {
|
||||
// First apply combined correction to the overall white power
|
||||
if ((LST_COLDWARM == light_subtype) || (LST_RGBWC == light_subtype)) {
|
||||
uint8_t w_idx[2] = {0, 1}; // if LST_COLDWARM, channels 0 and 1
|
||||
if (LST_RGBWC == light_subtype) { // if LST_RGBWC, channels 3 and 4
|
||||
w_idx[0] = 3;
|
||||
w_idx[1] = 4;
|
||||
}
|
||||
uint16_t white_bri = cur_col[w_idx[0]] + cur_col[w_idx[1]];
|
||||
// if sum of both channels is > 255, then channels are probablu uncorrelated
|
||||
if (white_bri <= 255) {
|
||||
// we calculate the gamma corrected sum of CW + WW
|
||||
uint16_t white_bri_10bits = ledGamma(white_bri, 10);
|
||||
uint8_t white_bri_8bits = ledGamma(white_bri);
|
||||
// then we split the total energy among the cold and warm leds
|
||||
cur_col_10bits[w_idx[0]] = changeUIntScale(cur_col[w_idx[0]], 0, white_bri, 0, white_bri_10bits);
|
||||
cur_col_10bits[w_idx[1]] = changeUIntScale(cur_col[w_idx[1]], 0, white_bri, 0, white_bri_10bits);
|
||||
cur_col[w_idx[0]] = changeUIntScale(cur_col[w_idx[0]], 0, white_bri, 0, white_bri_8bits);
|
||||
cur_col[w_idx[1]] = changeUIntScale(cur_col[w_idx[1]], 0, white_bri, 0, white_bri_8bits);
|
||||
} else {
|
||||
cur_col_10bits[w_idx[0]] = ledGamma(cur_col[w_idx[0]], 10);
|
||||
cur_col_10bits[w_idx[1]] = ledGamma(cur_col[w_idx[1]], 10);
|
||||
cur_col[w_idx[0]] = ledGamma(cur_col[w_idx[0]]);
|
||||
cur_col[w_idx[1]] = ledGamma(cur_col[w_idx[1]]);
|
||||
}
|
||||
}
|
||||
// then apply gamma correction to RGB channels
|
||||
if (LST_RGB <= light_subtype) {
|
||||
for (uint32_t i = 0; i < 3; i++) {
|
||||
cur_col_10bits[i] = ledGamma(cur_col[i], 10);
|
||||
cur_col[i] = ledGamma(cur_col[i]);
|
||||
}
|
||||
}
|
||||
// If RGBW or Single channel, also adjust White channel
|
||||
if (LST_COLDWARM != light_subtype) {
|
||||
cur_col_10bits[3] = ledGamma(cur_col[3], 10);
|
||||
cur_col[3] = ledGamma(cur_col[3]);
|
||||
}
|
||||
}
|
||||
calcGammaBulbs(cur_col, cur_col_10bits);
|
||||
|
||||
// Now see if we need to mix RGB and True White
|
||||
// Valid only for LST_RGBW, LST_RGBWC, rgbwwTable[4] is zero, and white is zero (see doc)
|
||||
|
@ -1931,6 +2018,79 @@ void LightAnimate(void)
|
|||
}
|
||||
}
|
||||
|
||||
// Do specific computation for Xiaomi Bulbs
|
||||
void calcGammaXiaomiBulbs(uint8_t cur_col[5], uint16_t cur_col_10bits[5]) {
|
||||
// Xiaomi Philips bulbs follow a different scheme:
|
||||
uint8_t cold; // channel 1 is the color tone, mapped to cold channel (0..255)
|
||||
light_state.getCW(&cold, nullptr);
|
||||
cur_col[1] = cold;
|
||||
cur_col_10bits[1] = changeUIntScale(cur_col[1], 0, 255, 0, 1023);
|
||||
// now set channel 0 to overall brightness
|
||||
uint8_t pxBri = light_state.getBriCT();
|
||||
// channel 0=intensity, channel1=temperature
|
||||
if (Settings.light_correction) { // gamma correction
|
||||
cur_col[0] = ledGamma(pxBri);
|
||||
cur_col_10bits[0] = ledGamma(pxBri, 10); // 10 bits gamma correction
|
||||
} else {
|
||||
cur_col[0] = pxBri;
|
||||
cur_col_10bits[0] = changeUIntScale(pxBri, 0, 255, 0, 1023); // no gamma, extend to 10 bits
|
||||
}
|
||||
}
|
||||
|
||||
// Just apply basic Gamma to each channel
|
||||
void calcGammaMultiChannels(uint8_t cur_col[5], uint16_t cur_col_10bits[5]) {
|
||||
// Apply gamma correction for 8 and 10 bits resolutions, if needed
|
||||
if (Settings.light_correction) {
|
||||
for (uint32_t i = 0; i < LST_MAX; i++) {
|
||||
cur_col_10bits[i] = ledGamma(cur_col[i], 10);
|
||||
cur_col[i] = ledGamma(cur_col[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void calcGammaBulbs(uint8_t cur_col[5], uint16_t cur_col_10bits[5]) {
|
||||
// Apply gamma correction for 8 and 10 bits resolutions, if needed
|
||||
if (Settings.light_correction) {
|
||||
// First apply combined correction to the overall white power
|
||||
if ((LST_COLDWARM == light_subtype) || (LST_RGBWC == light_subtype)) {
|
||||
uint8_t w_idx[2] = {0, 1}; // if LST_COLDWARM, channels 0 and 1
|
||||
if (LST_RGBWC == light_subtype) { // if LST_RGBWC, channels 3 and 4
|
||||
w_idx[0] = 3;
|
||||
w_idx[1] = 4;
|
||||
}
|
||||
uint16_t white_bri = cur_col[w_idx[0]] + cur_col[w_idx[1]];
|
||||
// if sum of both channels is > 255, then channels are probablu uncorrelated
|
||||
if (white_bri <= 255) {
|
||||
// we calculate the gamma corrected sum of CW + WW
|
||||
uint16_t white_bri_10bits = ledGamma(white_bri, 10);
|
||||
uint8_t white_bri_8bits = ledGamma(white_bri);
|
||||
// then we split the total energy among the cold and warm leds
|
||||
cur_col_10bits[w_idx[0]] = changeUIntScale(cur_col[w_idx[0]], 0, white_bri, 0, white_bri_10bits);
|
||||
cur_col_10bits[w_idx[1]] = changeUIntScale(cur_col[w_idx[1]], 0, white_bri, 0, white_bri_10bits);
|
||||
cur_col[w_idx[0]] = changeUIntScale(cur_col[w_idx[0]], 0, white_bri, 0, white_bri_8bits);
|
||||
cur_col[w_idx[1]] = changeUIntScale(cur_col[w_idx[1]], 0, white_bri, 0, white_bri_8bits);
|
||||
} else {
|
||||
cur_col_10bits[w_idx[0]] = ledGamma(cur_col[w_idx[0]], 10);
|
||||
cur_col_10bits[w_idx[1]] = ledGamma(cur_col[w_idx[1]], 10);
|
||||
cur_col[w_idx[0]] = ledGamma(cur_col[w_idx[0]]);
|
||||
cur_col[w_idx[1]] = ledGamma(cur_col[w_idx[1]]);
|
||||
}
|
||||
}
|
||||
// then apply gamma correction to RGB channels
|
||||
if (LST_RGB <= light_subtype) {
|
||||
for (uint32_t i = 0; i < 3; i++) {
|
||||
cur_col_10bits[i] = ledGamma(cur_col[i], 10);
|
||||
cur_col[i] = ledGamma(cur_col[i]);
|
||||
}
|
||||
}
|
||||
// If RGBW or Single channel, also adjust White channel
|
||||
if (LST_COLDWARM != light_subtype) {
|
||||
cur_col_10bits[3] = ledGamma(cur_col[3], 10);
|
||||
cur_col[3] = ledGamma(cur_col[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Commands
|
||||
\*********************************************************************************************/
|
||||
|
@ -2080,10 +2240,16 @@ void CmndChannel(void)
|
|||
bool coldim = false;
|
||||
// Set "Channel" directly - this allows Color and Direct PWM control to coexist
|
||||
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) {
|
||||
light_current_color[XdrvMailbox.index-1] = changeUIntScale(XdrvMailbox.payload,0,100,0,255);
|
||||
// if we change channels 1,2,3 then turn off CT mode (unless non-linked)
|
||||
if ((XdrvMailbox.index <= 3) && (light_controller.isCTRGBLinked())) {
|
||||
light_current_color[3] = light_current_color[4] = 0;
|
||||
light_current_color[XdrvMailbox.index - light_device] = changeUIntScale(XdrvMailbox.payload,0,100,0,255);
|
||||
if (light_pwm_multi_channels) {
|
||||
// if (!Settings.flag.not_power_linked) { // SetOption20
|
||||
// light_power = light_power | (1 << (XdrvMailbox.index - light_device)); // ask to turn on channel
|
||||
// }
|
||||
} else {
|
||||
// if we change channels 1,2,3 then turn off CT mode (unless non-linked)
|
||||
if ((XdrvMailbox.index <= 3) && (light_controller.isCTRGBLinked())) {
|
||||
light_current_color[3] = light_current_color[4] = 0;
|
||||
}
|
||||
}
|
||||
light_controller.changeChannels(light_current_color);
|
||||
coldim = true;
|
||||
|
|
|
@ -243,6 +243,22 @@ uint16_t prev_ct = 254;
|
|||
char prev_x_str[24] = "\0"; // store previously set xy by Alexa app
|
||||
char prev_y_str[24] = "\0";
|
||||
|
||||
uint8_t getLocalLightSubtype(uint8_t device) {
|
||||
if (light_type) {
|
||||
if (device >= light_device) {
|
||||
if (Settings.flag3.pmw_multi_channels) {
|
||||
return LST_SINGLE; // If SetOption68, each channel acts like a dimmer
|
||||
} else {
|
||||
return light_subtype; // the actual light
|
||||
}
|
||||
} else {
|
||||
return LST_NONE; // relays
|
||||
}
|
||||
} else {
|
||||
return LST_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
void HueLightStatus1(uint8_t device, String *response)
|
||||
{
|
||||
uint16_t ct = 0;
|
||||
|
@ -251,14 +267,18 @@ void HueLightStatus1(uint8_t device, String *response)
|
|||
uint16_t hue = 0;
|
||||
uint8_t sat = 0;
|
||||
uint8_t bri = 254;
|
||||
uint32_t echo_gen = findEchoGeneration(); // 1 for 1st gen =+ Echo Dot 2nd gen, 2 for 2nd gen and above
|
||||
// local_light_subtype simulates the light_subtype for 'device'
|
||||
// For relays LST_NONE, for dimmers LST_SINGLE
|
||||
uint8_t local_light_subtype = getLocalLightSubtype(device);
|
||||
|
||||
bri = LightGetBri(device); // get Dimmer corrected with SetOption68
|
||||
if (bri > 254) bri = 254; // Philips Hue bri is between 1 and 254
|
||||
if (bri < 1) bri = 1;
|
||||
|
||||
if (light_type) {
|
||||
light_state.getHSB(&hue, &sat, nullptr);
|
||||
bri = light_state.getBri();
|
||||
|
||||
if (bri > 254) bri = 254; // Philips Hue bri is between 1 and 254
|
||||
if (bri < 1) bri = 1;
|
||||
if ((bri > prev_bri ? bri - prev_bri : prev_bri - bri) < 1)
|
||||
bri = prev_bri;
|
||||
|
||||
|
@ -293,17 +313,17 @@ void HueLightStatus1(uint8_t device, String *response)
|
|||
*response += FPSTR(HUE_LIGHTS_STATUS_JSON1);
|
||||
response->replace("{state}", (power & (1 << (device-1))) ? "true" : "false");
|
||||
// Brightness for all devices with PWM
|
||||
//if (LST_SINGLE <= light_subtype) {
|
||||
light_status += "\"bri\":";
|
||||
light_status += String(bri);
|
||||
light_status += ",";
|
||||
//}
|
||||
if (LST_COLDWARM <= light_subtype) {
|
||||
if ((1 == echo_gen) || (LST_SINGLE <= local_light_subtype)) { // force dimmer for 1st gen Echo
|
||||
light_status += "\"bri\":";
|
||||
light_status += String(bri);
|
||||
light_status += ",";
|
||||
}
|
||||
if (LST_COLDWARM <= local_light_subtype) {
|
||||
light_status += F("\"colormode\":\"");
|
||||
light_status += (g_gotct ? "ct" : "hs");
|
||||
light_status += "\",";
|
||||
}
|
||||
if (LST_RGB <= light_subtype) { // colors
|
||||
if (LST_RGB <= local_light_subtype) { // colors
|
||||
if (prev_x_str[0] && prev_y_str[0]) {
|
||||
light_status += "\"xy\":[";
|
||||
light_status += prev_x_str;
|
||||
|
@ -327,10 +347,10 @@ void HueLightStatus1(uint8_t device, String *response)
|
|||
light_status += String(sat);
|
||||
light_status += ",";
|
||||
}
|
||||
if (LST_COLDWARM == light_subtype || LST_RGBW <= light_subtype) { // white temp
|
||||
if (LST_COLDWARM == local_light_subtype || LST_RGBW <= local_light_subtype) { // white temp
|
||||
light_status += "\"ct\":";
|
||||
light_status += String(ct > 0 ? ct : 284);
|
||||
light_status += ","; // if no ct, default to medium white
|
||||
light_status += String(ct > 0 ? ct : 284); // if no ct, default to medium white
|
||||
light_status += ",";
|
||||
}
|
||||
response->replace("{light_status}", light_status);
|
||||
}
|
||||
|
@ -338,7 +358,20 @@ void HueLightStatus1(uint8_t device, String *response)
|
|||
void HueLightStatus2(uint8_t device, String *response)
|
||||
{
|
||||
*response += FPSTR(HUE_LIGHTS_STATUS_JSON2);
|
||||
response->replace("{j1", Settings.friendlyname[device-1]);
|
||||
if (device <= MAX_FRIENDLYNAMES) {
|
||||
response->replace("{j1", Settings.friendlyname[device-1]);
|
||||
} else {
|
||||
char fname[33];
|
||||
strcpy(fname, Settings.friendlyname[MAX_FRIENDLYNAMES-1]);
|
||||
uint32_t fname_len = strlen(fname);
|
||||
if (fname_len >= 33-3) {
|
||||
fname[33-3] = 0x00;
|
||||
fname_len = 33-3;
|
||||
}
|
||||
fname[fname_len++] = '-';
|
||||
fname[fname_len++] = '0' + device - MAX_FRIENDLYNAMES;
|
||||
response->replace("{j1", fname);
|
||||
}
|
||||
response->replace("{j2", GetHueDeviceId(device));
|
||||
}
|
||||
|
||||
|
@ -357,10 +390,32 @@ uint32_t DecodeLightId(uint32_t id) {
|
|||
return id & 0xF;
|
||||
}
|
||||
|
||||
static const char * FIRST_GEN_UA[] = { // list of User-Agents signature
|
||||
"AEOBC", // Echo Dot 2ng Generation
|
||||
};
|
||||
|
||||
// Check if the Echo device is of 1st generation, which triggers different results
|
||||
uint32_t findEchoGeneration(void) {
|
||||
// result is 1 for 1st gen, 2 for 2nd gen and further
|
||||
String user_agent = WebServer->header("User-Agent");
|
||||
uint32_t gen = 2;
|
||||
|
||||
for (uint32_t i = 0; i < sizeof(FIRST_GEN_UA)/sizeof(char*); i++) {
|
||||
if (user_agent.indexOf(FIRST_GEN_UA[i]) >= 0) { // found
|
||||
gen = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, D_LOG_HTTP D_HUE " User-Agent: %s, gen=%d", user_agent.c_str(), gen); // Header collection is set in xdrv_01_webserver.ino, in StartWebserver()
|
||||
|
||||
return gen;
|
||||
}
|
||||
|
||||
void HueGlobalConfig(String *path)
|
||||
{
|
||||
String response;
|
||||
uint8_t maxhue = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : devices_present;
|
||||
uint8_t maxhue = (devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : devices_present;
|
||||
|
||||
path->remove(0,1); // cut leading / to get <id>
|
||||
response = F("{\"lights\":{\"");
|
||||
|
@ -403,7 +458,8 @@ void HueLights(String *path)
|
|||
bool on = false;
|
||||
bool change = false; // need to change a parameter to the light
|
||||
uint8_t device = 1;
|
||||
uint8_t maxhue = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : devices_present;
|
||||
uint8_t local_light_subtype = light_subtype;
|
||||
uint8_t maxhue = (devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : devices_present;
|
||||
|
||||
path->remove(0,path->indexOf("/lights")); // Remove until /lights
|
||||
if (path->endsWith("/lights")) { // Got /lights
|
||||
|
@ -426,6 +482,8 @@ void HueLights(String *path)
|
|||
if ((device < 1) || (device > maxhue)) {
|
||||
device = 1;
|
||||
}
|
||||
local_light_subtype = getLocalLightSubtype(device); // get the subtype for this device
|
||||
|
||||
if (WebServer->args()) {
|
||||
response = "[";
|
||||
|
||||
|
@ -452,14 +510,18 @@ void HueLights(String *path)
|
|||
resp = true;
|
||||
}
|
||||
|
||||
if (light_type) {
|
||||
light_state.getHSB(&hue, &sat, nullptr);
|
||||
bri = light_state.getBri(); // get the combined bri for CT and RGB, not only the RGB one
|
||||
ct = light_state.getCT();
|
||||
uint8_t color_mode = light_state.getColorMode();
|
||||
if (LCM_RGB == color_mode) { g_gotct = false; }
|
||||
if (LCM_CT == color_mode) { g_gotct = true; }
|
||||
// If LCM_BOTH == color_mode, leave g_gotct unchanged
|
||||
if (light_type && (local_light_subtype >= LST_SINGLE)) {
|
||||
if (!Settings.flag3.pmw_multi_channels) {
|
||||
light_state.getHSB(&hue, &sat, nullptr);
|
||||
bri = light_state.getBri(); // get the combined bri for CT and RGB, not only the RGB one
|
||||
ct = light_state.getCT();
|
||||
uint8_t color_mode = light_state.getColorMode();
|
||||
if (LCM_RGB == color_mode) { g_gotct = false; }
|
||||
if (LCM_CT == color_mode) { g_gotct = true; }
|
||||
// If LCM_BOTH == color_mode, leave g_gotct unchanged
|
||||
} else { // treat each channel as simple dimmer
|
||||
bri = LightGetBri(device);
|
||||
}
|
||||
}
|
||||
prev_x_str[0] = prev_y_str[0] = 0; // reset xy string
|
||||
|
||||
|
@ -551,14 +613,18 @@ void HueLights(String *path)
|
|||
resp = true;
|
||||
}
|
||||
if (change) {
|
||||
if (light_type) {
|
||||
if (g_gotct) {
|
||||
light_controller.changeCTB(ct, bri);
|
||||
} else {
|
||||
light_controller.changeHSB(hue, sat, bri);
|
||||
if (light_type && (local_light_subtype > LST_NONE)) { // not relay
|
||||
if (!Settings.flag3.pmw_multi_channels) {
|
||||
if (g_gotct) {
|
||||
light_controller.changeCTB(ct, bri);
|
||||
} else {
|
||||
light_controller.changeHSB(hue, sat, bri);
|
||||
}
|
||||
LightPreparePower();
|
||||
} else { // SetOption68 On, each channel is a dimmer
|
||||
LightSetBri(device, bri);
|
||||
}
|
||||
LightPreparePower();
|
||||
if (LST_COLDWARM <= light_subtype) {
|
||||
if (LST_COLDWARM <= local_light_subtype) {
|
||||
MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_COLOR));
|
||||
} else {
|
||||
MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_DIMMER));
|
||||
|
@ -577,6 +643,7 @@ void HueLights(String *path)
|
|||
}
|
||||
}
|
||||
else if(path->indexOf("/lights/") >= 0) { // Got /lights/ID
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "/lights path=%s", path->c_str());
|
||||
path->remove(0,8); // Remove /lights/
|
||||
device = DecodeLightId(atoi(path->c_str()));
|
||||
if ((device < 1) || (device > maxhue)) {
|
||||
|
@ -600,7 +667,7 @@ void HueGroups(String *path)
|
|||
* http://sonoff/api/username/groups?1={"name":"Woonkamer","lights":[],"type":"Room","class":"Living room"})
|
||||
*/
|
||||
String response = "{}";
|
||||
uint8_t maxhue = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : devices_present;
|
||||
uint8_t maxhue = (devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : devices_present;
|
||||
|
||||
if (path->endsWith("/0")) {
|
||||
response = FPSTR(HUE_GROUP0_STATUS_JSON);
|
||||
|
|
Loading…
Reference in New Issue