diff --git a/tools/cdata.js b/tools/cdata.js index 90619ba67..bb9854608 100644 --- a/tools/cdata.js +++ b/tools/cdata.js @@ -219,36 +219,10 @@ function writeChunks(srcDir, specs, resultFile) { } writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h", 'index'); -writeHtmlGzipped("wled00/data/simple.htm", "wled00/html_simple.h", 'simple'); writeHtmlGzipped("wled00/data/pixart/pixart.htm", "wled00/html_pixart.h", 'pixart'); writeHtmlGzipped("wled00/data/cpal/cpal.htm", "wled00/html_cpal.h", 'cpal'); writeHtmlGzipped("wled00/data/pxmagic/pxmagic.htm", "wled00/html_pxmagic.h", 'pxmagic'); -/* -writeChunks( - "wled00/data", - [ - { - file: "simple.css", - name: "PAGE_simpleCss", - method: "gzip", - filter: "css-minify", - }, - { - file: "simple.js", - name: "PAGE_simpleJs", - method: "gzip", - filter: "js-minify", - }, - { - file: "simple.htm", - name: "PAGE_simple", - method: "gzip", - filter: "html-minify-ui", - } - ], - "wled00/html_simplex.h" -); -*/ + writeChunks( "wled00/data", [ @@ -406,16 +380,6 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()====="; file: "favicon.ico", name: "favicon", method: "binary", - }, - { - file: "iro.js", - name: "iroJs", - method: "gzip" - }, - { - file: "rangetouch.js", - name: "rangetouchJs", - method: "gzip" } ], "wled00/html_other.h" diff --git a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h index 8a4b9a608..4d0777037 100644 --- a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h +++ b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h @@ -50,8 +50,7 @@ private: volatile unsigned long offTimerStart = 0; // off timer start time volatile bool PIRtriggered = false; // did PIR trigger? - byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // notification mode for stateUpdated(): CALL_MODE_NO_NOTIFY or CALL_MODE_DIRECT_CHANGE - byte sensorPinState = LOW; // current PIR sensor pin state + bool sensorPinState = LOW; // current PIR sensor pin state bool initDone = false; // status of initialization unsigned long lastLoop = 0; @@ -70,6 +69,7 @@ private: // Home Assistant bool HomeAssistantDiscovery = false; // is HA discovery turned on + int16_t idx = -1; // Domoticz virtual switch idx // strings to reduce flash memory usage (used more than twice) static const char _name[]; @@ -81,8 +81,8 @@ private: static const char _mqttOnly[]; static const char _offOnly[]; static const char _haDiscovery[]; - static const char _notify[]; static const char _override[]; + static const char _domoticzIDX[]; /** * check if it is daytime @@ -94,7 +94,7 @@ private: * switch strip on/off */ void switchStrip(bool switchOn); - void publishMqtt(const char* state); + void publishMqtt(bool switchOn); // Create an MQTT Binary Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop. void publishHomeAssistantAutodiscovery(); @@ -194,8 +194,8 @@ const char PIRsensorSwitch::_nightTime[] PROGMEM = "nighttime-only"; const char PIRsensorSwitch::_mqttOnly[] PROGMEM = "mqtt-only"; const char PIRsensorSwitch::_offOnly[] PROGMEM = "off-only"; const char PIRsensorSwitch::_haDiscovery[] PROGMEM = "HA-discovery"; -const char PIRsensorSwitch::_notify[] PROGMEM = "notifications"; const char PIRsensorSwitch::_override[] PROGMEM = "override"; +const char PIRsensorSwitch::_domoticzIDX[] PROGMEM = "domoticz-idx"; bool PIRsensorSwitch::isDayTime() { updateLocalTime(); @@ -235,24 +235,24 @@ void PIRsensorSwitch::switchStrip(bool switchOn) prevPlaylist = 0; prevPreset = 255; } - applyPreset(m_onPreset, NotifyUpdateMode); + applyPreset(m_onPreset, CALL_MODE_BUTTON_PRESET); return; } // preset not assigned if (bri == 0) { bri = briLast; - stateUpdated(NotifyUpdateMode); + stateUpdated(CALL_MODE_BUTTON); } } else { if (m_offPreset) { - applyPreset(m_offPreset, NotifyUpdateMode); + applyPreset(m_offPreset, CALL_MODE_BUTTON_PRESET); return; } else if (prevPlaylist) { - if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(prevPlaylist, NotifyUpdateMode); + if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(prevPlaylist, CALL_MODE_BUTTON_PRESET); prevPlaylist = 0; return; } else if (prevPreset) { - if (prevPreset<255) { if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(prevPreset, NotifyUpdateMode); } + if (prevPreset<255) { if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(prevPreset, CALL_MODE_BUTTON_PRESET); } else { if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyTemporaryPreset(); } prevPreset = 0; return; @@ -261,19 +261,29 @@ void PIRsensorSwitch::switchStrip(bool switchOn) if (bri != 0) { briLast = bri; bri = 0; - stateUpdated(NotifyUpdateMode); + stateUpdated(CALL_MODE_BUTTON); } } } -void PIRsensorSwitch::publishMqtt(const char* state) +void PIRsensorSwitch::publishMqtt(bool switchOn) { #ifndef WLED_DISABLE_MQTT //Check if MQTT Connected, otherwise it will crash the 8266 if (WLED_MQTT_CONNECTED) { - char buf[64]; + char buf[128]; sprintf_P(buf, PSTR("%s/motion"), mqttDeviceTopic); //max length: 33 + 7 = 40 - mqtt->publish(buf, 0, false, state); + mqtt->publish(buf, 0, false, switchOn?"on":"off"); + // Domoticz formatted message + if (idx > 0) { + StaticJsonDocument <128> msg; + msg[F("idx")] = idx; + msg[F("RSSI")] = WiFi.RSSI(); + msg[F("command")] = F("switchlight"); + msg[F("switchcmd")] = switchOn ? F("On") : F("Off"); + serializeJson(msg, buf, 128); + mqtt->publish("domoticz/in", 0, false, buf); + } } #endif } @@ -322,13 +332,11 @@ bool PIRsensorSwitch::updatePIRsensorState() if (sensorPinState == HIGH) { offTimerStart = 0; if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true); - else if (NotifyUpdateMode != CALL_MODE_NO_NOTIFY) updateInterfaces(CALL_MODE_WS_SEND); - publishMqtt("on"); } else { // start switch off timer offTimerStart = millis(); - if (NotifyUpdateMode != CALL_MODE_NO_NOTIFY) updateInterfaces(CALL_MODE_WS_SEND); } + publishMqtt(sensorPinState == HIGH); return true; } return false; @@ -338,11 +346,7 @@ bool PIRsensorSwitch::handleOffTimer() { if (offTimerStart > 0 && millis() - offTimerStart > m_switchOffDelay) { offTimerStart = 0; - if (enabled == true) { - if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()) || PIRtriggered)) switchStrip(false); - else if (NotifyUpdateMode != CALL_MODE_NO_NOTIFY) updateInterfaces(CALL_MODE_WS_SEND); - publishMqtt("off"); - } + if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()) || PIRtriggered)) switchStrip(false); return true; } return false; @@ -482,14 +486,13 @@ void PIRsensorSwitch::addToConfig(JsonObject &root) top[FPSTR(_offOnly)] = m_offOnly; top[FPSTR(_override)] = m_override; top[FPSTR(_haDiscovery)] = HomeAssistantDiscovery; - top[FPSTR(_notify)] = (NotifyUpdateMode != CALL_MODE_NO_NOTIFY); + top[FPSTR(_domoticzIDX)] = idx; DEBUG_PRINTLN(F("PIR config saved.")); } void PIRsensorSwitch::appendConfigData() { oappend(SET_F("addInfo('PIRsensorSwitch:HA-discovery',1,'HA=Home Assistant');")); // 0 is field type, 1 is actual field - oappend(SET_F("addInfo('PIRsensorSwitch:notifications',1,'Periodic WS updates');")); // 0 is field type, 1 is actual field oappend(SET_F("addInfo('PIRsensorSwitch:override',1,'Cancel timer on change');")); // 0 is field type, 1 is actual field } @@ -521,8 +524,7 @@ bool PIRsensorSwitch::readFromConfig(JsonObject &root) m_offOnly = top[FPSTR(_offOnly)] | m_offOnly; m_override = top[FPSTR(_override)] | m_override; HomeAssistantDiscovery = top[FPSTR(_haDiscovery)] | HomeAssistantDiscovery; - - NotifyUpdateMode = top[FPSTR(_notify)] ? CALL_MODE_DIRECT_CHANGE : CALL_MODE_NO_NOTIFY; + idx = top[FPSTR(_domoticzIDX)] | idx; if (!initDone) { // reading config prior to setup() @@ -549,5 +551,5 @@ bool PIRsensorSwitch::readFromConfig(JsonObject &root) DEBUG_PRINTLN(F(" config (re)loaded.")); } // use "return !top["newestParameter"].isNull();" when updating Usermod with new features - return !top[FPSTR(_override)].isNull(); + return !top[FPSTR(_domoticzIDX)].isNull(); } diff --git a/usermods/Temperature/usermod_temperature.h b/usermods/Temperature/usermod_temperature.h index a15baf878..1c2d67dee 100644 --- a/usermods/Temperature/usermod_temperature.h +++ b/usermods/Temperature/usermod_temperature.h @@ -48,6 +48,7 @@ class UsermodTemperature : public Usermod { bool enabled = true; bool HApublished = false; + int16_t idx = -1; // Domoticz virtual sensor idx // strings to reduce flash memory usage (used more than twice) static const char _name[]; @@ -55,6 +56,7 @@ class UsermodTemperature : public Usermod { static const char _readInterval[]; static const char _parasite[]; static const char _parasitePin[]; + static const char _domoticzIDX[]; //Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013 float readDallas(); @@ -264,7 +266,7 @@ void UsermodTemperature::loop() { #ifndef WLED_DISABLE_MQTT if (WLED_MQTT_CONNECTED) { - char subuf[64]; + char subuf[128]; strcpy(subuf, mqttDeviceTopic); if (temperature > -100.0f) { // dont publish super low temperature as the graph will get messed up @@ -274,6 +276,15 @@ void UsermodTemperature::loop() { mqtt->publish(subuf, 0, false, String(getTemperatureC()).c_str()); strcat_P(subuf, PSTR("_f")); mqtt->publish(subuf, 0, false, String(getTemperatureF()).c_str()); + if (idx > 0) { + StaticJsonDocument <128> msg; + msg[F("idx")] = idx; + msg[F("RSSI")] = WiFi.RSSI(); + msg[F("nvalue")] = 0; + msg[F("svalue")] = String(getTemperatureC()); + serializeJson(msg, subuf, 127); + mqtt->publish("domoticz/in", 0, false, subuf); + } } else { // publish something else to indicate status? } @@ -360,6 +371,7 @@ void UsermodTemperature::addToConfig(JsonObject &root) { top[FPSTR(_readInterval)] = readingInterval / 1000; top[FPSTR(_parasite)] = parasite; top[FPSTR(_parasitePin)] = parasitePin; + top[FPSTR(_domoticzIDX)] = idx; DEBUG_PRINTLN(F("Temperature config saved.")); } @@ -386,6 +398,7 @@ bool UsermodTemperature::readFromConfig(JsonObject &root) { readingInterval = min(120,max(10,(int)readingInterval)) * 1000; // convert to ms parasite = top[FPSTR(_parasite)] | parasite; parasitePin = top[FPSTR(_parasitePin)] | parasitePin; + idx = top[FPSTR(_domoticzIDX)] | idx; if (!initDone) { // first run: reading from cfg.json @@ -406,7 +419,7 @@ bool UsermodTemperature::readFromConfig(JsonObject &root) { } } // use "return !top["newestParameter"].isNull();" when updating Usermod with new features - return !top[FPSTR(_parasitePin)].isNull(); + return !top[FPSTR(_domoticzIDX)].isNull(); } void UsermodTemperature::appendConfigData() { @@ -430,3 +443,4 @@ const char UsermodTemperature::_enabled[] PROGMEM = "enabled"; const char UsermodTemperature::_readInterval[] PROGMEM = "read-interval-s"; const char UsermodTemperature::_parasite[] PROGMEM = "parasite-pwr"; const char UsermodTemperature::_parasitePin[] PROGMEM = "parasite-pwr-pin"; +const char UsermodTemperature::_domoticzIDX[] PROGMEM = "domoticz-idx"; diff --git a/usermods/multi_relay/usermod_multi_relay.h b/usermods/multi_relay/usermod_multi_relay.h index 0bf1871c7..1342ab6dc 100644 --- a/usermods/multi_relay/usermod_multi_relay.h +++ b/usermods/multi_relay/usermod_multi_relay.h @@ -534,10 +534,10 @@ void MultiRelay::setup() { * loop() is called continuously. Here you can check for events, read sensors, etc. */ void MultiRelay::loop() { - yield(); - if (!enabled || strip.isUpdating()) return; - static unsigned long lastUpdate = 0; + yield(); + if (!enabled || (strip.isUpdating() && millis() - lastUpdate < 100)) return; + if (millis() - lastUpdate < 100) return; // update only 10 times/s lastUpdate = millis(); @@ -803,13 +803,6 @@ bool MultiRelay::readFromConfig(JsonObject &root) { _relay[i].external = top[parName][FPSTR(_external)] | _relay[i].external; _relay[i].delay = top[parName][FPSTR(_delay_str)] | _relay[i].delay; _relay[i].button = top[parName][FPSTR(_button)] | _relay[i].button; - // begin backwards compatibility (beta) remove when 0.13 is released - parName += '-'; - _relay[i].pin = top[parName+"pin"] | _relay[i].pin; - _relay[i].invert = top[parName+FPSTR(_activeHigh)] | _relay[i].invert; - _relay[i].external = top[parName+FPSTR(_external)] | _relay[i].external; - _relay[i].delay = top[parName+FPSTR(_delay_str)] | _relay[i].delay; - // end compatibility _relay[i].delay = min(600,max(0,abs((int)_relay[i].delay))); // bounds checking max 10min } diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 692a29857..d53c99f4a 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -144,10 +144,13 @@ Segment& Segment::operator= (Segment &&orig) noexcept { } bool IRAM_ATTR Segment::allocateData(size_t len) { - if (data && _dataLen == len) return true; //already allocated + if (data && _dataLen >= len) { // already allocated enough (reduce fragmentation) + if (call == 0) memset(data, 0, len); // erase buffer if called during effect initialisation + return true; + } //DEBUG_PRINTF("-- Allocating data (%d): %p\n", len, this); deallocateData(); - if (len == 0) return(false); // nothing to do + if (len == 0) return false; // nothing to do if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) { // not enough memory DEBUG_PRINT(F("!!! Effect RAM depleted: ")); @@ -156,7 +159,7 @@ bool IRAM_ATTR Segment::allocateData(size_t len) { } // do not use SPI RAM on ESP32 since it is slow data = (byte*) malloc(len); - if (!data) { DEBUG_PRINTLN(F("!!! Allocation failed. !!!")); return false; } //allocation failed + if (!data) { DEBUG_PRINTLN(F("!!! Allocation failed. !!!")); return false; } // allocation failed Segment::addUsedSegmentData(len); //DEBUG_PRINTF("--- Allocated data (%p): %d/%d -> %p\n", this, len, Segment::getUsedSegmentData(), data); _dataLen = len; @@ -1021,15 +1024,16 @@ void Segment::blur(uint8_t blur_amount) { */ uint32_t Segment::color_wheel(uint8_t pos) { if (palette) return color_from_palette(pos, false, true, 0); + uint8_t w = W(currentColor(0)); pos = 255 - pos; if (pos < 85) { - return ((uint32_t)(255 - pos * 3) << 16) | ((uint32_t)(0) << 8) | (pos * 3); + return RGBW32((255 - pos * 3), 0, (pos * 3), w); } else if(pos < 170) { pos -= 85; - return ((uint32_t)(0) << 16) | ((uint32_t)(pos * 3) << 8) | (255 - pos * 3); + return RGBW32(0, (pos * 3), (255 - pos * 3), w); } else { pos -= 170; - return ((uint32_t)(pos * 3) << 16) | ((uint32_t)(255 - pos * 3) << 8) | (0); + return RGBW32((pos * 3), (255 - pos * 3), 0, w); } } @@ -1043,13 +1047,10 @@ uint32_t Segment::color_wheel(uint8_t pos) { * @returns Single color from palette */ uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri) { + uint32_t color = gamma32(currentColor(mcol)); + // default palette or no RGB support on segment - if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) { - uint32_t color = currentColor(mcol); - color = gamma32(color); - if (pbri == 255) return color; - return color_fade(color, pbri, true); - } + if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) return (pbri == 255) ? color : color_fade(color, pbri, true); uint8_t paletteIndex = i; if (mapping && virtualLength() > 1) paletteIndex = (i*255)/(virtualLength() -1); @@ -1058,7 +1059,7 @@ uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_ curPal = currentPalette(curPal, palette); CRGB fastled_col = ColorFromPalette(curPal, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global - return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, 0); + return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, W(color)); } diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 836993441..24f202d6a 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -77,7 +77,7 @@ uint8_t IRAM_ATTR ColorOrderMap::getPixelColorOrder(uint16_t pix, uint8_t defaul uint32_t Bus::autoWhiteCalc(uint32_t c) { uint8_t aWM = _autoWhiteMode; - if (_gAWM < 255) aWM = _gAWM; + if (_gAWM < AW_GLOBAL_DISABLED) aWM = _gAWM; if (aWM == RGBW_MODE_MANUAL_ONLY) return c; uint8_t w = W(c); //ignore auto-white calculation if w>0 and mode DUAL (DUAL behaves as BRIGHTER if w==0) diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index b4ddc6702..9d322749c 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -118,7 +118,7 @@ class Bus { , _needsRefresh(refresh) , _data(nullptr) // keep data access consistent across all types of buses { - _autoWhiteMode = Bus::hasWhite(_type) ? aw : RGBW_MODE_MANUAL_ONLY; + _autoWhiteMode = Bus::hasWhite(type) ? aw : RGBW_MODE_MANUAL_ONLY; }; virtual ~Bus() {} //throw the bus under the bus @@ -154,7 +154,7 @@ class Bus { } virtual bool hasWhite(void) { return Bus::hasWhite(_type); } static bool hasWhite(uint8_t type) { - if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_SK6812_RGBW || type == TYPE_TM1814) return true; // digital types with white channel + if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_SK6812_RGBW || type == TYPE_TM1814 || type == TYPE_UCS8904) return true; // digital types with white channel if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true; // analog types with white channel if (type == TYPE_NET_DDP_RGBW) return true; // network types with white channel return false; diff --git a/wled00/button.cpp b/wled00/button.cpp index fe414ca3f..f1487396a 100644 --- a/wled00/button.cpp +++ b/wled00/button.cpp @@ -99,7 +99,7 @@ bool isButtonPressed(uint8_t i) case BTN_TYPE_TOUCH: case BTN_TYPE_TOUCH_SWITCH: #if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) - if (touchRead(pin) <= touchThreshold) return true; + if (digitalPinToTouchChannel(btnPin[i]) >= 0 && touchRead(pin) <= touchThreshold) return true; #endif break; } @@ -110,6 +110,7 @@ void handleSwitch(uint8_t b) { // isButtonPressed() handles inverted/noninverted logic if (buttonPressedBefore[b] != isButtonPressed(b)) { + DEBUG_PRINT(F("Switch: State changed ")); DEBUG_PRINTLN(b); buttonPressedTime[b] = millis(); buttonPressedBefore[b] = !buttonPressedBefore[b]; } @@ -117,12 +118,15 @@ void handleSwitch(uint8_t b) if (buttonLongPressed[b] == buttonPressedBefore[b]) return; if (millis() - buttonPressedTime[b] > WLED_DEBOUNCE_THRESHOLD) { //fire edge event only after 50ms without change (debounce) + DEBUG_PRINT(F("Switch: Activating ")); DEBUG_PRINTLN(b); if (!buttonPressedBefore[b]) { // on -> off + DEBUG_PRINT(F("Switch: On -> Off ")); DEBUG_PRINTLN(b); if (macroButton[b]) applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET); else { //turn on if (!bri) {toggleOnOff(); stateUpdated(CALL_MODE_BUTTON);} } } else { // off -> on + DEBUG_PRINT(F("Switch: Off -> On ")); DEBUG_PRINTLN(b); if (macroLongPress[b]) applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET); else { //turn off if (bri) {toggleOnOff(); stateUpdated(CALL_MODE_BUTTON);} @@ -154,6 +158,8 @@ void handleAnalog(uint8_t b) static float filteredReading[WLED_MAX_BUTTONS] = {0.0f}; uint16_t rawReading; // raw value from analogRead, scaled to 12bit + DEBUG_PRINT(F("Analog: Reading button ")); DEBUG_PRINTLN(b); + #ifdef ESP8266 rawReading = analogRead(A0) << 2; // convert 10bit read to 12bit #else @@ -162,6 +168,8 @@ void handleAnalog(uint8_t b) #endif yield(); // keep WiFi task running - analog read may take several millis on ESP8266 + DEBUG_PRINT(F("Analog: Raw read = ")); DEBUG_PRINTLN(rawReading); + filteredReading[b] += POT_SMOOTHING * ((float(rawReading) / 16.0f) - filteredReading[b]); // filter raw input, and scale to [0..255] uint16_t aRead = max(min(int(filteredReading[b]), 255), 0); // squash into 8bit if(aRead <= POT_SENSITIVITY) aRead = 0; // make sure that 0 and 255 are used @@ -172,6 +180,8 @@ void handleAnalog(uint8_t b) // remove noise & reduce frequency of UI updates if (abs(int(aRead) - int(oldRead[b])) <= POT_SENSITIVITY) return; // no significant change in reading + DEBUG_PRINT(F("Analog: Filtered read = ")); DEBUG_PRINTLN(aRead); + // Unomment the next lines if you still see flickering related to potentiometer // This waits until strip finishes updating (why: strip was not updating at the start of handleButton() but may have started during analogRead()?) //unsigned long wait_started = millis(); @@ -184,6 +194,7 @@ void handleAnalog(uint8_t b) // if no macro for "short press" and "long press" is defined use brightness control if (!macroButton[b] && !macroLongPress[b]) { + DEBUG_PRINT(F("Analog: Action = ")); DEBUG_PRINTLN(macroDoublePress[b]); // if "double press" macro defines which option to change if (macroDoublePress[b] >= 250) { // global brightness @@ -219,6 +230,7 @@ void handleAnalog(uint8_t b) updateInterfaces(CALL_MODE_BUTTON); } } else { + DEBUG_PRINTLN(F("Analog: No action")); //TODO: // we can either trigger a preset depending on the level (between short and long entries) // or use it for RGBW direct control diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 2b6a4d8a2..250f79af9 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -31,9 +31,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { getStringFromJson(cmDNS, id[F("mdns")], 33); getStringFromJson(serverDescription, id[F("name")], 33); getStringFromJson(alexaInvocationName, id[F("inv")], 33); -#ifdef WLED_ENABLE_SIMPLE_UI CJSON(simplifiedUI, id[F("sui")]); -#endif JsonObject nw = doc["nw"]; #ifndef WLED_DISABLE_ESPNOW @@ -88,11 +86,10 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { // initialize LED pins and lengths prior to other HW (except for ethernet) JsonObject hw_led = hw["led"]; - uint8_t autoWhiteMode = RGBW_MODE_MANUAL_ONLY; uint16_t total = hw_led[F("total")] | strip.getLengthTotal(); CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]); CJSON(strip.milliampsPerLed, hw_led[F("ledma")]); // no longer used - Bus::setGlobalAWMode(hw_led[F("rgbwm")] | 255); + Bus::setGlobalAWMode(hw_led[F("rgbwm")] | AW_GLOBAL_DISABLED); CJSON(correctWB, hw_led["cct"]); CJSON(cctFromRgb, hw_led[F("cr")]); CJSON(strip.cctBlending, hw_led[F("cb")]); @@ -158,7 +155,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { } uint16_t length = elm["len"] | 1; - uint8_t colorOrder = (int)elm[F("order")]; + uint8_t colorOrder = (int)elm[F("order")]; // contains white channel swap option in upper nibble uint8_t skipFirst = elm[F("skip")]; uint16_t start = elm["start"] | 0; if (length==0 || start + length > MAX_LEDS) continue; // zero length or we reached max. number of LEDs, just stop @@ -166,7 +163,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { bool reversed = elm["rev"]; bool refresh = elm["ref"] | false; uint16_t freqkHz = elm[F("freq")] | 0; // will be in kHz for DotStar and Hz for PWM (not yet implemented fully) - uint8_t AWmode = elm[F("rgbwm")] | autoWhiteMode; + uint8_t AWmode = elm[F("rgbwm")] | RGBW_MODE_MANUAL_ONLY; uint8_t maPerLed = elm[F("ledma")] | strip.milliampsPerLed; // replace with 55 when removing strip.milliampsPerLed uint16_t maMax = elm[F("maxpwr")] | (strip.ablMilliampsMax * length) / total; // rough (incorrect?) per strip ABL calculation when no config exists // To disable brightness limiter we either set output max current to 0 or single LED current to 0 (we choose output max current) @@ -657,9 +654,7 @@ void serializeConfig() { id[F("mdns")] = cmDNS; id[F("name")] = serverDescription; id[F("inv")] = alexaInvocationName; -#ifdef WLED_ENABLE_SIMPLE_UI id[F("sui")] = simplifiedUI; -#endif JsonObject nw = doc.createNestedObject("nw"); #ifndef WLED_DISABLE_ESPNOW diff --git a/wled00/const.h b/wled00/const.h index e47d57649..ef1b31c01 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -342,8 +342,9 @@ // WLED Error modes #define ERR_NONE 0 // All good :) #define ERR_DENIED 1 // Permission denied -#define ERR_EEP_COMMIT 2 // Could not commit to EEPROM (wrong flash layout?) OBSOLETE +#define ERR_CONCURRENCY 2 // Conurrency (client active) #define ERR_NOBUF 3 // JSON buffer was not released in time, request cannot be handled at this time +#define ERR_NOT_IMPL 4 // Not implemented #define ERR_JSON 9 // JSON parsing failed (input too large?) #define ERR_FS_BEGIN 10 // Could not init filesystem (no partition?) #define ERR_FS_QUOTA 11 // The FS is full or the maximum file size is reached diff --git a/wled00/data/index.css b/wled00/data/index.css index 7faaeb47e..0ffdf68d3 100644 --- a/wled00/data/index.css +++ b/wled00/data/index.css @@ -370,6 +370,16 @@ button { padding: 5px 0 0; } +/* Quick load magin for simplified UI */ +.simplified #pql { + margin-bottom: 8px; +} + +/* Button margin for simplified UI */ +.simplified #fx .btn, .simplified #palw .btn { + margin-top: 0; +} + .smooth { transition: transform calc(var(--f, 1)*.5s) ease-out } .tab-label { @@ -413,6 +423,7 @@ button { position: sticky; bottom: 0; max-width: 300px; + z-index: 2; } #sliders .labels { @@ -754,13 +765,17 @@ input[type=range]::-moz-range-thumb { } #Colors .sliderwrap { - margin: 4px 0 0; + margin: 2px 0 0; } -/* Dynamically hide brightness slider label */ +/* Dynamically hide labels */ .hd { display: var(--bhd); } +/* Do not hide quick load label in simplified mode on small screen widths */ +.simplified #pql .hd { + display: var(--bhd) !important; +} #briwrap { min-width: 300px; @@ -1284,6 +1299,12 @@ TD .checkmark, TD .radiomark { margin-top: 0; } +/* Simplify segments */ +.simplified #segcont .lstI { + margin-top: 4px; + min-height: unset; +} + /* selected item/element */ .selected { /* has to be after .lstI since !important is not ok */ background: var(--c-4); @@ -1324,6 +1345,19 @@ TD .checkmark, TD .radiomark { top: calc(var(--sti) + 42px); } +dialog::backdrop { + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); +} +dialog { + max-height: 70%; + border: 0; + border-radius: 10px; + background: linear-gradient(rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0.1)), var(--c-3); + box-shadow: 4px 4px 10px 4px var(--c-1); + color: var(--c-f); +} + #fxlist .lstI.sticky, #pallist .lstI.sticky { top: var(--sti); diff --git a/wled00/data/index.htm b/wled00/data/index.htm index 21aa28d6d..ae685bdb9 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -7,52 +7,9 @@ WLED - - +
Loading WLED UI...
@@ -168,7 +125,7 @@ -
+
@@ -351,7 +308,7 @@

