From b4f7500a31a96b3579f42ad1095afd8a14ac8e87 Mon Sep 17 00:00:00 2001 From: Paul C Diem Date: Sun, 12 Apr 2020 23:17:25 -0500 Subject: [PATCH] Add light palette support --- tasmota/i18n.h | 1 + tasmota/my_user_config.h | 1 + tasmota/xdrv_04_light.ino | 186 +++++++++++++++++++++++++++++++-- tasmota/xdrv_35_pwm_dimmer.ino | 16 +-- 4 files changed, 182 insertions(+), 22 deletions(-) diff --git a/tasmota/i18n.h b/tasmota/i18n.h index 4e1b4098f..e03e54a4e 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -389,6 +389,7 @@ #define D_CMND_LED "Led" #define D_CMND_LEDTABLE "LedTable" #define D_CMND_FADE "Fade" +#define D_CMND_PALETTE "Palette" #define D_CMND_PIXELS "Pixels" #define D_CMND_RGBWWTABLE "RGBWWTable" #define D_CMND_ROTATION "Rotation" diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 9b03a6a4c..8aaf25d0d 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -436,6 +436,7 @@ #define USE_SM2135 // Add support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) #define USE_SONOFF_L1 // Add support for Sonoff L1 led control #define USE_ELECTRIQ_MOODL // Add support for ElectriQ iQ-wifiMOODL RGBW LED controller (+0k3 code) +#define USE_LIGHT_PALETTE // Add support for color palette (+0k9 code) // -- Counter input ------------------------------- #define USE_COUNTER // Enable inputs as counter (+0k8 code) diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino index 240a09417..2e663e9e2 100644 --- a/tasmota/xdrv_04_light.ino +++ b/tasmota/xdrv_04_light.ino @@ -131,12 +131,20 @@ const uint8_t LIGHT_COLOR_SIZE = 25; // Char array scolor size const char kLightCommands[] PROGMEM = "|" // No prefix D_CMND_COLOR "|" D_CMND_COLORTEMPERATURE "|" D_CMND_DIMMER "|" D_CMND_DIMMER_RANGE "|" D_CMND_LEDTABLE "|" D_CMND_FADE "|" D_CMND_RGBWWTABLE "|" D_CMND_SCHEME "|" D_CMND_SPEED "|" D_CMND_WAKEUP "|" D_CMND_WAKEUPDURATION "|" - D_CMND_WHITE "|" D_CMND_CHANNEL "|" D_CMND_HSBCOLOR "|UNDOCA" ; + D_CMND_WHITE "|" D_CMND_CHANNEL "|" D_CMND_HSBCOLOR +#ifdef USE_LIGHT_PALETTE + "|" D_CMND_PALETTE +#endif // USE_LIGHT_PALETTE + "|UNDOCA" ; void (* const LightCommand[])(void) PROGMEM = { &CmndColor, &CmndColorTemperature, &CmndDimmer, &CmndDimmerRange, &CmndLedTable, &CmndFade, &CmndRgbwwTable, &CmndScheme, &CmndSpeed, &CmndWakeup, &CmndWakeupDuration, - &CmndWhite, &CmndChannel, &CmndHsbColor, &CmndUndocA }; + &CmndWhite, &CmndChannel, &CmndHsbColor, +#ifdef USE_LIGHT_PALETTE + &CmndPalette, +#endif // USE_LIGHT_PALETTE + &CmndUndocA }; // Light color mode, either RGB alone, or white-CT alone, or both only available if ct_rgb_linked is false enum LightColorModes { @@ -276,6 +284,10 @@ struct LIGHT { #ifdef USE_DEVICE_GROUPS bool devgrp_no_channels_out = false; // don't share channels with device group (e.g. if scheme set by other device) #endif // USE_DEVICE_GROUPS +#ifdef USE_LIGHT_PALETTE + uint8_t palette_count = 0; // palette entry count + uint8_t * palette; // dynamically allocated palette color array +#endif // USE_LIGHT_PALETTE uint16_t fade_start_10[LST_MAX] = {0,0,0,0,0}; uint16_t fade_cur_10[LST_MAX]; uint16_t fade_end_10[LST_MAX]; // 10 bits resolution target channel values @@ -1625,6 +1637,22 @@ void LightPreparePower(power_t channels = 0xFFFFFFFF) { // 1 = only RGB, 2 = LightState(0); } +#ifdef USE_LIGHT_PALETTE +void LightSetPaletteEntry(void) +{ + uint8_t bri = light_state.getBri(); + uint8_t * palette_entry = &Light.palette[Light.wheel * LST_MAX]; + for (int i = 0; i < LST_MAX; i++) { + Light.new_color[i] = changeUIntScale(palette_entry[i], 0, 255, 0, bri); + } + light_state.setChannelsRaw(Light.new_color); + if (!Light.pwm_multi_channels) { + light_state.setCW(Light.new_color[3], Light.new_color[4], true); + if (Light.new_color[0] || Light.new_color[1] || Light.new_color[2]) light_state.addRGBMode(); + } +} +#endif // USE_LIGHT_PALETTE + void LightCycleColor(int8_t direction) { // if (Light.strip_timer_counter % (Settings.light_speed * 2)) { return; } // Speed 1: 24sec, 2: 48sec, 3: 72sec, etc @@ -1632,6 +1660,22 @@ void LightCycleColor(int8_t direction) if (Light.strip_timer_counter % (Settings.light_speed - 2)) { return; } // Speed 4: 24sec, 5: 36sec, 6: 48sec, etc } +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count) { + if (0 == direction) { + Light.wheel = random(Light.palette_count); + } + else { + Light.wheel += direction; + if (Light.wheel >= Light.palette_count) { + Light.wheel = (direction < 0 ? Light.palette_count - 1 : 0); + } + } + LightSetPaletteEntry(); + return; + } +#endif // USE_LIGHT_PALETTE + if (0 == direction) { if (Light.random == Light.wheel) { Light.random = random(255); @@ -2187,7 +2231,13 @@ void LightHandleDevGroupItem(void) break; case DGR_ITEM_LIGHT_FIXED_COLOR: if (Light.subtype >= LST_RGBW) { +#ifdef USE_LIGHT_PALETTE + value = value % (Light.palette_count ? Light.palette_count : MAX_FIXED_COLOR); + if (Light.palette_count || value) { +#else // USE_LIGHT_PALETTE + value = value % MAX_FIXED_COLOR; if (value) { +#endif // !USE_LIGHT_PALETTE bool save_decimal_text = Settings.flag.decimal_text; char str[16]; LightColorEntry(str, sprintf_P(str, PSTR("%u"), value)); @@ -2323,19 +2373,55 @@ void CmndSupportColor(void) { bool valid_entry = false; bool coldim = false; +#ifdef USE_LIGHT_PALETTE + uint8_t * color_ptr; +#endif // USE_LIGHT_PALETTE if (XdrvMailbox.data_len > 0) { - valid_entry = LightColorEntry(XdrvMailbox.data, XdrvMailbox.data_len); +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count) { + if (XdrvMailbox.data_len == 1 && ('+' == XdrvMailbox.data[0] || '-' == XdrvMailbox.data[0])) { + int8_t direction = ('+' == XdrvMailbox.data[0] ? 1 : -1); + Light.wheel += direction; + if (Light.wheel >= Light.palette_count) { + Light.wheel = (direction < 0 ? Light.palette_count - 1 : 0); + } + } + else { + Light.wheel = (atoi(XdrvMailbox.data) - 1); + } + color_ptr = &Light.palette[Light.wheel * LST_MAX]; + valid_entry = true; + } + else { + color_ptr = Light.entry_color; +#endif // USE_LIGHT_PALETTE + valid_entry = LightColorEntry(XdrvMailbox.data, XdrvMailbox.data_len); +#ifdef USE_LIGHT_PALETTE + } +#endif // USE_LIGHT_PALETTE if (valid_entry) { if (XdrvMailbox.index <= 2) { // Color(1), 2 - uint32_t old_bri = light_state.getBri(); - // change all channels to specified values - light_controller.changeChannels(Light.entry_color); - if (2 == XdrvMailbox.index) { - // If Color2, set back old brightness - light_controller.changeBri(old_bri); +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count && XdrvMailbox.index == 1) { + LightSetPaletteEntry(); } - + else { +#endif // USE_LIGHT_PALETTE + uint32_t old_bri = light_state.getBri(); + // change all channels to specified values +#ifdef USE_LIGHT_PALETTE + light_controller.changeChannels(color_ptr); +#else // USE_LIGHT_PALETTE + light_controller.changeChannels(Light.entry_color); +#endif // !USE_LIGHT_PALETTE + if (2 == XdrvMailbox.index) { + // If Color2, set back old brightness + light_controller.changeBri(old_bri); + } +#ifdef USE_LIGHT_PALETTE + } +#endif // USE_LIGHT_PALETTE Settings.light_scheme = 0; #ifdef USE_DEVICE_GROUPS LightUpdateScheme(); @@ -2747,6 +2833,86 @@ void CmndWakeupDuration(void) ResponseCmndNumber(Settings.light_wakeup); } +#ifdef USE_LIGHT_PALETTE +void CmndPalette(void) +{ + uint8_t * palette_entry; + char * color; + char * p; + + // Palette Color[ ...] + if (XdrvMailbox.data_len) { + Light.wheel = 0; + if (Light.palette) { + free(Light.palette); + Light.palette = nullptr; + Light.palette_count = 0; + } + if (XdrvMailbox.data_len > 1 || XdrvMailbox.data[0] != '0') { + for (int pass = 0;; pass++) { + Light.palette_count = 0; + color = XdrvMailbox.data; + for (;;) { + p = strchr(color, ' '); + if (p) *p = 0; + color = Trim(color); + if (*color && LightColorEntry(color, strlen(color))) { + if (pass) { + memcpy(palette_entry, Light.entry_color, LST_MAX); + if (!Light.pwm_multi_channels && LST_COLDWARM == Light.subtype) { + palette_entry[3] = palette_entry[0]; + palette_entry[4] = palette_entry[1]; + } + palette_entry += LST_MAX; + } + Light.palette_count++; + } + if (!p) break; + *p = ' '; + color = p + 1; + } + if (pass) break; + if (!(Light.palette = (uint8_t *)malloc(Light.palette_count * LST_MAX))) return; + palette_entry = Light.palette; + } + } + } + + char palette_str[5 * Light.subtype * Light.palette_count + 3]; + const char * fmt; + p = palette_str; + *p++ = '['; + if (Light.palette_count) { + palette_entry = Light.palette; + if (Settings.flag.decimal_text) { // SetOption17 - Switch between decimal or hexadecimal output + for (int entry = 0; entry < Light.palette_count; entry++) { + *p++ = '"'; + for (uint32_t i = 0; i < Light.subtype; i++) { + if (i > 0) *p++ = ','; + p += sprintf_P(p, PSTR("%d"), palette_entry[i]); + } + palette_entry += LST_MAX; + } + *p++ = '"'; + *p++ = ','; + } + else { + for (int entry = 0; entry < Light.palette_count; entry++) { + for (uint32_t i = 0; i < Light.subtype; i++) { + p += sprintf_P(p, PSTR("%02X"), palette_entry[i]); + } + palette_entry += LST_MAX; + } + *p++ = ','; + } + p--; + } + *p++ = ']'; + *p = 0; + ResponseCmndChar(palette_str); +} +#endif // USE_LIGHT_PALETTE + void CmndUndocA(void) { // Theos legacy status diff --git a/tasmota/xdrv_35_pwm_dimmer.ino b/tasmota/xdrv_35_pwm_dimmer.ino index 555ccbd4b..1f04db8c6 100644 --- a/tasmota/xdrv_35_pwm_dimmer.ino +++ b/tasmota/xdrv_35_pwm_dimmer.ino @@ -400,18 +400,10 @@ void PWMDimmerHandleButton(void) else #endif // USE_PWM_DIMMER_REMOTE uint8_value = Light.fixed_color_index; - if (is_down_button) { - if (uint8_value) - uint8_value--; - else - uint8_value = MAX_FIXED_COLOR; - } - else { - if (uint8_value < MAX_FIXED_COLOR) - uint8_value++; - else - uint8_value = 0; - } + if (is_down_button) + uint8_value--; + else + uint8_value++; #ifdef USE_PWM_DIMMER_REMOTE if (!active_device_is_local) active_remote_pwm_dimmer->fixed_color_index = uint8_value;