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 @@
-
@@ -378,6 +335,13 @@
+
+
+
+
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 += ``+
- `