- Made with ❤︎ by Aircoookie and the WLED community + Made with ❤︎ by Aircoookie and the WLED community

+ + + + diff --git a/wled00/data/index.js b/wled00/data/index.js index 02bb07ff9..03516c370 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -12,8 +12,11 @@ var currentPreset = -1; var lastUpdate = 0; var segCount = 0, ledCount = 0, lowestUnused = 0, maxSeg = 0, lSeg = 0; var pcMode = false, pcModeA = false, lastw = 0, wW; +var simplifiedUI = false; var tr = 7; var d = document; +const ranges = RangeTouch.setup('input[type="range"]', {}); +var retry = false; var palettesData; var fxdata = []; var pJson = {}, eJson = {}, lJson = {}; @@ -22,7 +25,7 @@ var pN = "", pI = 0, pNum = 0; var pmt = 1, pmtLS = 0, pmtLast = 0; var lastinfo = {}; var isM = false, mw = 0, mh=0; -var ws, cpick, ranges, wsRpt=0; +var ws, wsRpt=0; var cfg = { theme:{base:"dark", bg:{url:"", rnd: false, rndGrayscale: false, rndBlur: false}, alpha:{bg:0.6,tab:0.8}, color:{bg:""}}, comp :{colors:{picker: true, rgb: false, quick: true, hex: false}, @@ -38,6 +41,17 @@ var hol = [ [0,0,1,1,"https://images.alphacoders.com/119/1198800.jpg"] // new year ]; +var cpick = new iro.ColorPicker("#picker", { + width: 260, + wheelLightness: false, + wheelAngle: 270, + wheelDirection: "clockwise", + layout: [{ + component: iro.ui.Wheel, + options: {} + }] +}); + function handleVisibilityChange() {if (!d.hidden && new Date () - lastUpdate > 3000) requestJson();} function sCol(na, col) {d.documentElement.style.setProperty(na, col);} function gId(c) {return d.getElementById(c);} @@ -259,24 +273,30 @@ function onLoad() selectSlot(0); updateTablinks(0); + cpick.on("input:end", () => {setColor(1);}); + cpick.on("color:change", () => {updatePSliders()}); pmtLS = localStorage.getItem('wledPmt'); // Load initial data + // Once we figure out why ESP8266 sometimes corrupts JSON responses if they are made in quick succession + // we can remove all setTimeout() throttling loadPalettes(()=>{ - // fill effect extra data array - loadFXData(()=>{ - setTimeout(()=>{ // ESP8266 can't handle quick requests - // load and populate effects - loadFX(()=>{ - setTimeout(()=>{ // ESP8266 can't handle quick requests - loadPalettesData(()=>{ - requestJson();// will load presets and create WS - if (cfg.comp.css) setTimeout(()=>{loadSkinCSS('skinCss')},100); - }); - },100); - }); - },100); - }); + setTimeout(()=>{ // ESP8266 can't handle quick requests + // fill effect extra data array + loadFXData(()=>{ + setTimeout(()=>{ // ESP8266 can't handle quick requests + // load and populate effects + loadFX(()=>{ + setTimeout(()=>{ // ESP8266 can't handle quick requests + loadPalettesData(()=>{ + requestJson();// will load presets and create WS + if (cfg.comp.css) setTimeout(()=>{loadSkinCSS('skinCss')},100); + }); + },50); + }); + },50); + }); + },50); }); resetUtil(); @@ -500,8 +520,13 @@ function loadPalettes(callback = null) .then((json)=>{ lJson = Object.entries(json); populatePalettes(); + retry = false; }) .catch((e)=>{ + if (!retry) { + retry = true; + setTimeout(loadPalettes, 500); // retry + } showToast(e, true); }) .finally(()=>{ @@ -522,9 +547,13 @@ function loadFX(callback = null) .then((json)=>{ eJson = Object.entries(json); populateEffects(); + retry = false; }) .catch((e)=>{ - //setTimeout(loadFX, 250); // retry + if (!retry) { + retry = true; + setTimeout(loadFX, 500); // retry + } showToast(e, true); }) .finally(()=>{ @@ -547,10 +576,14 @@ function loadFXData(callback = null) // add default value for Solid fxdata.shift() fxdata.unshift(";!;"); + retry = false; }) .catch((e)=>{ fxdata = []; - //setTimeout(loadFXData, 250); // retry + if (!retry) { + retry = true; + setTimeout(loadFXData, 500); // retry + } showToast(e, true); }) .finally(()=>{ @@ -624,11 +657,12 @@ function parseInfo(i) { if (name === "Dinnerbone") d.documentElement.style.transform = "rotate(180deg)"; // Minecraft easter egg if (i.live) name = "(Live) " + name; if (loc) name = "(L) " + name; - d.title = name; - ledCount = i.leds.count; - //syncTglRecv = i.str; - maxSeg = i.leds.maxseg; - pmt = i.fs.pmt; + d.title = name; + simplifiedUI = i.simplifiedui; + ledCount = i.leds.count; + //syncTglRecv = i.str; + maxSeg = i.leds.maxseg; + pmt = i.fs.pmt; gId('buttonNodes').style.display = lastinfo.ndc > 0 ? null:"none"; // do we have a matrix set-up mw = i.leds.matrix ? i.leds.matrix.w : 0; @@ -750,6 +784,7 @@ function populateSegments(s) let rvXck = ``; let miXck = ``; let rvYck = "", miYck =""; + let smpl = simplifiedUI ? 'hide' : ''; if (isMSeg) { rvYck = ``; miYck = ``; @@ -768,23 +803,23 @@ function populateSegments(s) ``+ ``+ ``; - cn += `
`+ - `