diff --git a/CHANGELOG.md b/CHANGELOG.md index c53f4e74..6c282d66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,25 @@ ### Builds after release 0.12.0 +#### Build 2107021 + +- Added WebSockets support to UI + +#### Build 2107020 + +- Send websockets on every state change +- Improved Aurora effect + +#### Build 2107011 + +- Added MQTT button feedback option (PR #2011) + +#### Build 2107010 + +- Added JSON IR codes (PR #1941) +- Adjusted the width of WiFi and LED settings input fields +- Fixed a minor visual issue with slider trail not reaching thumb on low values + #### Build 2106302 - Fixed settings page broken by using "%" in input fields diff --git a/platformio.ini b/platformio.ini index e9cdf661..80b56a4d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -185,18 +185,18 @@ upload_speed = 115200 # ------------------------------------------------------------------------------ lib_compat_mode = strict lib_deps = - fastled/FastLED @ 3.3.2 - NeoPixelBus @ ^2.6.0 + fastled/FastLED @ 3.4.0 + NeoPixelBus @ 2.6.4 ESPAsyncTCP @ 1.2.0 ESPAsyncUDP AsyncTCP @ 1.0.3 - IRremoteESP8266 @ 2.7.3 + IRremoteESP8266 @ 2.7.18 https://github.com/lorol/LITTLEFS.git https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.2 #For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line #TFT_eSPI #For use SSD1306 OLED display uncomment following - U8g2@~2.27.2 + U8g2@~2.28.8 #For Dallas sensor uncomment following 2 lines OneWire@~2.3.5 ; milesburton/DallasTemperature@^3.9.0 diff --git a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h index 7bd5d2b6..ae63ed47 100644 --- a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h +++ b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h @@ -193,16 +193,16 @@ public: */ void setup() { - // pin retrieved from cfg.json (readFromConfig()) prior to running setup() - if (!pinManager.allocatePin(PIRsensorPin,false)) { - PIRsensorPin = -1; // allocation failed - enabled = false; - DEBUG_PRINTLN(F("PIRSensorSwitch pin allocation failed.")); - } else { - // PIR Sensor mode INPUT_PULLUP - pinMode(PIRsensorPin, INPUT_PULLUP); - if (enabled) { + if (enabled) { + // pin retrieved from cfg.json (readFromConfig()) prior to running setup() + if (PIRsensorPin >= 0 && pinManager.allocatePin(PIRsensorPin,false)) { + // PIR Sensor mode INPUT_PULLUP + pinMode(PIRsensorPin, INPUT_PULLUP); sensorPinState = digitalRead(PIRsensorPin); + } else { + if (PIRsensorPin >= 0) DEBUG_PRINTLN(F("PIRSensorSwitch pin allocation failed.")); + PIRsensorPin = -1; // allocation failed + enabled = false; } } initDone = true; @@ -221,8 +221,8 @@ public: */ void loop() { - // only check sensors 10x/s - if (millis() - lastLoop < 100 || strip.isUpdating()) return; + // only check sensors 4x/s + if (!enabled || millis() - lastLoop < 250 || strip.isUpdating()) return; lastLoop = millis(); if (!updatePIRsensorState()) { diff --git a/usermods/Temperature/usermod_temperature.h b/usermods/Temperature/usermod_temperature.h index f5da7d86..32a12902 100644 --- a/usermods/Temperature/usermod_temperature.h +++ b/usermods/Temperature/usermod_temperature.h @@ -107,19 +107,20 @@ class UsermodTemperature : public Usermod { void setup() { int retries = 10; - // pin retrieved from cfg.json (readFromConfig()) prior to running setup() - if (!pinManager.allocatePin(temperaturePin,false)) { - temperaturePin = -1; // allocation failed - enabled = false; - DEBUG_PRINTLN(F("Temperature pin allocation failed.")); - } else { - if (enabled) { - // config says we are enabled + if (enabled) { + // config says we are enabled + DEBUG_PRINTLN(F("Allocating temperature pin...")); + // pin retrieved from cfg.json (readFromConfig()) prior to running setup() + if (temperaturePin >= 0 && pinManager.allocatePin(temperaturePin)) { oneWire = new OneWire(temperaturePin); if (!oneWire->reset()) enabled = false; // resetting 1-Wire bus yielded an error else while ((enabled=findSensor()) && retries--) delay(25); // try to find sensor + } else { + if (temperaturePin >= 0) DEBUG_PRINTLN(F("Temperature pin allocation failed.")); + temperaturePin = -1; // allocation failed + enabled = false; } } initDone = true; diff --git a/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h b/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h index 0e21f085..91fea6d8 100644 --- a/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h +++ b/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h @@ -133,6 +133,7 @@ class FourLineDisplayUsermod : public Usermod { if (type == NONE) return; if (!pinManager.allocatePin(sclPin)) { sclPin = -1; type = NONE; return;} if (!pinManager.allocatePin(sdaPin)) { pinManager.deallocatePin(sclPin); sclPin = sdaPin = -1; type = NONE; return; } + DEBUG_PRINTLN(F("Allocating display.")); switch (type) { case SSD1306: #ifdef ESP8266 @@ -184,12 +185,19 @@ class FourLineDisplayUsermod : public Usermod { type = NONE; return; } - (static_cast(u8x8))->begin(); + initDone = true; + if (u8x8 != nullptr) { + DEBUG_PRINTLN(F("Starting display.")); + (static_cast(u8x8))->begin(); + } else { + DEBUG_PRINTLN(F("Display init failed.")); + type = NONE; + return; + } setFlipMode(flip); setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255 setPowerSave(0); drawString(0, 0, "Loading..."); - initDone = true; } // gets called every time WiFi is (re-)connected. Initialize own network @@ -648,6 +656,7 @@ class FourLineDisplayUsermod : public Usermod { type = newType; DEBUG_PRINTLN(F(" config loaded.")); } else { + DEBUG_PRINTLN(F(" config (re)loaded.")); // changing parameters from settings page if (sclPin!=newScl || sdaPin!=newSda || type!=newType) { if (type != NONE) delete (static_cast(u8x8)); @@ -665,7 +674,6 @@ class FourLineDisplayUsermod : public Usermod { setContrast(contrast); setFlipMode(flip); if (needsRedraw && !wakeDisplay()) redraw(true); - DEBUG_PRINTLN(F(" config (re)loaded.")); } // use "return !top["newestParameter"].isNull();" when updating Usermod with new features return true; diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 8364083f..246c4b2c 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -3926,7 +3926,6 @@ uint16_t WS2812FX::mode_tv_simulator(void) { */ //CONFIG -#define BACKLIGHT 5 #define W_MAX_COUNT 20 //Number of simultaneous waves #define W_MAX_SPEED 6 //Higher number, higher speed #define W_WIDTH_FACTOR 6 //Higher number, smaller waves @@ -4051,9 +4050,13 @@ uint16_t WS2812FX::mode_aurora(void) { } } + uint8_t backlight = 1; //dimmer backlight if less active colors + if (SEGCOLOR(0)) backlight++; + if (SEGCOLOR(1)) backlight++; + if (SEGCOLOR(2)) backlight++; //Loop through LEDs to determine color for(int i = 0; i < SEGLEN; i++) { - CRGB mixedRgb = CRGB(BACKLIGHT, BACKLIGHT, BACKLIGHT); + CRGB mixedRgb = CRGB(backlight, backlight, backlight); //For each LED we must check each wave if it is "active" at this position. //If there are multiple waves active on a LED we multiply their values. @@ -4065,7 +4068,7 @@ uint16_t WS2812FX::mode_aurora(void) { } } - setPixelColor(i, mixedRgb[0], mixedRgb[1], mixedRgb[2], BACKLIGHT); + setPixelColor(i, mixedRgb[0], mixedRgb[1], mixedRgb[2]); } return FRAMETIME; diff --git a/wled00/FX.h b/wled00/FX.h index ea431158..81f5e7d1 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -319,6 +319,27 @@ class WS2812FX { vLength = (vLength + 1) /2; // divide by 2 if mirror, leave at least a single LED return vLength; } + uint8_t differs(Segment& b) { + uint8_t d = 0; + if (start != b.start) d |= SEG_DIFFERS_BOUNDS; + if (stop != b.stop) d |= SEG_DIFFERS_BOUNDS; + if (offset != b.offset) d |= SEG_DIFFERS_GSO; + if (grouping != b.grouping) d |= SEG_DIFFERS_GSO; + if (spacing != b.spacing) d |= SEG_DIFFERS_GSO; + if (opacity != b.opacity) d |= SEG_DIFFERS_BRI; + if (mode != b.mode) d |= SEG_DIFFERS_FX; + if (speed != b.speed) d |= SEG_DIFFERS_FX; + if (intensity != b.intensity) d |= SEG_DIFFERS_FX; + if (palette != b.palette) d |= SEG_DIFFERS_FX; + + if ((options & 0b00101111) != (b.options & 0b00101111)) d |= SEG_DIFFERS_OPT; + for (uint8_t i = 0; i < NUM_COLORS; i++) + { + if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL; + } + + return d; + } } segment; // segment runtime parameters @@ -619,7 +640,6 @@ class WS2812FX { gammaCorrectBri = false, gammaCorrectCol = true, applyToAllSelected = true, - segmentsAreIdentical(Segment* a, Segment* b), setEffectConfig(uint8_t m, uint8_t s, uint8_t i, uint8_t p), // return true if the strip is being sent pixel updates isUpdating(void); diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index b5adde68..63d46d4f 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1018,23 +1018,6 @@ uint32_t WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8 return crgb_to_col(fastled_col); } -//@returns `true` if color, mode, speed, intensity and palette match -bool WS2812FX::segmentsAreIdentical(Segment* a, Segment* b) -{ - //if (a->start != b->start) return false; - //if (a->stop != b->stop) return false; - for (uint8_t i = 0; i < NUM_COLORS; i++) - { - if (a->colors[i] != b->colors[i]) return false; - } - if (a->mode != b->mode) return false; - if (a->speed != b->speed) return false; - if (a->intensity != b->intensity) return false; - if (a->palette != b->palette) return false; - //if (a->getOption(SEG_OPTION_REVERSED) != b->getOption(SEG_OPTION_REVERSED)) return false; - return true; -} - //load custom mapping table from JSON file void WS2812FX::deserializeMap(uint8_t n) { diff --git a/wled00/button.cpp b/wled00/button.cpp index afe10e02..ff0f4ee6 100644 --- a/wled00/button.cpp +++ b/wled00/button.cpp @@ -19,7 +19,7 @@ void shortPressAction(uint8_t b) } // publish MQTT message - if (WLED_MQTT_CONNECTED) { + if (buttonPublishMqtt && WLED_MQTT_CONNECTED) { char subuf[64]; sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b); mqtt->publish(subuf, 0, false, "short"); @@ -74,7 +74,7 @@ void handleSwitch(uint8_t b) } // publish MQTT message - if (WLED_MQTT_CONNECTED) { + if (buttonPublishMqtt && WLED_MQTT_CONNECTED) { char subuf[64]; if (buttonType[b] == BTN_TYPE_PIR_SENSOR) sprintf_P(subuf, PSTR("%s/motion/%d"), mqttDeviceTopic, (int)b); else sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b); @@ -96,7 +96,7 @@ void handleAnalog(uint8_t b) if (buttonType[b] == BTN_TYPE_ANALOG_INVERTED) aRead = 255 - aRead; - // remove noise & reduce frequency of UI updates (3*13mV) + // remove noise & reduce frequency of UI updates aRead &= 0xFC; if (oldRead[b] == aRead) return; // no change in reading @@ -199,7 +199,7 @@ void handleButton() else _setRandomColor(false,true); // publish MQTT message - if (WLED_MQTT_CONNECTED) { + if (buttonPublishMqtt && WLED_MQTT_CONNECTED) { char subuf[64]; sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b); mqtt->publish(subuf, 0, false, "long"); @@ -227,7 +227,7 @@ void handleButton() applyPreset(macroDoublePress[b]); // publish MQTT message - if (WLED_MQTT_CONNECTED) { + if (buttonPublishMqtt && WLED_MQTT_CONNECTED) { char subuf[64]; sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b); mqtt->publish(subuf, 0, false, "double"); diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 45e3bce8..6ffaceef 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -121,7 +121,8 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { if (lC > ledCount) ledCount = lC; // fix incorrect total length (honour analog setup) // read multiple button configuration - JsonArray hw_btn_ins = hw[F("btn")]["ins"]; + JsonObject btn_obj = hw["btn"]; + JsonArray hw_btn_ins = btn_obj[F("ins")]; if (!hw_btn_ins.isNull()) { uint8_t s = 0; for (JsonObject btn : hw_btn_ins) { @@ -133,7 +134,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { } else { btnPin[s] = -1; } - JsonArray hw_btn_ins_0_macros = btn[F("macros")]; + JsonArray hw_btn_ins_0_macros = btn["macros"]; CJSON(macroButton[s], hw_btn_ins_0_macros[0]); CJSON(macroLongPress[s],hw_btn_ins_0_macros[1]); CJSON(macroDoublePress[s], hw_btn_ins_0_macros[2]); @@ -158,7 +159,8 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { macroDoublePress[s] = 0; } } - CJSON(touchThreshold,hw[F("btn")][F("tt")]); + CJSON(touchThreshold,btn_obj[F("tt")]); + CJSON(buttonPublishMqtt,btn_obj["mqtt"]); int hw_ir_pin = hw["ir"]["pin"] | -2; // 4 if (hw_ir_pin > -2) { @@ -190,8 +192,8 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { CJSON(briMultiplier, light[F("scale-bri")]); CJSON(strip.paletteBlend, light[F("pal-mode")]); - float light_gc_bri = light[F("gc")]["bri"]; - float light_gc_col = light[F("gc")]["col"]; // 2.8 + float light_gc_bri = light["gc"]["bri"]; + float light_gc_col = light["gc"]["col"]; // 2.8 if (light_gc_bri > 1.5) strip.gammaCorrectBri = true; else if (light_gc_bri > 0.5) strip.gammaCorrectBri = false; if (light_gc_col > 1.5) strip.gammaCorrectCol = true; @@ -210,7 +212,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { if (nightlightDelayMinsDefault != prev) nightlightDelayMins = nightlightDelayMinsDefault; CJSON(nightlightTargetBri, light_nl[F("tbri")]); - CJSON(macroNl, light_nl[F("macro")]); + CJSON(macroNl, light_nl["macro"]); JsonObject def = doc[F("def")]; CJSON(bootPreset, def[F("ps")]); @@ -234,10 +236,10 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { prev = notifyDirectDefault; CJSON(notifyDirectDefault, if_sync_send[F("dir")]); if (notifyDirectDefault != prev) notifyDirect = notifyDirectDefault; - CJSON(notifyButton, if_sync_send[F("btn")]); - CJSON(notifyAlexa, if_sync_send[F("va")]); - CJSON(notifyHue, if_sync_send[F("hue")]); - CJSON(notifyMacro, if_sync_send[F("macro")]); + CJSON(notifyButton, if_sync_send["btn"]); + CJSON(notifyAlexa, if_sync_send["va"]); + CJSON(notifyHue, if_sync_send["hue"]); + CJSON(notifyMacro, if_sync_send["macro"]); CJSON(notifyTwice, if_sync_send[F("twice")]); JsonObject if_nodes = interfaces["nodes"]; @@ -261,10 +263,10 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { CJSON(arlsDisableGammaCorrection, if_live[F("no-gc")]); // false CJSON(arlsOffset, if_live[F("offset")]); // 0 - CJSON(alexaEnabled, interfaces[F("va")][F("alexa")]); // false + CJSON(alexaEnabled, interfaces["va"][F("alexa")]); // false - CJSON(macroAlexaOn, interfaces[F("va")][F("macros")][0]); - CJSON(macroAlexaOff, interfaces[F("va")][F("macros")][1]); + CJSON(macroAlexaOn, interfaces["va"]["macros"][0]); + CJSON(macroAlexaOff, interfaces["va"]["macros"][1]); #ifndef WLED_DISABLE_BLYNK const char* apikey = interfaces["blynk"][F("token")] | "Hidden"; @@ -291,7 +293,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { #endif #ifndef WLED_DISABLE_HUESYNC - JsonObject if_hue = interfaces[F("hue")]; + JsonObject if_hue = interfaces["hue"]; CJSON(huePollingEnabled, if_hue["en"]); CJSON(huePollLightId, if_hue["id"]); tdd = if_hue[F("iv")] | -1; @@ -339,7 +341,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { CJSON(countdownHour, cntdwn_goal[3]); CJSON(countdownMin, cntdwn_goal[4]); CJSON(countdownSec, cntdwn_goal[5]); - CJSON(macroCountdown, cntdwn[F("macro")]); + CJSON(macroCountdown, cntdwn["macro"]); setCountdown(); JsonArray timers = tm["ins"]; @@ -349,7 +351,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { if (it<8 && timer[F("hour")]==255) it=8; // hour==255 -> sunrise/sunset CJSON(timerHours[it], timer[F("hour")]); CJSON(timerMinutes[it], timer["min"]); - CJSON(timerMacro[it], timer[F("macro")]); + CJSON(timerMacro[it], timer["macro"]); byte dowPrev = timerWeekday[it]; //note: act is currently only 0 or 1. @@ -538,6 +540,7 @@ void serializeConfig() { hw_btn_ins_0_macros.add(macroDoublePress[i]); } hw_btn[F("tt")] = touchThreshold; + hw_btn["mqtt"] = buttonPublishMqtt; JsonObject hw_ir = hw.createNestedObject("ir"); hw_ir["pin"] = irPin; @@ -567,7 +570,7 @@ void serializeConfig() { light_nl[F("mode")] = nightlightMode; light_nl["dur"] = nightlightDelayMinsDefault; light_nl[F("tbri")] = nightlightTargetBri; - light_nl[F("macro")] = macroNl; + light_nl["macro"] = macroNl; JsonObject def = doc.createNestedObject("def"); def[F("ps")] = bootPreset; @@ -587,10 +590,10 @@ void serializeConfig() { JsonObject if_sync_send = if_sync.createNestedObject("send"); if_sync_send[F("dir")] = notifyDirect; - if_sync_send[F("btn")] = notifyButton; - if_sync_send[F("va")] = notifyAlexa; - if_sync_send[F("hue")] = notifyHue; - if_sync_send[F("macro")] = notifyMacro; + if_sync_send["btn"] = notifyButton; + if_sync_send["va"] = notifyAlexa; + if_sync_send["hue"] = notifyHue; + if_sync_send["macro"] = notifyMacro; if_sync_send[F("twice")] = notifyTwice; JsonObject if_nodes = interfaces.createNestedObject("nodes"); @@ -682,7 +685,7 @@ void serializeConfig() { JsonArray goal = cntdwn.createNestedArray(F("goal")); goal.add(countdownYear); goal.add(countdownMonth); goal.add(countdownDay); goal.add(countdownHour); goal.add(countdownMin); goal.add(countdownSec); - cntdwn[F("macro")] = macroCountdown; + cntdwn["macro"] = macroCountdown; JsonArray timers_ins = timers.createNestedArray("ins"); @@ -692,7 +695,7 @@ void serializeConfig() { timers_ins0["en"] = (timerWeekday[i] & 0x01); timers_ins0[F("hour")] = timerHours[i]; timers_ins0["min"] = timerMinutes[i]; - timers_ins0[F("macro")] = timerMacro[i]; + timers_ins0["macro"] = timerMacro[i]; timers_ins0[F("dow")] = timerWeekday[i] >> 1; } @@ -752,7 +755,7 @@ bool deserializeConfigSec() { #endif #ifndef WLED_DISABLE_HUESYNC - getStringFromJson(hueApiKey, interfaces[F("hue")][F("key")], 47); + getStringFromJson(hueApiKey, interfaces["hue"][F("key")], 47); #endif JsonObject ota = doc["ota"]; diff --git a/wled00/const.h b/wled00/const.h index de521c88..882d51ba 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -77,6 +77,7 @@ #define NOTIFIER_CALL_MODE_PRESET_CYCLE 8 #define NOTIFIER_CALL_MODE_BLYNK 9 #define NOTIFIER_CALL_MODE_ALEXA 10 +#define NOTIFIER_CALL_MODE_WS_SEND 11 //special call mode, not for notifier, updates websocket only //RGB to RGBW conversion mode #define RGBW_MODE_MANUAL_ONLY 0 //No automatic white channel calculation. Manual white channel slider @@ -195,6 +196,14 @@ #define SEG_OPTION_FREEZE 5 //Segment contents will not be refreshed #define SEG_OPTION_TRANSITIONAL 7 +//Segment differs return byte +#define SEG_DIFFERS_BRI 0x01 +#define SEG_DIFFERS_OPT 0x02 +#define SEG_DIFFERS_COL 0x04 +#define SEG_DIFFERS_FX 0x08 +#define SEG_DIFFERS_BOUNDS 0x10 +#define SEG_DIFFERS_GSO 0x20 + //Playlist option byte #define PL_OPTION_SHUFFLE 0x01 diff --git a/wled00/data/index.css b/wled00/data/index.css index 668a9bd4..bbc2c79e 100644 --- a/wled00/data/index.css +++ b/wled00/data/index.css @@ -140,6 +140,10 @@ button { padding: 0; vertical-align: middle; } +.segt TD.h { + font-size: 13px; + padding: 2px 0 0; +} .keytd { text-align: left; @@ -573,6 +577,8 @@ input[type=range]::-moz-range-thumb { transition-duration: 0.5s; -webkit-backface-visibility: hidden; -webkit-transform:translate3d(0,0,0); + overflow: clip; + text-overflow: clip; } .btn-s { @@ -595,9 +601,11 @@ input[type=range]::-moz-range-thumb { } .btn-xs, .btn-pl-del, .btn-pl-add { width: 42px; + height: 42px; } .btn-pl-del, .btn-pl-add { margin: 0; + white-space: nowrap; } #qcs-w { margin-top: 10px; @@ -975,13 +983,13 @@ input[type=number]::-webkit-outer-spin-button { z-index: 1; } -#selectPalette .lstI.selected { - top: 80px; - bottom: 0; +#pallist .lstI.selected { + top: 27px; + bottom: -11px; } -#selectPalette .lstI.sticky { - top: 40px; +#pallist .lstI.sticky { + top: -11px; } .lstI.sticky { diff --git a/wled00/data/index.htm b/wled00/data/index.htm index c148eeba..0679cb24 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -102,11 +102,11 @@

Color palette

- +
-
+