Merge branch 'display-enh' into patch-1

This commit is contained in:
Blaž Kristan 2022-01-02 11:34:45 +01:00 committed by GitHub
commit afd376ddbc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 4090 additions and 2881 deletions

View File

@ -2,6 +2,11 @@
### Builds after release 0.12.0
#### Build 2112080
- Version bump to 0.13.0-b6 "Toki"
- Added "ESP02" (ESP8266 with 2M of flash) to PIO/release binaries
#### Build 2112070
- Added new effect "Fairy", replacing "Police All"

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "wled",
"version": "0.13.1-bl5",
"version": "0.13.1-bl6",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "wled",
"version": "0.13.1-bl5",
"version": "0.13.1-bl6",
"description": "Tools for WLED project",
"main": "tools/cdata.js",
"directories": {

View File

@ -12,7 +12,7 @@
; default_envs = travis_esp8266, travis_esp32
# Release binaries
default_envs = nodemcuv2, esp01_1m_full, esp32dev, esp32_eth
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth
# Build everything
; default_envs = esp32dev, esp8285_4CH_MagicHome, esp8285_4CH_H801, codm-controller-0.6-rev2, codm-controller-0.6, esp32s2_saola, d1_mini_5CH_Shojo_PCB, d1_mini, sp501e, travis_esp8266, travis_esp32, nodemcuv2, esp32_eth, anavi_miracle_controller, esp07, esp01_1m_full, m5atom, h803wf, d1_mini_ota, heltec_wifi_kit_8, esp8285_5CH_H801, d1_mini_debug, wemos_shield_esp32, elekstube_ips
@ -20,6 +20,7 @@ default_envs = nodemcuv2, esp01_1m_full, esp32dev, esp32_eth
# Single binaries (uncomment your board)
; default_envs = elekstube_ips
; default_envs = nodemcuv2
; default_envs = esp8266_2m
; default_envs = esp01_1m_full
; default_envs = esp07
; default_envs = d1_mini
@ -53,14 +54,14 @@ extra_configs =
arduino_core_2_6_3 = espressif8266@2.3.3
arduino_core_2_7_4 = espressif8266@2.6.2
arduino_core_3_0_0 = espressif8266@3.0.0
;arduino_core_3_2_0 = espressif8266@3.2.0
arduino_core_3_2_0 = espressif8266@3.2.0
# Development platforms
arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop
arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/stage
# Platform to use for ESP8266
platform_wled_default = ${common.arduino_core_2_7_4}
platform_wled_default = ${common.arduino_core_3_2_0}
# We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization
platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
platformio/toolchain-xtensa @ ~2.40802.200502
@ -105,6 +106,7 @@ build_flags =
-DBEARSSL_SSL_BASIC
-D CORE_DEBUG_LEVEL=0
-D NDEBUG
-Dregister=
#build_flags for the IRremoteESP8266 library (enabled decoders have to appear here)
-D _IR_ENABLE_DEFAULT_=false
-D DECODE_HASH=true
@ -242,6 +244,13 @@ build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266
lib_deps = ${esp8266.lib_deps}
[env:esp8266_2m]
board = esp_wroom_02
platform = ${common.platform_wled_default}
board_build.ldscript = ${common.ldscript_2m512k}
build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02
lib_deps = ${esp8266.lib_deps}
[env:esp01_1m_full]
board = esp01_1m
platform = ${common.platform_wled_default}

View File

@ -118,7 +118,8 @@ private:
*/
void switchStrip(bool switchOn)
{
if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return;
if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return; //if lights on and off only, do nothing
if (PIRtriggered && switchOn) return; //if already on and triggered before, do nothing
PIRtriggered = switchOn;
if (switchOn) {
if (m_onPreset) {
@ -188,10 +189,12 @@ private:
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 /*if (bri != 0)*/ {
} else {
// start switch off timer
offTimerStart = millis();
if (NotifyUpdateMode != CALL_MODE_NO_NOTIFY) updateInterfaces(CALL_MODE_WS_SEND);
}
return true;
}
@ -203,14 +206,13 @@ private:
*/
bool handleOffTimer()
{
if (offTimerStart > 0 && millis() - offTimerStart > m_switchOffDelay)
{
if (enabled == true)
{
if (offTimerStart > 0 && millis() - offTimerStart > m_switchOffDelay) {
offTimerStart = 0;
if (enabled == true) {
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(false);
else if (NotifyUpdateMode != CALL_MODE_NO_NOTIFY) updateInterfaces(CALL_MODE_WS_SEND);
publishMqtt("off");
}
offTimerStart = 0;
return true;
}
return false;

View File

@ -148,7 +148,7 @@ class PWMFanUsermod : public Usermod {
int pwmStep = ((100 - minPWMValuePct) * newPWMvalue) / (7*100);
int pwmMinimumValue = (minPWMValuePct * newPWMvalue) / 100;
if ((temp == NAN) || (temp <= 0.0)) {
if ((temp == NAN) || (temp <= -100.0)) {
DEBUG_PRINTLN(F("WARNING: no temperature value available. Cannot do temperature control. Will set PWM fan to 255."));
} else if (difftemp <= 0.0) {
// Temperature is below target temperature. Run fan at minimum speed.

View File

@ -123,6 +123,11 @@ public:
}
}
uint16_t getLastLDRValue()
{
return lastLDRValue;
}
void addToJsonInfo(JsonObject &root)
{
JsonObject user = root[F("u")];

View File

@ -37,7 +37,7 @@ class UsermodTemperature : public Usermod {
// used to determine when we can read the sensors temperature
// we have to wait at least 93.75 ms after requestTemperatures() is called
unsigned long lastTemperaturesRequest;
float temperature = -100.0f; // default to -100, DS18B20 only goes down to -50C
float temperature = -127.0f; // default to -127, DS18B20 only goes down to -50C
// indicates requestTemperatures has been called but the sensor measurement is not complete
bool waitingForConversion = false;
// flag set at startup if DS18B20 sensor not found, avoids trying to keep getting
@ -60,7 +60,6 @@ class UsermodTemperature : public Usermod {
if (oneWire->reset()) { // if reset() fails there are no OneWire devices
oneWire->skip(); // skip ROM
oneWire->write(0xBE); // read (temperature) from EEPROM
delayMicroseconds(250);
oneWire->read_bytes(data, 9); // first 2 bytes contain temperature
#ifdef WLED_DEBUG
if (OneWire::crc8(data,8) != data[8]) {
@ -85,7 +84,8 @@ class UsermodTemperature : public Usermod {
break;
}
}
return retVal;
for (byte i=1; i<9; i++) data[0] &= data[i];
return data[0]==0xFF ? -127.0f : retVal;
}
void requestTemperatures() {
@ -176,7 +176,7 @@ class UsermodTemperature : public Usermod {
}
// we were waiting for a conversion to complete, have we waited log enough?
if (now - lastTemperaturesRequest >= 100 /* 93.75ms per the datasheet but can be up to 750ms */) {
if (now - lastTemperaturesRequest >= 750 /* 93.75ms per the datasheet but can be up to 750ms */) {
readTemperature();
if (WLED_MQTT_CONNECTED) {

View File

@ -61,10 +61,10 @@ class QuinLEDAnPentaUsermod : public Usermod
float shtLastKnownHumidity = 0;
// Pin/IO vars
const int8_t anPentaPins[5] = {14, 13, 12, 4, 2};
const int8_t anPentaLEDPins[5] = {14, 13, 12, 4, 2};
int8_t oledSpiClk = 15;
int8_t oledSpiData = 16;
int8_t oledSpiCs = 0;
int8_t oledSpiCs = 27;
int8_t oledSpiDc = 32;
int8_t oledSpiRst = 33;
int8_t shtSda = 1;
@ -75,7 +75,7 @@ class QuinLEDAnPentaUsermod : public Usermod
{
for(int8_t i = 0; i <= 4; i++)
{
if(anPentaPins[i] == pin)
if(anPentaLEDPins[i] == pin)
return true;
}
return false;
@ -313,7 +313,7 @@ class QuinLEDAnPentaUsermod : public Usermod
byte drawnLines = 0;
for (int8_t app = 0; app <= 4; app++) {
for (int8_t clp = 0; clp <= 4; clp++) {
if (anPentaPins[app] == currentLedPins[clp]) {
if (anPentaLEDPins[app] == currentLedPins[clp]) {
char charCurrentLedcReads[17];
sprintf(charCurrentLedcReads, "LED %d:", app+1);
if (oledUseProgressBars) {

View File

@ -1,12 +1,12 @@
# QuinLED-An-Penta
The (un)official usermod to get the best out of the QuinLED-An-Penta, like using the OLED and the SHT30 temperature/humidity sensor.
The (un)official usermod to get the best out of the QuinLED-An-Penta (https://quinled.info/quinled-an-penta/), like using the OLED and the SHT30 temperature/humidity sensor.
## Requirements
* "u8gs" by olikraus, v2.28 or higher: https://github.com/olikraus/u8g2
* "SHT85" by Rob Tillaart, v0.2 or higher: https://github.com/RobTillaart/SHT85
## Usermod installation
Simply copy the below block (build task) to your `platformio_override.ini` and compile WLED using this new build task. Or use an existing one and add the buildflag `-D QUINLED_AN_PENTA`.
Simply copy the below block (build task) to your `platformio_override.ini` and compile WLED using this new build task. Or use an existing one, add the buildflag `-D QUINLED_AN_PENTA` and the below library dependencies.
ESP32 (**without** ethernet):
```
@ -33,14 +33,19 @@ This mod has been optimized for an SSD1306 driven 128x64 OLED. Using a smaller O
I highly recommend using these "two color monochromatic OLEDs", which have the first 16 pixels in a different color than the other 48, e.g. a yellow/blue OLED.
Also note, you need to have an **SPI** driven OLED, **not i2c**!
### Limitations combined with Ethernet
The initial development of this mod had been done with a beta version of the QuinLED-An-Penta, which had a different IO layout for the OLED: The CS pin used to be IO_0, but has been changed to IO27 with the first v1 public release. Unfortunately, IO27 is used by the Ethernet boards, so WLED will not let you enable the OLED screen, if you're using it with Ethernet. This unfortunately makes the development I've done to support/show Ethernet information void, as it cannot be used.
However (and I've not tried this, as I don't own a v1 board): You can try to modify this mod and try to use IO27 for the OLED and share it with the Ethernet board. It is "just" the chip select pin, so there is a chance that both can coexist and use the same IO. You need to skip WLEDs PinManager for the CS pin, so WLED will not block using it. If you don't know how this works: Leave it. If you know what I'm talking about: Try it and please let me know on the Intermit.Tech (QuinLED) Discord server: https://discord.gg/WdbAauG
### My OLED flickers after some time, what should I do?
That's a tricky one: During development I saw that the OLED sometimes starts to "bug out" / flicker and won't work anymore. This seems to be caused by the high PWM interference the board produces. It seems to loose it's settings and then doesn't know how to draw anymore. Turns out the only way to fix this is to call the libraries `begin()` method again which will re-initialize the display.
That's a tricky one: During development I saw that the OLED sometimes starts to "bug out" / flicker and won't work anymore. This seems to be caused by the high PWM interference the board produces. It seems to loose its settings and then doesn't know how to draw anymore. Turns out the only way to fix this is to call the libraries `begin()` method again which will re-initialize the display.
If you're facing this issue, you can enable a setting I've added which will call the `begin()` roughly every 60 seconds between a page change. This will make the page change take ~500ms, but will fix the display.
## Configuration
Navigate to the "Config" and then to the "Usermods" section. If you compiled WLED with `-D QUINLED_AN_PENTA`, you will see the config for it there:
* Enable-OLED:
* What it does: Enabled the optional SPI driven OLED that can be mounted to the 7-pin female header
* What it does: Enables the optional SPI driven OLED that can be mounted to the 7-pin female header. Won't work with Ethernet, read above.
* Possible values: Enabled/Disabled
* Default: Disabled
* OLED-Use-Progress-Bars:
@ -60,10 +65,15 @@ Navigate to the "Config" and then to the "Usermods" section. If you compiled WLE
* Possible values: Enabled/Disabled
* Default: Disabled
* Enable-SHT30-Temp-Humidity-Sensor:
* What it does: Enabled the onboard SHT30 temperature and humidity sensor
* What it does: Enables the onboard SHT30 temperature and humidity sensor
* Possible values: Enabled/Disabled
* Default: Disabled
## Change log
2021-12
* Adjusted IO layout to match An-Penta v1r1
2021-10
* First implementation.
* First implementation.
## Credits
ezcGman | Andy: Find me on the Intermit.Tech (QuinLED) Discord server: https://discord.gg/WdbAauG

View File

@ -0,0 +1,129 @@
# Seven Segment Display Reloaded
Usermod that uses the overlay feature to create a configurable seven segment display.
Optimized for maximum configurability and use with seven segment clocks by parallyze (https://www.instructables.com/member/parallyze/instructables/)
Very loosely based on the existing usermod "seven segment display".
## Installation
Add the compile-time option `-D USERMOD_SSDR` to your `platformio.ini` (or `platformio_override.ini`) or use `#define USERMOD_SSDR` in `my_config.h`.
For the auto brightness option, the usermod SN_Photoresistor has to be installed as well. See SN_Photoresistor/readme.md for instructions.
## Settings
All settings can be controlled the usermod setting page.
Part of the settings can be controlled through MQTT with a raw payload or through a json request to /json/state.
### enabled
Enables/disables this overlay usermod
### inverted
Enables the inverted mode in which the background should be enabled and the digits should be black (leds off)
### Colon-blinking
Enables the blinking colon(s) if they are defined
### enable-auto-brightness
Enables the auto brightness feature. Can be only used with the usermod SN_Photoresistor installed.
### auto-brightness-min / auto-brightness-max
The lux value calculated from usermod SN_Photoresistor will be mapped to the values defined here.
The mapping is 0 - 1000 lux will be mapped to auto-brightness-min - auto-brightness-max
The mA current protection of WLED will override the calculated value if it is too high.
### Display-Mask
Defines the type of the time/date display.
For example "H:m" (default)
- H - 00-23 hours
- h - 01-12 hours
- k - 01-24 hours
- m - 00-59 minutes
- s - 00-59 seconds
- d - 01-31 day of month
- M - 01-12 month
- y - 21 last two positions of year
- Y - 2021 year
- : for a colon
### LED-Numbers
- LED-Numbers-Hours
- LED-Numbers-Minutes
- LED-Numbers-Seconds
- LED-Numbers-Colons
- LED-Numbers-Day
- LED-Numbers-Month
- LED-Numbers-Year
See following example for usage.
## Example
Example for Leds definition
```
< A >
/\ /\
F B
\/ \/
< G >
/\ /\
E C
\/ \/
< D >
```
Leds or Range of Leds are seperated by a comma ","
Segments are seperated by a semicolon ";" and are read as A;B;C;D;E;F;G
Digits are seperated by colon ":" -> A;B;C;D;E;F;G:A;B;C;D;E;F;G
Ranges are defined as lower to higher (lower first)
For example, an clock definition for the following clock (https://www.instructables.com/Lazy-7-Quick-Build-Edition/) is
- hour "59,46;47-48;50-51;52-53;54-55;57-58;49,56:0,13;1-2;4-5;6-7;8-9;11-12;3,10"
- minute "37-38;39-40;42-43;44,31;32-33;35-36;34,41:21-22;23-24;26-27;28,15;16-17;19-20;18,25"
or
- hour "6,7;8,9;11,12;13,0;1,2;4,5;3,10:52,53;54,55;57,58;59,46;47,48;50,51;49,56"
- minute "15,28;16,17;19,20;21,22;23,24;26,27;18,25:31,44;32,33;35,36;37,38;39,40;42,43;34,41"
depending on the orientation.
# The example detailed:
hour "59,46;47-48;50-51;52-53;54-55;57-58;49,56:0,13;1-2;4-5;6-7;8-9;11-12;3,10"
there are two digits seperated by ":"
- 59,46;47-48;50-51;52-53;54-55;57-58;49,56
- 0,13;1-2;4-5;6-7;8-9;11-12;3,10
In the first digit,
the **segment A** consists of the leds number **59 and 46**., **segment B** consists of the leds number **47, 48** and so on
The second digit starts again with **segment A** and leds **0 and 13**, **segment B** consists of the leds number **1 and 2** and so on
### first digit of the hour
- Segment A: 59, 46
- Segment B: 47, 48
- Segment C: 50, 51
- Segment D: 52, 53
- Segment E: 54, 55
- Segment F: 57, 58
- Segment G: 49, 56
### second digit of the hour
- Segment A: 0, 13
- Segment B: 1, 2
- Segment C: 4, 5
- Segment D: 6, 7
- Segment E: 8, 9
- Segment F: 11, 12
- Segment G: 3, 10

View File

@ -0,0 +1,555 @@
#pragma once
#include "wled.h"
class UsermodSSDR : public Usermod {
//#define REFRESHTIME 497
private:
//Runtime variables.
unsigned long umSSDRLastRefresh = 0;
unsigned long umSSDRResfreshTime = 3000;
bool umSSDRDisplayTime = false;
bool umSSDRInverted = false;
bool umSSDRColonblink = true;
bool umSSDREnableLDR = false;
String umSSDRHours = "";
String umSSDRMinutes = "";
String umSSDRSeconds = "";
String umSSDRColons = "";
String umSSDRDays = "";
String umSSDRMonths = "";
String umSSDRYears = "";
uint16_t umSSDRLength = 0;
uint16_t umSSDRBrightnessMin = 0;
uint16_t umSSDRBrightnessMax = 255;
bool* umSSDRMask = 0;
/*// H - 00-23 hours
// h - 01-12 hours
// k - 01-24 hours
// m - 00-59 minutes
// s - 00-59 seconds
// d - 01-31 day of month
// M - 01-12 month
// y - 21 last two positions of year
// Y - 2021 year
// : for a colon
*/
String umSSDRDisplayMask = "H:m"; //This should reflect physical equipment.
/* Segment order, seen from the front:
< A >
/\ /\
F B
\/ \/
< G >
/\ /\
E C
\/ \/
< D >
*/
uint8_t umSSDRNumbers[11][7] = {
// A B C D E F G
{ 1, 1, 1, 1, 1, 1, 0 }, // 0
{ 0, 1, 1, 0, 0, 0, 0 }, // 1
{ 1, 1, 0, 1, 1, 0, 1 }, // 2
{ 1, 1, 1, 1, 0, 0, 1 }, // 3
{ 0, 1, 1, 0, 0, 1, 1 }, // 4
{ 1, 0, 1, 1, 0, 1, 1 }, // 5
{ 1, 0, 1, 1, 1, 1, 1 }, // 6
{ 1, 1, 1, 0, 0, 0, 0 }, // 7
{ 1, 1, 1, 1, 1, 1, 1 }, // 8
{ 1, 1, 1, 1, 0, 1, 1 }, // 9
{ 0, 0, 0, 0, 0, 0, 0 } // blank
};
//String to reduce flash memory usage
static const char _str_name[];
static const char _str_ldrEnabled[];
static const char _str_timeEnabled[];
static const char _str_inverted[];
static const char _str_colonblink[];
static const char _str_displayMask[];
static const char _str_hours[];
static const char _str_minutes[];
static const char _str_seconds[];
static const char _str_colons[];
static const char _str_days[];
static const char _str_months[];
static const char _str_years[];
static const char _str_minBrightness[];
static const char _str_maxBrightness[];
#ifdef USERMOD_ID_SN_PHOTORESISTOR
Usermod_SN_Photoresistor *ptr;
#else
void* ptr = nullptr;
#endif
void _overlaySevenSegmentDraw() {
int displayMaskLen = static_cast<int>(umSSDRDisplayMask.length());
bool colonsDone = false;
_setAllFalse();
for (int index = 0; index < displayMaskLen; index++) {
int timeVar = 0;
switch (umSSDRDisplayMask[index]) {
case 'h':
timeVar = hourFormat12(localTime);
_showElements(&umSSDRHours, timeVar, 0, 1);
break;
case 'H':
timeVar = hour(localTime);
_showElements(&umSSDRHours, timeVar, 0, 1);
break;
case 'k':
timeVar = hour(localTime) + 1;
_showElements(&umSSDRHours, timeVar, 0, 0);
break;
case 'm':
timeVar = minute(localTime);
_showElements(&umSSDRMinutes, timeVar, 0, 0);
break;
case 's':
timeVar = second(localTime);
_showElements(&umSSDRSeconds, timeVar, 0, 0);
break;
case 'd':
timeVar = day(localTime);
_showElements(&umSSDRDays, timeVar, 0, 0);
break;
case 'M':
timeVar = month(localTime);
_showElements(&umSSDRMonths, timeVar, 0, 0);
break;
case 'y':
timeVar = second(localTime);
_showElements(&umSSDRYears, timeVar, 0, 0);
break;
case 'Y':
timeVar = year(localTime);
_showElements(&umSSDRYears, timeVar, 0, 0);
break;
case ':':
if (!colonsDone) { // only call _setColons once as all colons are printed when the first colon is found
_setColons();
colonsDone = true;
}
break;
}
}
_setMaskToLeds();
}
void _setColons() {
if ( umSSDRColonblink ) {
if ( second(localTime) % 2 == 0 ) {
_showElements(&umSSDRColons, 0, 1, 0);
}
} else {
_showElements(&umSSDRColons, 0, 1, 0);
}
}
void _showElements(String *map, int timevar, bool isColon, bool removeZero
) {
if (!(*map).equals("") && !(*map) == NULL) {
int length = String(timevar).length();
bool addZero = false;
if (length == 1) {
length = 2;
addZero = true;
}
int timeArr[length];
if(addZero) {
if(removeZero)
{
timeArr[1] = 10;
timeArr[0] = timevar;
}
else
{
timeArr[1] = 0;
timeArr[0] = timevar;
}
} else {
int count = 0;
while (timevar) {
timeArr[count] = timevar%10;
timevar /= 10;
count++;
};
}
int colonsLen = static_cast<int>((*map).length());
int count = 0;
int countSegments = 0;
int countDigit = 0;
bool range = false;
int lastSeenLedNr = 0;
for (int index = 0; index < colonsLen; index++) {
switch ((*map)[index]) {
case '-':
lastSeenLedNr = _checkForNumber(count, index, map);
count = 0;
range = true;
break;
case ':':
_setLeds(_checkForNumber(count, index, map), lastSeenLedNr, range, countSegments, timeArr[countDigit], isColon);
count = 0;
range = false;
countDigit++;
countSegments = 0;
break;
case ';':
_setLeds(_checkForNumber(count, index, map), lastSeenLedNr, range, countSegments, timeArr[countDigit], isColon);
count = 0;
range = false;
countSegments++;
break;
case ',':
_setLeds(_checkForNumber(count, index, map), lastSeenLedNr, range, countSegments, timeArr[countDigit], isColon);
count = 0;
range = false;
break;
default:
count++;
break;
}
}
_setLeds(_checkForNumber(count, colonsLen, map), lastSeenLedNr, range, countSegments, timeArr[countDigit], isColon);
}
}
void _setLeds(int lednr, int lastSeenLedNr, bool range, int countSegments, int number, bool colon) {
if ((colon && umSSDRColonblink) || umSSDRNumbers[number][countSegments]) {
if (range) {
for(int i = lastSeenLedNr; i <= lednr; i++) {
umSSDRMask[i] = true;
}
} else {
umSSDRMask[lednr] = true;
}
}
}
void _setMaskToLeds() {
for(int i = 0; i <= umSSDRLength; i++) {
if ((!umSSDRInverted && !umSSDRMask[i]) || (umSSDRInverted && umSSDRMask[i])) {
strip.setPixelColor(i, 0x000000);
}
}
}
void _setAllFalse() {
for(int i = 0; i <= umSSDRLength; i++) {
umSSDRMask[i] = false;
}
}
int _checkForNumber(int count, int index, String *map) {
String number = (*map).substring(index - count, index);
return number.toInt();
}
void _publishMQTTint_P(const char *subTopic, int value)
{
if(mqtt == NULL) return;
char buffer[64];
char valBuffer[12];
sprintf_P(buffer, PSTR("%s/%S/%S"), mqttDeviceTopic, _str_name, subTopic);
sprintf_P(valBuffer, PSTR("%d"), value);
mqtt->publish(buffer, 2, true, valBuffer);
}
void _publishMQTTstr_P(const char *subTopic, String Value)
{
if(mqtt == NULL) return;
char buffer[64];
sprintf_P(buffer, PSTR("%s/%S/%S"), mqttDeviceTopic, _str_name, subTopic);
mqtt->publish(buffer, 2, true, Value.c_str(), Value.length());
}
bool _cmpIntSetting_P(char *topic, char *payload, const char *setting, void *value)
{
if (strcmp_P(topic, setting) == 0)
{
*((int *)value) = strtol(payload, NULL, 10);
_publishMQTTint_P(setting, *((int *)value));
return true;
}
return false;
}
bool _handleSetting(char *topic, char *payload) {
if (_cmpIntSetting_P(topic, payload, _str_timeEnabled, &umSSDRDisplayTime)) {
return true;
}
if (_cmpIntSetting_P(topic, payload, _str_ldrEnabled, &umSSDREnableLDR)) {
return true;
}
if (_cmpIntSetting_P(topic, payload, _str_inverted, &umSSDRInverted)) {
return true;
}
if (_cmpIntSetting_P(topic, payload, _str_colonblink, &umSSDRColonblink)) {
return true;
}
if (strcmp_P(topic, _str_displayMask) == 0) {
umSSDRDisplayMask = String(payload);
_publishMQTTstr_P(_str_displayMask, umSSDRDisplayMask);
return true;
}
return false;
}
void _updateMQTT()
{
_publishMQTTint_P(_str_timeEnabled, umSSDRDisplayTime);
_publishMQTTint_P(_str_ldrEnabled, umSSDREnableLDR);
_publishMQTTint_P(_str_inverted, umSSDRInverted);
_publishMQTTint_P(_str_colonblink, umSSDRColonblink);
_publishMQTTstr_P(_str_hours, umSSDRHours);
_publishMQTTstr_P(_str_minutes, umSSDRMinutes);
_publishMQTTstr_P(_str_seconds, umSSDRSeconds);
_publishMQTTstr_P(_str_colons, umSSDRColons);
_publishMQTTstr_P(_str_days, umSSDRDays);
_publishMQTTstr_P(_str_months, umSSDRMonths);
_publishMQTTstr_P(_str_years, umSSDRYears);
_publishMQTTstr_P(_str_displayMask, umSSDRDisplayMask);
_publishMQTTint_P(_str_minBrightness, umSSDRBrightnessMin);
_publishMQTTint_P(_str_maxBrightness, umSSDRBrightnessMax);
}
void _addJSONObject(JsonObject& root) {
JsonObject ssdrObj = root[FPSTR(_str_name)];
if (ssdrObj.isNull()) {
ssdrObj = root.createNestedObject(FPSTR(_str_name));
}
ssdrObj[FPSTR(_str_timeEnabled)] = umSSDRDisplayTime;
ssdrObj[FPSTR(_str_ldrEnabled)] = umSSDREnableLDR;
ssdrObj[FPSTR(_str_inverted)] = umSSDRInverted;
ssdrObj[FPSTR(_str_colonblink)] = umSSDRColonblink;
ssdrObj[FPSTR(_str_displayMask)] = umSSDRDisplayMask;
ssdrObj[FPSTR(_str_hours)] = umSSDRHours;
ssdrObj[FPSTR(_str_minutes)] = umSSDRMinutes;
ssdrObj[FPSTR(_str_seconds)] = umSSDRSeconds;
ssdrObj[FPSTR(_str_colons)] = umSSDRColons;
ssdrObj[FPSTR(_str_days)] = umSSDRDays;
ssdrObj[FPSTR(_str_months)] = umSSDRMonths;
ssdrObj[FPSTR(_str_years)] = umSSDRYears;
ssdrObj[FPSTR(_str_minBrightness)] = umSSDRBrightnessMin;
ssdrObj[FPSTR(_str_maxBrightness)] = umSSDRBrightnessMax;
}
public:
//Functions called by WLED
/*
* setup() is called once at boot. WiFi is not yet connected at this point.
* You can use it to initialize variables, sensors or similar.
*/
void setup() {
umSSDRLength = strip.getLengthTotal();
if (umSSDRMask != 0) {
umSSDRMask = (bool*) realloc(umSSDRMask, umSSDRLength * sizeof(bool));
} else {
umSSDRMask = (bool*) malloc(umSSDRLength * sizeof(bool));
}
_setAllFalse();
#ifdef USERMOD_ID_SN_PHOTORESISTOR
ptr = (Usermod_SN_Photoresistor*) usermods.lookup(USERMOD_ID_SN_PHOTORESISTOR);
#endif
DEBUG_PRINTLN(F("Setup done"));
}
/*
* loop() is called continuously. Here you can check for events, read sensors, etc.
*/
void loop() {
if (!umSSDRDisplayTime || strip.isUpdating()) {
return;
}
#ifdef USERMOD_ID_SN_PHOTORESISTOR
if(bri != 0 && umSSDREnableLDR && (millis() - umSSDRLastRefresh > umSSDRResfreshTime)) {
if (ptr != nullptr) {
uint16_t lux = ptr->getLastLDRValue();
uint16_t brightness = map(lux, 0, 1000, umSSDRBrightnessMin, umSSDRBrightnessMax);
if (bri != brightness) {
bri = brightness;
colorUpdated(1);
}
}
umSSDRLastRefresh = millis();
}
#endif
}
void handleOverlayDraw() {
if (umSSDRDisplayTime) {
_overlaySevenSegmentDraw();
}
}
/*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
* Below it is shown how this could be used for e.g. a light sensor
*/
void addToJsonInfo(JsonObject& root) {
JsonObject user = root[F("u")];
if (user.isNull()) {
user = root.createNestedObject(F("u"));
}
JsonArray enabled = user.createNestedArray("Time enabled");
enabled.add(umSSDRDisplayTime);
JsonArray invert = user.createNestedArray("Time inverted");
invert.add(umSSDRInverted);
JsonArray blink = user.createNestedArray("Blinking colon");
blink.add(umSSDRColonblink);
JsonArray ldrEnable = user.createNestedArray("Auto Brightness enabled");
ldrEnable.add(umSSDREnableLDR);
}
/*
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
void addToJsonState(JsonObject& root) {
JsonObject user = root[F("u")];
if (user.isNull()) {
user = root.createNestedObject(F("u"));
}
_addJSONObject(user);
}
/*
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
void readFromJsonState(JsonObject& root) {
JsonObject user = root[F("u")];
if (!user.isNull()) {
JsonObject ssdrObj = user[FPSTR(_str_name)];
umSSDRDisplayTime = ssdrObj[FPSTR(_str_timeEnabled)] | umSSDRDisplayTime;
umSSDREnableLDR = ssdrObj[FPSTR(_str_ldrEnabled)] | umSSDREnableLDR;
umSSDRInverted = ssdrObj[FPSTR(_str_inverted)] | umSSDRInverted;
umSSDRColonblink = ssdrObj[FPSTR(_str_colonblink)] | umSSDRColonblink;
umSSDRDisplayMask = ssdrObj[FPSTR(_str_displayMask)] | umSSDRDisplayMask;
}
}
void onMqttConnect(bool sessionPresent) {
char subBuffer[48];
if (mqttDeviceTopic[0] != 0)
{
_updateMQTT();
//subscribe for sevenseg messages on the device topic
sprintf_P(subBuffer, PSTR("%s/%S/+/set"), mqttDeviceTopic, _str_name);
mqtt->subscribe(subBuffer, 2);
}
if (mqttGroupTopic[0] != 0)
{
//subcribe for sevenseg messages on the group topic
sprintf_P(subBuffer, PSTR("%s/%S/+/set"), mqttGroupTopic, _str_name);
mqtt->subscribe(subBuffer, 2);
}
}
bool onMqttMessage(char *topic, char *payload) {
//If topic beings iwth sevenSeg cut it off, otherwise not our message.
size_t topicPrefixLen = strlen_P(PSTR("/wledSS/"));
if (strncmp_P(topic, PSTR("/wledSS/"), topicPrefixLen) == 0) {
topic += topicPrefixLen;
} else {
return false;
}
//We only care if the topic ends with /set
size_t topicLen = strlen(topic);
if (topicLen > 4 &&
topic[topicLen - 4] == '/' &&
topic[topicLen - 3] == 's' &&
topic[topicLen - 2] == 'e' &&
topic[topicLen - 1] == 't')
{
//Trim /set and handle it
topic[topicLen - 4] = '\0';
_handleSetting(topic, payload);
}
return true;
}
void addToConfig(JsonObject &root) {
_addJSONObject(root);
}
bool readFromConfig(JsonObject &root) {
JsonObject top = root[FPSTR(_str_name)];
if (top.isNull()) {
DEBUG_PRINT(FPSTR(_str_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
umSSDRDisplayTime = (top[FPSTR(_str_timeEnabled)] | umSSDRDisplayTime);
umSSDREnableLDR = (top[FPSTR(_str_ldrEnabled)] | umSSDREnableLDR);
umSSDRInverted = (top[FPSTR(_str_inverted)] | umSSDRInverted);
umSSDRColonblink = (top[FPSTR(_str_colonblink)] | umSSDRColonblink);
umSSDRDisplayMask = top[FPSTR(_str_displayMask)] | umSSDRDisplayMask;
umSSDRHours = top[FPSTR(_str_hours)] | umSSDRHours;
umSSDRMinutes = top[FPSTR(_str_minutes)] | umSSDRMinutes;
umSSDRSeconds = top[FPSTR(_str_seconds)] | umSSDRSeconds;
umSSDRColons = top[FPSTR(_str_colons)] | umSSDRColons;
umSSDRDays = top[FPSTR(_str_days)] | umSSDRDays;
umSSDRMonths = top[FPSTR(_str_months)] | umSSDRMonths;
umSSDRYears = top[FPSTR(_str_years)] | umSSDRYears;
umSSDRBrightnessMin = top[FPSTR(_str_minBrightness)] | umSSDRBrightnessMin;
umSSDRBrightnessMax = top[FPSTR(_str_maxBrightness)] | umSSDRBrightnessMax;
DEBUG_PRINT(FPSTR(_str_name));
DEBUG_PRINTLN(F(" config (re)loaded."));
return true;
}
/*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
* This could be used in the future for the system to determine whether your usermod is installed.
*/
uint16_t getId() {
return USERMOD_ID_SSDR;
}
};
const char UsermodSSDR::_str_name[] PROGMEM = "UsermodSSDR";
const char UsermodSSDR::_str_timeEnabled[] PROGMEM = "enabled";
const char UsermodSSDR::_str_inverted[] PROGMEM = "inverted";
const char UsermodSSDR::_str_colonblink[] PROGMEM = "Colon-blinking";
const char UsermodSSDR::_str_displayMask[] PROGMEM = "Display-Mask";
const char UsermodSSDR::_str_hours[] PROGMEM = "LED-Numbers-Hours";
const char UsermodSSDR::_str_minutes[] PROGMEM = "LED-Numbers-Minutes";
const char UsermodSSDR::_str_seconds[] PROGMEM = "LED-Numbers-Seconds";
const char UsermodSSDR::_str_colons[] PROGMEM = "LED-Numbers-Colons";
const char UsermodSSDR::_str_days[] PROGMEM = "LED-Numbers-Day";
const char UsermodSSDR::_str_months[] PROGMEM = "LED-Numbers-Month";
const char UsermodSSDR::_str_years[] PROGMEM = "LED-Numbers-Year";
const char UsermodSSDR::_str_ldrEnabled[] PROGMEM = "enable-auto-brightness";
const char UsermodSSDR::_str_minBrightness[] PROGMEM = "auto-brightness-min";
const char UsermodSSDR::_str_maxBrightness[] PROGMEM = "auto-brightness-max";

View File

@ -264,7 +264,7 @@ class FourLineDisplayUsermod : public Usermod {
bool enabled = true;
// needRedraw marks if redraw is required to prevent often redrawing.
bool needRedraw = true;
//bool needRedraw = true;
// Next variables hold the previous known values to determine if redraw is
// required.
@ -386,7 +386,8 @@ class FourLineDisplayUsermod : public Usermod {
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...");
//drawString(0, 0, "Loading...");
overlay(PSTR("Loading..."),3000,0);
}
// gets called every time WiFi is (re-)connected. Initialize own network
@ -465,6 +466,7 @@ class FourLineDisplayUsermod : public Usermod {
* or if forceRedraw).
*/
void redraw(bool forceRedraw) {
bool needRedraw = false;
unsigned long now = millis();
if (type == NONE || !enabled) return;
@ -484,9 +486,11 @@ class FourLineDisplayUsermod : public Usermod {
if (forceRedraw) {
knownHour = 99;
needRedraw = true;
clear();
} else if ((bri == 0 && powerON) || (bri > 0 && !powerON)) { //trigger power icon
powerON = !powerON;
drawStatusIcons();
updateBrightness();
lastRedraw = millis();
return;
} else if (knownnightlight != nightlightActive) { //trigger moon icon
@ -503,21 +507,21 @@ class FourLineDisplayUsermod : public Usermod {
} else if (knownMode != effectCurrent) {
knownMode = effectCurrent;
if (displayTurnedOff) needRedraw = true;
else showCurrentEffectOrPalette(knownMode, JSON_mode_names, 3);
else { showCurrentEffectOrPalette(knownMode, JSON_mode_names, 3); return; }
} else if (knownPalette != effectPalette) {
knownPalette = effectPalette;
if (displayTurnedOff) needRedraw = true;
else showCurrentEffectOrPalette(knownPalette, JSON_palette_names, 2);
knownPalette = effectPalette;
if (displayTurnedOff) needRedraw = true;
else { showCurrentEffectOrPalette(knownPalette, JSON_palette_names, 2); return; }
} else if (knownBrightness != bri) {
if (displayTurnedOff && nightlightActive){needRedraw = false; knownBrightness = bri;}
else if(displayTurnedOff) needRedraw = true;
else updateBrightness();
if (displayTurnedOff && nightlightActive) { needRedraw = false; knownBrightness = bri; }
else if (displayTurnedOff) needRedraw = true;
else { updateBrightness(); return; }
} else if (knownEffectSpeed != effectSpeed) {
if (displayTurnedOff) needRedraw = true;
else updateSpeed();
else { updateSpeed(); return; }
} else if (knownEffectIntensity != effectIntensity) {
if (displayTurnedOff) needRedraw = true;
else updateIntensity();
else { updateIntensity(); return; }
}
if (!needRedraw) {
@ -526,22 +530,18 @@ class FourLineDisplayUsermod : public Usermod {
if (sleepMode && !displayTurnedOff && (millis() - lastRedraw > screenTimeout)) {
// We will still check if there is a change in redraw()
// and turn it back on if it changed.
clear();
sleepOrClock(true);
} else if (displayTurnedOff && clockMode) {
showTime();
}
return;
} else {
clear();
}
needRedraw = false;
lastRedraw = millis();
lastRedraw = now;
if (displayTurnedOff) {
// Turn the display back on
sleepOrClock(false);
}
// Turn the display back on
wakeDisplay();
// Update last known values.
knownBrightness = bri;
@ -647,24 +647,19 @@ class FourLineDisplayUsermod : public Usermod {
knownMode = effectCurrent;
knownPalette = effectPalette;
if (overlayUntil == 0) {
char smallBuffer1[MAX_MODE_LINE_SPACE];
char smallBuffer2[MAX_MODE_LINE_SPACE];
char smallBuffer3[MAX_MODE_LINE_SPACE+1];
bool spaceHit = false;
uint8_t printedChars = 0;
uint8_t smallChars1 = 0;
uint8_t smallChars2 = 0;
uint8_t smallChars3 = 0;
// Find the mode name in JSON
printedChars = extractModeName(inputEffPal, qstring, lineBuffer, MAX_JSON_CHARS-1);
uint8_t printedChars = extractModeName(inputEffPal, qstring, lineBuffer, MAX_JSON_CHARS-1);
if (lineHeight == 2) { // use this code for 8 line display
char smallBuffer1[MAX_MODE_LINE_SPACE];
char smallBuffer2[MAX_MODE_LINE_SPACE];
uint8_t smallChars1 = 0;
uint8_t smallChars2 = 0;
if (printedChars < MAX_MODE_LINE_SPACE) { // use big font if the text fits
for (;printedChars < (MAX_MODE_LINE_SPACE-1); printedChars++) lineBuffer[printedChars]=' ';
lineBuffer[printedChars] = 0;
drawString(1, row*lineHeight, lineBuffer);
} else { // for long names divide the text into 2 lines and print them small
bool spaceHit = false;
for (uint8_t i = 0; i < printedChars; i++) {
switch (lineBuffer[i]) {
case ' ':
@ -689,6 +684,8 @@ class FourLineDisplayUsermod : public Usermod {
drawString(1, row*lineHeight+1, smallBuffer2, true);
}
} else { // use this code for 4 ling displays
char smallBuffer3[MAX_MODE_LINE_SPACE+1];
uint8_t smallChars3 = 0;
if (printedChars > MAX_MODE_LINE_SPACE) printedChars = MAX_MODE_LINE_SPACE;
for (uint8_t i = 0; i < printedChars; i++) smallBuffer3[smallChars3++] = lineBuffer[i];
for (; smallChars3 < (MAX_MODE_LINE_SPACE); smallChars3++) smallBuffer3[smallChars3]=' ';
@ -707,11 +704,10 @@ class FourLineDisplayUsermod : public Usermod {
*/
bool wakeDisplay() {
if (type == NONE || !enabled) return false;
knownHour = 99;
if (displayTurnedOff) {
clear();
// Turn the display back on
sleepOrClock(false);
redraw(true);
return true;
}
return false;
@ -723,29 +719,23 @@ class FourLineDisplayUsermod : public Usermod {
* Clears the screen and prints.
*/
void overlay(const char* line1, long showHowLong, byte glyphType) {
if (displayTurnedOff) {
// Turn the display back on
sleepOrClock(false);
}
// Turn the display back on
wakeDisplay();
// Print the overlay
clear();
if (glyphType > 0) {
if (lineHeight == 2) drawGlyph(5, 0, glyphType, u8x8_font_benji_custom_icons_6x6, true);
else drawGlyph(7, lineHeight, glyphType, u8x8_font_benji_custom_icons_2x2, true);
if (line1) drawString(0, 3*lineHeight, line1);
} else {
if (line1) drawString(0, 2*(lineHeight-1), line1);
}
if (line1) drawString(0, 3*lineHeight, line1);
overlayUntil = millis() + showHowLong;
}
void networkOverlay(const char* line1, long showHowLong) {
if (displayTurnedOff) {
// Turn the display back on
sleepOrClock(false);
}
// Turn the display back on
wakeDisplay();
// Print the overlay
clear();
// First row string
if (line1) {
String l1 = line1;
l1.trim();
@ -782,16 +772,15 @@ class FourLineDisplayUsermod : public Usermod {
*/
void sleepOrClock(bool enabled) {
if (enabled) {
displayTurnedOff = true;
if (clockMode) {
clear();
knownMinute = knownHour = 99;
showTime();
} else
setPowerSave(1);
displayTurnedOff = true;
} else {
setPowerSave(0);
displayTurnedOff = false;
setPowerSave(0);
}
}
@ -801,7 +790,7 @@ class FourLineDisplayUsermod : public Usermod {
* the useAMPM configuration.
*/
void showTime() {
if (type == NONE || !enabled) return;
if (type == NONE || !enabled || !displayTurnedOff) return;
char lineBuffer[LINE_BUFFER_SIZE];
static byte lastSecond;

View File

@ -19,17 +19,20 @@
// Change between modes by pressing a button.
//
// Dependencies
// * This usermod REQURES the ModeSortUsermod
// * This Usermod works best coupled with
// FourLineDisplayUsermod.
//
// If FourLineDisplayUsermod is used the folowing options are also inabled
// If FourLineDisplayUsermod is used the folowing options are also enabled
//
// * main color
// * saturation of main color
// * display network (long press buttion)
//
#ifdef USERMOD_MODE_SORT
#error "Usermod Mode Sort is no longer required. Remove -D USERMOD_MODE_SORT from platformio.ini"
#endif
#ifndef ENCODER_DT_PIN
#define ENCODER_DT_PIN 18
#endif
@ -49,10 +52,70 @@
#define LAST_UI_STATE 4
#endif
// Number of modes at the start of the list to not sort
#define MODE_SORT_SKIP_COUNT 1
// Which list is being sorted
static char **listBeingSorted;
/**
* Modes and palettes are stored as strings that
* end in a quote character. Compare two of them.
* We are comparing directly within either
* JSON_mode_names or JSON_palette_names.
*/
static int re_qstringCmp(const void *ap, const void *bp) {
char *a = listBeingSorted[*((byte *)ap)];
char *b = listBeingSorted[*((byte *)bp)];
int i = 0;
do {
char aVal = pgm_read_byte_near(a + i);
if (aVal >= 97 && aVal <= 122) {
// Lowercase
aVal -= 32;
}
char bVal = pgm_read_byte_near(b + i);
if (bVal >= 97 && bVal <= 122) {
// Lowercase
bVal -= 32;
}
// Relly we shouldn't ever get to '\0'
if (aVal == '"' || bVal == '"' || aVal == '\0' || bVal == '\0') {
// We're done. one is a substring of the other
// or something happenend and the quote didn't stop us.
if (aVal == bVal) {
// Same value, probably shouldn't happen
// with this dataset
return 0;
}
else if (aVal == '"' || aVal == '\0') {
return -1;
}
else {
return 1;
}
}
if (aVal == bVal) {
// Same characters. Move to the next.
i++;
continue;
}
// We're done
if (aVal < bVal) {
return -1;
}
else {
return 1;
}
} while (true);
// We shouldn't get here.
return 0;
}
class RotaryEncoderUIUsermod : public Usermod {
private:
int fadeAmount = 5; // Amount to change every step (brightness)
int fadeAmount = 5; // Amount to change every step (brightness)
unsigned long currentTime;
unsigned long loopTime;
unsigned long buttonHoldTime;
@ -72,7 +135,16 @@ private:
void* display = nullptr;
#endif
// Pointers the start of the mode names within JSON_mode_names
char **modes_qstrings = nullptr;
// Array of mode indexes in alphabetical order.
byte *modes_alpha_indexes = nullptr;
// Pointers the start of the palette names within JSON_palette_names
char **palettes_qstrings = nullptr;
// Array of palette indexes in alphabetical order.
byte *palettes_alpha_indexes = nullptr;
unsigned char Enc_A;
@ -95,6 +167,82 @@ private:
static const char _CLK_pin[];
static const char _SW_pin[];
/**
* Sort the modes and palettes to the index arrays
* modes_alpha_indexes and palettes_alpha_indexes.
*/
void sortModesAndPalettes() {
modes_qstrings = re_findModeStrings(JSON_mode_names, strip.getModeCount());
modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT);
palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount());
palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount());
// How many palette names start with '*' and should not be sorted?
// (Also skipping the first one, 'Default').
int skipPaletteCount = 1;
while (pgm_read_byte_near(palettes_qstrings[skipPaletteCount++]) == '*') ;
re_sortModes(palettes_qstrings, palettes_alpha_indexes, strip.getPaletteCount(), skipPaletteCount);
}
byte *re_initIndexArray(int numModes) {
byte *indexes = (byte *)malloc(sizeof(byte) * numModes);
for (byte i = 0; i < numModes; i++) {
indexes[i] = i;
}
return indexes;
}
/**
* Return an array of mode or palette names from the JSON string.
* They don't end in '\0', they end in '"'.
*/
char **re_findModeStrings(const char json[], int numModes) {
char **modeStrings = (char **)malloc(sizeof(char *) * numModes);
uint8_t modeIndex = 0;
bool insideQuotes = false;
// advance past the mark for markLineNum that may exist.
char singleJsonSymbol;
// Find the mode name in JSON
bool complete = false;
for (size_t i = 0; i < strlen_P(json); i++) {
singleJsonSymbol = pgm_read_byte_near(json + i);
if (singleJsonSymbol == '\0') break;
switch (singleJsonSymbol) {
case '"':
insideQuotes = !insideQuotes;
if (insideQuotes) {
// We have a new mode or palette
modeStrings[modeIndex] = (char *)(json + i + 1);
}
break;
case '[':
break;
case ']':
if (!insideQuotes) complete = true;
break;
case ',':
if (!insideQuotes) modeIndex++;
default:
if (!insideQuotes) break;
}
if (complete) break;
}
return modeStrings;
}
/**
* Sort either the modes or the palettes using quicksort.
*/
void re_sortModes(char **modeNames, byte *indexes, int count, int numSkip) {
listBeingSorted = modeNames;
qsort(indexes + numSkip, count - numSkip, sizeof(byte), re_qstringCmp);
listBeingSorted = nullptr;
}
public:
/*
* setup() is called once at boot. WiFi is not yet connected at this point.
@ -121,9 +269,7 @@ public:
currentTime = millis();
loopTime = currentTime;
ModeSortUsermod *modeSortUsermod = (ModeSortUsermod*) usermods.lookup(USERMOD_ID_MODE_SORT);
modes_alpha_indexes = modeSortUsermod->getModesAlphaIndexes();
palettes_alpha_indexes = modeSortUsermod->getPalettesAlphaIndexes();
if (!initDone) sortModesAndPalettes();
#ifdef USERMOD_FOUR_LINE_DISPLAY
// This Usermod uses FourLineDisplayUsermod for the best experience.
@ -350,7 +496,7 @@ public:
}
#endif
if (increase) bri = (bri + fadeAmount <= 255) ? (bri + fadeAmount) : 255;
else bri = (bri - fadeAmount >= 0) ? (bri - fadeAmount) : 0;
else bri = (bri - fadeAmount >= 0) ? (bri - fadeAmount) : 0;
lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY
display->updateBrightness();
@ -365,8 +511,7 @@ public:
return;
}
#endif
if (increase) effectCurrentIndex = (effectCurrentIndex + 1 >= strip.getModeCount()) ? 0 : (effectCurrentIndex + 1);
else effectCurrentIndex = (effectCurrentIndex - 1 < 0) ? (strip.getModeCount() - 1) : (effectCurrentIndex - 1);
effectCurrentIndex = max(min((increase ? effectCurrentIndex+1 : effectCurrentIndex-1), strip.getModeCount()-1), 0);
effectCurrent = modes_alpha_indexes[effectCurrentIndex];
lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY
@ -382,8 +527,7 @@ public:
return;
}
#endif
if (increase) effectSpeed = (effectSpeed + fadeAmount <= 255) ? (effectSpeed + fadeAmount) : 255;
else effectSpeed = (effectSpeed - fadeAmount >= 0) ? (effectSpeed - fadeAmount) : 0;
effectSpeed = max(min((increase ? effectSpeed+fadeAmount : effectSpeed-fadeAmount), 255), 0);
lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY
display->updateSpeed();
@ -398,8 +542,7 @@ public:
return;
}
#endif
if (increase) effectIntensity = (effectIntensity + fadeAmount <= 255) ? (effectIntensity + fadeAmount) : 255;
else effectIntensity = (effectIntensity - fadeAmount >= 0) ? (effectIntensity - fadeAmount) : 0;
effectIntensity = max(min((increase ? effectIntensity+fadeAmount : effectIntensity-fadeAmount), 255), 0);
lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY
display->updateIntensity();
@ -414,8 +557,7 @@ public:
return;
}
#endif
if (increase) effectPaletteIndex = (effectPaletteIndex + 1 >= strip.getPaletteCount()) ? 0 : (effectPaletteIndex + 1);
else effectPaletteIndex = (effectPaletteIndex - 1 < 0) ? (strip.getPaletteCount() - 1) : (effectPaletteIndex - 1);
effectPaletteIndex = max(min((increase ? effectPaletteIndex+1 : effectPaletteIndex-1), strip.getPaletteCount()-1), 0);
effectPalette = palettes_alpha_indexes[effectPaletteIndex];
lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY
@ -449,8 +591,7 @@ public:
return;
}
#endif
if (increase) { if (currentSat1<252) currentSat1 += 4; }
else { if (currentSat1>3) currentSat1 -= 4; }
currentSat1 = max(min((increase ? currentSat1+fadeAmount : currentSat1-fadeAmount), 255), 0);
colorHStoRGB(currentHue1*256, currentSat1, col);
strip.applyToAllSelected = true;
strip.setColor(0, colorFromRgbw(col));

View File

@ -2091,7 +2091,7 @@ uint16_t WS2812FX::mode_colortwinkle()
}
}
}
return FRAMETIME;
return FRAMETIME_FIXED;
}
@ -2876,7 +2876,7 @@ uint16_t WS2812FX::candle(bool multi)
}
}
return FRAMETIME;
return FRAMETIME_FIXED;
}
uint16_t WS2812FX::mode_candle()
@ -3904,18 +3904,24 @@ uint16_t WS2812FX::mode_washing_machine(void) {
Modified, originally by Mark Kriegsman https://gist.github.com/kriegsman/1f7ccbbfa492a73c015e
*/
uint16_t WS2812FX::mode_blends(void) {
uint16_t dataSize = sizeof(uint32_t) * SEGLEN; // max segment length of 56 pixels on 16 segment ESP8266
uint16_t pixelLen = SEGLEN > UINT8_MAX ? UINT8_MAX : SEGLEN;
uint16_t dataSize = sizeof(uint32_t) * (pixelLen + 1); // max segment length of 56 pixels on 16 segment ESP8266
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
uint32_t* pixels = reinterpret_cast<uint32_t*>(SEGENV.data);
uint8_t blendSpeed = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 128);
uint8_t shift = (now * ((SEGMENT.speed >> 3) +1)) >> 8;
uint8_t shift = (now * ((SEGMENT.speed >> 3) +1)) >> 8;
for (int i = 0; i < SEGLEN; i++) {
for (int i = 0; i < pixelLen; i++) {
pixels[i] = color_blend(pixels[i], color_from_palette(shift + quadwave8((i + 1) * 16), false, PALETTE_SOLID_WRAP, 255), blendSpeed);
setPixelColor(i, pixels[i]);
shift += 3;
}
uint16_t offset = 0;
for (int i = 0; i < SEGLEN; i++) {
setPixelColor(i, pixels[offset++]);
if (offset > pixelLen) offset = 0;
}
return FRAMETIME;
}

View File

@ -48,7 +48,8 @@
/* Not used in all effects yet */
#define WLED_FPS 42
#define FRAMETIME (1000/WLED_FPS)
#define FRAMETIME_FIXED (1000/WLED_FPS)
#define FRAMETIME _frametime
/* each segment uses 52 bytes of SRAM memory, so if you're application fails because of
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
@ -71,7 +72,7 @@
#define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / MAX_NUM_SEGMENTS)
#define LED_SKIP_AMOUNT 1
#define MIN_SHOW_DELAY 15
#define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15)
#define NUM_COLORS 3 /* number of colors per segment */
#define SEGMENT _segments[_segment_index]
@ -655,6 +656,7 @@ class WS2812FX {
setPixelColor(uint16_t n, uint32_t c),
setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
show(void),
setTargetFps(uint8_t fps),
setPixelSegment(uint8_t n),
deserializeMap(uint8_t n=0);
@ -685,6 +687,7 @@ class WS2812FX {
getActiveSegmentsNum(void),
//getFirstSelectedSegment(void),
getMainSegmentId(void),
getTargetFps(void),
gamma8(uint8_t),
gamma8_cal(uint8_t, float),
sin_gap(uint16_t),
@ -856,6 +859,8 @@ class WS2812FX {
uint16_t _usedSegmentData = 0;
uint16_t _transitionDur = 750;
uint8_t _targetFps = 42;
uint16_t _frametime = (1000/42);
uint16_t _cumulativeFps = 2;
bool
@ -922,6 +927,11 @@ class WS2812FX {
transitionProgress(uint8_t tNr);
};
extern const char JSON_mode_names[];
extern const char JSON_palette_names[];
// the following has been moved to FX_fcn.cpp instead
/*
// WLEDSR: extensions
// Technical notes
// ===============
@ -1009,9 +1019,9 @@ const char JSON_mode_names[] PROGMEM = R"=====([
"Stream 2",
"Oscillate",
"Pride 2015",
"Juggle@!,Trail;!,!,;!",
"Juggle@!=16,Trail=240;!,!,;!",
"Palette@!,;;!",
"Fire 2012@Spark rate,Decay;;!",
"Fire 2012@Spark rate=120,Decay=64;;!",
"Colorwaves",
"Bpm",
"Fill Noise",
@ -1033,7 +1043,7 @@ const char JSON_mode_names[] PROGMEM = R"=====([
"Spots@Spread,Width;!,!,;!",
"Spots Fade@Spread,Width;!,!,;!",
"Glitter",
"Candle@Flicker rate,Flicker intensity;!,!,;0",
"Candle@Flicker rate=96,Flicker intensity=224;!,!,;0",
"Fireworks Starburst",
"Fireworks 1D@Gravity,Firing side;!,!,;!",
"Bouncing Balls@Gravity,# of balls;!,!,;!",
@ -1047,9 +1057,9 @@ const char JSON_mode_names[] PROGMEM = R"=====([
"Ripple Rainbow",
"Heartbeat",
"Pacifica",
"Candle Multi@Flicker rate,Flicker intensity;!,!,;0",
"Candle Multi@Flicker rate=96,Flicker intensity=224;!,!,;0",
"Solid Glitter@,!;!,,;0",
"Sunrise@Time [min],;;0",
"Sunrise@Time [min]=60,;;0",
"Phased",
"Twinkleup@!,Intensity;!,!,;!",
"Noise Pal",
@ -1076,5 +1086,5 @@ const char JSON_palette_names[] PROGMEM = R"=====([
"Semi Blue","Pink Candy","Red Reaf","Aqua Flash","Yelblu Hot","Lite Light","Red Flash","Blink Red","Red Shift","Red Tide",
"Candy2"
])=====";
*/
#endif

View File

@ -165,12 +165,12 @@ void WS2812FX::service() {
_triggered = false;
}
void WS2812FX::setPixelColor(uint16_t n, uint32_t c) {
void IRAM_ATTR WS2812FX::setPixelColor(uint16_t n, uint32_t c) {
setPixelColor(n, R(c), G(c), B(c), W(c));
}
//used to map from segment index to physical pixel, taking into account grouping, offsets, reverse and mirroring
uint16_t WS2812FX::realPixelIndex(uint16_t i) {
uint16_t IRAM_ATTR WS2812FX::realPixelIndex(uint16_t i) {
int16_t iGroup = i * SEGMENT.groupLength();
/* reverse just an individual segment */
@ -187,7 +187,7 @@ uint16_t WS2812FX::realPixelIndex(uint16_t i) {
return realIndex;
}
void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
void IRAM_ATTR WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
{
if (SEGLEN) {//from segment
uint16_t realIndex = realPixelIndex(i);
@ -350,6 +350,15 @@ uint16_t WS2812FX::getFps() {
return _cumulativeFps +1;
}
uint8_t WS2812FX::getTargetFps() {
return _targetFps;
}
void WS2812FX::setTargetFps(uint8_t fps) {
if (fps > 0 && fps <= 120) _targetFps = fps;
_frametime = 1000 / _targetFps;
}
/**
* Forces the next frame to be computed on all active segments.
*/
@ -775,7 +784,7 @@ void WS2812FX::setTransitionMode(bool t)
/*
* color blend function
*/
uint32_t WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) {
uint32_t IRAM_ATTR WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) {
if(blend == 0) return color1;
uint16_t blendmax = b16 ? 0xFFFF : 0xFF;
if(blend == blendmax) return color2;
@ -878,13 +887,13 @@ void WS2812FX::blur(uint8_t blur_amount)
}
}
uint16_t WS2812FX::triwave16(uint16_t in)
uint16_t IRAM_ATTR WS2812FX::triwave16(uint16_t in)
{
if (in < 0x8000) return in *2;
return 0xFFFF - (in - 0x8000)*2;
}
uint8_t WS2812FX::sin_gap(uint16_t in) {
uint8_t IRAM_ATTR WS2812FX::sin_gap(uint16_t in) {
if (in & 0x100) return 0;
//if (in > 255) return 0;
return sin8(in + 192); //correct phase shift of sine so that it starts and stops at 0
@ -951,13 +960,13 @@ uint8_t WS2812FX::get_random_wheel_index(uint8_t pos) {
}
uint32_t WS2812FX::crgb_to_col(CRGB fastled)
uint32_t IRAM_ATTR WS2812FX::crgb_to_col(CRGB fastled)
{
return RGBW32(fastled.red, fastled.green, fastled.blue, 0);
}
CRGB WS2812FX::col_to_crgb(uint32_t color)
CRGB IRAM_ATTR WS2812FX::col_to_crgb(uint32_t color)
{
CRGB fastled_col;
fastled_col.red = R(color);
@ -1080,7 +1089,7 @@ void WS2812FX::handle_palette(void)
* @param pbri Value to scale the brightness of the returned color by. Default is 255. (no scaling)
* @returns Single color from palette
*/
uint32_t WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri)
uint32_t IRAM_ATTR WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri)
{
if (SEGMENT.palette == 0 && mcol < 3) {
uint32_t color = SEGCOLOR(mcol);
@ -1121,11 +1130,7 @@ void WS2812FX::deserializeMap(uint8_t n) {
return;
}
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(7)) return;
#endif
DEBUG_PRINT(F("Reading LED map from "));
DEBUG_PRINTLN(fileName);
@ -1208,4 +1213,159 @@ WS2812FX* WS2812FX::instance = nullptr;
//Bus static member definition, would belong in bus_manager.cpp
int16_t Bus::_cct = -1;
uint8_t Bus::_cctBlend = 0;
uint8_t Bus::_autoWhiteMode = RGBW_MODE_DUAL;
uint8_t Bus::_autoWhiteMode = RGBW_MODE_DUAL;
// WLEDSR: extensions
// Technical notes
// ===============
// If an effect name is followed by an @, slider and color control is effective.
// See setSliderAndColorControl in index.js for implementation
// If not effective then:
// - For AC effects (id<128) 2 sliders and 3 colors and the palette will be shown
// - For SR effects (id>128) 5 sliders and 3 colors and the palette will be shown
// If effective (@)
// - a ; seperates slider controls (left) from color controls (middle) and palette control (right)
// - if left, middle or right is empty no controls are shown
// - a , seperates slider controls (max 5) or color controls (max 3). Palette has only one value
// - a ! means that the default is used.
// - For sliders: Effect speeds, Effect intensity, Custom 1, Custom 2, Custom 3
// - For colors: Fx color, Background color, Custom
// - For palette: prompt for color palette OR palette ID if numeric (will hide palette selection)
//
// Note: If palette is on and no colors are specified 1,2 and 3 is shown in each color circle.
// If a color is specified, the 1,2 or 3 is replaced by that specification.
// Note: Effects can override default pattern behaviour
// - FadeToBlack can override the background setting
// - Defining SEGCOL(<i>) can override a specific palette using these values (e.g. Color Gradient)
const char JSON_mode_names[] PROGMEM = R"=====([
"Solid",
"Blink@!,;!,!,;!",
"Breathe@!,;!,!;!",
"Wipe@!,!;!,!,;!",
"Wipe Random@!,;;!",
"Random Colors@!,Fade time;;!",
"Sweep@!,!;!,!,;!",
"Dynamic",
"Colorloop@!,Saturation;;!",
"Rainbow",
"Scan@!,# of dots;!,!,;!",
"Scan Dual@!,# of dots;!,!,;!",
"Fade",
"Theater@!,Gap size;!,!,;!",
"Theater Rainbow",
"Running@!,Wave width;!,!,;!",
"Saw@!,Width;!,!,;!",
"Twinkle",
"Dissolve",
"Dissolve Rnd",
"Sparkle",
"Sparkle Dark",
"Sparkle+",
"Strobe",
"Strobe Rainbow",
"Strobe Mega",
"Blink Rainbow",
"Android",
"Chase",
"Chase Random",
"Chase Rainbow",
"Chase Flash",
"Chase Flash Rnd",
"Rainbow Runner",
"Colorful",
"Traffic Light",
"Sweep Random",
"Chase 2@!,Width;!,!,;!",
"Aurora",
"Stream",
"Scanner",
"Lighthouse",
"Fireworks",
"Rain",
"Tetrix@!,Width;!,!,;!",
"Fire Flicker",
"Gradient",
"Loading",
"Police@!,Width;;0",
"Fairy",
"Two Dots@!,Dot size;1,2,Bg;!",
"Fairy Twinkle",
"Running Dual",
"Halloween",
"Chase 3@!,Size;1,2,3;0",
"Tri Wipe@!,Width;1,2,3;0",
"Tri Fade",
"Lightning",
"ICU",
"Multi Comet",
"Scanner Dual",
"Stream 2",
"Oscillate",
"Pride 2015",
"Juggle@!=16,Trail=240;!,!,;!",
"Palette@!,;;!",
"Fire 2012@Spark rate=120,Decay=64;;!",
"Colorwaves",
"Bpm",
"Fill Noise",
"Noise 1",
"Noise 2",
"Noise 3",
"Noise 4",
"Colortwinkles",
"Lake",
"Meteor@!,Trail length;!,!,;!",
"Meteor Smooth@!,Trail length;!,!,;!",
"Railway",
"Ripple",
"Twinklefox",
"Twinklecat",
"Halloween Eyes",
"Solid Pattern@Fg size,Bg size;Fg,Bg,;!=0",
"Solid Pattern Tri@,Size;1,2,3;!=0",
"Spots@Spread,Width;!,!,;!",
"Spots Fade@Spread,Width;!,!,;!",
"Glitter",
"Candle@Flicker rate=96,Flicker intensity=224;!,!,;0",
"Fireworks Starburst",
"Fireworks 1D@Gravity,Firing side;!,!,;!",
"Bouncing Balls@Gravity,# of balls;!,!,;!",
"Sinelon",
"Sinelon Dual",
"Sinelon Rainbow",
"Popcorn@",
"Drip@Gravity,# of drips;!,!;!",
"Plasma@Phase,;1,2,3;!",
"Percent@,% of fill;!,!,;!",
"Ripple Rainbow",
"Heartbeat",
"Pacifica",
"Candle Multi@Flicker rate=96,Flicker intensity=224;!,!,;0",
"Solid Glitter@,!;!,,;0",
"Sunrise@Time [min]=60,;;0",
"Phased",
"Twinkleup@!,Intensity;!,!,;!",
"Noise Pal",
"Sine",
"Phased Noise",
"Flow",
"Chunchun@!,Gap size;!,!,;!",
"Dancing Shadows",
"Washing Machine",
"Candy Cane",
"Blends",
"TV Simulator",
"Dynamic Smooth"
])=====";
const char JSON_palette_names[] PROGMEM = R"=====([
"Default","* Random Cycle","* Color 1","* Colors 1&2","* Color Gradient","* Colors Only","Party","Cloud","Lava","Ocean",
"Forest","Rainbow","Rainbow Bands","Sunset","Rivendell","Breeze","Red & Blue","Yellowout","Analogous","Splash",
"Pastel","Sunset 2","Beech","Vintage","Departure","Landscape","Beach","Sherbet","Hult","Hult 64",
"Drywet","Jul","Grintage","Rewhi","Tertiary","Fire","Icefire","Cyane","Light Pink","Autumn",
"Magenta","Magred","Yelmag","Yelblu","Orange & Teal","Tiamat","April Night","Orangery","C9","Sakura",
"Aurora","Atlantica","C9 2","C9 New","Temperature","Aurora 2","Retro Clown","Candy","Toxy Reaf","Fairy Reaf",
"Semi Blue","Pink Candy","Red Reaf","Aqua Flash","Yelblu Hot","Lite Light","Red Flash","Blink Red","Red Shift","Red Tide",
"Candy2"
])=====";

View File

@ -288,7 +288,7 @@ class BusPwm : public Bus {
if (!pinManager.allocatePin(currentPin, true, PinOwner::BusPwm)) {
deallocatePins(); return;
}
_pins[i] = currentPin; // store only after allocatePin() succeeds
_pins[i] = currentPin; //store only after allocatePin() succeeds
#ifdef ESP8266
pinMode(_pins[i], OUTPUT);
#else
@ -396,7 +396,7 @@ class BusPwm : public Bus {
private:
uint8_t _pins[5] = {255, 255, 255, 255, 255};
uint8_t _data[5] = {255, 255, 255, 255, 255};
uint8_t _data[5] = {0};
#ifdef ARDUINO_ARCH_ESP32
uint8_t _ledcStart = 255;
#endif

View File

@ -20,7 +20,7 @@ void shortPressAction(uint8_t b)
default: ++effectCurrent %= strip.getModeCount(); colorUpdated(CALL_MODE_BUTTON); break;
}
} else {
applyPreset(macroButton[b], CALL_MODE_BUTTON);
applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET);
}
// publish MQTT message
@ -39,7 +39,7 @@ void longPressAction(uint8_t b)
default: bri += 8; colorUpdated(CALL_MODE_BUTTON); buttonPressedTime[b] = millis(); break; // repeatable action
}
} else {
applyPreset(macroLongPress[b], CALL_MODE_BUTTON);
applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET);
}
// publish MQTT message
@ -58,7 +58,7 @@ void doublePressAction(uint8_t b)
default: ++effectPalette %= strip.getPaletteCount(); colorUpdated(CALL_MODE_BUTTON); break;
}
} else {
applyPreset(macroDoublePress[b], CALL_MODE_BUTTON);
applyPreset(macroDoublePress[b], CALL_MODE_BUTTON_PRESET);
}
// publish MQTT message
@ -105,12 +105,12 @@ void handleSwitch(uint8_t b)
if (millis() - buttonPressedTime[b] > WLED_DEBOUNCE_THRESHOLD) { //fire edge event only after 50ms without change (debounce)
if (!buttonPressedBefore[b]) { // on -> off
if (macroButton[b]) applyPreset(macroButton[b], CALL_MODE_BUTTON);
if (macroButton[b]) applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET);
else { //turn on
if (!bri) {toggleOnOff(); colorUpdated(CALL_MODE_BUTTON);}
}
} else { // off -> on
if (macroLongPress[b]) applyPreset(macroLongPress[b], CALL_MODE_BUTTON);
if (macroLongPress[b]) applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET);
else { //turn off
if (bri) {toggleOnOff(); colorUpdated(CALL_MODE_BUTTON);}
}

View File

@ -86,8 +86,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
Bus::setAutoWhiteMode(hw_led[F("rgbwm")] | Bus::getAutoWhiteMode());
CJSON(correctWB, hw_led["cct"]);
CJSON(cctFromRgb, hw_led[F("cr")]);
CJSON(strip.cctBlending, hw_led[F("cb")]);
Bus::setCCTBlend(strip.cctBlending);
CJSON(strip.cctBlending, hw_led[F("cb")]);
Bus::setCCTBlend(strip.cctBlending);
strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS
JsonArray ins = hw_led["ins"];
@ -369,7 +370,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(timerMinutes[it], timer["min"]);
CJSON(timerMacro[it], timer["macro"]);
byte dowPrev = timerWeekday[it];
byte dowPrev = timerWeekday[it];
//note: act is currently only 0 or 1.
//the reason we are not using bool is that the on-disk type in 0.11.0 was already int
int actPrev = timerWeekday[it] & 0x01;
@ -379,7 +380,17 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
int act = timer["en"] | actPrev;
if (act) timerWeekday[it]++;
}
if (it<8) {
JsonObject start = timer["start"];
byte startm = start["mon"];
if (startm) timerMonth[it] = (startm << 4);
CJSON(timerDay[it], start["day"]);
JsonObject end = timer["end"];
CJSON(timerDayEnd[it], end["day"]);
byte endm = end["mon"];
if (startm) timerMonth[it] += endm & 0x0F;
if (!(timerMonth[it] & 0x0F)) timerMonth[it] += 12; //default end month to 12
}
it++;
}
@ -430,11 +441,7 @@ void deserializeConfigFromFS() {
return;
}
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(1)) return;
#endif
DEBUG_PRINTLN(F("Reading settings from /cfg.json..."));
@ -458,11 +465,7 @@ void serializeConfig() {
DEBUG_PRINTLN(F("Writing settings to /cfg.json..."));
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(2)) return;
#endif
JsonArray rev = doc.createNestedArray("rev");
rev.add(1); //major settings revision
@ -543,8 +546,9 @@ void serializeConfig() {
hw_led[F("ledma")] = strip.milliampsPerLed;
hw_led["cct"] = correctWB;
hw_led[F("cr")] = cctFromRgb;
hw_led[F("cb")] = strip.cctBlending;
hw_led[F("rgbwm")] = Bus::getAutoWhiteMode();
hw_led[F("cb")] = strip.cctBlending;
hw_led["fps"] = strip.getTargetFps();
hw_led[F("rgbwm")] = Bus::getAutoWhiteMode();
JsonArray hw_led_ins = hw_led.createNestedArray("ins");
@ -746,6 +750,14 @@ void serializeConfig() {
timers_ins0["min"] = timerMinutes[i];
timers_ins0["macro"] = timerMacro[i];
timers_ins0[F("dow")] = timerWeekday[i] >> 1;
if (i<8) {
JsonObject start = timers_ins0.createNestedObject("start");
start["mon"] = (timerMonth[i] >> 4) & 0xF;
start["day"] = timerDay[i];
JsonObject end = timers_ins0.createNestedObject("end");
end["mon"] = timerMonth[i] & 0xF;
end["day"] = timerDayEnd[i];
}
}
JsonObject ota = doc.createNestedObject("ota");
@ -782,11 +794,7 @@ void serializeConfig() {
bool deserializeConfigSec() {
DEBUG_PRINTLN(F("Reading settings from /wsec.json..."));
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(3)) return false;
#endif
bool success = readObjectFromFile("/wsec.json", nullptr, &doc);
if (!success) {
@ -831,11 +839,7 @@ bool deserializeConfigSec() {
void serializeConfigSec() {
DEBUG_PRINTLN(F("Writing settings to /wsec.json..."));
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(4)) return;
#endif
JsonObject nw = doc.createNestedObject("nw");

View File

@ -64,6 +64,7 @@
#define USERMOD_ID_SEVEN_SEGMENT_DISPLAY 21 //Usermod "usermod_v2_seven_segment_display.h"
#define USERMOD_RGB_ROTARY_ENCODER 22 //Usermod "rgb-rotary-encoder.h"
#define USERMOD_ID_QUINLED_AN_PENTA 23 //Usermod "quinled-an-penta.h"
#define USERMOD_ID_SSDR 24 //Usermod "usermod_v2_seven_segment_display_reloaded.h"
//Access point behavior
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
@ -74,7 +75,7 @@
//Notifier callMode
#define CALL_MODE_INIT 0 //no updates on init, can be used to disable updates
#define CALL_MODE_DIRECT_CHANGE 1
#define CALL_MODE_BUTTON 2
#define CALL_MODE_BUTTON 2 //default button actions applied to selected segments
#define CALL_MODE_NOTIFICATION 3
#define CALL_MODE_NIGHTLIGHT 4
#define CALL_MODE_NO_NOTIFY 5
@ -84,6 +85,7 @@
#define CALL_MODE_BLYNK 9
#define CALL_MODE_ALEXA 10
#define CALL_MODE_WS_SEND 11 //special call mode, not for notifier, updates websocket only
#define CALL_MODE_BUTTON_PRESET 12 //button/IR JSON preset/macro
//RGB to RGBW conversion mode
#define RGBW_MODE_MANUAL_ONLY 0 //No automatic white channel calculation. Manual white channel slider
@ -301,11 +303,7 @@
#define JSON_BUFFER_SIZE 20480
#endif
#ifdef WLED_USE_DYNAMIC_JSON
#define MIN_HEAP_SIZE (JSON_BUFFER_SIZE+512)
#else
#define MIN_HEAP_SIZE (MAX_LED_MEMORY+2048)
#endif
#define MIN_HEAP_SIZE (MAX_LED_MEMORY+2048)
// Maximum size of node map (list of other WLED instances)
#ifdef ESP8266

View File

@ -8,11 +8,11 @@ var expanded = [false];
var powered = [true];
var nlDur = 60, nlTar = 0;
var nlMode = false;
var selectedFx = 0;
var selectedFx = 0, prevFx = -1;
var selectedPal = 0;
var sliderControl = ""; //WLEDSR: used by togglePcMode
var csel = 0;
var currentPreset = -1;
var currentPreset = -1, prevPS = -1;
var lastUpdate = 0;
var segCount = 0, ledCount = 0, lowestUnused = 0, maxSeg = 0, lSeg = 0;
var pcMode = false, pcModeA = false, lastw = 0;
@ -1048,8 +1048,10 @@ function updateSelectedFx()
var selectedEffect = parent.querySelector(`.lstI[data-id="${selectedFx}"]`);
if (selectedEffect) {
selectedEffect.classList.add('selected');
var fx = (selectedFx != prevFx) && currentPreset==-1; //effect changed & preset==none
var ps = (prevPS != currentPreset) && currentPreset==-1; // preset changed & preset==none
// WLEDSR: extract the Slider and color control string from the HTML element and set it.
setSliderAndColorControl(selectedFx);
setSliderAndColorControl(selectedFx, (fx || ps));
}
}
@ -1115,6 +1117,7 @@ function readState(s,command=false)
nlTar = s.nl.tbri;
nlFade = s.nl.fade;
syncSend = s.udpn.send;
prevPS = currentPreset;
if (s.pl<0) currentPreset = s.ps;
else currentPreset = s.pl;
@ -1178,6 +1181,7 @@ function readState(s,command=false)
showToast('Error ' + s.error + ": " + errstr, true);
}
prevFx = selectedFx;
selectedPal = i.pal;
selectedFx = i.fx;
redrawPalPrev(); // if any color changed (random palette did at least)
@ -1185,7 +1189,27 @@ function readState(s,command=false)
}
// WLEDSR: control HTML elements for Slider and Color Control
function setSliderAndColorControl(idx)
// Technical notes
// ===============
// If an effect name is followed by an @, slider and color control is effective.
// If not effective then:
// - For AC effects (id<128) 2 sliders and 3 colors and the palette will be shown
// - For SR effects (id>128) 5 sliders and 3 colors and the palette will be shown
// If effective (@)
// - a ; seperates slider controls (left) from color controls (middle) and palette control (right)
// - if left, middle or right is empty no controls are shown
// - a , seperates slider controls (max 5) or color controls (max 3). Palette has only one value
// - a ! means that the default is used.
// - For sliders: Effect speeds, Effect intensity, Custom 1, Custom 2, Custom 3
// - For colors: Fx color, Background color, Custom
// - For palette: prompt for color palette OR palette ID if numeric (will hide palette selection)
//
// Note: If palette is on and no colors are specified 1,2 and 3 is shown in each color circle.
// If a color is specified, the 1,2 or 3 is replaced by that specification.
// Note: Effects can override default pattern behaviour
// - FadeToBlack can override the background setting
// - Defining SEGCOL(<i>) can override a specific palette using these values (e.g. Color Gradient)
function setSliderAndColorControl(idx, applyDef=false)
{
if (!(Array.isArray(fxdata) && fxdata.length>idx)) return;
var topPosition = 0;
@ -1195,15 +1219,25 @@ function setSliderAndColorControl(idx)
var slOnOff = (extras.length==0 || extras[0]=='')?[]:extras[0].split(",");
var coOnOff = (extras.length<2 || extras[1]=='')?[]:extras[1].split(",");
var paOnOff = (extras.length<3 || extras[2]=='')?[]:extras[2].split(",");
var obj = {"seg":{}};
// set html slider items on/off
var nSliders = Math.floor((gId("Effects").children.length - 1) / 2); // p (label) & div for each slider + FX list
var nSliders = Math.min(5,Math.floor((gId("Effects").children.length - 1) / 2)); // p (label) & div for each slider + FX list
for (let i=0; i<nSliders; i++) {
var slider = gId("slider" + i);
var label = gId("sliderLabel" + i);
// if (not controlDefined and for AC speed or intensity and for SR alle sliders) or slider has a value
if ((!controlDefined && i < ((idx<128)?2:nSliders)) || (slOnOff.length>i && slOnOff[i] != "")) {
label.style.display = "block";
if (slOnOff.length>i && slOnOff[i].indexOf("=")>0) {
//embeded default values
var dPos = slOnOff[i].indexOf("=");
var v = Math.max(0,Math.min(255,parseInt(slOnOff[i].substr(dPos+1))));
if (i==0) { if (applyDef) gId("sliderSpeed").value = v; obj.seg.sx = v; }
else if (i==1) { if (applyDef) gId("sliderIntensity").value = v; obj.seg.ix = v; }
else { if (applyDef) gId("sliderC"+(i-1)).value = v; obj.seg["C"+(i-1)] = v}
slOnOff[i] = slOnOff[i].substring(0,dPos-1);
}
if (slOnOff.length>i && slOnOff[i]!="!") label.innerHTML = slOnOff[i];
else if (i==0) label.innerHTML = "Effect speed";
else if (i==1) label.innerHTML = "Effect intensity";
@ -1263,20 +1297,9 @@ function setSliderAndColorControl(idx)
hide = false;
} else {
btn.style.display = "none";
if (i>0 && csel==i) selectSlot(0);
}
}
selectSlot(0);
/*
// perhaps too aggressive
var ccfg = cfg.comp.colors;
gId("picker").style.display = hide && ccfg.picker ? "none" : "block";
gId("vwrap").style.display = hide && ccfg.picker ? "none" : "block";
gId("kwrap").style.display = hide && ccfg.picker && cct ? "none" : "block";
gId("wwrap").style.display = hide ? "none" : "block";
gId("wbal").style.display = hide && !cct ? "none" : "block";
gId("rgbwrap").style.display = hide && ccfg.rgb ? "none" : "block";
gId("qcs-w").style.display = hide && ccfg.quick ? "none" : "block";
*/
gId("cslLabel").innerHTML = cslLabel;
// set palette on/off
@ -1285,14 +1308,26 @@ function setSliderAndColorControl(idx)
// if not controlDefined or palette has a value
if ((!controlDefined) || (paOnOff.length>0 && paOnOff[0]!="" && isNaN(paOnOff[0]))) {
palw.style.display = "inline-block";
if (paOnOff.length>0 && paOnOff[0].indexOf("=")>0) {
//embeded default values
var dPos = paOnOff[0].indexOf("=");
var v = Math.max(0,Math.min(255,parseInt(paOnOff[0].substr(dPos+1))));
var p = d.querySelector(`#pallist input[name="palette"][value="${v}"]`);
if (applyDef && p) {
p.checked = true;
obj.seg.pal = v;
}
paOnOff[0] = paOnOff[0].substring(0,dPos-1);
}
if (paOnOff.length>0 && paOnOff[0] != "!") pall.innerHTML = paOnOff[0];
else pall.innerHTML = '<i class="icons sel-icon" onclick="tglHex()">&#xe2b3;</i> Color palette';
} else {
// disable label and slider
palw.style.display = "none";
// if numeric set as selected palette
if (paOnOff.length>0 && paOnOff[0]!="" && !isNaN(paOnOff[0]) && parseInt(paOnOff[0])!=selectedPal) setPalette(parseInt(paOnOff[0]));
if (paOnOff.length>0 && paOnOff[0]!="" && !isNaN(paOnOff[0]) && parseInt(paOnOff[0])!=selectedPal) obj.seg.pal = parseInt(paOnOff[0]);
}
if (!isEmpty(obj.seg) && applyDef) requestJson(obj); //update default values (may need throttling on ESP8266)
}
var jsonTimeout;
@ -1308,7 +1343,7 @@ function requestJson(command=null)
var useWs = (ws && ws.readyState === WebSocket.OPEN);
var type = command ? 'post':'get';
if (command) {
command.v = true; // force complete /json/si API response
if (useWs || !command.ps) command.v = true; // force complete /json/si API response (ps is async so no point)
command.time = Math.floor(Date.now() / 1000);
var t = gId('tt');
if (t.validity.valid && command.transition==null) {
@ -1322,6 +1357,8 @@ function requestJson(command=null)
if (useWs) {
ws.send(req?req:'{"v":true}');
return;
} else if (command && command.ps) { //refresh UI if we don't use WS (async loading of presets)
setTimeout(requestJson,200);
}
fetch(url, {
@ -1698,7 +1735,8 @@ function rptSeg(s)
if (stop == 0) {return;}
var rev = gId(`seg${s}rev`).checked;
var mi = gId(`seg${s}mi`).checked;
var obj = {"seg": {"id": 0, "n": name, "start": start, "stop": (cfg.comp.seglen?start:0)+stop}, "rev": rev, "mi": mi, "on": !powered[s], "bri": parseInt(gId(`seg${s}bri`).value)};
var sel = gId(`seg${s}sel`).checked;
var obj = {"seg": {"id": 0, "n": name, "start": start, "stop": (cfg.comp.seglen?start:0)+stop, "rev": rev, "mi": mi, "on": !powered[s], "bri": parseInt(gId(`seg${s}bri`).value), "sel": sel}};
if (gId(`seg${s}grp`)) {
var grp = parseInt(gId(`seg${s}grp`).value);
var spc = parseInt(gId(`seg${s}spc`).value);

View File

@ -353,7 +353,7 @@ ${i+1}:
c += `<option value="7" ${t==7?"selected":""}>Analog</option>`;
c += `<option value="8" ${t==8?"selected":""}>Analog inverted</option>`;
c += `</select>`;
c += `<span style="cursor: pointer;" onclick="off('${bt}')">&nbsp;&#215;</span><br>`;
c += `<span style="cursor: pointer;" onclick="off('${bt}')">&nbsp;&#x2715;</span><br>`;
gId("btns").innerHTML = c;
}
function tglSi(cs) {
@ -515,10 +515,10 @@ ${i+1}:
<option value=6>6-key black</option>
<option value=7>9-key red</option>
<option value=8>JSON remote</option>
</select><span style="cursor: pointer;" onclick="off('IR')">&nbsp;&#215;</span><br>
<div id="json" style="display:none;">JSON file: <input type="file" name="data" accept=".json"> <input type="button" class="btn" value="Upload" onclick="uploadFile('/ir.json');"><br></div>
</select><span style="cursor: pointer;" onclick="off('IR')">&nbsp;&#x2715;</span><br>
<div id="json" style="display:none;">JSON file: <input type="file" name="data" accept=".json"><button type="button" class="sml" onclick="uploadFile('/ir.json')">Upload</button><br></div>
<a href="https://kno.wled.ge/interfaces/infrared/" target="_blank">IR info</a><br>
Relay GPIO: <input type="number" min="-1" max="33" name="RL" onchange="UI()" class="xs"> Invert <input type="checkbox" name="RM"><span style="cursor: pointer;" onclick="off('RL')">&nbsp;&#215;</span><br>
Relay GPIO: <input type="number" min="-1" max="33" name="RL" onchange="UI()" class="xs"> Invert <input type="checkbox" name="RM"><span style="cursor: pointer;" onclick="off('RL')">&nbsp;&#x2715;</span><br>
<hr style="width:260px">
<h3>Defaults</h3>
Turn LEDs on after power up/reset: <input type="checkbox" name="BO"><br>
@ -563,8 +563,9 @@ ${i+1}:
<option value="2">Linear (never wrap)</option>
<option value="3">None (not recommended)</option>
</select><br>
Target refresh rate: <input type="number" class="s" min="1" max="120" name="FR" required> FPS
<hr style="width:260px">
<div id="cfg">Config template: <input type="file" name="data2" accept=".json"> <input type="button" class="btn" value="Apply" onclick="loadCfg(d.Sf.data2);"><br></div>
<div id="cfg">Config template: <input type="file" name="data2" accept=".json"><button type="button" class="sml" onclick="loadCfg(d.Sf.data2)">Apply</button><br></div>
<hr>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
</form>

View File

@ -7,6 +7,7 @@
<title>Time Settings</title>
<script>
var d=document;
var ms=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
function H()
{
window.open("https://kno.wled.ge/features/settings/#time-settings");
@ -17,12 +18,21 @@
}
function S()
{
BTa();GetV();Cs();FC();
BTa();GetV();updLoc();Cs();FC();
}
function gId(s)
{
return d.getElementById(s);
}
function gN(s) {
return d.getElementsByName(s)[0];
}
function expand(o,i)
{
var t = gId("WD"+i);
t.style.display = t.style.display!=="none" ? "none" : "";
o.innerHTML = t.style.display==="none" ? "&#128467;" : "&#x2715;";
}
function Cs()
{
gId("cac").style.display="none";
@ -42,46 +52,71 @@
gId("coc").style.display="none";
}
}
function BTa()
{
var ih="<tr><th>En.</th><th>Hour</th><th>Minute</th><th>Preset</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";
for (i=0;i<8;i++)
{
ih+="<tr><td><input name=\"W"+i+"\" id=\"W"+i+"\" type=\"hidden\"><input id=\"W"+i+"0\" type=\"checkbox\"></td><td><input name=\"H"+i+"\" class=\"xs\" type=\"number\" min=\"0\" max=\"24\"></td><td><input name=\"N"+i+"\" class=\"xs\" type=\"number\" min=\"0\" max=\"59\"></td><td><input name=\"T"+i+"\" class=\"s\" type=\"number\" min=\"0\" max=\"250\"></td>";
for (j=1;j<8;j++) ih+="<td><input id=\"W"+i+j+"\" type=\"checkbox\"></td>";
ih+="</tr>";
}
ih+="<tr><td><input name=\"W8\" id=\"W8\" type=\"hidden\"><input id=\"W80\" type=\"checkbox\"></td><td>Sunrise<input name=\"H8\" value=\"255\" type=\"hidden\"></td><td><input name=\"N8\" class=\"xs\" type=\"number\" min=\"-59\" max=\"59\"></td><td><input name=\"T8\" class=\"s\" type=\"number\" min=\"0\" max=\"250\"></td>";
for (j=1;j<8;j++) ih+="<td><input id=\"W8"+j+"\" type=\"checkbox\"></td>";
ih+="</tr><tr><td><input name=\"W9\" id=\"W9\" type=\"hidden\"><input id=\"W90\" type=\"checkbox\"></td><td>Sunset<input name=\"H9\" value=\"255\" type=\"hidden\"></td><td><input name=\"N9\" class=\"xs\" type=\"number\" min=\"-59\" max=\"59\"></td><td><input name=\"T9\" class=\"s\" type=\"number\" min=\"0\" max=\"250\"></td>";
for (j=1;j<8;j++) ih+="<td><input id=\"W9"+j+"\" type=\"checkbox\"></td>";
ih+="</tr>";
gId("TMT").innerHTML=ih;
}
function FC()
{
for(j=0;j<8;j++)
{
for(i=0;i<10;i++) gId("W"+i+j).checked=gId("W"+i).value>>j&1;
}
}
function Wd()
{
a=[0,0,0,0,0,0,0,0,0,0];
for(i=0;i<10;i++)
{
m=1;
for(j=0;j<8;j++)
{
a[i]+=gId("W"+i+j).checked*m;m*=2;
}
gId("W"+i).value=a[i];
}
}
function BTa()
{
var ih="<thead><tr><th>En.</th><th>Hour</th><th>Minute</th><th>Preset</th><th></th></tr></thead>";
for (i=0;i<8;i++) {
ih+=`<tr><td><input name="W${i}" id="W${i}" type="hidden"><input id="W${i}0" type="checkbox"></td>
<td><input name="H${i}" class="xs" type="number" min="0" max="24"></td>
<td><input name="N${i}" class="xs" type="number" min="0" max="59"></td>
<td><input name="T${i}" class="s" type="number" min="0" max="250"></td>
<td><div id="CB${i}" onclick="expand(this,${i})" class="cal">&#128467;</div></td></tr>`;
ih+=`<tr><td colspan=5><div id="WD${i}" style="display:none;"><hr>Run on weekdays`;
ih+=`<table><tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr><tr>`
for (j=1;j<8;j++) ih+=`<td><input id="W${i}${j}" type="checkbox"></td>`;
ih+=`</tr></table>from <select name="M${i}">`;
for (j=0;j<12;j++) ih+=`<option value="${j+1}">${ms[j]}</option>`;
ih+=`</select><input name="D${i}" class="xs" type="number" min="1" max="31"></input> to <select name="P${i}">`;
for (j=0;j<12;j++) ih+=`<option value="${j+1}">${ms[j]}</option>`;
ih+=`</select><input name="E${i}" class="xs" type="number" min="1" max="31"></input>
<hr></div></td></tr>`;
}
ih+=`<tr><td><input name="W8" id="W8" type="hidden"><input id="W80" type="checkbox"></td>
<td>Sunrise<input name="H8" value="255" type="hidden"></td>
<td><input name="N8" class="xs" type="number" min="-59" max="59"></td>
<td><input name="T8" class="s" type="number" min="0" max="250"></td>
<td><div onclick="expand(this,8)" class="cal">&#128467;</div></td></tr><tr><td colspan=5>`;
ih+=`<div id="WD8" style="display:none;"><hr><table><tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr><tr>`;
for (j=1;j<8;j++) ih+=`<td><input id="W8${j}" type="checkbox"></td>`;
ih+="</tr></table><hr></div></td></tr>";
ih+=`<tr><td><input name="W9" id="W9" type="hidden"><input id="W90" type="checkbox"></td>
<td>Sunset<input name="H9" value="255" type="hidden"></td>
<td><input name="N9" class="xs" type="number" min="-59" max="59"></td>
<td><input name="T9" class="s" type="number" min="0" max="250"></td>
<td><div onclick="expand(this,9)" class="cal">&#128467;</div></td></tr><tr><td colspan=5>`;
ih+=`<div id="WD9" style="display:none;"><hr><table><tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr><tr>`;
for (j=1;j<8;j++) ih+=`<td><input id="W9${j}" type="checkbox"></td>`;
ih+="</tr></table><hr></div></td></tr>";
gId("TMT").innerHTML=ih;
}
function FC()
{
for(i=0;i<10;i++)
{
let wd = gId("W"+i).value;
for(j=0;j<8;j++) {
gId("W"+i+j).checked=wd>>j&1;
}
if ((wd&127) != 127 || (i<8 && (gN("M"+i).value != 1 || gN("D"+i).value != 1 || gN("P"+i).value != 12 || gN("E"+i).value != 31))) {
expand(gId("CB"+i),i); //expand macros with custom DOW or date range set
}
}
}
function Wd()
{
a = [0,0,0,0,0,0,0,0,0,0];
for (i=0; i<10; i++) {
m=1;
for(j=0;j<8;j++) { a[i]+=gId(("W"+i)+j).checked*m; m*=2;}
gId("W"+i).value=a[i];
}
if (d.Sf.LTR.value==="S") { d.Sf.LT.value = -1*parseFloat(d.Sf.LT.value); }
if (d.Sf.LNR.value==="W") { d.Sf.LN.value = -1*parseFloat(d.Sf.LN.value); }
}
function addRow(i,p,l,d) {
var t = gId("macros"); // table
var rCnt = t.rows.length; // get the number of rows.
var tr = t.insertRow(rCnt); // table row.
var rCnt = t.rows.length; // get the number of rows.
var tr = t.insertRow(rCnt); // table row.
var b = String.fromCharCode((i<10?48:55)+i);
var td = document.createElement('td'); // TABLE DEFINITION.
td = tr.insertCell(0);
@ -93,6 +128,10 @@
td = tr.insertCell(3);
td.innerHTML = `<input name="MD${b}" type="number" class="s" min="0" max="250" value="${d}" required>`;
}
function updLoc(i) {
if (parseFloat(d.Sf.LT.value)<0) { d.Sf.LTR.value = "S"; d.Sf.LT.value = -1*parseFloat(d.Sf.LT.value); } else d.Sf.LTR.value = "N";
if (parseFloat(d.Sf.LN.value)<0) { d.Sf.LNR.value = "W"; d.Sf.LN.value = -1*parseFloat(d.Sf.LN.value); } else d.Sf.LNR.value = "E";
}
function GetV()
{
//values injected by server while sending HTML
@ -137,8 +176,8 @@
</select><br>
UTC offset: <input name="UO" type="number" min="-65500" max="65500" required> seconds (max. 18 hours)<br>
Current local time is <span class="times">unknown</span>.<br>
Latitude (N): <input name="LT" type="number" class="l" min="-66.6" max="66.6" step="0.01"><br>
Longitude (E): <input name="LN" type="number" class="l" min="-180" max="180" step="0.01">
Latitude: <select name="LTR"><option value="N">N</option><option value="S">S</option></select><input name="LT" type="number" class="xl" min="0" max="66.6" step="0.01"><br>
Longitude: <select name="LNR"><option value="E">E</option><option value="W">W</option></select><input name="LN" type="number" class="xl" min="0" max="180" step="0.01">
<div id="sun" class="times"></div>
<h3>Clock</h3>
Clock Overlay:
@ -161,12 +200,12 @@
</div>
Countdown Mode: <input type="checkbox" name="CE"><br>
Countdown Goal:<br>
Date: <nowrap>20<input name="CY" class="xs" type="number" min="0" max="99" required>-<input name="CI" class="xs" type="number" min="1" max="12" required>-<input name="CD" class="xs" type="number" min="1" max="31" required></nowrap><br>
Time: <nowrap><input name="CH" class="xs" type="number" min="0" max="23" required>:<input name="CM" class="xs" type="number" min="0" max="59" required>:<input name="CS" class="xs" type="number" min="0" max="59" required></nowrap><br>
Date:&nbsp;<nowrap>20<input name="CY" class="xs" type="number" min="0" max="99" required>-<input name="CI" class="xs" type="number" min="1" max="12" required>-<input name="CD" class="xs" type="number" min="1" max="31" required></nowrap><br>
Time:&nbsp;<nowrap><input name="CH" class="xs" type="number" min="0" max="23" required>:<input name="CM" class="xs" type="number" min="0" max="59" required>:<input name="CS" class="xs" type="number" min="0" max="59" required></nowrap><br>
<h3>Macro presets</h3>
<b>Macros have moved!</b><br>
<i>Presets now also can be used as macros to save both JSON and HTTP API commands.<br>
Just enter the preset id below!</i>
<b>Macros have moved!</b><br>
<i>Presets now also can be used as macros to save both JSON and HTTP API commands.<br>
Just enter the preset ID below!</i>
<i>Use 0 for the default action instead of a preset</i><br>
Alexa On/Off Preset: <input name="A0" class="m" type="number" min="0" max="250" required> <input name="A1" class="m" type="number" min="0" max="250" required><br>
Countdown-Over Preset: <input name="MC" class="m" type="number" min="0" max="250" required><br>
@ -186,9 +225,10 @@
</table>
<a href="https://kno.wled.ge/features/macros/#analog-button" target="_blank">Analog Button setup</a>
<h3>Time-controlled presets</h3>
<div style="display: inline-block">
<table id="TMT">
</table></div><hr>
<div style="display: inline-block">
<table id="TMT" style="min-width:330px;"></table>
</div>
<hr>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
</form>
</body>

View File

@ -759,15 +759,9 @@ function genPalPrevCss(id)
} else {
if (selColors) {
let e = element[1] - 1;
//if (Array.isArray(selColors[e])) {
r = selColors[e][0];
g = selColors[e][1];
b = selColors[e][2];
//} else {
// r = (selColors[e]>>16) & 0xFF;
// g = (selColors[e]>> 8) & 0xFF;
// b = (selColors[e] ) & 0xFF;
//}
r = selColors[e][0];
g = selColors[e][1];
b = selColors[e][2];
}
}
if (index === false) {
@ -931,18 +925,10 @@ function readState(s,command=false)
for (let e = cd.length-1; e >= 0; e--)
{
var r,g,b,w;
//if (Array.isArray(i.col[e])) {
r = i.col[e][0];
g = i.col[e][1];
b = i.col[e][2];
if (isRgbw) w = i.col[e][3];
//} else {
// // unsigned long RGBW (@blazoncek v2 experimental API implementation)
// r = (i.col[e]>>16) & 0xFF;
// g = (i.col[e]>> 8) & 0xFF;
// b = (i.col[e] ) & 0xFF;
// if (isRgbw) w = (i.col[e] >> 24) & 0xFF;
//}
r = i.col[e][0];
g = i.col[e][1];
b = i.col[e][2];
if (isRgbw) w = i.col[e][3];
cd[e].style.backgroundColor = "rgb(" + r + "," + g + "," + b + ")";
if (isRgbw) whites[e] = parseInt(w);
selectSlot(csel);
@ -993,7 +979,7 @@ function requestJson(command=null)
var useWs = (ws && ws.readyState === WebSocket.OPEN);
var type = command ? 'post':'get';
if (command) {
command.v = true; // force complete /json/si API response
if (useWs || !command.ps) command.v = true; // force complete /json/si API response
command.time = Math.floor(Date.now() / 1000);
var t = gId('tt');
if (t.validity.valid && command.transition==null) {
@ -1007,6 +993,8 @@ function requestJson(command=null)
if (useWs) {
ws.send(req?req:'{"v":true}');
return;
} else if (command && command.ps) { //refresh UI if we don't use WS (async loading of presets)
setTimeout(requestJson,200);
}
fetch(url, {

View File

@ -31,6 +31,13 @@ button, .btn {
cursor: pointer;
text-decoration: none;
}
button.sml {
padding: 8px;
border-radius: 20px;
font-size: 15px;
min-width: 40px;
margin: 0 0 0 10px;
}
.toprow {
top: 0;
position: sticky;
@ -85,6 +92,9 @@ input[type="checkbox"] {
transform: scale(1.5);
margin-right: 10px;
}
td input[type="checkbox"] {
margin-right: revert;
}
select {
margin: 2px;
background: #333;
@ -92,13 +102,22 @@ select {
font-family: Verdana, sans-serif;
border: 0.5ch solid #333;
}
tr {
line-height: 100%;
}
td {
padding: 2px;
}
.d5 {
width: 4rem !important;
}
.cal {
font-size:1.5rem;
cursor:pointer
}
#TMT table {
width: 100%;
}
#toast {
opacity: 0;
background-color: #444;
@ -140,9 +159,6 @@ td {
}
@media screen and (max-width: 480px) {
input[type="checkbox"] {
transform: scale(1);
}
input[type="number"].s {
width: 40px;
}

View File

@ -194,6 +194,7 @@ int16_t loadPlaylist(JsonObject playlistObject, byte presetId = 0);
void handlePlaylist();
//presets.cpp
void handlePresets();
bool applyPreset(byte index, byte callMode = CALL_MODE_DIRECT_CHANGE);
inline bool applyTemporaryPreset() {return applyPreset(255);};
void savePreset(byte index, bool persist = true, const char* pname = nullptr, JsonObject saveobj = JsonObject());

View File

@ -43,54 +43,54 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()=====";
#endif
// Autogenerated from wled00/data/update.htm, do not edit!!
const uint16_t PAGE_update_length = 734;
const uint16_t PAGE_update_length = 733;
const uint8_t PAGE_update[] PROGMEM = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x85, 0x54, 0xef, 0x6b, 0xdc, 0x46,
0x10, 0xfd, 0xae, 0xbf, 0x62, 0xb3, 0xa6, 0x60, 0x43, 0x4e, 0xb2, 0xef, 0x48, 0x29, 0x3a, 0x49,
0xa5, 0xae, 0xfd, 0x21, 0x50, 0x88, 0x21, 0x49, 0x4b, 0x28, 0xa5, 0xac, 0xb4, 0x23, 0x69, 0xb8,
0xd5, 0xae, 0xb2, 0x3b, 0xba, 0xf3, 0xf5, 0xf0, 0xff, 0x9e, 0xd1, 0xca, 0x76, 0x9c, 0x94, 0xd2,
0x2f, 0x62, 0x7f, 0xcc, 0x7b, 0x33, 0xf3, 0xe6, 0xad, 0x8a, 0x57, 0x37, 0xef, 0x7e, 0xfd, 0xf0,
0xe9, 0xee, 0x56, 0xf4, 0x34, 0x98, 0xaa, 0x78, 0xfc, 0x82, 0xd2, 0x55, 0x31, 0x00, 0x29, 0xd1,
0x10, 0xfd, 0xae, 0xbf, 0x62, 0xb3, 0xa6, 0x60, 0x43, 0x4e, 0xb2, 0xef, 0x20, 0x14, 0x9d, 0xa4,
0x50, 0xd7, 0xfe, 0x10, 0x28, 0xc4, 0x90, 0xa4, 0xa5, 0x94, 0x12, 0x56, 0xda, 0x91, 0x34, 0xdc,
0x6a, 0x57, 0xd9, 0x1d, 0xdd, 0xf9, 0x7a, 0xf8, 0x7f, 0xef, 0x68, 0x65, 0x3b, 0x4e, 0x43, 0xe8,
0x17, 0xb1, 0x3f, 0xe6, 0xbd, 0x99, 0x79, 0xf3, 0x56, 0xc5, 0xab, 0x9b, 0xf7, 0xbf, 0x7e, 0xfc,
0xf3, 0xee, 0x56, 0xf4, 0x34, 0x98, 0xaa, 0x78, 0xfc, 0x82, 0xd2, 0x55, 0x31, 0x00, 0x29, 0xd1,
0x38, 0x4b, 0x60, 0xa9, 0x94, 0x07, 0xd4, 0xd4, 0x97, 0x1a, 0xf6, 0xd8, 0xc0, 0x2a, 0x6e, 0xa4,
0xb0, 0x6a, 0x80, 0x52, 0xee, 0x11, 0x0e, 0xa3, 0xf3, 0x24, 0xab, 0xa4, 0x20, 0x24, 0x03, 0xd5,
0x1f, 0xbf, 0xdd, 0xde, 0x88, 0x8f, 0xa3, 0x56, 0x04, 0x45, 0xb6, 0x1c, 0x15, 0xa1, 0xf1, 0x38,
0x1f, 0xbf, 0xdd, 0xde, 0x88, 0x4f, 0xa3, 0x56, 0x04, 0x45, 0xb6, 0x1c, 0x15, 0xa1, 0xf1, 0x38,
0x52, 0x95, 0xb4, 0x93, 0x6d, 0x08, 0x9d, 0x15, 0xd7, 0xe7, 0x17, 0xa7, 0x03, 0x5a, 0xed, 0x0e,
0x69, 0x8f, 0x81, 0x9c, 0x3f, 0xa6, 0xb5, 0x6a, 0x76, 0xe7, 0x17, 0x0f, 0xcf, 0x21, 0x1f, 0x39,
0x44, 0xbb, 0x66, 0x1a, 0xb8, 0x82, 0xb4, 0x03, 0xba, 0x35, 0x30, 0x2f, 0xaf, 0x8f, 0x6f, 0xf5,
0x69, 0x8f, 0x81, 0x9c, 0x3f, 0xa6, 0xb5, 0x6a, 0x76, 0xe7, 0x17, 0x0f, 0xcf, 0x21, 0x9f, 0x38,
0x44, 0xbb, 0x66, 0x1a, 0xb8, 0x82, 0xb4, 0x03, 0xba, 0x35, 0x30, 0x2f, 0xaf, 0x8f, 0xef, 0xf4,
0xb9, 0x9c, 0x5a, 0x79, 0x91, 0x06, 0x3a, 0x1a, 0x48, 0x35, 0x86, 0xd1, 0xa8, 0x63, 0x29, 0xad,
0xb3, 0x20, 0x5f, 0xff, 0x27, 0x64, 0x08, 0xdd, 0xbf, 0x31, 0xb5, 0x71, 0xcd, 0x4e, 0x3e, 0x24,
0x45, 0xf6, 0x58, 0x62, 0x11, 0x03, 0xaa, 0x24, 0xad, 0xe9, 0x34, 0x57, 0xd4, 0x79, 0x37, 0x59,
0x9d, 0x9f, 0x6d, 0x36, 0x9b, 0x6d, 0xe3, 0x8c, 0xf3, 0xf9, 0x59, 0xdb, 0xb6, 0xdb, 0x96, 0x95,
0x59, 0xb5, 0x6a, 0x40, 0x73, 0xcc, 0x7f, 0x07, 0xaf, 0x95, 0x55, 0xaf, 0x83, 0xb2, 0x61, 0x15,
0xc0, 0x63, 0xbb, 0xad, 0x9d, 0xd7, 0xe0, 0xf3, 0x74, 0xd3, 0xf4, 0x22, 0x38, 0x83, 0x5a, 0x44,
0x82, 0xc7, 0xb4, 0x39, 0x5a, 0x83, 0x16, 0x56, 0x31, 0xf9, 0x42, 0x15, 0xf0, 0x1f, 0xc8, 0xd7,
0x97, 0xe3, 0xfd, 0x76, 0x50, 0xbe, 0x43, 0x9b, 0xff, 0xf4, 0xbc, 0x5c, 0x91, 0x1b, 0xf3, 0xab,
0xf5, 0x78, 0xff, 0x80, 0x76, 0x9c, 0xe8, 0x4f, 0x3a, 0x8e, 0x50, 0xb6, 0x68, 0xe0, 0xaf, 0xd3,
0x57, 0xe8, 0xd5, 0x8f, 0x7c, 0x5f, 0x3b, 0x7d, 0x3c, 0xfd, 0x4f, 0x65, 0x04, 0xf7, 0xb4, 0x52,
0x06, 0x3b, 0x9b, 0x37, 0x2c, 0x0d, 0xf8, 0xed, 0xcb, 0x2e, 0xd7, 0xeb, 0xf5, 0xcb, 0x2e, 0x63,
0x95, 0x3d, 0x60, 0xd7, 0x13, 0x17, 0x77, 0xf9, 0xc3, 0xc3, 0x19, 0x8b, 0x78, 0x7a, 0xea, 0x62,
0xd6, 0x3b, 0x2a, 0x17, 0x15, 0x2b, 0xb2, 0xc5, 0x38, 0x73, 0x11, 0x6c, 0xa2, 0xf5, 0xe2, 0x83,
0xf7, 0xae, 0xa5, 0x83, 0xf2, 0xf0, 0x6c, 0x08, 0xbe, 0x28, 0x5a, 0xe7, 0x07, 0xc1, 0x06, 0xeb,
0x9d, 0x2e, 0xe5, 0xdd, 0xbb, 0xf7, 0x1f, 0xa4, 0x48, 0x54, 0x1c, 0x7a, 0x29, 0xb3, 0x29, 0x06,
0x4a, 0x81, 0x7c, 0xc7, 0x53, 0x16, 0xc0, 0x76, 0x98, 0x3b, 0x96, 0xc3, 0x64, 0x08, 0x47, 0xe5,
0x29, 0x9b, 0xf1, 0x2b, 0x8e, 0x52, 0x52, 0x38, 0x1b, 0xa6, 0x7a, 0x40, 0xb6, 0x28, 0xdb, 0x85,
0x3d, 0xf8, 0xd6, 0x06, 0x52, 0xc6, 0x80, 0x16, 0x7b, 0xf0, 0x81, 0x19, 0x73, 0x71, 0x99, 0x5e,
0x6d, 0xd2, 0x2b, 0x16, 0xfb, 0x4d, 0x51, 0xfb, 0xea, 0xc6, 0x1d, 0xac, 0x71, 0x4a, 0x0b, 0xea,
0x41, 0x18, 0xce, 0x14, 0x48, 0xd4, 0x68, 0x95, 0x3f, 0xe6, 0xa2, 0x50, 0x22, 0xe9, 0x3d, 0xb4,
0xa5, 0xec, 0x89, 0xc6, 0x90, 0x67, 0x59, 0x87, 0xd4, 0x4f, 0x75, 0xda, 0xb8, 0x21, 0xfb, 0x05,
0x7d, 0xe3, 0x9c, 0xdb, 0x21, 0x64, 0x73, 0x67, 0x99, 0x07, 0x03, 0x2a, 0x40, 0x90, 0x82, 0x78,
0x4c, 0xc0, 0x15, 0xfc, 0x5d, 0x1b, 0x65, 0x77, 0xb2, 0x2a, 0x70, 0xe8, 0x44, 0x12, 0x7c, 0xf3,
0x95, 0x87, 0x4f, 0xd2, 0xd0, 0x23, 0x18, 0x1d, 0x52, 0x74, 0x8f, 0xb4, 0x4f, 0x14, 0xdf, 0x53,
0xa7, 0x61, 0xdf, 0xfd, 0x1c, 0x45, 0x2d, 0x5b, 0xae, 0x70, 0x15, 0x3e, 0x4f, 0xac, 0xe0, 0xfc,
0xc0, 0x32, 0x55, 0xcd, 0x3d, 0x14, 0xd1, 0x08, 0x62, 0x91, 0x65, 0x76, 0x82, 0x14, 0x8d, 0x51,
0x21, 0xb0, 0x9d, 0xe9, 0xe9, 0x5d, 0x3e, 0xc9, 0xe8, 0xe1, 0xf3, 0x84, 0x1e, 0xf4, 0x4b, 0x60,
0xb2, 0x20, 0x17, 0xe5, 0xbe, 0xc1, 0xee, 0x95, 0x99, 0xf8, 0x66, 0x19, 0xd6, 0x2b, 0xb9, 0x80,
0xea, 0x89, 0x88, 0xdf, 0xe3, 0x02, 0x5a, 0x36, 0xdf, 0x80, 0x12, 0x67, 0x1b, 0x83, 0xcd, 0xae,
0x94, 0xd7, 0xf3, 0x0c, 0xae, 0xd9, 0x4f, 0x45, 0xb6, 0xc4, 0xb1, 0x2b, 0xe6, 0x61, 0x55, 0x85,
0xc6, 0x7d, 0x9c, 0xe8, 0xfc, 0x08, 0x99, 0xb1, 0x8a, 0x19, 0xd0, 0x76, 0x69, 0x9a, 0x72, 0x6c,
0xcc, 0x93, 0xdc, 0x45, 0x35, 0x84, 0x76, 0xc2, 0x3a, 0xe2, 0x04, 0x8e, 0x37, 0xce, 0x73, 0x07,
0xad, 0x87, 0xd0, 0xc7, 0x81, 0x8d, 0xaa, 0x03, 0x91, 0x5f, 0x14, 0x19, 0xf3, 0x31, 0xf7, 0xe2,
0xb5, 0x2c, 0xfe, 0xb7, 0xbe, 0x00, 0xc6, 0x3c, 0x8c, 0x02, 0xcd, 0x04, 0x00, 0x00
0xb3, 0x20, 0x5f, 0xff, 0x10, 0x32, 0x84, 0xee, 0x7b, 0x4c, 0x6d, 0x5c, 0xb3, 0x93, 0x0f, 0x49,
0x91, 0x3d, 0x96, 0x58, 0xc4, 0x80, 0x2a, 0x49, 0x6b, 0x3a, 0xcd, 0x15, 0x75, 0xde, 0x4d, 0x56,
0xe7, 0x67, 0x9b, 0xcd, 0x66, 0xdb, 0x38, 0xe3, 0x7c, 0x7e, 0xd6, 0xb6, 0xed, 0xb6, 0x65, 0x65,
0x56, 0xad, 0x1a, 0xd0, 0x1c, 0xf3, 0xdf, 0xc1, 0x6b, 0x65, 0xd5, 0xeb, 0xa0, 0x6c, 0x58, 0x05,
0xf0, 0xd8, 0x6e, 0x6b, 0xe7, 0x35, 0xf8, 0x3c, 0xdd, 0x34, 0xbd, 0x08, 0xce, 0xa0, 0x16, 0x91,
0xe0, 0x31, 0x6d, 0x8e, 0xd6, 0xa0, 0x85, 0x55, 0x4c, 0xbe, 0x50, 0x05, 0xfc, 0x07, 0xf2, 0xf5,
0xe5, 0x78, 0xbf, 0x1d, 0x94, 0xef, 0xd0, 0xe6, 0x3f, 0x3f, 0x2f, 0x57, 0xe4, 0xc6, 0xfc, 0x6a,
0x3d, 0xde, 0x3f, 0xa0, 0x1d, 0x27, 0xfa, 0x8b, 0x8e, 0x23, 0x94, 0x2d, 0x1a, 0xf8, 0xfb, 0xf4,
0x15, 0x7a, 0xf5, 0x86, 0xef, 0x6b, 0xa7, 0x8f, 0xa7, 0xff, 0xa9, 0x8c, 0xe0, 0x9e, 0x56, 0xca,
0x60, 0x67, 0xf3, 0x86, 0xa5, 0x01, 0xbf, 0x7d, 0xd9, 0xe5, 0x7a, 0xbd, 0x7e, 0xd9, 0x65, 0xac,
0xb2, 0x07, 0xec, 0x7a, 0xe2, 0xe2, 0x2e, 0x7f, 0x7a, 0x38, 0x63, 0x11, 0x4f, 0x4f, 0x5d, 0xcc,
0x7a, 0x47, 0xe5, 0xa2, 0x62, 0x45, 0xb6, 0x18, 0x67, 0x2e, 0x82, 0x4d, 0xb4, 0x5e, 0x7c, 0xf0,
0xc1, 0xb5, 0x74, 0x50, 0x1e, 0x9e, 0x0d, 0xc1, 0x17, 0x45, 0xeb, 0xfc, 0x20, 0xd8, 0x60, 0xbd,
0xd3, 0xa5, 0xbc, 0x7b, 0xff, 0xe1, 0xa3, 0x14, 0x89, 0x8a, 0x43, 0x2f, 0x65, 0x36, 0xc5, 0x40,
0x29, 0x90, 0xef, 0x78, 0xca, 0x02, 0xd8, 0x0e, 0x73, 0xc7, 0x72, 0x98, 0x0c, 0xe1, 0xa8, 0x3c,
0x65, 0x33, 0x7e, 0xc5, 0x51, 0x4a, 0x0a, 0x67, 0xc3, 0x54, 0x0f, 0xc8, 0x16, 0x65, 0xbb, 0xb0,
0x07, 0xdf, 0xd9, 0x40, 0xca, 0x18, 0xd0, 0x62, 0x0f, 0x3e, 0x30, 0x63, 0x2e, 0x2e, 0xd3, 0xab,
0x4d, 0x7a, 0xc5, 0x62, 0xbf, 0x29, 0x6a, 0x5f, 0xdd, 0xb8, 0x83, 0x35, 0x4e, 0x69, 0x41, 0x3d,
0x08, 0xc3, 0x99, 0x02, 0x89, 0x1a, 0xad, 0xf2, 0xc7, 0x5c, 0x14, 0x4a, 0x24, 0xbd, 0x87, 0xb6,
0x94, 0x3d, 0xd1, 0x18, 0xf2, 0x2c, 0xeb, 0x90, 0xfa, 0xa9, 0x4e, 0x1b, 0x37, 0x64, 0xbf, 0xa0,
0x6f, 0x9c, 0x73, 0x3b, 0x84, 0x6c, 0xee, 0x2c, 0xf3, 0x60, 0x40, 0x05, 0x08, 0x52, 0x10, 0x8f,
0x09, 0xb8, 0x82, 0xcf, 0xb5, 0x51, 0x76, 0x27, 0xab, 0x02, 0x87, 0x4e, 0x24, 0xc1, 0x37, 0x5f,
0x79, 0xf8, 0x24, 0x0d, 0x3d, 0x82, 0xd1, 0x21, 0x45, 0xf7, 0x48, 0xfb, 0x44, 0xf1, 0x5f, 0xea,
0x34, 0xec, 0xbb, 0xb7, 0x51, 0xd4, 0xb2, 0xe5, 0x0a, 0x57, 0xe1, 0xcb, 0xc4, 0x0a, 0xce, 0x0f,
0x2c, 0x53, 0xd5, 0xdc, 0x43, 0x11, 0x8d, 0x20, 0x16, 0x59, 0x66, 0x27, 0x48, 0xd1, 0x18, 0x15,
0x02, 0xdb, 0x99, 0x9e, 0xde, 0xe5, 0x93, 0x8c, 0x1e, 0xbe, 0x4c, 0xe8, 0x41, 0xbf, 0x04, 0x26,
0x0b, 0x72, 0x51, 0xee, 0x1b, 0xec, 0x5e, 0x99, 0x89, 0x6f, 0x96, 0x61, 0xbd, 0x92, 0x0b, 0xa8,
0x9e, 0x88, 0xf8, 0x3d, 0x2e, 0xa0, 0x65, 0xf3, 0x0d, 0x28, 0x71, 0xb6, 0x31, 0xd8, 0xec, 0x4a,
0x79, 0x3d, 0xcf, 0xe0, 0x9a, 0xfd, 0x54, 0x64, 0x4b, 0x1c, 0xbb, 0x62, 0x1e, 0x56, 0x55, 0x68,
0xdc, 0xc7, 0x89, 0xce, 0x8f, 0x90, 0x19, 0xab, 0x98, 0x01, 0x6d, 0x97, 0xa6, 0x29, 0xc7, 0xc6,
0x3c, 0xc9, 0x5d, 0x54, 0x43, 0x68, 0x27, 0xac, 0x23, 0x4e, 0xe0, 0x78, 0xe3, 0x3c, 0x77, 0xd0,
0x7a, 0x08, 0x7d, 0x1c, 0xd8, 0xa8, 0x3a, 0x10, 0xf9, 0x45, 0x91, 0x31, 0x1f, 0x73, 0x2f, 0x5e,
0xcb, 0xe2, 0x7f, 0xeb, 0x5f, 0xd6, 0x9c, 0xdd, 0xf9, 0xcd, 0x04, 0x00, 0x00
};

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@
*/
// Autogenerated from wled00/data/simple.htm, do not edit!!
const uint16_t PAGE_simple_L = 17522;
const uint16_t PAGE_simple_L = 17543;
const uint8_t PAGE_simple[] PROGMEM = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0xe5, 0xbd, 0x69, 0x77, 0xe2, 0xbc,
0xb2, 0x30, 0xfa, 0x3d, 0xbf, 0x82, 0x76, 0xef, 0x9d, 0xc6, 0x1b, 0x07, 0xcc, 0x18, 0x86, 0x26,
@ -967,142 +967,143 @@ const uint8_t PAGE_simple[] PROGMEM = {
0xf9, 0xfd, 0xdb, 0x6f, 0x82, 0x5f, 0x84, 0xa4, 0x0a, 0xc8, 0xbf, 0xb0, 0x8c, 0x09, 0x85, 0x2c,
0x44, 0xc3, 0xd0, 0x84, 0x3c, 0xc9, 0xf3, 0x9e, 0x7b, 0x89, 0x4a, 0x22, 0xf3, 0xd3, 0xc0, 0x10,
0x5b, 0x43, 0x57, 0xfd, 0xe7, 0x81, 0x21, 0x02, 0xb0, 0x8a, 0x7d, 0x6e, 0x04, 0x12, 0x95, 0x63,
0xfe, 0x59, 0x1a, 0x13, 0x80, 0xa6, 0xce, 0x04, 0x39, 0x33, 0x70, 0x30, 0xe8, 0x52, 0x38, 0xea,
0x1a, 0xa5, 0x5e, 0xda, 0x30, 0xa7, 0x49, 0x1e, 0x03, 0x1a, 0xf8, 0x30, 0x57, 0x70, 0x1c, 0x66,
0x0b, 0xe8, 0x48, 0xb6, 0x9a, 0x0a, 0x14, 0xcc, 0x1e, 0x60, 0x89, 0x40, 0xa7, 0xeb, 0x11, 0x76,
0xe8, 0xf9, 0xf7, 0xfd, 0x65, 0x98, 0x15, 0xff, 0xa5, 0xbb, 0x16, 0x58, 0x4d, 0xfa, 0x52, 0x77,
0xd0, 0x8f, 0x10, 0xae, 0x50, 0x97, 0xf8, 0x65, 0xd2, 0x95, 0xe6, 0x41, 0x70, 0x2e, 0xe1, 0x3d,
0xde, 0x88, 0x51, 0x12, 0xf4, 0x64, 0x1d, 0x48, 0x88, 0xa5, 0xed, 0xc7, 0xbb, 0x00, 0x92, 0xd7,
0x85, 0xbc, 0x98, 0xbe, 0x53, 0xda, 0x10, 0xf0, 0xe8, 0x1b, 0xc1, 0xcb, 0x3a, 0x30, 0x7a, 0x1e,
0x2f, 0x3d, 0xdb, 0x41, 0xef, 0x3e, 0xa0, 0x03, 0xcf, 0x54, 0x69, 0x4c, 0xe7, 0xa0, 0x48, 0xad,
0xe1, 0x75, 0xb8, 0x16, 0x32, 0xfb, 0xbb, 0xdb, 0xd6, 0x4e, 0x99, 0x5b, 0x0a, 0xf8, 0x2e, 0x94,
0xaa, 0xf3, 0x6f, 0x86, 0x11, 0x26, 0xff, 0x2f, 0xab, 0x1e, 0x7d, 0x1e, 0x2f, 0x30, 0xd8, 0x47,
0x1f, 0xad, 0xa2, 0x80, 0xfa, 0x04, 0xe4, 0x48, 0xa8, 0x0e, 0x82, 0x1b, 0xc4, 0x7e, 0x98, 0x0e,
0x4d, 0x89, 0xe8, 0x21, 0x6b, 0x75, 0x0f, 0xaf, 0x02, 0xcf, 0x87, 0xf4, 0x0e, 0xe2, 0xea, 0x1d,
0xee, 0x77, 0x95, 0x44, 0xe5, 0x3e, 0x5f, 0x0d, 0xaf, 0x68, 0xbc, 0x14, 0x02, 0x56, 0x9d, 0x3d,
0x02, 0x63, 0x82, 0xb0, 0xc3, 0x0a, 0xff, 0xa6, 0xbf, 0x3e, 0x6e, 0xc0, 0xbb, 0x6f, 0x5d, 0x89,
0x9c, 0xd8, 0x59, 0x98, 0x46, 0x95, 0xdd, 0xb3, 0x83, 0x9f, 0x61, 0x7d, 0x23, 0x7c, 0xbf, 0xd4,
0xc2, 0xbd, 0x78, 0x07, 0xcc, 0xcd, 0xf0, 0x0d, 0x42, 0x42, 0xd2, 0xbb, 0x9a, 0x87, 0x7d, 0xf3,
0xb1, 0x8b, 0x38, 0xdc, 0xe3, 0xaa, 0x18, 0x9b, 0xe5, 0xcd, 0x82, 0x7f, 0x75, 0x7a, 0x9d, 0x55,
0xd9, 0xe7, 0x42, 0x57, 0xa8, 0xe3, 0x3b, 0x86, 0x80, 0xde, 0x42, 0x29, 0xf8, 0x42, 0x2f, 0x9e,
0x8b, 0x77, 0xca, 0xdb, 0x7d, 0xf1, 0x66, 0x20, 0xdc, 0x55, 0xec, 0x93, 0x7b, 0x49, 0x90, 0xfb,
0xc0, 0xb3, 0x23, 0x5f, 0x5e, 0x9f, 0x59, 0x58, 0x1c, 0xdd, 0x91, 0x59, 0xd7, 0x29, 0x5a, 0xec,
0x6f, 0x7b, 0xc5, 0xf6, 0x58, 0x3d, 0xf6, 0xc8, 0x56, 0x79, 0x1d, 0xa3, 0xeb, 0x88, 0x67, 0x6a,
0xd5, 0xbd, 0x5d, 0x5b, 0xf7, 0x5d, 0x22, 0x31, 0x73, 0xcb, 0xed, 0xd6, 0x86, 0x5c, 0x64, 0x4d,
0xd4, 0xb6, 0xa9, 0xba, 0xd0, 0x84, 0xcd, 0x3b, 0xc0, 0x58, 0x16, 0xd7, 0x2c, 0x33, 0xe1, 0xdd,
0x83, 0x54, 0x91, 0xae, 0xe2, 0x1b, 0x5a, 0xbc, 0xa0, 0x58, 0xb7, 0x58, 0x3d, 0xb2, 0xa7, 0xdc,
0xec, 0x84, 0x01, 0xd6, 0x36, 0xe6, 0xc4, 0x9a, 0x62, 0x46, 0x3c, 0xbb, 0x67, 0xe6, 0xcf, 0x06,
0xb0, 0xe9, 0x62, 0x90, 0x78, 0x69, 0x6f, 0x47, 0x21, 0x8a, 0x4a, 0xa6, 0x7c, 0x45, 0x6a, 0xd6,
0x36, 0x65, 0xe0, 0xb9, 0xa8, 0x55, 0x90, 0x66, 0x1c, 0xa4, 0x77, 0x39, 0xf3, 0x5a, 0xb0, 0x1b,
0x32, 0xd1, 0x00, 0x8d, 0x80, 0x8e, 0x22, 0x3b, 0x14, 0x33, 0xe3, 0x39, 0x72, 0xc8, 0x52, 0xb1,
0x21, 0xeb, 0x17, 0x9f, 0x76, 0x83, 0x37, 0x6a, 0x41, 0x8e, 0x99, 0xfd, 0xb5, 0xa6, 0xeb, 0xab,
0xdb, 0xf1, 0x6e, 0xe9, 0xdc, 0xaf, 0xcf, 0x06, 0x1a, 0x3a, 0x8f, 0xc5, 0x02, 0x48, 0x48, 0x8c,
0x01, 0x80, 0x7e, 0x5c, 0xa5, 0x1b, 0x43, 0xa8, 0x1f, 0x54, 0x83, 0x3d, 0x7a, 0x82, 0x1a, 0x44,
0xd0, 0xc7, 0xe5, 0x32, 0x4a, 0x45, 0x91, 0xab, 0x59, 0xc3, 0x27, 0x38, 0xc2, 0x17, 0x69, 0xba,
0x9d, 0xab, 0xad, 0x78, 0x1e, 0x70, 0x79, 0x78, 0x69, 0x6b, 0x49, 0x23, 0x74, 0x37, 0xeb, 0x8a,
0xf3, 0x82, 0x61, 0x61, 0x0d, 0xa4, 0xed, 0xed, 0x75, 0x3d, 0xa0, 0xef, 0x87, 0x03, 0xd1, 0x6a,
0xde, 0x6b, 0x64, 0x9a, 0x5c, 0xc8, 0x64, 0x20, 0x4d, 0x34, 0xd3, 0xaa, 0x72, 0xf6, 0xd0, 0x34,
0x31, 0x96, 0x8d, 0x02, 0xaa, 0x7a, 0x57, 0x36, 0xc7, 0xc6, 0x19, 0xbe, 0xe6, 0x36, 0x3c, 0xcc,
0xd5, 0x0e, 0xfe, 0x1b, 0xa3, 0x5c, 0x83, 0xab, 0x3f, 0x1f, 0x66, 0xb4, 0x07, 0xff, 0xc1, 0x28,
0x83, 0x70, 0x9d, 0xcd, 0xb4, 0x01, 0x62, 0xe2, 0x8b, 0x17, 0x05, 0x44, 0x7e, 0x2e, 0x57, 0xab,
0x53, 0x06, 0xb8, 0xb1, 0x3a, 0x30, 0xb6, 0xaa, 0xaf, 0xe2, 0x84, 0x69, 0x8c, 0xc6, 0x81, 0xf8,
0x1e, 0xe7, 0x28, 0x54, 0xb6, 0x47, 0xea, 0xb3, 0xd5, 0xd8, 0x64, 0x08, 0xab, 0x4d, 0xf5, 0x66,
0xd5, 0xd0, 0xd1, 0xba, 0x38, 0x38, 0xef, 0xe2, 0xcf, 0x10, 0xbc, 0x08, 0x11, 0xaf, 0x01, 0x08,
0xf3, 0x53, 0x25, 0x71, 0x38, 0xf4, 0x45, 0x4a, 0xd1, 0x81, 0xae, 0x0c, 0x6f, 0xd5, 0xfa, 0xe6,
0xe3, 0x28, 0x63, 0x37, 0x6e, 0xaf, 0x41, 0x98, 0x3d, 0x5b, 0x0b, 0x2b, 0x62, 0x2c, 0xad, 0x0c,
0x2e, 0x74, 0xdf, 0xf4, 0xba, 0x39, 0x58, 0x0f, 0x72, 0xc5, 0xc2, 0x5a, 0x01, 0x8b, 0xb7, 0xf9,
0xc5, 0x67, 0x15, 0xaf, 0x79, 0x23, 0x71, 0xdc, 0xb2, 0xf8, 0x7a, 0x9f, 0xd5, 0x2d, 0xd0, 0xfa,
0x5f, 0xd6, 0x22, 0x07, 0xe1, 0x70, 0x07, 0x31, 0x0d, 0x0a, 0x2c, 0x2a, 0x2b, 0x21, 0x85, 0xc7,
0x7b, 0x23, 0xde, 0xc8, 0x3b, 0x7d, 0xe7, 0x1d, 0xcb, 0x73, 0x23, 0x17, 0x69, 0xd0, 0x62, 0x64,
0x6e, 0x9c, 0x48, 0xdb, 0xbe, 0xc5, 0x8a, 0x21, 0x43, 0x78, 0xa7, 0x1f, 0xa9, 0x05, 0xec, 0x76,
0xb3, 0x85, 0xbe, 0xb2, 0x57, 0x89, 0x5b, 0x94, 0x6b, 0xdc, 0xfb, 0xf4, 0xa5, 0xed, 0xa0, 0xcf,
0x3b, 0xcc, 0xd8, 0x8d, 0x7b, 0xdb, 0xdd, 0x6c, 0xf7, 0xa8, 0x36, 0xbb, 0x20, 0x0b, 0x31, 0xe1,
0x15, 0x5f, 0xab, 0x7b, 0x46, 0x8c, 0xe0, 0x0d, 0x46, 0xf5, 0x07, 0x1e, 0x55, 0x6a, 0xad, 0xa1,
0xa2, 0x39, 0x80, 0x51, 0x04, 0x51, 0x02, 0x4d, 0x77, 0x07, 0x8c, 0xbe, 0x03, 0xa7, 0x8e, 0x5b,
0x60, 0x40, 0xea, 0x8b, 0x41, 0x55, 0x14, 0x6c, 0xf8, 0x9b, 0xe0, 0xcb, 0x51, 0x41, 0x93, 0x8c,
0x9c, 0x2c, 0xf5, 0x83, 0xa3, 0xdd, 0x0d, 0xd1, 0x54, 0x51, 0xe4, 0x6b, 0xaa, 0x09, 0x56, 0xcf,
0x20, 0x5c, 0x2c, 0x5f, 0x8a, 0x95, 0xe3, 0x97, 0xd0, 0x57, 0x9d, 0x24, 0x69, 0xa2, 0x24, 0x63,
0x64, 0xf4, 0x80, 0xde, 0xc9, 0x36, 0xc0, 0x0d, 0x32, 0xbe, 0xc6, 0x3a, 0x07, 0x89, 0xcb, 0x38,
0x5e, 0x08, 0x3a, 0x19, 0xfc, 0x97, 0x60, 0xc4, 0x37, 0x28, 0xe8, 0xf0, 0x5c, 0x2e, 0x1b, 0xae,
0x09, 0x8a, 0x42, 0x2d, 0x84, 0xb2, 0x9b, 0x90, 0x4b, 0xcb, 0x0a, 0xe3, 0xf2, 0x38, 0x94, 0xd1,
0x8f, 0xb8, 0xc1, 0xc2, 0x4e, 0xb0, 0xa0, 0x55, 0xff, 0x2d, 0x24, 0x61, 0xce, 0x1e, 0x6b, 0x43,
0x70, 0xea, 0xeb, 0x5a, 0x10, 0xcc, 0xfa, 0x3a, 0xf8, 0xb5, 0xf8, 0x80, 0xdd, 0xad, 0x5e, 0x82,
0xbb, 0xbc, 0x0e, 0x7e, 0x98, 0x4b, 0xfe, 0xd7, 0x06, 0x24, 0xf8, 0x89, 0x11, 0x95, 0x21, 0x4e,
0xc3, 0x59, 0xaa, 0x5a, 0xba, 0x17, 0x07, 0xc0, 0x7f, 0x3c, 0x5e, 0xfd, 0xf6, 0x11, 0xb9, 0x6d,
0x6f, 0xc7, 0x3a, 0xd5, 0x64, 0x6f, 0x33, 0x49, 0x72, 0x03, 0x7b, 0x02, 0x12, 0x06, 0xfe, 0xe8,
0xe6, 0xf4, 0x87, 0x40, 0xe2, 0xf3, 0xd1, 0xa5, 0xb6, 0xa7, 0x90, 0xfb, 0x42, 0xf5, 0xdc, 0x30,
0xe9, 0xd6, 0xd7, 0xb1, 0x1a, 0x9f, 0xca, 0xfd, 0xc8, 0x81, 0x18, 0x3c, 0xb0, 0xd2, 0x19, 0xaf,
0x82, 0x94, 0xea, 0x8f, 0x1f, 0x26, 0x4c, 0xac, 0x09, 0x73, 0x68, 0xa6, 0x65, 0x21, 0x0c, 0xfc,
0xa7, 0xf0, 0x03, 0xff, 0x07, 0x09, 0x54, 0xcb, 0xd2, 0xa6, 0x63, 0x1a, 0xdc, 0x7a, 0xfd, 0x13,
0x4c, 0x3a, 0xb4, 0x83, 0xfb, 0xe9, 0xde, 0xac, 0x0e, 0x03, 0x05, 0x0c, 0x22, 0xb0, 0xfd, 0x68,
0x93, 0x00, 0xf7, 0xa3, 0x56, 0xa1, 0xc9, 0x6a, 0x8e, 0x55, 0xa4, 0xf6, 0x75, 0xac, 0xea, 0x47,
0xb5, 0x51, 0x5e, 0x46, 0x38, 0x98, 0x1d, 0x93, 0x28, 0xee, 0x6b, 0x53, 0xd6, 0xca, 0x4f, 0x45,
0x71, 0xe2, 0x52, 0x0d, 0x11, 0x38, 0x50, 0xc2, 0x8b, 0x1f, 0xbd, 0xcd, 0xc7, 0x27, 0xb8, 0xb9,
0xc0, 0xf6, 0x08, 0xf0, 0x77, 0xd2, 0xbf, 0xea, 0xc4, 0xbd, 0x30, 0x12, 0x2f, 0xb7, 0x0c, 0x2e,
0xcd, 0xcd, 0x60, 0x00, 0x64, 0x66, 0xaa, 0xe1, 0x89, 0x83, 0xa5, 0x2b, 0x41, 0x2c, 0x98, 0xa2,
0xc8, 0x79, 0x7f, 0xcf, 0xc2, 0x4a, 0x26, 0x07, 0x4a, 0xaa, 0x9e, 0x2d, 0xf3, 0x7b, 0x79, 0x7a,
0x7c, 0x1b, 0x5b, 0xe7, 0x85, 0x81, 0xb2, 0x97, 0xf3, 0x7e, 0xe6, 0xc5, 0x12, 0x20, 0xb6, 0x50,
0xa8, 0xd7, 0x07, 0x0a, 0x4d, 0x49, 0xd5, 0xf3, 0x98, 0x22, 0x96, 0x43, 0x29, 0x00, 0x80, 0xad,
0x4a, 0x76, 0xbb, 0xaf, 0x37, 0x4b, 0x94, 0x08, 0xea, 0xbf, 0x06, 0x36, 0x5e, 0xee, 0x33, 0x50,
0x96, 0x78, 0x53, 0x92, 0xf8, 0x4f, 0x21, 0x51, 0x04, 0x33, 0xeb, 0x17, 0xde, 0x2c, 0x50, 0xc9,
0x8a, 0x8c, 0x6c, 0x14, 0xa3, 0x67, 0x45, 0xdc, 0x53, 0xc1, 0x9d, 0xc3, 0xd4, 0x1d, 0x8e, 0xf9,
0x91, 0xc5, 0xec, 0x5f, 0x53, 0xec, 0x9f, 0x82, 0x26, 0x91, 0x46, 0xb9, 0xaf, 0xf8, 0x72, 0x31,
0x81, 0x84, 0x76, 0xf4, 0x38, 0xfa, 0xe2, 0x4b, 0x6b, 0x98, 0x60, 0x77, 0x18, 0x73, 0xd4, 0x21,
0x9c, 0x64, 0x2d, 0xc3, 0x6a, 0xdd, 0xf0, 0xae, 0x4d, 0xda, 0x10, 0xb7, 0xfe, 0xac, 0x2a, 0xbd,
0x53, 0x23, 0xe4, 0x3c, 0x8b, 0x84, 0xed, 0xb0, 0x6e, 0xb9, 0x32, 0xeb, 0x83, 0xa3, 0xc4, 0x92,
0x3e, 0x63, 0x6e, 0x25, 0x87, 0x77, 0xac, 0x39, 0x82, 0x49, 0x46, 0x36, 0x75, 0x1c, 0xb6, 0x1b,
0x3c, 0xc1, 0x1d, 0x4c, 0x7f, 0x7b, 0x09, 0x4f, 0x72, 0x78, 0xc7, 0x96, 0x43, 0x8d, 0xd6, 0xf1,
0xac, 0x37, 0x1d, 0x16, 0x3b, 0xc4, 0xc3, 0x2f, 0x99, 0x83, 0x01, 0x08, 0x70, 0x19, 0x29, 0xb7,
0x58, 0xe2, 0x59, 0x8d, 0xc8, 0x38, 0xc4, 0xc8, 0x0d, 0x24, 0x9b, 0xef, 0xf8, 0xa3, 0x1d, 0x8e,
0x1f, 0x22, 0x5f, 0x8c, 0xaa, 0x91, 0x9b, 0x6c, 0xa1, 0x7f, 0xd5, 0x70, 0x67, 0xf1, 0x16, 0x93,
0xcf, 0x4e, 0x01, 0xc7, 0x3b, 0x44, 0x77, 0xf8, 0xfe, 0xf8, 0x14, 0xf0, 0x6c, 0x7f, 0x84, 0xfb,
0x63, 0xa0, 0x58, 0x44, 0xe3, 0x31, 0xff, 0x03, 0xf7, 0xd7, 0xbf, 0xe1, 0xf7, 0x32, 0xeb, 0x7b,
0x8b, 0x08, 0xa2, 0x57, 0x4f, 0xef, 0x87, 0xd1, 0x64, 0xa6, 0x47, 0x00, 0xe3, 0xbb, 0x99, 0x1e,
0xee, 0x6f, 0xb8, 0x98, 0x63, 0x05, 0x29, 0xa9, 0x2c, 0xde, 0x66, 0xca, 0x0b, 0x20, 0x8e, 0xab,
0x4e, 0xf2, 0x3f, 0x8f, 0xf9, 0x74, 0x5f, 0x46, 0x80, 0xe2, 0x30, 0x38, 0x25, 0x9f, 0x36, 0xc8,
0xcc, 0x8b, 0xf5, 0xe8, 0x6a, 0xb2, 0x8e, 0xf7, 0x59, 0xc7, 0xac, 0x18, 0x8c, 0x57, 0x77, 0x43,
0x4a, 0xe2, 0x76, 0xac, 0x1f, 0x4e, 0x8d, 0x2b, 0xd5, 0xe1, 0xd7, 0x6e, 0x07, 0x07, 0x71, 0x77,
0x1a, 0x46, 0x21, 0x04, 0xf7, 0xea, 0x68, 0xc1, 0x01, 0x11, 0xf3, 0x87, 0x86, 0xf7, 0x08, 0xd9,
0x6b, 0xa3, 0xec, 0x68, 0x1c, 0x91, 0xbb, 0x61, 0x7f, 0x0b, 0xbd, 0x4d, 0x3b, 0xe6, 0x1d, 0xbe,
0xe3, 0xb8, 0x29, 0xe1, 0x1d, 0x23, 0x35, 0x3b, 0xd6, 0x5f, 0xc3, 0x0f, 0xff, 0x74, 0x3b, 0x1d,
0x2d, 0xcf, 0xef, 0xed, 0x64, 0xa9, 0xd3, 0x83, 0x6d, 0x35, 0x2f, 0xa3, 0xf7, 0x88, 0x18, 0xcc,
0x63, 0xbc, 0xce, 0x8e, 0xab, 0x79, 0xe1, 0x48, 0x18, 0xf5, 0xa7, 0x99, 0x63, 0x3b, 0x8a, 0xb5,
0x9a, 0xe3, 0xaa, 0x27, 0x78, 0x26, 0x37, 0x4d, 0xdf, 0xff, 0x90, 0xc4, 0x8b, 0xd1, 0x10, 0x08,
0x4e, 0x1b, 0xbd, 0x7c, 0x26, 0x89, 0x2e, 0x4f, 0xf6, 0xc4, 0x2e, 0x04, 0xe2, 0xc2, 0x13, 0x34,
0x66, 0xaf, 0x8d, 0x60, 0xf7, 0xee, 0xda, 0x5e, 0xa0, 0x47, 0xd8, 0x0e, 0x91, 0xf5, 0xb1, 0x95,
0x5c, 0xbb, 0x01, 0xb3, 0x9a, 0x13, 0x36, 0x0d, 0x58, 0xee, 0x92, 0xc5, 0xf2, 0xfd, 0x77, 0x73,
0xcd, 0xfd, 0xa4, 0xc1, 0x8b, 0x59, 0x79, 0xe1, 0xb2, 0x9e, 0xa5, 0x91, 0x7e, 0x53, 0x41, 0xa3,
0x7d, 0xa9, 0x8b, 0xc2, 0x4c, 0x64, 0xde, 0x5b, 0x66, 0xa4, 0x76, 0xe9, 0x0d, 0xda, 0xe8, 0x53,
0x88, 0x70, 0xf2, 0xb1, 0xc1, 0x1c, 0xd9, 0xfe, 0x66, 0x7d, 0x9a, 0x9d, 0xdc, 0x50, 0xe9, 0x25,
0xd9, 0xc4, 0xde, 0x8f, 0x27, 0xfc, 0x10, 0x7f, 0x56, 0x43, 0xc1, 0xfa, 0x03, 0xc9, 0x6e, 0x5b,
0x26, 0x8d, 0xbc, 0x42, 0x28, 0xd1, 0xc0, 0x14, 0xb2, 0xb2, 0x73, 0xc8, 0x02, 0x09, 0x82, 0x73,
0x78, 0x50, 0x17, 0xd0, 0xe9, 0x6e, 0xa6, 0x8a, 0x4b, 0x6f, 0x57, 0x35, 0xcc, 0xd0, 0x95, 0xd7,
0x64, 0x28, 0xae, 0xcc, 0x0d, 0x81, 0xf2, 0xed, 0x01, 0x7a, 0xc8, 0x31, 0x12, 0x17, 0xe5, 0xdd,
0x34, 0xec, 0x17, 0x81, 0x59, 0x76, 0x31, 0x65, 0x27, 0xfd, 0x23, 0x2a, 0xfc, 0xef, 0xdf, 0xe1,
0xae, 0x3b, 0xb1, 0xdf, 0x26, 0x5e, 0xc3, 0x06, 0x08, 0xf4, 0xd0, 0x03, 0xd0, 0x30, 0x44, 0xec,
0xd1, 0x47, 0xa6, 0x1b, 0xbf, 0x14, 0x7a, 0x07, 0x2e, 0xff, 0x83, 0x61, 0xfe, 0xa7, 0xeb, 0x15,
0xb8, 0x35, 0x47, 0xc2, 0x7f, 0x37, 0xd7, 0x05, 0x5d, 0xb9, 0xae, 0x81, 0x2f, 0x49, 0x6f, 0x3e,
0x44, 0x3e, 0x4c, 0x52, 0xd4, 0x02, 0x62, 0x46, 0x05, 0x2b, 0xe0, 0x8d, 0x7e, 0xa5, 0x33, 0x66,
0xdd, 0xd9, 0x99, 0x89, 0xde, 0xb5, 0x35, 0x94, 0x87, 0x99, 0x78, 0x4d, 0x4d, 0x2a, 0x69, 0xff,
0xcb, 0xcc, 0x4c, 0x83, 0x73, 0x78, 0x39, 0xbe, 0x26, 0x7e, 0xc1, 0x5b, 0xb8, 0x92, 0xac, 0x8f,
0x7b, 0x22, 0x70, 0xcb, 0xef, 0x22, 0xef, 0x27, 0x60, 0x38, 0x84, 0xbd, 0x87, 0x09, 0xc6, 0x5e,
0x3a, 0x9b, 0xdb, 0xde, 0xfe, 0xa3, 0xf1, 0xd5, 0xeb, 0x2e, 0x3a, 0x00, 0x0e, 0x0c, 0x75, 0xf5,
0x6e, 0x67, 0x6e, 0x67, 0x47, 0xe3, 0x5c, 0x8a, 0xdc, 0xa9, 0xdb, 0xd8, 0xb7, 0xec, 0x8e, 0xe1,
0x05, 0xd4, 0x48, 0xb8, 0x08, 0x5f, 0x6d, 0xb7, 0x0b, 0x20, 0x83, 0x36, 0xc1, 0xe8, 0xe1, 0xe1,
0xc0, 0x3f, 0x45, 0x66, 0x16, 0x00, 0xb9, 0xa4, 0x1f, 0x62, 0x14, 0xcc, 0x23, 0xbb, 0x98, 0x7a,
0xb7, 0xf4, 0x51, 0xce, 0xf4, 0x80, 0xb7, 0x72, 0xd7, 0x42, 0x7a, 0x0b, 0xbe, 0x66, 0xdd, 0x43,
0xef, 0x09, 0xc1, 0x58, 0xcf, 0x9a, 0x7f, 0xef, 0x2e, 0x1e, 0xbf, 0x4c, 0x71, 0xd1, 0xbb, 0x78,
0x47, 0x90, 0xb6, 0x93, 0xfc, 0x33, 0x67, 0xf3, 0xbe, 0x58, 0x8d, 0x65, 0xb3, 0x76, 0x00, 0x8f,
0x7c, 0x0c, 0x2e, 0xbd, 0x93, 0x12, 0x2f, 0xff, 0x0d, 0x53, 0x85, 0x7f, 0x13, 0x1f, 0x11, 0xd2,
0xe9, 0xb4, 0xc3, 0x34, 0x2f, 0xcf, 0x96, 0xf6, 0xd4, 0x0d, 0xe2, 0x5e, 0x77, 0x8f, 0x01, 0x1d,
0x5e, 0x7c, 0x3a, 0x94, 0xf3, 0x2e, 0xb0, 0xde, 0xde, 0x66, 0x4f, 0x26, 0x1f, 0x9c, 0xba, 0x4d,
0xe0, 0xd1, 0x8c, 0x84, 0xc9, 0xbb, 0x39, 0xb0, 0x42, 0xf9, 0x7d, 0xba, 0x50, 0x7f, 0xff, 0x8e,
0x8a, 0x47, 0x10, 0xdd, 0x90, 0x8a, 0x17, 0x8f, 0xf3, 0x42, 0xa8, 0x37, 0x90, 0x26, 0xd0, 0x5a,
0x7c, 0x75, 0x6d, 0x79, 0xcc, 0x5b, 0xfa, 0x7a, 0xe0, 0xca, 0x30, 0x96, 0xee, 0xa5, 0x63, 0xee,
0xbc, 0xac, 0x5e, 0xf9, 0x05, 0x2a, 0x1e, 0x94, 0x60, 0x3e, 0x75, 0x7a, 0x4b, 0x19, 0xd0, 0xc1,
0x6a, 0xa9, 0xa1, 0x89, 0x07, 0x0d, 0xd1, 0x2b, 0x47, 0xb9, 0xdc, 0xe6, 0x82, 0xe1, 0xfb, 0xc6,
0x3e, 0x2e, 0x49, 0x41, 0x82, 0xc4, 0xe7, 0x04, 0x5c, 0x91, 0x9f, 0x94, 0x1b, 0x8f, 0x3e, 0x2b,
0x16, 0x5c, 0x5d, 0xe6, 0x95, 0xdb, 0xfa, 0x9e, 0x71, 0x5f, 0xce, 0x90, 0xf8, 0x9e, 0xc1, 0xcd,
0x3c, 0xfc, 0x1e, 0x38, 0x43, 0x7d, 0x2f, 0xf1, 0xff, 0x02, 0xbd, 0x7f, 0x71, 0xf0, 0x34, 0xc0,
0x00, 0x00
0xfe, 0x59, 0x1a, 0x13, 0xc0, 0x2f, 0xbe, 0x40, 0x4d, 0xe4, 0x65, 0xd0, 0x47, 0x30, 0x8c, 0xea,
0xcc, 0xfb, 0x8a, 0x9b, 0xd5, 0xe1, 0x08, 0x6c, 0x94, 0x80, 0x69, 0xc3, 0x9c, 0x26, 0x79, 0x0c,
0x6e, 0xe0, 0xc3, 0x1c, 0xc2, 0x71, 0x98, 0x5d, 0xa0, 0x23, 0x09, 0x6b, 0x2a, 0x50, 0x33, 0x7b,
0x80, 0xe5, 0x02, 0x03, 0xa8, 0x47, 0x58, 0xa3, 0xe7, 0xeb, 0xf7, 0x97, 0x64, 0x56, 0xfc, 0x97,
0xee, 0x5a, 0x63, 0x35, 0xe9, 0x4b, 0xdd, 0x41, 0x9f, 0x42, 0xb8, 0x42, 0x5d, 0xe2, 0x97, 0x49,
0x57, 0xb2, 0x07, 0x81, 0xba, 0x84, 0xf7, 0xf8, 0x24, 0x46, 0x4c, 0xd0, 0x53, 0x76, 0x20, 0x2d,
0x96, 0xb6, 0x1f, 0xfb, 0x02, 0x08, 0x8f, 0x86, 0xbf, 0x60, 0xb8, 0x2b, 0x0e, 0x32, 0x12, 0x2e,
0x12, 0x9a, 0x78, 0x21, 0x87, 0xbe, 0x0b, 0xe6, 0xeb, 0x36, 0x7d, 0x37, 0xb6, 0x21, 0xe0, 0x61,
0x39, 0x82, 0xd7, 0x7b, 0x60, 0xbc, 0x3d, 0x5e, 0x93, 0xb6, 0x83, 0xfb, 0x01, 0x80, 0x40, 0x3c,
0x85, 0xa5, 0x31, 0x2d, 0x85, 0x4e, 0x43, 0x0d, 0x2f, 0xd0, 0xb5, 0x50, 0x3c, 0xdc, 0xdd, 0xb6,
0x76, 0xca, 0xdc, 0x52, 0xc0, 0xb7, 0xa7, 0x54, 0x9d, 0x7f, 0x33, 0xf0, 0x30, 0xf9, 0x7f, 0x59,
0x59, 0xe9, 0xf3, 0x78, 0xe5, 0xc1, 0x3e, 0x7a, 0x75, 0x15, 0x05, 0x14, 0x2e, 0x4a, 0x1c, 0x1a,
0x55, 0x44, 0x02, 0xa5, 0x85, 0xa5, 0x44, 0x34, 0x97, 0xb5, 0xda, 0x8a, 0x57, 0x81, 0xe7, 0x43,
0x9a, 0x0a, 0x71, 0x35, 0x15, 0xf7, 0xbb, 0x4a, 0xa2, 0x9a, 0x02, 0x5f, 0x0d, 0xf3, 0x00, 0xbc,
0x46, 0x02, 0xd6, 0xa9, 0x3d, 0x02, 0xf3, 0x83, 0xb0, 0xe3, 0x0d, 0xff, 0x9e, 0x87, 0x7f, 0xd5,
0xe6, 0x77, 0x5f, 0xd4, 0x12, 0x39, 0xe4, 0xb3, 0x30, 0x8d, 0x2a, 0xbb, 0x9a, 0x07, 0x3f, 0xc3,
0x2a, 0x4a, 0xf8, 0x4a, 0xaa, 0x85, 0x7b, 0x57, 0x0f, 0x58, 0xa8, 0xe1, 0x4b, 0x87, 0x84, 0xa4,
0x77, 0x9b, 0x0f, 0xfb, 0xe6, 0x63, 0x77, 0x77, 0xb8, 0x27, 0x5c, 0x31, 0x9c, 0xcb, 0x9b, 0x06,
0xff, 0xb6, 0xf5, 0x3a, 0xab, 0xb2, 0xcf, 0x85, 0x6e, 0x5d, 0xc7, 0xd7, 0x12, 0x01, 0xc1, 0x85,
0x52, 0xf0, 0x1d, 0x60, 0x3c, 0x17, 0xef, 0x94, 0xb7, 0x61, 0xe3, 0x4d, 0x41, 0xb8, 0xab, 0xd8,
0x27, 0xf7, 0x5e, 0x21, 0xf7, 0x81, 0x67, 0xa7, 0xc4, 0xbc, 0x3e, 0xb3, 0x48, 0x3a, 0xba, 0x89,
0xb3, 0xae, 0x53, 0xb4, 0xd8, 0xdf, 0xf6, 0x8a, 0x6d, 0xcb, 0x7a, 0x1c, 0x95, 0x31, 0x83, 0x3a,
0x06, 0xe4, 0x11, 0xcf, 0x3a, 0xab, 0x7b, 0x1b, 0xbd, 0xee, 0xeb, 0x47, 0x62, 0x16, 0x9a, 0xdb,
0xad, 0x0d, 0xb9, 0xc8, 0xcd, 0xa8, 0x39, 0x54, 0x75, 0xa1, 0x09, 0x9b, 0x37, 0x8d, 0xb1, 0x2c,
0x2e, 0x5a, 0x66, 0xf5, 0xbb, 0x67, 0xaf, 0x22, 0x5d, 0xc5, 0x97, 0xba, 0x78, 0x71, 0xb4, 0x6e,
0xb1, 0x7a, 0x64, 0x1b, 0xba, 0xd9, 0x09, 0x03, 0xac, 0x6d, 0xcc, 0x89, 0x35, 0xc5, 0xec, 0x7e,
0x76, 0x35, 0xcd, 0x9f, 0x0d, 0x60, 0xd3, 0x5d, 0x22, 0xf1, 0xd2, 0xde, 0x26, 0x44, 0x14, 0x95,
0x4c, 0x5f, 0x8b, 0xd4, 0xac, 0x6d, 0xca, 0xc0, 0xa3, 0x54, 0xab, 0x20, 0xcd, 0x38, 0x48, 0xef,
0x3e, 0xe7, 0xb5, 0x60, 0x37, 0x64, 0xa2, 0xcd, 0x1a, 0x01, 0x1d, 0x45, 0x76, 0x28, 0xcc, 0xc6,
0xf3, 0xfd, 0x90, 0xa5, 0x62, 0x43, 0xd6, 0x2f, 0x3e, 0xed, 0xc6, 0x7b, 0xd4, 0x82, 0x1c, 0x33,
0xfb, 0x6b, 0x4d, 0xd7, 0x57, 0x77, 0xf0, 0xdd, 0xd2, 0xb9, 0x5f, 0x9f, 0x0d, 0x34, 0x74, 0x84,
0x8b, 0xc5, 0x9c, 0x90, 0x18, 0x03, 0x00, 0x95, 0xba, 0x4a, 0xf7, 0x92, 0x50, 0xa5, 0xa8, 0x06,
0xdb, 0xfa, 0x04, 0x95, 0x8e, 0xa0, 0x8f, 0xcb, 0x65, 0x94, 0x8a, 0x22, 0xb7, 0xb9, 0x86, 0x0f,
0x7d, 0x84, 0xef, 0xde, 0x74, 0x3b, 0x57, 0x5b, 0x71, 0x56, 0xe0, 0xf2, 0xf0, 0xd2, 0xd6, 0x92,
0x46, 0xe8, 0x3a, 0xd7, 0x15, 0x7f, 0x07, 0xc3, 0xc2, 0x1a, 0x48, 0xdb, 0xdb, 0xeb, 0x7a, 0x40,
0x5f, 0x29, 0x07, 0x12, 0xd8, 0xbc, 0xd7, 0xc8, 0x34, 0xb9, 0x90, 0xc9, 0x40, 0x9a, 0x68, 0xa6,
0x55, 0xe5, 0xec, 0xa1, 0x69, 0x62, 0xf8, 0x1b, 0x05, 0x54, 0xf5, 0x6e, 0x79, 0x8e, 0x8d, 0x33,
0x7c, 0x33, 0x6e, 0x78, 0x98, 0xab, 0x1d, 0xfc, 0x37, 0x46, 0xb9, 0x06, 0x57, 0x7f, 0x3e, 0xcc,
0x68, 0x0f, 0xfe, 0x83, 0x51, 0x06, 0x11, 0x3e, 0x9b, 0x69, 0x03, 0xc4, 0xc4, 0x17, 0x2f, 0x70,
0x88, 0xfc, 0x5c, 0xae, 0x56, 0xa7, 0x0c, 0x70, 0x63, 0x75, 0x60, 0x6c, 0x55, 0x5f, 0x13, 0x0a,
0xd3, 0x18, 0x0d, 0x1d, 0xf1, 0x9d, 0xd4, 0x51, 0xa8, 0x6c, 0x5b, 0xd5, 0x67, 0xab, 0xb1, 0xc9,
0x10, 0x56, 0x9b, 0xea, 0xcd, 0xaa, 0xa1, 0xd3, 0x78, 0x71, 0x70, 0xde, 0x5d, 0xa1, 0x21, 0x78,
0x11, 0x22, 0x5e, 0x03, 0x10, 0xe6, 0xa7, 0x4a, 0xe2, 0x70, 0xe8, 0xbb, 0x97, 0xa2, 0x03, 0x5d,
0x19, 0xde, 0xaa, 0xc1, 0xce, 0xc7, 0x51, 0xc6, 0x2e, 0xe9, 0x5e, 0x83, 0x30, 0x7b, 0xb6, 0x16,
0x56, 0xc4, 0xbe, 0x5a, 0x19, 0x5c, 0xe8, 0x8a, 0xea, 0x75, 0x73, 0xb0, 0x1e, 0xe4, 0x8a, 0x51,
0xb6, 0x02, 0x16, 0x2f, 0x00, 0x8c, 0xcf, 0x2a, 0xde, 0x0c, 0x47, 0xe2, 0xb8, 0x65, 0x21, 0xf9,
0x3e, 0xab, 0x5b, 0xa0, 0xc3, 0x60, 0x59, 0x8b, 0x9c, 0x9d, 0xc3, 0x4d, 0xc7, 0x34, 0xe8, 0xb9,
0xa8, 0xad, 0x84, 0x34, 0x1e, 0xef, 0x25, 0x7a, 0x23, 0xef, 0xc0, 0x9e, 0x77, 0x92, 0xcf, 0x0d,
0x76, 0xa4, 0x71, 0x8e, 0x91, 0xb9, 0x71, 0x22, 0x6d, 0xfb, 0x46, 0x2e, 0x46, 0x19, 0xe1, 0x35,
0x80, 0xa4, 0x16, 0xb0, 0xdb, 0xcd, 0x46, 0xfd, 0xca, 0xf6, 0x26, 0xee, 0x6a, 0xae, 0xd9, 0x11,
0xa0, 0xef, 0x79, 0x07, 0xb5, 0xdf, 0x61, 0xf6, 0x71, 0xdc, 0x41, 0xef, 0x66, 0xbb, 0xa7, 0xbb,
0xd9, 0x9d, 0x5a, 0x88, 0x09, 0xaf, 0xf8, 0x5a, 0xe5, 0x33, 0x62, 0x37, 0x6f, 0xb0, 0xc3, 0x3f,
0x70, 0xc2, 0x52, 0x03, 0x0f, 0x35, 0xcd, 0x01, 0x8c, 0x22, 0x08, 0x2c, 0x68, 0xba, 0x9b, 0x66,
0xf4, 0xb5, 0x39, 0x75, 0xdc, 0x35, 0x03, 0x52, 0x5f, 0x0c, 0xaa, 0xa2, 0x60, 0xc3, 0xdf, 0x04,
0xdf, 0xa7, 0x0a, 0xaa, 0x64, 0xe4, 0x30, 0xaa, 0x1f, 0x4f, 0xed, 0xee, 0xa1, 0xa6, 0x8a, 0x22,
0x5f, 0x53, 0xcd, 0x05, 0x49, 0x0f, 0xc2, 0xc5, 0xf2, 0xa5, 0x58, 0x39, 0x7e, 0x09, 0x7d, 0xd5,
0x49, 0x92, 0x26, 0x4a, 0x32, 0x06, 0x53, 0x0f, 0xe8, 0x35, 0x6e, 0x03, 0xdc, 0x53, 0xe3, 0x6b,
0xac, 0x73, 0x90, 0xb8, 0x8c, 0xe3, 0x85, 0xa0, 0x5f, 0xc2, 0x7f, 0x6f, 0x46, 0x7c, 0x4f, 0x83,
0x0e, 0xcf, 0xe5, 0xb2, 0xe1, 0x9a, 0xa0, 0x28, 0xd4, 0x42, 0x28, 0xbb, 0x09, 0x79, 0xc1, 0xac,
0x30, 0x2e, 0x8f, 0x43, 0x19, 0xfd, 0x88, 0xe7, 0x2c, 0xec, 0x37, 0x0b, 0x5a, 0xf5, 0x5f, 0x5c,
0x12, 0xe6, 0xec, 0xb1, 0x36, 0x04, 0xa7, 0xbe, 0xae, 0x05, 0xc1, 0xac, 0xaf, 0x83, 0x5f, 0x8b,
0x0f, 0xd8, 0xdd, 0x1d, 0x26, 0xb8, 0x31, 0xec, 0xe0, 0x87, 0xb9, 0xe4, 0x7f, 0x6d, 0x40, 0x82,
0x9f, 0x18, 0x51, 0x19, 0xe2, 0x34, 0x9c, 0xa5, 0xaa, 0xa5, 0x7b, 0xd7, 0x00, 0xfc, 0xc7, 0xe3,
0x6d, 0x71, 0x1f, 0x91, 0xdb, 0xf6, 0x76, 0xac, 0x53, 0x4d, 0xf6, 0x02, 0x94, 0x24, 0x37, 0xb0,
0x27, 0x20, 0x61, 0xe0, 0x8f, 0xee, 0x67, 0x7f, 0x08, 0x24, 0x3e, 0x1f, 0x5d, 0x6a, 0xa2, 0x0a,
0xb9, 0x2f, 0x54, 0xcf, 0x0d, 0x93, 0x6e, 0x7d, 0x1d, 0xab, 0xf1, 0xa9, 0xdc, 0x0f, 0x36, 0x88,
0xc1, 0x03, 0xc3, 0x9e, 0xf1, 0x2a, 0x48, 0xa9, 0xfe, 0xf8, 0x61, 0xc2, 0xc4, 0x9a, 0x30, 0x87,
0x66, 0x5a, 0x16, 0xc2, 0xc0, 0x7f, 0x0a, 0x3f, 0xf0, 0x7f, 0x90, 0x40, 0xb5, 0x2c, 0x6d, 0x3a,
0xa6, 0xc1, 0xad, 0xd7, 0x3f, 0xc1, 0xa6, 0x43, 0x73, 0xb9, 0x9f, 0xee, 0xcd, 0xea, 0x30, 0x50,
0xc0, 0x20, 0x02, 0xdb, 0x8f, 0x36, 0x09, 0x70, 0x3f, 0x6a, 0x15, 0x9a, 0xac, 0xe6, 0x58, 0x45,
0x6a, 0x86, 0xc7, 0xaa, 0x7e, 0x54, 0x1b, 0xe5, 0x65, 0x84, 0x83, 0xd9, 0x31, 0x89, 0xe2, 0xbe,
0x69, 0x65, 0xad, 0xfc, 0x54, 0x14, 0x27, 0x2e, 0xd5, 0x10, 0x81, 0x03, 0x25, 0xbc, 0xf8, 0xd1,
0x41, 0x7d, 0x7c, 0x82, 0xfb, 0x11, 0x6c, 0x5b, 0x01, 0x7f, 0x27, 0xfd, 0xdb, 0x51, 0xdc, 0x3b,
0x26, 0xf1, 0x3e, 0xcc, 0xe0, 0x9e, 0xdd, 0x0c, 0xc6, 0x4c, 0x66, 0xa6, 0x1a, 0x1e, 0x52, 0x58,
0xba, 0x12, 0xc4, 0x82, 0x29, 0x8a, 0x5c, 0x11, 0xe0, 0x59, 0x58, 0xc9, 0xe4, 0x40, 0x49, 0xd5,
0xb3, 0x65, 0x7e, 0x2f, 0x4f, 0x4f, 0x7c, 0x63, 0xeb, 0xbc, 0x30, 0x50, 0xf6, 0x72, 0xde, 0xcf,
0xbc, 0x58, 0x02, 0xc4, 0x16, 0x0a, 0xf5, 0xfa, 0x40, 0xa1, 0x29, 0xa9, 0x7a, 0x1e, 0x53, 0xc4,
0x72, 0x28, 0x05, 0x00, 0xb0, 0x55, 0xc9, 0x2e, 0x04, 0xf6, 0x66, 0x89, 0x12, 0x41, 0xfd, 0xd7,
0xc0, 0xc6, 0xfb, 0x80, 0x06, 0xca, 0x12, 0x2f, 0x57, 0x12, 0xff, 0x29, 0x24, 0x8a, 0x60, 0x66,
0xfd, 0xc2, 0xcb, 0x08, 0x2a, 0x59, 0x91, 0x91, 0x8d, 0x62, 0xf4, 0xac, 0x88, 0x47, 0x2b, 0xb8,
0xa6, 0x98, 0x7a, 0xd0, 0x31, 0x3f, 0xb2, 0x98, 0xfd, 0x9b, 0x8d, 0xfd, 0x83, 0xd3, 0x24, 0xd2,
0x28, 0xf7, 0x15, 0xdf, 0x47, 0x26, 0x90, 0xd0, 0x26, 0x20, 0x47, 0xdf, 0x95, 0x69, 0x0d, 0x13,
0xec, 0xda, 0x63, 0x8e, 0xfa, 0x90, 0x93, 0xac, 0x65, 0x58, 0xad, 0x1b, 0x5e, 0xcf, 0x49, 0x1b,
0xe2, 0xd6, 0x1f, 0x6f, 0xa5, 0xd7, 0x70, 0x84, 0xfc, 0x6d, 0x91, 0x48, 0x1f, 0xd6, 0x2d, 0x57,
0x66, 0x7d, 0x70, 0xfa, 0x58, 0xd2, 0x67, 0xcc, 0xfb, 0xe4, 0xf0, 0x8e, 0x35, 0x47, 0x30, 0xc9,
0xc8, 0x3e, 0x90, 0xc3, 0x36, 0x90, 0x27, 0xb8, 0xe9, 0xe9, 0xef, 0x48, 0xe1, 0xe1, 0x0f, 0xef,
0xa4, 0x73, 0xa8, 0xd1, 0x3a, 0x1e, 0x0f, 0xa7, 0xc3, 0x62, 0xe7, 0x7e, 0xf8, 0x25, 0xf3, 0x30,
0x00, 0x01, 0x2e, 0x23, 0xe5, 0x16, 0x4b, 0x3c, 0xde, 0x11, 0x19, 0x87, 0x18, 0xb9, 0xb4, 0x64,
0xf3, 0xb5, 0x80, 0xb4, 0xc3, 0xf1, 0x73, 0xe7, 0x8b, 0x51, 0x35, 0x72, 0xf9, 0x2d, 0xf4, 0xaf,
0x1a, 0xee, 0x2c, 0x5e, 0x7c, 0xf2, 0xd9, 0xc1, 0xe1, 0x78, 0x87, 0xe8, 0xa6, 0xe0, 0x1f, 0x1f,
0x1c, 0x9e, 0xed, 0x8f, 0x70, 0x4b, 0x0d, 0x14, 0x8b, 0x68, 0x08, 0xe7, 0x7f, 0xe0, 0xff, 0xfa,
0x37, 0x1c, 0x5f, 0x66, 0x7d, 0x6f, 0x11, 0x41, 0xf4, 0xea, 0x81, 0xff, 0x30, 0x9a, 0xcc, 0xf4,
0x08, 0x60, 0x7c, 0x37, 0xd3, 0xc3, 0xfd, 0x0d, 0x77, 0x79, 0xac, 0x20, 0x25, 0x95, 0xc5, 0x0b,
0x50, 0x79, 0x01, 0xc4, 0x71, 0xd5, 0x49, 0xfe, 0xe7, 0x61, 0xa2, 0xee, 0xfb, 0x0b, 0x50, 0x1c,
0x06, 0x07, 0xeb, 0xd3, 0x06, 0x99, 0x79, 0xe1, 0x21, 0x5d, 0x4d, 0xd6, 0xf1, 0x0a, 0xec, 0x98,
0x15, 0x83, 0x21, 0xee, 0x6e, 0x14, 0x4a, 0xdc, 0x8e, 0xf5, 0x23, 0xb0, 0x71, 0xa5, 0x3a, 0xfc,
0xda, 0x1d, 0xe4, 0x20, 0x54, 0x4f, 0xc3, 0xc0, 0x85, 0xe0, 0x2a, 0x1e, 0x2d, 0x38, 0x53, 0x62,
0xfe, 0xd0, 0xf0, 0xea, 0x21, 0x7b, 0x6d, 0x60, 0x1e, 0x0d, 0x3d, 0x72, 0xf7, 0xf8, 0x6f, 0xa1,
0xb7, 0x69, 0xc7, 0xbc, 0xc3, 0xd7, 0x22, 0x37, 0x25, 0xbc, 0x96, 0xa4, 0x66, 0xc7, 0xfa, 0x6b,
0xf8, 0x11, 0xa3, 0x6e, 0xa7, 0xa3, 0xe5, 0xf9, 0xbd, 0x9d, 0x2c, 0x75, 0x7a, 0xb0, 0xdd, 0xe9,
0x65, 0xf4, 0xea, 0x11, 0x83, 0xc5, 0xd3, 0xac, 0xb3, 0xe3, 0x6a, 0x5e, 0x04, 0x13, 0x06, 0x0a,
0x6a, 0xe6, 0xd8, 0x8e, 0x62, 0xad, 0xe6, 0xb8, 0xea, 0x09, 0x1e, 0xe3, 0x4d, 0xd3, 0x57, 0x46,
0x24, 0xf1, 0x2e, 0x35, 0x04, 0x82, 0xd3, 0x46, 0xef, 0xab, 0x49, 0xa2, 0xcf, 0x93, 0x3d, 0xb1,
0x3b, 0x84, 0xb8, 0xf0, 0x04, 0x8d, 0xd9, 0x9b, 0x26, 0xd8, 0x55, 0xbd, 0xb6, 0x17, 0x1b, 0x12,
0xb6, 0x43, 0x64, 0x7d, 0x6c, 0x25, 0xd7, 0xee, 0xd9, 0xac, 0xe6, 0x84, 0x4d, 0x03, 0x96, 0xbb,
0x64, 0xe1, 0x7f, 0xff, 0xdd, 0x5c, 0x73, 0xa5, 0x69, 0xf0, 0x2e, 0x57, 0x5e, 0xb8, 0xac, 0x67,
0x69, 0x70, 0xe0, 0x54, 0xd0, 0x68, 0x5f, 0xea, 0xa2, 0x30, 0x13, 0x99, 0xfb, 0x96, 0x19, 0xa9,
0x5d, 0x7a, 0xe9, 0x36, 0xfa, 0x14, 0x22, 0x9c, 0x7c, 0x6c, 0x30, 0x7f, 0xb7, 0xbf, 0xbf, 0x9f,
0x66, 0x87, 0x3d, 0x54, 0x7a, 0xaf, 0x36, 0xb1, 0xf7, 0xe3, 0x09, 0x3f, 0xc4, 0x9f, 0xd5, 0x50,
0x7c, 0xff, 0x40, 0xb2, 0xdb, 0x96, 0x49, 0x83, 0xb5, 0x10, 0x4a, 0x34, 0x96, 0x85, 0xac, 0x6c,
0x36, 0xb2, 0xd8, 0x83, 0xe0, 0xe8, 0x1e, 0xd4, 0x05, 0x74, 0xba, 0xfb, 0xaf, 0xe2, 0xd2, 0xdb,
0x88, 0x0d, 0x33, 0x74, 0xe5, 0x35, 0x19, 0x0a, 0x45, 0x73, 0xa3, 0xa6, 0x7c, 0x7b, 0x80, 0x9e,
0x8b, 0x8c, 0x84, 0x52, 0x79, 0x97, 0x13, 0xfb, 0x45, 0x60, 0x96, 0x5d, 0x4c, 0xd9, 0x49, 0xff,
0x54, 0x0b, 0xff, 0xfb, 0x77, 0xb8, 0xeb, 0x4e, 0xec, 0xb7, 0x89, 0x37, 0xb7, 0x01, 0x02, 0x3d,
0xf4, 0x00, 0x34, 0x8c, 0x2a, 0x7b, 0xf4, 0x91, 0xe9, 0x86, 0x3c, 0x85, 0x5e, 0x9b, 0xcb, 0xff,
0x60, 0x98, 0xff, 0xe9, 0x7a, 0x05, 0x6e, 0xcd, 0x91, 0xf0, 0xdf, 0xcd, 0x75, 0x71, 0x5a, 0xae,
0x6b, 0xe0, 0x4b, 0xd2, 0x9b, 0x0f, 0x91, 0x0f, 0x93, 0x14, 0xb5, 0x80, 0x98, 0x51, 0xc1, 0x0a,
0x78, 0xa3, 0x5f, 0xe9, 0x8c, 0x59, 0x77, 0x76, 0x66, 0xa2, 0x77, 0xd3, 0x0d, 0xe5, 0x61, 0x26,
0xde, 0x6c, 0x93, 0x4a, 0xda, 0xff, 0x32, 0x33, 0xd3, 0xe0, 0xe8, 0x5e, 0x8e, 0xaf, 0x89, 0x5f,
0xf0, 0xe2, 0xae, 0x24, 0xeb, 0xe3, 0x9e, 0x08, 0xdc, 0xf2, 0xbb, 0xc8, 0xfb, 0x09, 0x18, 0x41,
0x61, 0xef, 0x61, 0x82, 0xb1, 0x97, 0xce, 0xe6, 0xb6, 0xb7, 0xff, 0x68, 0x7c, 0xf5, 0xba, 0x8b,
0x0e, 0x80, 0x03, 0x43, 0x5d, 0xbd, 0x0e, 0x9a, 0xdb, 0xd9, 0xd1, 0x38, 0x97, 0x22, 0x77, 0xea,
0x36, 0xf6, 0x2d, 0xbb, 0x63, 0x78, 0x31, 0x38, 0x12, 0x2e, 0xc2, 0x57, 0xdb, 0xed, 0x02, 0xc8,
0xa0, 0x4d, 0x30, 0x7a, 0x78, 0x9e, 0xf0, 0x4f, 0x91, 0x99, 0x05, 0x40, 0x2e, 0xe9, 0x87, 0x18,
0x05, 0xf3, 0xc8, 0x2e, 0xa6, 0xde, 0xc5, 0x7e, 0x94, 0x33, 0x3d, 0xe0, 0x45, 0xde, 0xb5, 0x90,
0xde, 0x82, 0x6f, 0x66, 0xf7, 0xd0, 0x7b, 0x42, 0x30, 0x3c, 0xb4, 0xe6, 0x5f, 0xd5, 0x8b, 0x27,
0x36, 0x53, 0x5c, 0xf4, 0xfa, 0xde, 0x11, 0xa4, 0xed, 0x24, 0xff, 0xcc, 0xd9, 0xbc, 0x2f, 0x56,
0x63, 0xd9, 0xac, 0x1d, 0xc0, 0x23, 0x1f, 0x83, 0x4b, 0xaf, 0xb1, 0xc4, 0xfb, 0x82, 0xc3, 0x54,
0xe1, 0x5f, 0xde, 0x47, 0x84, 0x74, 0x3a, 0xed, 0x30, 0xcd, 0xcb, 0xb3, 0xa5, 0x3d, 0x75, 0x83,
0xb8, 0x37, 0xe4, 0x63, 0x0c, 0x88, 0x17, 0xd2, 0x0e, 0xe5, 0xbc, 0x3b, 0xaf, 0xb7, 0xb7, 0xd9,
0x93, 0xc9, 0x07, 0x07, 0x75, 0x13, 0x78, 0x9a, 0x23, 0x61, 0xf2, 0x6e, 0x0e, 0xac, 0x50, 0x7e,
0x9f, 0x2e, 0xd4, 0xdf, 0xbf, 0xa3, 0xe2, 0x11, 0x44, 0x37, 0xa4, 0xe2, 0x5d, 0xe5, 0xbc, 0x10,
0xea, 0x0d, 0xa4, 0x09, 0xb4, 0x16, 0x5f, 0x5d, 0x5b, 0x1e, 0xf3, 0x96, 0xbe, 0x1e, 0xb8, 0x32,
0x8c, 0xa5, 0x7b, 0x4f, 0x99, 0x3b, 0x2f, 0xab, 0xb7, 0x84, 0x81, 0x8a, 0x07, 0x25, 0x98, 0x4f,
0x9d, 0x5e, 0x6c, 0x06, 0x74, 0xb0, 0x5a, 0x6a, 0x68, 0xe2, 0xd9, 0x44, 0xf4, 0xca, 0x51, 0x2e,
0xb7, 0xb9, 0x60, 0xf8, 0x8a, 0xb2, 0x8f, 0x4b, 0x52, 0x90, 0x20, 0xf1, 0x39, 0x01, 0x57, 0xe4,
0x27, 0xe5, 0xc6, 0xa3, 0xcf, 0x8a, 0x05, 0xb7, 0x9d, 0x79, 0xe5, 0xb6, 0xbe, 0x67, 0xdc, 0xf7,
0x39, 0x24, 0xbe, 0x67, 0x70, 0x37, 0x0f, 0xbf, 0x07, 0xce, 0x50, 0xdf, 0x4b, 0xfc, 0xbf, 0x68,
0x99, 0x89, 0x0f, 0x67, 0xc0, 0x00, 0x00
};

File diff suppressed because it is too large Load Diff

View File

@ -189,7 +189,7 @@ void sendImprovInfoResponse() {
out[11] = 4; //Firmware len ("WLED")
out[12] = 'W'; out[13] = 'L'; out[14] = 'E'; out[15] = 'D';
uint8_t lengthSum = 17;
uint8_t vlen = sprintf_P(out+lengthSum,PSTR("0.13.0-b5/%i"),VERSION);
uint8_t vlen = sprintf_P(out+lengthSum,PSTR("0.13.0-b6/%i"),VERSION);
out[16] = vlen; lengthSum += vlen;
uint8_t hlen = 7;
#ifdef ESP8266

View File

@ -71,12 +71,10 @@ void decBrightness()
// apply preset or fallback to a effect and palette if it doesn't exist
void presetFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID)
{
byte prevError = errorFlag;
if (!applyPreset(presetID, CALL_MODE_BUTTON)) {
effectCurrent = effectID;
effectPalette = paletteID;
errorFlag = prevError; //clear error 12 from non-existent preset
}
applyPreset(presetID, CALL_MODE_BUTTON_PRESET);
//these two will be overwritten if preset exists in handlePresets()
effectCurrent = effectID;
effectPalette = paletteID;
}
/*
@ -90,7 +88,7 @@ bool decodeIRCustom(uint32_t code)
{
//just examples, feel free to modify or remove
case IRCUSTOM_ONOFF : toggleOnOff(); break;
case IRCUSTOM_MACRO1 : applyPreset(1, CALL_MODE_BUTTON); break;
case IRCUSTOM_MACRO1 : applyPreset(1, CALL_MODE_BUTTON_PRESET); break;
default: return false;
}
@ -575,11 +573,7 @@ void decodeIRJson(uint32_t code)
JsonObject fdo;
JsonObject jsonCmdObj;
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(13)) return;
#endif
sprintf_P(objKey, PSTR("\"0x%lX\":"), (unsigned long)code);
@ -614,9 +608,9 @@ void decodeIRJson(uint32_t code)
lastValidCode = code;
decBrightness();
} else if (cmdStr.startsWith(F("!presetF"))) { //!presetFallback
uint8_t p1 = fdo["PL"] ? fdo["PL"] : 1;
uint8_t p2 = fdo["FX"] ? fdo["FX"] : random8(MODE_COUNT);
uint8_t p3 = fdo["FP"] ? fdo["FP"] : 0;
uint8_t p1 = fdo["PL"] | 1;
uint8_t p2 = fdo["FX"] | random8(MODE_COUNT);
uint8_t p3 = fdo["FP"] | 0;
presetFallback(p1, p2, p3);
}
} else {
@ -638,7 +632,7 @@ void decodeIRJson(uint32_t code)
colorUpdated(CALL_MODE_BUTTON);
} else if (!jsonCmdObj.isNull()) {
// command is JSON object
deserializeState(jsonCmdObj, CALL_MODE_BUTTON);
deserializeState(jsonCmdObj, CALL_MODE_BUTTON_PRESET);
}
releaseJSONBufferLock();
}

View File

@ -239,7 +239,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
return; // seg.differs(prev);
}
// deserializes WLED state (fileDoc points to doc object if called from web server)
// deserializes WLED state (fileDoc points to doc object (root) if called from web server, MQTT, IR, preset; not from UDP)
bool deserializeState(JsonObject root, byte callMode, byte presetId)
{
DEBUG_PRINTLN(F("Deserializing state"));
@ -526,7 +526,7 @@ void serializeInfo(JsonObject root)
}
leds[F("pwr")] = strip.currentMilliamps;
leds[F("fps")] = strip.getFps();
leds["fps"] = strip.getFps();
leds[F("maxpwr")] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0;
leds[F("maxseg")] = strip.getMaxSegments();
//leds[F("seglock")] = false; //might be used in the future to prevent modifications to segment config
@ -959,12 +959,8 @@ void serveJson(AsyncWebServerRequest* request)
return;
}
#ifdef WLED_USE_DYNAMIC_JSON
AsyncJsonResponse* response = new AsyncJsonResponse(JSON_BUFFER_SIZE, subJson==6);
#else
if (!requestJSONBufferLock(17)) return;
AsyncJsonResponse *response = new AsyncJsonResponse(&doc, subJson==6);
#endif
JsonVariant lDoc = response->getRoot();

View File

@ -76,10 +76,11 @@ bool colorChanged()
void colorUpdated(int callMode)
{
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification)
// 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa
// 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa 11: ws send only 12: button preset
if (callMode != CALL_MODE_INIT &&
callMode != CALL_MODE_DIRECT_CHANGE &&
callMode != CALL_MODE_NO_NOTIFY) strip.applyToAllSelected = true; //if not from JSON api, which directly sets segments
callMode != CALL_MODE_NO_NOTIFY &&
callMode != CALL_MODE_BUTTON_PRESET) strip.applyToAllSelected = true; //if not from JSON api, which directly sets segments
bool someSel = false;

View File

@ -91,20 +91,16 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties
colorUpdated(CALL_MODE_DIRECT_CHANGE);
} else if (strcmp_P(topic, PSTR("/api")) == 0) {
DEBUG_PRINTLN(F("MQTT JSON buffer requested."));
if (!requestJSONBufferLock(15)) { delete[] payloadStr; return; }
if (payload[0] == '{') { //JSON API
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(15)) return;
#endif
deserializeJson(doc, payloadStr);
deserializeState(doc.as<JsonObject>());
releaseJSONBufferLock();
} else { //HTTP API
String apireq = "win&";
apireq += (char*)payloadStr;
handleSet(nullptr, apireq);
}
releaseJSONBufferLock();
} else if (strlen(topic) != 0) {
// non standard topic, check with usermods
usermods.onMqttMessage(topic, payloadStr);

View File

@ -318,6 +318,32 @@ byte weekdayMondayFirst()
return wd;
}
bool isTodayInDateRange(byte monthStart, byte dayStart, byte monthEnd, byte dayEnd)
{
if (monthStart == 0 || dayStart == 0) return true;
if (monthEnd == 0) monthEnd = monthStart;
if (dayEnd == 0) dayEnd = 31;
byte d = day(localTime);
byte m = month(localTime);
if (monthStart < monthEnd) {
if (m > monthStart && m < monthEnd) return true;
if (m == monthStart) return (d >= dayStart);
if (m == monthEnd) return (d <= dayEnd);
return false;
}
if (monthEnd < monthStart) { //range spans change of year
if (m > monthStart || m < monthEnd) return true;
if (m == monthStart) return (d >= dayStart);
if (m == monthEnd) return (d <= dayEnd);
return false;
}
//start month and end month are the same
if (dayEnd < dayStart) return (m != monthStart || (d <= dayEnd || d >= dayStart)); //all year, except the designated days in this month
return (m == monthStart && d >= dayStart && d <= dayEnd); //just the designated days this month
}
void checkTimers()
{
if (lastTimerMinute != minute(localTime)) //only check once a new minute begins
@ -331,10 +357,12 @@ void checkTimers()
for (uint8_t i = 0; i < 8; i++)
{
if (timerMacro[i] != 0
&& (timerWeekday[i] & 0x01) //timer is enabled
&& (timerHours[i] == hour(localTime) || timerHours[i] == 24) //if hour is set to 24, activate every hour
&& timerMinutes[i] == minute(localTime)
&& (timerWeekday[i] & 0x01) //timer is enabled
&& ((timerWeekday[i] >> weekdayMondayFirst()) & 0x01)) //timer should activate at current day of week
&& ((timerWeekday[i] >> weekdayMondayFirst()) & 0x01) //timer should activate at current day of week
&& isTodayInDateRange(((timerMonth[i] >> 4) & 0x0F), timerDay[i], timerMonth[i] & 0x0F, timerDayEnd[i])
)
{
unloadPlaylist();
applyPreset(timerMacro[i]);

View File

@ -107,7 +107,7 @@ int16_t loadPlaylist(JsonObject playlistObj, byte presetId) {
playlistRepeat = rep;
if (playlistRepeat > 0) playlistRepeat++; //add one extra repetition immediately since it will be deducted on first start
playlistEndPreset = playlistObj[F("end")] | 0;
playlistEndPreset = playlistObj["end"] | 0;
shuffle = shuffle || playlistObj["r"];
if (shuffle) playlistOptions += PL_OPTION_SHUFFLE;
@ -119,6 +119,7 @@ int16_t loadPlaylist(JsonObject playlistObj, byte presetId) {
void handlePlaylist() {
static unsigned long presetCycledTime = 0;
// if fileDoc is not null JSON buffer is in use so just quit
if (currentPlaylist < 0 || playlistEntries == nullptr || fileDoc != nullptr) return;
if (millis() - presetCycledTime > (100*playlistEntryDur)) {

View File

@ -4,97 +4,118 @@
* Methods to handle saving and loading presets to/from the filesystem
*/
// called from: handleSet(), deserializeState(), applyMacro(), handlePlaylist(), checkCountdown(), checkTimers(), handleNightlight(), presetFallback()
// shortPressAction(), longPressAction(), doublePressAction(), handleSwitch(), onAlexaChange()
#ifdef ARDUINO_ARCH_ESP32
static char *tmpRAMbuffer = nullptr;
#endif
static volatile byte presetToApply = 0;
static volatile byte callModeToApply = 0;
bool applyPreset(byte index, byte callMode)
{
if (index == 0) return false;
const char *filename = index < 255 ? "/presets.json" : "/tmp.json";
uint8_t core = 1;
//crude way to determine if this was called by a network request
#ifdef ARDUINO_ARCH_ESP32
core = xPortGetCoreID();
#endif
//only allow use of fileDoc from the core responsible for network requests
//do not use active network request doc from preset called by main loop (playlist, schedule, ...)
if (fileDoc && core) {
errorFlag = readObjectFromFileUsingId(filename, index, fileDoc) ? ERR_NONE : ERR_FS_PLOAD;
JsonObject fdo = fileDoc->as<JsonObject>();
if (fdo["ps"] == index) fdo.remove("ps"); //remove load request for same presets to prevent recursive crash
#ifdef WLED_DEBUG_FS
serializeJson(*fileDoc, Serial);
#endif
deserializeState(fdo, callMode, index);
} else {
DEBUG_PRINTLN(F("Apply preset JSON buffer requested."));
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(9)) return false;
#endif
errorFlag = readObjectFromFileUsingId(filename, index, &doc) ? ERR_NONE : ERR_FS_PLOAD;
JsonObject fdo = doc.as<JsonObject>();
if (fdo["ps"] == index) fdo.remove("ps");
#ifdef WLED_DEBUG_FS
serializeJson(doc, Serial);
#endif
deserializeState(fdo, callMode, index);
releaseJSONBufferLock();
}
if (!errorFlag) {
if (index < 255) currentPreset = index;
return true;
}
return false;
presetToApply = index;
callModeToApply = callMode;
return true;
}
void handlePresets()
{
if (presetToApply == 0 || fileDoc) return; //JSON buffer allocated (apply preset in next cycle) or no preset waiting
JsonObject fdo;
const char *filename = presetToApply < 255 ? "/presets.json" : "/tmp.json";
// allocate buffer
DEBUG_PRINTLN(F("Apply preset JSON buffer requested."));
if (!requestJSONBufferLock(9)) return; // will also assign fileDoc
#ifdef ARDUINO_ARCH_ESP32
if (presetToApply==255 && tmpRAMbuffer!=nullptr) {
deserializeJson(*fileDoc,tmpRAMbuffer);
errorFlag = ERR_NONE;
} else
#endif
{
errorFlag = readObjectFromFileUsingId(filename, presetToApply, fileDoc) ? ERR_NONE : ERR_FS_PLOAD;
}
fdo = fileDoc->as<JsonObject>();
fdo.remove("ps"); //remove load request for presets to prevent recursive crash
deserializeState(fdo, callModeToApply, presetToApply);
#if defined(ARDUINO_ARCH_ESP32)
//Aircoookie recommended not to delete buffer
if (presetToApply==255 && tmpRAMbuffer!=nullptr) {
free(tmpRAMbuffer);
tmpRAMbuffer = nullptr;
}
#endif
releaseJSONBufferLock(); // will also clear fileDoc
if (!errorFlag && presetToApply < 255) currentPreset = presetToApply;
if (callModeToApply == CALL_MODE_BUTTON_PRESET) errorFlag = ERR_NONE; //ignore error on button press
presetToApply = 0; //clear request for preset
callModeToApply = 0;
}
//called from handleSet(PS=) [network callback (fileDoc==nullptr), IR (irrational), deserializeState, UDP] and deserializeState() [network callback (filedoc!=nullptr)]
void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj)
{
if (index == 0 || (index > 250 && persist) || (index<255 && !persist)) return;
JsonObject sObj = saveobj;
bool bufferAllocated = false;
const char *filename = persist ? "/presets.json" : "/tmp.json";
if (!fileDoc) {
// called from handleSet() HTTP API
DEBUG_PRINTLN(F("Save preset JSON buffer requested."));
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(10)) return;
#endif
sObj = doc.to<JsonObject>();
if (pname) sObj["n"] = pname;
DEBUGFS_PRINTLN(F("Save current state"));
serializeState(sObj, true);
if (persist) currentPreset = index;
writeObjectToFileUsingId(filename, index, &doc);
releaseJSONBufferLock();
} else { //from JSON API (fileDoc != nullptr)
DEBUGFS_PRINTLN(F("Reuse recv buffer"));
sObj.remove(F("psave"));
sObj.remove(F("v"));
if (!sObj["o"]) {
DEBUGFS_PRINTLN(F("Save current state"));
serializeState(sObj, true, sObj["ib"], sObj["sb"]);
if (persist) currentPreset = index;
}
sObj.remove("o");
sObj.remove("ib");
sObj.remove("sb");
sObj.remove(F("error"));
sObj.remove(F("time"));
writeObjectToFileUsingId(filename, index, fileDoc);
sObj = fileDoc->to<JsonObject>();
bufferAllocated = true;
}
if (pname) sObj["n"] = pname;
sObj.remove(F("psave"));
sObj.remove(F("v"));
if (!sObj["o"]) {
DEBUGFS_PRINTLN(F("Serialize current state"));
if (sObj["ib"].isNull() && sObj["sb"].isNull()) serializeState(sObj, true);
else serializeState(sObj, true, sObj["ib"], sObj["sb"]);
if (persist) currentPreset = index;
}
sObj.remove("o");
sObj.remove("ib");
sObj.remove("sb");
sObj.remove(F("error"));
sObj.remove(F("time"));
#if defined(ARDUINO_ARCH_ESP32)
if (index==255) {
if (tmpRAMbuffer!=nullptr) free(tmpRAMbuffer);
size_t len = measureJson(*fileDoc) + 1;
DEBUG_PRINTLN(len);
// if possible use SPI RAM on ESP32
#ifdef WLED_USE_PSRAM
if (psramFound())
tmpRAMbuffer = (char*) ps_malloc(len);
else
#endif
tmpRAMbuffer = (char*) malloc(len);
if (tmpRAMbuffer!=nullptr) {
serializeJson(*fileDoc, tmpRAMbuffer, len);
} else {
writeObjectToFileUsingId(filename, index, fileDoc);
}
} else
#endif
writeObjectToFileUsingId(filename, index, fileDoc);
if (persist) presetsModifiedTime = toki.second(); //unix time
if (bufferAllocated) releaseJSONBufferLock();
updateFSInfo();
}

View File

@ -77,6 +77,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
strip.cctBlending = request->arg(F("CB")).toInt();
Bus::setCCTBlend(strip.cctBlending);
Bus::setAutoWhiteMode(request->arg(F("AW")).toInt());
strip.setTargetFps(request->arg(F("FR")).toInt());
for (uint8_t s = 0; s < WLED_MAX_BUSSES; s++) {
char lp[4] = "L0"; lp[2] = 48+s; lp[3] = 0; //ascii 0-9 //strip data pin
@ -291,6 +292,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
//start ntp if not already connected
if (ntpEnabled && WLED_CONNECTED && !ntpConnected) ntpConnected = ntpUdp.begin(ntpLocalPort);
ntpLastSyncTime = 0; // force new NTP query
longitude = request->arg(F("LN")).toFloat();
latitude = request->arg(F("LT")).toFloat();
@ -336,21 +338,27 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
}
char k[3]; k[2] = 0;
for (int i = 0; i<10; i++)
{
k[1] = i+48;//ascii 0,1,2,3
for (int i = 0; i<10; i++) {
k[1] = i+48;//ascii 0,1,2,3,...
k[0] = 'H'; //timer hours
timerHours[i] = request->arg(k).toInt();
k[0] = 'N'; //minutes
timerMinutes[i] = request->arg(k).toInt();
k[0] = 'T'; //macros
timerMacro[i] = request->arg(k).toInt();
k[0] = 'W'; //weekdays
timerWeekday[i] = request->arg(k).toInt();
if (i<8) {
k[0] = 'M'; //start month
timerMonth[i] = request->arg(k).toInt() & 0x0F;
timerMonth[i] <<= 4;
k[0] = 'P'; //end month
timerMonth[i] += (request->arg(k).toInt() & 0x0F);
k[0] = 'D'; //start day
timerDay[i] = request->arg(k).toInt();
k[0] = 'E'; //end day
timerDayEnd[i] = request->arg(k).toInt();
}
}
}
@ -418,11 +426,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
//USERMODS
if (subPage == 8)
{
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(5)) return;
#endif
JsonObject um = doc.createNestedObject("um");

View File

@ -17,6 +17,7 @@ void notify(byte callMode, bool followUp)
case CALL_MODE_INIT: return;
case CALL_MODE_DIRECT_CHANGE: if (!notifyDirect) return; break;
case CALL_MODE_BUTTON: if (!notifyButton) return; break;
case CALL_MODE_BUTTON_PRESET: if (!notifyButton) return; break;
case CALL_MODE_NIGHTLIGHT: if (!notifyDirect) return; break;
case CALL_MODE_HUE: if (!notifyHue) return; break;
case CALL_MODE_PRESET_CYCLE: if (!notifyDirect) return; break;
@ -461,16 +462,17 @@ void handleNotifications()
// API over UDP
udpIn[packetSize] = '\0';
if (!requestJSONBufferLock(18)) return;
if (udpIn[0] >= 'A' && udpIn[0] <= 'Z') { //HTTP API
String apireq = "win&";
apireq += (char*)udpIn;
handleSet(nullptr, apireq);
} else if (udpIn[0] == '{') { //JSON API
DynamicJsonDocument jsonBuffer(2048);
DeserializationError error = deserializeJson(jsonBuffer, udpIn);
JsonObject root = jsonBuffer.as<JsonObject>();
DeserializationError error = deserializeJson(doc, udpIn);
JsonObject root = doc.as<JsonObject>();
if (!error && !root.isNull()) deserializeState(root);
}
releaseJSONBufferLock();
}

View File

@ -104,6 +104,10 @@
#include "../usermods/seven_segment_display/usermod_v2_seven_segment_display.h"
#endif
#ifdef USERMOD_SSDR
#include "../usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h"
#endif
#ifdef QUINLED_AN_PENTA
#include "../usermods/quinled-an-penta/quinled-an-penta.h"
#endif
@ -200,6 +204,10 @@ void registerUsermods()
usermods.add(new SevenSegmentDisplay());
#endif
#ifdef USERMOD_SSDR
usermods.add(new UsermodSSDR());
#endif
#ifdef QUINLED_AN_PENTA
usermods.add(new QuinLEDAnPentaUsermod());
#endif

View File

@ -65,12 +65,14 @@ void WLED::loop()
yield();
handleIO();
handleIR();
#ifndef WLED_DISABLE_ALEXA
handleAlexa();
#endif
yield();
if (doReboot)
reset();
if (doReboot) reset();
if (doCloseFile) {
closeFile();
yield();
@ -78,21 +80,25 @@ void WLED::loop()
if (!realtimeMode || realtimeOverride) // block stuff if WARLS/Adalight is enabled
{
if (apActive)
dnsServer.processNextRequest();
#ifndef WLED_DISABLE_OTA
if (WLED_CONNECTED && aOtaEnabled)
ArduinoOTA.handle();
#endif
if (apActive) dnsServer.processNextRequest();
#ifndef WLED_DISABLE_OTA
if (WLED_CONNECTED && aOtaEnabled) ArduinoOTA.handle();
#endif
handleNightlight();
handlePlaylist();
yield();
#ifndef WLED_DISABLE_HUESYNC
handleHue();
#ifndef WLED_DISABLE_BLYNK
handleBlynk();
#endif
yield();
#endif
#ifndef WLED_DISABLE_BLYNK
handleBlynk();
yield();
#endif
handlePresets();
yield();
#ifdef WLED_DEBUG
@ -100,10 +106,10 @@ void WLED::loop()
#endif
if (!offMode || strip.isOffRefreshRequred)
strip.service();
#ifdef ESP8266
#ifdef ESP8266
else if (!noWifiSleep)
delay(1); //required to make sure ESP enters modem sleep (see #1184)
#endif
#endif
#ifdef WLED_DEBUG
stripMillis = millis() - stripMillis;
if (stripMillis > 50) DEBUG_PRINTLN("Slow strip.");
@ -120,6 +126,7 @@ void WLED::loop()
if (lastMqttReconnectAttempt > millis()) {
rolloverMillis++;
lastMqttReconnectAttempt = 0;
ntpLastSyncTime = 0;
strip.restartRuntime();
}
if (millis() - lastMqttReconnectAttempt > 30000) {

View File

@ -3,12 +3,12 @@
/*
Main sketch, global variable declarations
@title WLED project sketch
@version 0.13.1-bl4
@version 0.13.1-bl6
@author Christian Schwinne
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2112201
#define VERSION 2201012
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG
@ -503,13 +503,17 @@ WLED_GLOBAL byte dP[] _INIT_N(({ 255, 255, 255, 255, 255, 255 }));
WLED_GLOBAL unsigned long countdownTime _INIT(1514764800L);
WLED_GLOBAL bool countdownOverTriggered _INIT(true);
// timer
WLED_GLOBAL byte lastTimerMinute _INIT(0);
WLED_GLOBAL byte timerHours[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
//timer
WLED_GLOBAL byte lastTimerMinute _INIT(0);
WLED_GLOBAL byte timerHours[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
WLED_GLOBAL int8_t timerMinutes[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
WLED_GLOBAL byte timerMacro[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
WLED_GLOBAL byte timerWeekday[] _INIT_N(({ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 })); // weekdays to activate on
// bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity
WLED_GLOBAL byte timerMacro[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
//weekdays to activate on, bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity
WLED_GLOBAL byte timerWeekday[] _INIT_N(({ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }));
//upper 4 bits start, lower 4 bits end month (default 28: start month 1 and end month 12)
WLED_GLOBAL byte timerMonth[] _INIT_N(({28,28,28,28,28,28,28,28}));
WLED_GLOBAL byte timerDay[] _INIT_N(({1,1,1,1,1,1,1,1}));
WLED_GLOBAL byte timerDayEnd[] _INIT_N(({31,31,31,31,31,31,31,31}));
// blynk
WLED_GLOBAL bool blynkEnabled _INIT(false);
@ -610,10 +614,8 @@ WLED_GLOBAL int8_t loadLedmap _INIT(-1);
// Usermod manager
WLED_GLOBAL UsermodManager usermods _INIT(UsermodManager());
#ifndef WLED_USE_DYNAMIC_JSON
// global ArduinoJson buffer
WLED_GLOBAL StaticJsonDocument<JSON_BUFFER_SIZE> doc;
#endif
WLED_GLOBAL volatile uint8_t jsonBufferLock _INIT(0);
// enable additional debug output

View File

@ -382,11 +382,7 @@ void deEEP() {
DEBUG_PRINTLN(F("Preset file not found, attempting to load from EEPROM"));
DEBUGFS_PRINTLN(F("Allocating saving buffer for dEEP"));
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(8)) return;
#endif
JsonObject sObj = doc.to<JsonObject>();
sObj.createNestedObject("0");

View File

@ -48,11 +48,7 @@ void handleSerial()
Serial.print("WLED"); Serial.write(' '); Serial.println(VERSION);
} else if (next == '{') { //JSON API
bool verboseResponse = false;
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(16)) return;
#endif
Serial.setTimeout(100);
DeserializationError error = deserializeJson(doc, Serial);
if (error) {

View File

@ -119,34 +119,30 @@ void initServer()
AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/json", [](AsyncWebServerRequest *request) {
bool verboseResponse = false;
bool isConfig = false;
{ //scope JsonDocument so it releases its buffer
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(14)) return;
#endif
DeserializationError error = deserializeJson(doc, (uint8_t*)(request->_tempObject));
JsonObject root = doc.as<JsonObject>();
if (error || root.isNull()) {
releaseJSONBufferLock();
request->send(400, "application/json", F("{\"error\":9}"));
return;
}
const String& url = request->url();
isConfig = url.indexOf("cfg") > -1;
if (!isConfig) {
#ifdef WLED_DEBUG
DEBUG_PRINTLN(F("Serialized HTTP"));
serializeJson(root,Serial);
DEBUG_PRINTLN();
#endif
verboseResponse = deserializeState(root);
} else {
verboseResponse = deserializeConfig(root); //use verboseResponse to determine whether cfg change should be saved immediately
}
if (!requestJSONBufferLock(14)) return;
DeserializationError error = deserializeJson(doc, (uint8_t*)(request->_tempObject));
JsonObject root = doc.as<JsonObject>();
if (error || root.isNull()) {
releaseJSONBufferLock();
request->send(400, "application/json", F("{\"error\":9}"));
return;
}
const String& url = request->url();
isConfig = url.indexOf("cfg") > -1;
if (!isConfig) {
#ifdef WLED_DEBUG
DEBUG_PRINTLN(F("Serialized HTTP"));
serializeJson(root,Serial);
DEBUG_PRINTLN();
#endif
verboseResponse = deserializeState(root);
} else {
verboseResponse = deserializeConfig(root); //use verboseResponse to determine whether cfg change should be saved immediately
}
releaseJSONBufferLock();
if (verboseResponse) {
if (!isConfig) {
serveJson(request); return; //if JSON contains "v"

View File

@ -37,11 +37,7 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
bool verboseResponse = false;
{ //scope JsonDocument so it releases its buffer
DEBUG_PRINTLN(F("WS JSON receive buffer requested."));
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(11)) return;
#endif
DeserializationError error = deserializeJson(doc, data, len);
JsonObject root = doc.as<JsonObject>();
@ -105,11 +101,7 @@ void sendDataWs(AsyncWebSocketClient * client)
{ //scope JsonDocument so it releases its buffer
DEBUG_PRINTLN(F("WS JSON send buffer requested."));
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(12)) return;
#endif
JsonObject state = doc.createNestedObject("state");
serializeState(state);

View File

@ -249,21 +249,17 @@ void getSettingsJS(byte subPage, char* dest)
{
char nS[8];
// Pin reservations will become unnecessary when settings pages will read cfg.json directly
// add reserved and usermod pins as d.um_p array
oappend(SET_F("d.um_p=[6,7,8,9,10,11"));
{ // scope so buffer can be released earlier
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(3072);
#else
if (!requestJSONBufferLock(6)) return;
#endif
JsonObject mods = doc.createNestedObject(F("um"));
usermods.addToConfig(mods);
if (!mods.isNull()) fillUMPins(mods);
releaseJSONBufferLock();
if (requestJSONBufferLock(6)) {
// if we can't allocate JSON buffer ignore usermod pins
JsonObject mods = doc.createNestedObject(F("um"));
usermods.addToConfig(mods);
if (!mods.isNull()) fillUMPins(mods);
releaseJSONBufferLock();
}
#ifdef WLED_ENABLE_DMX
@ -336,6 +332,7 @@ void getSettingsJS(byte subPage, char* dest)
sappend('c',SET_F("CCT"),correctWB);
sappend('c',SET_F("CR"),cctFromRgb);
sappend('v',SET_F("CB"),strip.cctBlending);
sappend('v',SET_F("FR"),strip.getTargetFps());
sappend('v',SET_F("AW"),Bus::getAutoWhiteMode());
for (uint8_t s=0; s < busses.getNumBusses(); s++) {
@ -461,7 +458,7 @@ void getSettingsJS(byte subPage, char* dest)
memset(fpass,'*',l);
sappends('s',SET_F("MQPASS"),fpass);
sappends('s',SET_F("MQCID"),mqttClientID);
sappends('s',SET_F("MD"),mqttDeviceTopic);
sappends('s',"MD",mqttDeviceTopic);
sappends('s',SET_F("MG"),mqttGroupTopic);
sappend('c',SET_F("BM"),buttonPublishMqtt);
#endif
@ -552,6 +549,12 @@ void getSettingsJS(byte subPage, char* dest)
k[0] = 'N'; sappend('v',k,timerMinutes[i]);
k[0] = 'T'; sappend('v',k,timerMacro[i]);
k[0] = 'W'; sappend('v',k,timerWeekday[i]);
if (i<8) {
k[0] = 'M'; sappend('v',k,(timerMonth[i] >> 4) & 0x0F);
k[0] = 'P'; sappend('v',k,timerMonth[i] & 0x0F);
k[0] = 'D'; sappend('v',k,timerDay[i]);
k[0] = 'E'; sappend('v',k,timerDayEnd[i]);
}
}
}