Merge branch 'dev' into sensor-info

This commit is contained in:
Blaz Kristan 2022-01-09 22:00:09 +01:00
commit 35779dad8b
30 changed files with 4542 additions and 4051 deletions

View File

@ -187,6 +187,7 @@ build_flags =
-DFP_IN_IROM
;-Wno-deprecated-declarations
;-Wno-register
-Wno-misleading-indentation
; NONOSDK22x_190703 = 2.2.2-dev(38a443e)
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
; lwIP 2 - Higher Bandwidth no Features

View File

@ -111,17 +111,17 @@ class Animated_Staircase : public Usermod {
}
if (i >= onIndex && i < offIndex) {
segments->setOption(SEG_OPTION_ON, 1, 1);
segments->setOption(SEG_OPTION_ON, 1, i);
// We may need to copy mode and colors from segment 0 to make sure
// changes are propagated even when the config is changed during a wipe
// segments->mode = mainsegment.mode;
// segments->colors[0] = mainsegment.colors[0];
} else {
segments->setOption(SEG_OPTION_ON, 0, 1);
segments->setOption(SEG_OPTION_ON, 0, i);
}
// Always mark segments as "transitional", we are animating the staircase
segments->setOption(SEG_OPTION_TRANSITIONAL, 1, 1);
segments->setOption(SEG_OPTION_TRANSITIONAL, 1, i);
}
colorUpdated(CALL_MODE_DIRECT_CHANGE);
}
@ -296,7 +296,7 @@ class Animated_Staircase : public Usermod {
maxSegmentId = i - 1;
break;
}
segments->setOption(SEG_OPTION_ON, 1, 1);
segments->setOption(SEG_OPTION_ON, 1, i);
}
colorUpdated(CALL_MODE_DIRECT_CHANGE);
DEBUG_PRINTLN(F("Animated Staircase disabled."));

View File

@ -58,7 +58,6 @@ private:
byte prevPreset = 0;
byte prevPlaylist = 0;
bool savedState = false;
uint32_t offTimerStart = 0; // off timer start time
byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // notification mode for colorUpdated(): CALL_MODE_NO_NOTIFY or CALL_MODE_DIRECT_CHANGE
@ -77,6 +76,7 @@ private:
bool m_mqttOnly = false; // flag to send MQTT message only (assuming it is enabled)
// flag to enable triggering only if WLED is initially off (LEDs are not on, preventing running effect being overwritten by PIR)
bool m_offOnly = false;
bool m_offMode = offMode;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
@ -123,13 +123,15 @@ private:
PIRtriggered = switchOn;
if (switchOn) {
if (m_onPreset) {
if (currentPlaylist>0) prevPlaylist = currentPlaylist;
else if (currentPreset>0) prevPreset = currentPreset;
else {
if (currentPlaylist>0 && !offMode) {
prevPlaylist = currentPlaylist;
unloadPlaylist();
} else if (currentPreset>0 && !offMode) {
prevPreset = currentPreset;
} else {
saveTemporaryPreset();
savedState = true;
prevPlaylist = 0;
prevPreset = 0;
prevPreset = 255;
}
applyPreset(m_onPreset, NotifyUpdateMode);
return;
@ -148,13 +150,10 @@ private:
prevPlaylist = 0;
return;
} else if (prevPreset) {
applyPreset(prevPreset, NotifyUpdateMode);
if (prevPreset<255) applyPreset(prevPreset, NotifyUpdateMode);
else applyTemporaryPreset();
prevPreset = 0;
return;
} else if (savedState) {
applyTemporaryPreset();
savedState = false;
return;
}
// preset not assigned
if (bri != 0) {

View File

@ -185,6 +185,7 @@ class UsermodTemperature : public Usermod {
// we were waiting for a conversion to complete, have we waited log enough?
if (now - lastTemperaturesRequest >= 750 /* 93.75ms per the datasheet but can be up to 750ms */) {
readTemperature();
if (getTemperatureC() < -100.0f) lastMeasurement = now - readingInterval + 300; // force new measurement in 300ms
if (WLED_MQTT_CONNECTED) {
char subuf[64];

View File

@ -65,7 +65,8 @@ private:
{ 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
{ 1, 1, 1, 1, 0, 1, 1 }, // 9
{ 0, 0, 0, 0, 0, 0, 0 } // blank
};
//String to reduce flash memory usage
@ -100,39 +101,39 @@ private:
switch (umSSDRDisplayMask[index]) {
case 'h':
timeVar = hourFormat12(localTime);
_showElements(&umSSDRHours, timeVar, 0);
_showElements(&umSSDRHours, timeVar, 0, 1);
break;
case 'H':
timeVar = hour(localTime);
_showElements(&umSSDRHours, timeVar, 0);
_showElements(&umSSDRHours, timeVar, 0, 1);
break;
case 'k':
timeVar = hour(localTime) + 1;
_showElements(&umSSDRHours, timeVar, 0);
_showElements(&umSSDRHours, timeVar, 0, 0);
break;
case 'm':
timeVar = minute(localTime);
_showElements(&umSSDRMinutes, timeVar, 0);
_showElements(&umSSDRMinutes, timeVar, 0, 0);
break;
case 's':
timeVar = second(localTime);
_showElements(&umSSDRSeconds, timeVar, 0);
_showElements(&umSSDRSeconds, timeVar, 0, 0);
break;
case 'd':
timeVar = day(localTime);
_showElements(&umSSDRDays, timeVar, 0);
_showElements(&umSSDRDays, timeVar, 0, 0);
break;
case 'M':
timeVar = month(localTime);
_showElements(&umSSDRMonths, timeVar, 0);
_showElements(&umSSDRMonths, timeVar, 0, 0);
break;
case 'y':
timeVar = second(localTime);
_showElements(&umSSDRYears, timeVar, 0);
_showElements(&umSSDRYears, timeVar, 0, 0);
break;
case 'Y':
timeVar = year(localTime);
_showElements(&umSSDRYears, timeVar, 0);
_showElements(&umSSDRYears, timeVar, 0, 0);
break;
case ':':
if (!colonsDone) { // only call _setColons once as all colons are printed when the first colon is found
@ -148,14 +149,16 @@ private:
void _setColons() {
if ( umSSDRColonblink ) {
if ( second(localTime) % 2 == 0 ) {
_showElements(&umSSDRColons, 0, 1);
_showElements(&umSSDRColons, 0, 1, 0);
}
} else {
_showElements(&umSSDRColons, 0, 1);
_showElements(&umSSDRColons, 0, 1, 0);
}
}
void _showElements(String *map, int timevar, bool isColon) {
void _showElements(String *map, int timevar, bool isColon, bool removeZero
) {
if (!(*map).equals("") && !(*map) == NULL) {
int length = String(timevar).length();
bool addZero = false;
@ -165,8 +168,16 @@ private:
}
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) {

View File

@ -0,0 +1,398 @@
#pragma once
//WLED custom fonts, curtesy of @Benji (https://github.com/Proto-molecule)
/*
Fontname: wled_logo_akemi_4x4
Copyright:
Glyphs: 3/3
BBX Build Mode: 3
* this logo ...WLED/images/wled_logo_akemi.png
* encode map = 1, 2, 3
*/
const uint8_t u8x8_wled_logo_akemi_4x4[388] U8X8_FONT_SECTION("u8x8_wled_logo_akemi_4x4") =
"\1\3\4\4\0\0\0\0\0\0\0\0\0\340\360\10\350\10\350\210\270\210\350\210\270\350\10\360\340\0\0\0"
"\0\0\200\200\0\0@\340\300\340@\0\0\377\377\377\377\377\377\37\37\207\207\371\371\371\377\377\377\0\0\374"
"\374\7\7\371\0\0\6\4\15\34x\340\200\177\177\377\351yy\376\356\357\217\177\177\177o\377\377\0\70\77"
"\277\376~\71\0\0\0\0\0\0\0\1\3\3\3\1\0\0\37\77\353\365\77\37\0\0\0\0\5\7\2\3"
"\7\4\0\0\300\300\300\300\200\200\200\0\0\0\0\0\0\0\200\200\300\300\300\300\200\200\0\0\0\0\0\0"
"\0\200\200\300\371\37\37\371\371\7\7\377\374\0\0\0\374\377\377\37\37\341\341\377\377\377\377\374\0\0\0\374"
"\377\7\7\231\371\376>\371\371>~\377\277\70\0\270\377\177\77\376\376\71\371\371\71\177\377\277\70\0\70\377"
"\177>\376\371\377\377\0\77\77\0\0\4\7\2\7\5\0\0\0\377\377\0\77\77\0\0\0\5\7\2\7\5"
"\0\0\377\377\300\300\300\200\200\0\0\0\0\0\0\0\200\200\300\300\300\300\300\200\200\0\0\0\0\0\0\0"
"\0\0\0\0\231\231\231\371\377\377\374\0\0\0\374\377\347\347\371\1\1\371\371\7\7\377\374\0\0\0@\340"
"\300\340@\0\71\371\371\71\177\377\277\70\0\70\277\377\177\71\371\370\70\371\371~\376\377\77\70\200\340x\34"
"\15\4\6\0\0\77\77\0\0\0\5\7\2\7\5\0\0\0\377\377\0\77\77\0\0\1\3\3\1\1\0\0"
"\0\0\0";
/*
Fontname: wled_logo_akemi_5x5
Copyright: Created with Fony 1.4.7
Glyphs: 3/3
BBX Build Mode: 3
* this logo ...WLED/images/wled_logo_akemi.png
* encoded = 1, 2, 3
*/
/*
const uint8_t u8x8_wled_logo_akemi_5x5[604] U8X8_FONT_SECTION("u8x8_wled_logo_akemi_5x5") =
"\1\3\5\5\0\0\0\0\0\0\0\0\0\0\0\0\340\340\374\14\354\14\354\14|\14\354\14||\14\354"
"\14\374\340\340\0\0\0\0\0\0\0\200\0\0\0\200\200\0\200\200\0\0\0\0\377\377\377\376\377\376\377\377"
"\377\377\77\77\307\307\307\307\306\377\377\377\0\0\0\360\374>\77\307\0\0\61cg\357\347\303\301\200\0\0"
"\377\377\377\317\317\317\317\360\360\360\374\374\377\377\377\377\377\377\377\377\0\0\200\377\377\340\340\37\0\0\0\0"
"\0\0\1\3\17\77\374\360\357\357\177\36\14\17\357\377\376\376>\376\360\357\17\17\14>\177o\340\300\343c"
"{\77\17\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\17\37\37\362\375\37\37\17\0\0"
"\0\0\1\1\1\0\1\1\1\0\0\0\200\300\300\300\300\200\200\0\0\0\0\0\0\0\0\0\0\0\200\200"
"\300\300\300\300\200\200\0\0\0\0\0\0\0\0\0\0\0\0\200\200\307\307\377\377\307\307\307\77>\374\360\0"
"\0\0\360\374\376\377\377\377\7\7\7\377\377\377\377\376\374\360\0\0\0\0\360\374\36\37\37\343\37\37\340\340"
"\37\37\37\340\340\377\377\200\0\200\377\377\377\340\340\340\37\37\37\37\37\37\37\377\377\377\200\0\0\200\377\377"
"\340\340\340\34\377\377\3\3\377\377\3\17\77{\343\303\300\303\343s\77\37\3\377\377\3\3\377\377\3\17\77"
"{\343\303\300\300\343{\37\17\3\377\377\377\377\0\0\37\37\0\0\1\1\1\1\0\1\1\1\1\0\0\377"
"\377\0\0\37\37\0\0\1\1\1\1\0\0\1\1\1\0\0\377\377\300\300\300\200\200\0\0\0\0\0\0\0"
"\0\0\0\0\200\200\300\300\300\300\200\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\343\343\343\343"
"\343\377\376\374\360\0\0\0\360\374\376\77\77\307\307\7\7\307\307\307\77>\374\360\0\0\0\0\0\200\200\0"
"\200\200\0\0\34\34\34\37\37\377\377\377\377\200\0\200\377\377\377\377\37\37\37\0\0\37\37\37\340\340\377\377"
"\200\0\0\0\1\303\347\357gc\61\0\3\3\377\377\3\7\37\177s\343\300\303s{\37\17\7\3\377\377"
"\3\3\377\377\3\37\77scp<\36\17\3\1\0\0\0\0\0\0\0\37\37\0\0\0\1\1\1\0\1"
"\1\1\0\0\0\0\377\377\0\0\37\37\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
*/
/*
Fontname: wled_logo_2x2
Copyright: Created with Fony 1.4.7
Glyphs: 4/4
BBX Build Mode: 3
* this logo https://cdn.discordapp.com/attachments/706623245935444088/927361780613799956/wled_scaled.png
* encode map = 1, 2, 3, 4
*/
const uint8_t u8x8_wled_logo_2x2[133] U8X8_FONT_SECTION("u8x8_wled_logo_2x2") =
"\1\4\2\2\0\0\0\0\0\200\200\360\360\16\16\16\16\0\0\0\340\340\340\340\340\37\37\1\1\0\0\0"
"\0\0\0\0\360\360\16\16\16\200\200\16\16\16\360\360\0\0\0\200\37\37\340\340\340\37\37\340\340\340\37\37"
"\0\0\0\37\200~~\0\0\0\0\0\0\0\360\360\216\216\216\216\37\340\340\340\340\340\340\340\0\0\37\37"
"\343\343\343\343\16\16\0\0ppp\16\16\376\376\16\16\16\360\360\340\340\0\0\0\0\0\340\340\377\377\340"
"\340\340\37\37";
/*
Fontname: wled_logo_4x4
Copyright: Created with Fony 1.4.7
Glyphs: 4/4
BBX Build Mode: 3
* this logo https://cdn.discordapp.com/attachments/706623245935444088/927361780613799956/wled_scaled.png
* encode map = 1, 2, 3, 4
*/
/*
const uint8_t u8x8_wled_logo_4x4[517] U8X8_FONT_SECTION("u8x8_wled_logo_4x4") =
"\1\4\4\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\374\374\374\374\374\374\374\374\374"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\300\300\300\300\300\377\377\377\377\377\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\17\17\17\17\17\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\370\370\370\370\370\370\370\370\370\7\7\7\7\7\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\374\374\374\374\374\0\0\0\0\0\374\374\374\374\374\0\0\0\0\0\0\0"
"\0\0\0\0\0\377\377\377\377\377\0\0\0\0\0\300\300\300\300\300\0\0\0\0\0\377\377\377\377\377\0\0"
"\0\0\300\300\0\377\377\377\377\377\0\0\0\0\0\377\377\377\377\377\0\0\0\0\0\377\377\377\377\377\0\0"
"\0\0\377\377\0\7\7\7\7\7\370\370\370\370\370\7\7\7\7\7\370\370\370\370\370\7\7\7\7\7\0\0"
"\0\0\7\7\0\0\0\374\374\374\374\374\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\374\374\374"
"\374\374\374\374\300\300\300\77\77\77\77\77\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\300\300\300"
"\300\300\300\300\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\37\37\37"
"\37\37\37\37\7\7\7\370\370\370\370\370\370\370\370\370\370\370\370\370\0\0\0\0\7\7\7\7\7\370\370\370"
"\370\370\370\370\374\374\374\374\374\374\0\0\0\0\0\0\0\0\374\374\374\374\374\374\374\374\374\374\374\374\374\374"
"\0\0\0\0\300\300\0\0\0\0\0\0\0\77\77\77\77\77\0\0\0\0\377\377\377\377\377\0\0\0\0\377"
"\377\377\377\377\37\37\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\0\0\0\0\377"
"\377\377\377\377\370\370\370\370\370\370\0\0\0\0\0\0\0\0\370\370\370\370\377\377\377\377\377\370\370\370\370\377"
"\7\7\7\7";
*/
/*
Fontname: 4LineDisplay_WLED_icons_1x
Copyright:
Glyphs: 13/13
BBX Build Mode: 3
* 1 = sun
* 2 = skip forward
* 3 = fire
* 4 = custom palette
* 5 = puzzle piece
* 6 = moon
* 7 = brush
* 8 = contrast
* 9 = power-standby
* 10 = star
* 11 = heart
* 12 = Akemi
*-----------
* 20 = wifi
* 21 = media-play
*/
const uint8_t u8x8_4LineDisplay_WLED_icons_1x1[172] U8X8_FONT_SECTION("u8x8_4LineDisplay_WLED_icons_1x1") =
"\1\25\1\1\0B\30<<\30B\0~<\30\0~<\30\0p\374\77\216\340\370\360\0<n\372\377"
"\275\277\26\34\374\374\77\77\374\374\60\60<~~\360\340``\0\200\340\360p\14\16\6\1<~\377\377"
"\201\201B<\70D\200\217\200D\70\0\0\10x<<x\10\0\14\36>||>\36\14\64 \336\67"
";\336 \64\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\1\11\311"
"\311\1\2\0\0~<<\30\30\0";
/*
Fontname: 4LineDisplay_WLED_icons_2x
Copyright:
Glyphs: 11/11
BBX Build Mode: 3
* 1 = sun
* 2 = skip forward
* 3 = fire
* 4 = custom palette
* 5 = puzzle piece
* 6 = moon
* 7 = brush
* 8 = contrast
* 9 = power-standby
* 10 = star
* 11 = heart
* 12 = Akemi
*/
const uint8_t u8x8_4LineDisplay_WLED_icons_2x2[389] U8X8_FONT_SECTION("u8x8_4LineDisplay_WLED_icons_2x2") =
"\1\14\2\2\200\200\14\14\300\340\360\363\363\360\340\300\14\14\200\200\1\1\60\60\3\7\17\317\317\17\7\3"
"\60\60\1\1\374\370\360\340\340\300\200\0\374\370\360\340\340\300\200\0\77\37\17\7\7\3\1\0\77\37\17\7"
"\7\3\1\0\0\200\340\360\377\376\374\360\0\0\300\200\0\0\0\0\17\77\177\377\17\7\301\340\370\374\377\377"
"\377|\0\0\360\370\234\236\376\363\363\377\377\363\363\376><\370\360\3\17\77yy\377\377\377\377\317\17\17"
"\17\17\7\3\360\360\360\360\366\377\377\366\360\360\360\360\0\0\0\0\377\377\377\377\237\17\17\237\377\377\377\377"
"\6\17\17\6\340\370\374\376\377\340\200\0\0\0\0\0\0\0\0\0\3\17\37\77\177\177\177\377\376|||"
"\70\30\14\0\0\0\0\0\0\0\0``\360\370|<\36\7\2\0\300\360\376\377\177\77\36\0\1\1\0"
"\0\0\0\0\340\370\374\376\376\377\377\377\3\3\7\6\16<\370\340\7\37\77\177\177\377\377\377\300\300\340`"
"p<\37\7\300\340p\30\0\0\377\377\0\0\30p\340\300\0\0\17\37\70`\340\300\300\300\300\340`\70"
"\37\17\0\0\0@\300\300\300\300\340\374\374\340\300\300\300\300@\0\0\0\0\1s\77\37\17\17\37\77s"
"\1\0\0\0\360\370\374\374\374\374\370\360\360\370\374\374\374\374\370\360\0\1\3\7\17\37\77\177\177\77\37\17"
"\7\3\1\0\200\200\0\0\0\360\370\374<\334\330\360\0\0\200\200\2\2\14\30\24\37\6~\7\177\7\37"
"\24\30\16\2";
/*
Fontname: 4LineDisplay_WLED_icons_4x
Copyright:
Glyphs: 11/11
BBX Build Mode: 3
* 1 = sun
* 2 = skip forward
* 3 = fire
* 4 = custom palette
* 5 = puzzle piece
* 6 = moon
* 7 = brush
* 8 = contrast
* 9 = power-standby
* 10 = star
* 11 = heart
* 12 = Akemi
*/
/*
const uint8_t u8x8_4LineDisplay_WLED_icons_4x4[1540] U8X8_FONT_SECTION("u8x8_4LineDisplay_WLED_icons_4x4") =
"\1\14\4\4\0\0\0\0`\360\360`\0\0\0\0\0\0\6\17\17\6\0\0\0\0\0\0`\360\360`"
"\0\0\0\0\200\300\300\200\0\0\0\0\340\370\374\376\376\377\377\377\377\377\377\376\376\374\370\340\0\0\0\0"
"\200\300\300\200\1\3\3\1\0\0\0\0\7\37\77\177\177\377\377\377\377\377\377\177\177\77\37\7\0\0\0\0"
"\1\3\3\1\0\0\0\0\6\17\17\6\0\0\0\0\0\0`\360\360`\0\0\0\0\0\0\6\17\17\6"
"\0\0\0\0\360\340\300\200\200\0\0\0\0\0\0\0\0\0\0\0\360\340\300\200\200\0\0\0\0\0\0\0"
"\0\0\0\0\377\377\377\377\377\377\376\374\374\370\360\340\340\300\200\0\377\377\377\377\377\377\376\374\374\370\360\340"
"\340\300\200\0\377\377\377\377\377\377\177\77\77\37\17\7\7\3\1\0\377\377\377\377\377\377\177\77\77\37\17\7"
"\7\3\1\0\17\7\3\1\1\0\0\0\0\0\0\0\0\0\0\0\17\7\3\1\1\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\200\341\376\374\370\360\340\300\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\200\300\360\370\374\376\377\377\377\377\377\377\377\377~\0\0\0\0\20\340\300\200\0\0\0\0"
"\0\0\0\0~\377\377\377\377\377\377\377\377\177\77\37\17\3\1\0\200\300\340\370\376\377\377\377\377\376\374\340"
"\0\0\0\0\0\3\7\17\37\77\177\207\1\0\0\0\340\370\374\377\377\377\377\377\377\377\377\377\377\177\77\17"
"\0\0\0\0\0\0\0\200\300\360\370\370\374\34\16\16\16\36\377\377\377\377\37\16\16\16\36\374\374\370\370\360"
"\340\300\0\0\340\374\377\377\217\7\7\7\217\377\376\376\376\377\377\377\377\377\377\376\376\376\377\377\217\7\7\7"
"\217\377\377\374\17\177\377\377\377\37\17\17\17\37\377\377\377\377\377\377\377\377\377\377\377\377\177\177\177\177\77\77"
"\77\37\17\7\0\0\0\3\7\17\36>>\177\177\377\377\377\377\377\377\371p\60\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0<\376\377\377\377\377\376<\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0"
"\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377~~\377\377"
"\377\377~<\377\377\377\377\377\377\377\377\303\1\0\0\0\0\1\303\377\377\377\377\377\377\377\377\0\0\0\0"
"\0\0\0\0\0\0\200\340\360\370\374\374\376\376\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\370\377\377\377\377\377\377\377\377\377\376\360\300\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\7\77\377\377\377\377\377\377\377\377\377\377\377\377\377\376\374\370\370\360\360\360\340\340\340\340\340\340"
"\340\340\60\0\0\0\0\1\3\7\17\37\37\77\77\77\177\177\177\177\177\177\177\177\77\77\77\37\37\17\7\3"
"\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\200\200\300\340\340\360\370\374\374"
"~\77\16\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\30\34>~\377\377\377\377\177\77\37\7\3\0"
"\0\0\0\0\0\0\0\0\0\360\374\376\377\377\377\377\377\376\374\370\0\0\0\3\3\1\0\0\0\0\0\0"
"\0\0\0\0@@\340\370\374\377\377\377\177\177\177\77\37\17\7\1\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\200\300\340\360\370\374\374\376\376\376\377\377\377\377\17\17\17\37\36\36>|\374\370\360\340"
"\300\200\0\0\360\376\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\1\3\37"
"\377\377\376\360\17\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\200\300\370"
"\377\377\177\17\0\0\1\3\7\17\37\77\77\177\177\177\377\377\377\377\360\360\360\370xx|>\77\37\17\7"
"\3\1\0\0\0\0\0\0\0\200\300\200\0\0\0\0\377\377\377\377\0\0\0\0\200\300\200\0\0\0\0\0"
"\0\0\0\0\300\360\374\376\177\37\7\3\3\0\0\0\377\377\377\377\0\0\0\3\3\7\37\177\376\374\360\300"
"\0\0\0\0\77\377\377\377\340\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\200\340\377\377\377\77"
"\0\0\0\0\0\0\3\7\17\37><|x\370\360\360\360\360\360\360\370x|<>\37\17\7\3\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\340\374\374\340\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\20\60p\360\360\360\360\360\360\360\360\370\377\377\377\377\377\377\370\360\360\360\360\360\360\360\360"
"p\60\20\0\0\0\0\0\0\0\1\3\7\317\377\377\377\377\377\377\377\377\377\377\377\377\317\7\3\1\0\0"
"\0\0\0\0\0\0\0\0\0\0\0p>\37\17\17\7\3\1\0\0\1\3\7\17\17\37>p\0\0\0"
"\0\0\0\0\0\200\300\340\340\360\360\360\360\360\360\340\340\300\200\0\0\200\300\340\340\360\360\360\360\360\360\340"
"\340\300\200\0~\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376\376\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377~\0\1\3\7\17\37\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\37\17"
"\7\3\1\0\0\0\0\0\0\0\0\0\0\1\3\7\17\37\77\177\177\77\37\17\7\3\1\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\200\300\340\340\360\360\360\360\340\340\300\200\0\0\0\0\0\0"
"\0\0\0\0\0@\340\300\340@\0\0\0\376\377\377\177\177\177\237\207\347\371\371\371\377\376\0\0\0\0@"
"\340\300\340@\2\4\4\35x\340\200\0\30\237\377\177\36\376\376\37\37\377\377\37\177\377\237\30\0\200\340x"
"\34\5\4\2\0\0\0\0\0\1\3\3\3\1\0\0\0\17\17\0\0\17\17\0\0\0\1\3\3\3\1\0"
"\0\0\0";
*/
/*
Fontname: 4LineDisplay_WLED_icons_6x
Copyright:
Glyphs: 11/11
BBX Build Mode: 3
* 1 = sun
* 2 = skip forward
* 3 = fire
* 4 = custom palette
* 5 = puzzle piece
* 6 = moon
* 7 = brush
* 8 = contrast
* 9 = power-standby
* 10 = star
* 11 = heart
* 12 = Akemi
*/
const uint8_t u8x8_4LineDisplay_WLED_icons_6x6[3460] U8X8_FONT_SECTION("u8x8_4LineDisplay_WLED_icons_6x6") =
"\1\14\6\6\0\0\0\0\0\0\200\300\300\300\300\200\0\0\0\0\0\0\0\0\0\36\77\77\77\77\36\0"
"\0\0\0\0\0\0\0\0\200\300\300\300\300\200\0\0\0\0\0\0\0\0\0\0\0\0\7\17\17\17\17\7"
"\0\0\0\0\200\300\340\340\340\360\360\360\360\360\360\340\340\340\300\200\0\0\0\0\7\17\17\17\17\7\0\0"
"\0\0\0\0\300\340\340\340\340\300\0\0\0\0\0\0\340\374\376\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\376\374\340\0\0\0\0\0\0\300\340\340\340\340\300\3\7\7\7\7\3\0\0\0\0\0\0"
"\7\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\7\0\0\0\0\0\0\3\7"
"\7\7\7\3\0\0\0\0\0\0\340\360\360\360\360\340\0\0\0\0\1\3\7\7\7\17\17\17\17\17\17\7"
"\7\7\3\1\0\0\0\0\340\360\360\360\360\340\0\0\0\0\0\0\0\0\0\0\0\0\1\3\3\3\3\1"
"\0\0\0\0\0\0\0\0\0x\374\374\374\374x\0\0\0\0\0\0\0\0\0\1\3\3\3\3\1\0\0"
"\0\0\0\0\300\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\200\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\376\376\374\370\360\360\340\300\200"
"\200\0\0\0\0\0\0\0\0\0\0\0\377\377\377\376\376\374\370\360\360\340\300\200\200\0\0\0\0\0\0\0"
"\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376\374\374\370\360\340\340\300\200\0\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\376\374\374\370\360\340\340\300\200\0\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\177\77\77\37\17\7\7\3\1\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\77\37\17\7"
"\7\3\1\0\377\377\377\177\177\77\37\17\17\7\3\1\1\0\0\0\0\0\0\0\0\0\0\0\377\377\377\177"
"\177\77\37\17\17\7\3\1\1\0\0\0\0\0\0\0\0\0\0\0\3\1\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\376\374\374\370\360\340\300\200\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\200\340\360\374"
"\377\377\377\377\377\377\377\377\377\376\370\300\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\300\340\360\374\376\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\37\0\0\0\0"
"\0\0\4\370\360\360\340\300\200\0\0\0\0\0\0\0\0\0\0\0\370\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\177\77\37\7\3\0\0\0\0\0\200\300\360\374\377\377\377\377\377\377\377\376\370\340\0\0\0"
"\0\0\0\0\3\37\177\377\377\377\377\377\377\377\377\377\77\17\7\1\0\0\0\0\0\200\300\360\370\374\376\377"
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\1\3\7\17\37\77\77\177\200"
"\0\0\0\0\0\0\340\374\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\17\1\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\200\300\340\340\360\360\370|<>>>~\377\377\377\377\377\377\377\177"
"\77\36\36\36\36<|\370\370\360\360\340\340\200\0\0\0\0\0\0\0\0\300\360\374\376\377\377\377\377\377\377"
"\377\360\340\300\300\300\300\340\360\377\377\377\377\377\377\370\360\340\340\340\340\360\370\377\377\377\377\377\377\377\377\377"
"\374\360\340\200\360\377\377\377\377\377\207\3\1\1\1\1\3\207\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\207\3\1\1\1\1\3\207\377\377\377\377\377\17\377\377\377\377\377\377\377\376~>>"
"\77\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376\376\376\376\377\377\377"
"\177\77\37\7\0\0\3\17\77\177\377\377\360\340\300\300\300\300\340\360\377\377\377\377\377\377\377\377\377\377\77\17"
"\17\7\7\7\7\7\7\7\7\7\3\3\3\3\1\0\0\0\0\0\0\0\0\0\0\0\0\1\3\7\17\37"
"\37\77\77\177\177\177\377\377\377\377\377\377\377\377\377~\30\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\370\374\376\377\377\377\377\377\377\376\374\360\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\360\360\360\360\360\360\360\360\360\360\360\360"
"\360\363\377\377\377\377\377\377\377\377\363\360\360\360\360\360\360\360\360\360\360\360\360\360\0\0\0\0\0\0\0\0"
"\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\374\374\376\376\377\377\377\377"
"\377\376\374\360\377\377\377\377\377\377\377\377\377\377\377\377\177\77\37\17\17\17\17\17\17\37\77\177\377\377\377\377"
"\377\377\377\377\377\377\377\377\3\3\7\7\17\17\17\17\7\7\3\0\377\377\377\377\377\377\377\377\377\377\377\377"
"\360\300\0\0\0\0\0\0\0\0\300\360\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\200\300\340\360\360\370\374\374\376\376\7\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\360\374\376\377\377\377\377\377\377\377"
"\377\377\377\340\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\374\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\374\360\300\200\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\17\177\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\376\374\370\360\360\340\340\300\300\300\200\200\200\200\0\0\0\0\0\0\200\200"
"\200\200\0\0\0\0\1\7\37\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\37\7\1\0\0\0\0\0\0\0\0\0\0\1\3\3\7"
"\17\17\37\37\37\77\77\77\77\177\177\177\177\177\177\77\77\77\77\37\37\37\17\17\7\3\3\1\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\200\200\300\340\360\360\370\374\374\376\377~\34\10\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\200\300\300\340\360\360\370\374\376\376\377\377\377\377\377\377\177\77\17\7\3"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\6\17\17\37\77\177\377"
"\377\377\377\377\377\377\77\37\7\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\370\374\376"
"\376\377\377\377\377\377\377\376\376\374\370\340\0\0\0\0\3\17\7\3\1\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\200\360\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\17\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`px\374\376\377\377\377\377\377\377"
"\177\177\177\77\77\37\17\7\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\200\300\340\360\360\370\374\374\374\376\376\376\377\377\377\377\377\77\77\77\77"
"\177~~\376\374\374\374\370\360\360\340\300\200\0\0\0\0\0\0\0\0\0\340\360\374\376\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\1\1\3\7\17\37\177\377\377\376\374"
"\360\340\0\0\370\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\1\17\377\377\377\377\377\370\37\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\200\360\377\377"
"\377\377\377\37\0\0\7\17\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0"
"\0\0\0\0\0\200\200\300\340\360\370\376\377\377\177\77\17\7\0\0\0\0\0\0\0\0\0\1\3\7\17\17"
"\37\77\77\77\177\177\177\377\377\377\377\377\374\374\374\374\376~~\177\77\77\77\37\17\17\7\3\1\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\200\300\340\360\370\374\376\376|"
"x \0\0\0\0\377\377\377\377\377\377\0\0\0\0 x|\376\376\374\370\360\340\300\200\0\0\0\0\0"
"\0\0\0\0\300\370\376\377\377\377\177\17\7\1\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0"
"\0\0\0\0\1\7\37\177\377\377\377\376\370\200\0\0\0\0\0\0\177\377\377\377\377\377\200\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\200\377\377\377\377\377\177\0\0"
"\0\0\0\0\0\7\37\177\377\377\377\374\370\340\300\200\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\200\200\300\340\370\374\377\377\377\177\37\7\0\0\0\0\0\0\0\0\0\0\0\0\1\3\7\17\37\37\77"
"\77\177~~~\374\374\374\374\374\374\374\374~~~\177\77\77\37\37\17\7\3\1\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\340\374\374\340\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\300\370\377\377\377\377\377\377\370\300\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\4\14\34<<|\374\374\374\374\374\374\374\374\374\374\374\376\377\377\377\377\377\377\377\377\377"
"\377\376\374\374\374\374\374\374\374\374\374\374\374|<<\34\14\4\0\0\0\0\0\0\0\0\0\1\3\3\7"
"\17\37\77\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\77\37\17\7\3\3\1\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\370\377\377\377\377\377\377\177\77\37\17\17\37\77\177"
"\377\377\377\377\377\377\370\300\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0p>"
"\37\17\17\7\3\1\0\0\0\0\0\0\0\0\0\0\0\0\1\3\7\17\17\37>p\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\200\200\200\300\300\300\300\300\300\200\200\200\0\0\0\0\0\0\0\0\0\0"
"\0\0\200\200\200\300\300\300\300\300\300\200\200\200\0\0\0\0\0\0\200\360\370\374\376\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\376\374\370\360\200\200\360\370\374\376\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376"
"\374\370\360\200\37\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\37\0\0\1\3\7\17\37\77\177\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\37\17\7"
"\3\1\0\0\0\0\0\0\0\0\0\0\0\0\1\3\7\17\37\77\177\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\177\77\37\17\7\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\1\3\7\17\37\77\77\37\17\7\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\300\300\300\300\300\300"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\340\370\370\376\376\377\377\377\377\377\377\377\377\77\77\77>\376\370\370\340\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0 p\360\340\360p \0\0\0\0\0\0\377\377\377\377\177\177\177\177\177\207\207\340\340\377"
"\377\377\377\377\377\377\377\0\0\0\0\0 p\360\340\360p \0\6\4\14\14\15|x\360\200\200\0\0"
"pp\177\177\377\377\374|\374\374\374\177\177\177\377\377\377\177\377\377\377\377\177pp\0\0\200\200\360x}"
"\14\14\4\6\0\0\0\0\0\0\0\3\37\37|ppp\34\34\37\3\3\0\377\377\377\0\0\0\377\377"
"\377\0\3\3\37\37\34ppp~\37\37\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\7\7\7\0\0\0\7\7\7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0";
/*
Fontname: akemi_8x8
Copyright: Created with Fony 1.4.7
Glyphs: 1/1
BBX Build Mode: 3
* 12 = Akemi
*/
/*
const uint8_t u8x8_akemi_8x8[516] U8X8_FONT_SECTION("u8x8_akemi_8x8") =
"\14\14\10\10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\200\200\200\200\200\200\200\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\340\340\370\370\376\376\376\376"
"\377\377\377\377\377\377\377\377\376\376\376\376\370\370\340\340\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\376\376\377\377\377\377\377\377\377\377"
"\377\377\377\377\37\37\37\343\343\343\343\343\343\377\377\377\376\376\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\30\30~~\370\370~~\30\30\0\0\0\0\0\0\0\377\377\377\377\377\77\77\77\77\77"
"\77\300\300\300\370\370\370\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\30\0f\0\200\0\0"
"\0\0\0\0\6\6\30\30\30\31\371\370\370\340\340\0\0\0\0\0\340\340\377\377\377\377\377\376\376\376\376\376"
"\376\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\371\346\346\6\6\6\6\6\0\340\340\340\341\0\0"
"\0\0\0\0\0\0\0\0\0\0\1\1\37\37\377\376\376\340\340\200\201\201\341\341\177\177\37\37\1\1\377\377"
"\377\377\1\1\1\1\377\377\377\377\1\1\37\37\177\177\341\341\201\201\200\200\370\370\376\376\37\37\1\1\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\7\7\7\7\7\7\1\1\0\0\0\0\0\0\377\377"
"\377\377\0\0\0\0\377\377\377\377\0\0\0\0\0\0\1\1\7\7\7\7\7\7\1\1\0\0\0\0\0\0"
"\0\0\0";
*/

View File

@ -2,6 +2,7 @@
#include "wled.h"
#include <U8x8lib.h> // from https://github.com/olikraus/u8g2/
#include "4LD_wled_fonts.c"
//
// Insired by the usermod_v2_four_line_display
@ -109,133 +110,6 @@ typedef enum {
SSD1306_SPI64 // U8X8_SSD1306_128X64_NONAME_HW_SPI
} DisplayType;
/*
Fontname: benji_custom_icons_1x
Copyright:
Glyphs: 1/1
BBX Build Mode: 3
* 4 = custom palette
*/
const uint8_t u8x8_font_benji_custom_icons_1x1[13] U8X8_FONT_SECTION("u8x8_font_benji_custom_icons_1x1") =
"\4\4\1\1<n\372\377\275\277\26\34";
/*
Fontname: benji_custom_icons_2x
Copyright:
Glyphs: 8/8
BBX Build Mode: 3
// all the icons uses are consolidated into a single library to simplify code
// these are just the required icons stripped from the U8x8 libraries in addition to a few new custom icons
* 1 = sun
* 2 = skip forward
* 3 = fire
* 4 = custom palette
* 5 = puzzle piece
* 6 = moon
* 7 = brush
* 8 = custom saturation
*/
const uint8_t u8x8_font_benji_custom_icons_2x2[261] U8X8_FONT_SECTION("u8x8_font_benji_custom_icons_2x2") =
"\1\10\2\2\200\200\14\14\300\340\360\363\363\360\340\300\14\14\200\200\1\1\60\60\3\7\17\317\317\17\7\3"
"\60\60\1\1\374\370\360\340\340\300\200\0\374\370\360\340\340\300\200\0\77\37\17\7\7\3\1\0\77\37\17\7"
"\7\3\1\0\0\200\340\360\377\376\374\360\0\0\300\200\0\0\0\0\17\77\177\377\17\7\301\340\370\374\377\377"
"\377|\0\0\360\370\234\236\376\363\363\377\377\363\363\376><\370\360\3\17\77yy\377\377\377\377\317\17\17"
"\17\17\7\3\360\360\360\360\366\377\377\366\360\360\360\360\0\0\0\0\377\377\377\377\237\17\17\237\377\377\377\377"
"\6\17\17\6\340\370\374\376\377\340\200\0\0\0\0\0\0\0\0\0\3\17\37\77\177\177\177\377\376|||"
"\70\30\14\0\0\0\0\0\0\0\0``\360\370|<\36\7\2\0\300\360\376\377\177\77\36\0\1\1\0"
"\0\0\0\0\200\200\14\14\300\340\360\363\363\360\340\300\14\14\200\200\1\1\60\60\3\4\10\310\310\10\4\3"
"\60\60\1\1";
/*
Fontname: benji_custom_icons_6x
Copyright:
Glyphs: 8/8
BBX Build Mode: 3
// 6x6 icons libraries take up a lot of memory thus all the icons uses are consolidated into a single library
// these are just the required icons stripped from the U8x8 libraries in addition to a few new custom icons
* 1 = sun
* 2 = skip forward
* 3 = fire
* 4 = custom palette
* 5 = puzzle piece
* 6 = moon
* 7 = brush
* 8 = custom saturation
*/
const uint8_t u8x8_font_benji_custom_icons_6x6[2308] U8X8_FONT_SECTION("u8x8_font_benji_custom_icons_6x6") =
"\1\10\6\6\0\0\0\0\0\0\200\300\300\300\300\200\0\0\0\0\0\0\0\0\0\36\77\77\77\77\36\0"
"\0\0\0\0\0\0\0\0\200\300\300\300\300\200\0\0\0\0\0\0\0\0\0\0\0\0\7\17\17\17\17\7"
"\0\0\0\0\200\300\340\340\340\360\360\360\360\360\360\340\340\340\300\200\0\0\0\0\7\17\17\17\17\7\0\0"
"\0\0\0\0\300\340\340\340\340\300\0\0\0\0\0\0\340\374\376\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\376\374\340\0\0\0\0\0\0\300\340\340\340\340\300\3\7\7\7\7\3\0\0\0\0\0\0"
"\7\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\7\0\0\0\0\0\0\3\7"
"\7\7\7\3\0\0\0\0\0\0\340\360\360\360\360\340\0\0\0\0\1\3\7\7\7\17\17\17\17\17\17\7"
"\7\7\3\1\0\0\0\0\340\360\360\360\360\340\0\0\0\0\0\0\0\0\0\0\0\0\1\3\3\3\3\1"
"\0\0\0\0\0\0\0\0\0x\374\374\374\374x\0\0\0\0\0\0\0\0\0\1\3\3\3\3\1\0\0"
"\0\0\0\0\300\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\200\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\376\376\374\370\360\360\340\300\200"
"\200\0\0\0\0\0\0\0\0\0\0\0\377\377\377\376\376\374\370\360\360\340\300\200\200\0\0\0\0\0\0\0"
"\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376\374\374\370\360\340\340\300\200\0\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\376\374\374\370\360\340\340\300\200\0\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\177\77\77\37\17\7\7\3\1\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\77\37\17\7"
"\7\3\1\0\377\377\377\177\177\77\37\17\17\7\3\1\1\0\0\0\0\0\0\0\0\0\0\0\377\377\377\177"
"\177\77\37\17\17\7\3\1\1\0\0\0\0\0\0\0\0\0\0\0\3\1\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\376\374\374\370\360\340\300\200\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\200\340\360\374"
"\377\377\377\377\377\377\377\377\377\376\370\300\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\300\340\360\374\376\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\37\0\0\0\0"
"\0\0\4\370\360\360\340\300\200\0\0\0\0\0\0\0\0\0\0\0\370\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\177\77\37\7\3\0\0\0\0\0\200\300\360\374\377\377\377\377\377\377\377\376\370\340\0\0\0"
"\0\0\0\0\3\37\177\377\377\377\377\377\377\377\377\377\77\17\7\1\0\0\0\0\0\200\300\360\370\374\376\377"
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\1\3\7\17\37\77\77\177\200"
"\0\0\0\0\0\0\340\374\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\17\1\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\200\300\340\340\360\360\370|<>>>~\377\377\377\377\377\377\377\177"
"\77\36\36\36\36<|\370\370\360\360\340\340\200\0\0\0\0\0\0\0\0\300\360\374\376\377\377\377\377\377\377"
"\377\360\340\300\300\300\300\340\360\377\377\377\377\377\377\370\360\340\340\340\340\360\370\377\377\377\377\377\377\377\377\377"
"\374\360\340\200\360\377\377\377\377\377\207\3\1\1\1\1\3\207\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\207\3\1\1\1\1\3\207\377\377\377\377\377\17\377\377\377\377\377\377\377\376~>>"
"\77\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376\376\376\376\377\377\377"
"\177\77\37\7\0\0\3\17\77\177\377\377\360\340\300\300\300\300\340\360\377\377\377\377\377\377\377\377\377\377\77\17"
"\17\7\7\7\7\7\7\7\7\7\3\3\3\3\1\0\0\0\0\0\0\0\0\0\0\0\0\1\3\7\17\37"
"\37\77\77\177\177\177\377\377\377\377\377\377\377\377\377~\30\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\370\374\376\377\377\377\377\377\377\376\374\360\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\360\360\360\360\360\360\360\360\360\360\360\360"
"\360\363\377\377\377\377\377\377\377\377\363\360\360\360\360\360\360\360\360\360\360\360\360\360\0\0\0\0\0\0\0\0"
"\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\374\374\376\376\377\377\377\377"
"\377\376\374\360\377\377\377\377\377\377\377\377\377\377\377\377\177\77\37\17\17\17\17\17\17\37\77\177\377\377\377\377"
"\377\377\377\377\377\377\377\377\3\3\7\7\17\17\17\17\7\7\3\0\377\377\377\377\377\377\377\377\377\377\377\377"
"\360\300\0\0\0\0\0\0\0\0\300\360\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\200\300\340\360\360\370\374\374\376\376\7\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\360\374\376\377\377\377\377\377\377\377"
"\377\377\377\340\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\374\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\374\360\300\200\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\17\177\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\376\374\370\360\360\340\340\300\300\300\200\200\200\200\0\0\0\0\0\0\200\200"
"\200\200\0\0\0\0\1\7\37\77\177\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\377\377\377\177\77\37\7\1\0\0\0\0\0\0\0\0\0\0\1\3\3\7"
"\17\17\37\37\37\77\77\77\77\177\177\177\177\177\177\77\77\77\77\37\37\37\17\17\7\3\3\1\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\200\200\300\340\360\360\370\374\374\376\377~\34\10\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\200\300\300\340\360\360\370\374\376\376\377\377\377\377\377\377\177\77\17\7\3"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\6\17\17\37\77\177\377"
"\377\377\377\377\377\377\77\37\7\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\300\370\374\376"
"\376\377\377\377\377\377\377\376\376\374\370\340\0\0\0\0\3\17\7\3\1\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\200\360\377\377\377\377\377\377\377\377\377\377\377\377\377\377\177\17\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`px\374\376\377\377\377\377\377\377"
"\177\177\177\77\77\37\17\7\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\200\300\300\200\0\0\0\0\0\0\0\0\0\14\36\77\77\36\14\0\0"
"\0\0\0\0\0\0\0\200\300\300\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\7\17\17\7\3"
"\0\200\300\340\360\360\370\370\370\374\374\374\374\370\370\370\360\360\340\300\200\0\3\7\17\17\7\3\0\0\0\0"
"\0\0\0\0\300\340\360\360\340\300\0\0\0\0\340\374\377\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177"
"\177\177\177\177\177\377\374\340\0\0\0\0\300\340\360\360\340\300\0\0\0\1\3\3\1\0\0\0\0\0\1\17"
"\77\177\370\340\300\200\200\0\0\0\0\0\0\0\0\200\200\300\340\370\177\77\17\1\0\0\0\0\0\1\3\3"
"\1\0\0\0\0\0\0\0\0\0\60x\374\374x\60\0\0\0\1\3\3\7\7\7\16\16\16\16\7\7\7"
"\3\3\1\0\0\0\60x\374\374x\60\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\14\36\77\77\36\14\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0";
class FourLineDisplayUsermod : public Usermod {
@ -256,16 +130,13 @@ class FourLineDisplayUsermod : public Usermod {
bool flip = false; // flip display 180°
uint8_t contrast = 10; // screen contrast
uint8_t lineHeight = 1; // 1 row or 2 rows
uint32_t refreshRate = USER_LOOP_REFRESH_RATE_MS; // in ms
uint16_t refreshRate = USER_LOOP_REFRESH_RATE_MS; // in ms
uint32_t screenTimeout = SCREEN_TIMEOUT_MS; // in ms
bool sleepMode = true; // allow screen sleep?
bool clockMode = false; // display clock
bool showSeconds = true; // display clock with seconds
bool enabled = true;
// needRedraw marks if redraw is required to prevent often redrawing.
bool needRedraw = true;
// Next variables hold the previous known values to determine if redraw is
// required.
String knownSsid = "";
@ -387,7 +258,7 @@ class FourLineDisplayUsermod : public Usermod {
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...");
overlay(PSTR("Loading..."),3000,0);
overlayLogo(3500);
}
// gets called every time WiFi is (re-)connected. Initialize own network
@ -405,7 +276,7 @@ class FourLineDisplayUsermod : public Usermod {
if (!enabled || strip.isUpdating()) return;
unsigned long now = millis();
if (now < nextUpdate) return;
nextUpdate = now + ((clockMode && showSeconds) ? 1000 : refreshRate);
nextUpdate = now + ((displayTurnedOff && clockMode && showSeconds) ? 1000 : refreshRate);
redraw(false);
}
@ -437,6 +308,11 @@ class FourLineDisplayUsermod : public Usermod {
if (!ignoreLH && lineHeight==2) u8x8->draw1x2Glyph(col, row, glyph);
else u8x8->drawGlyph(col, row, glyph);
}
void draw2x2Glyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font) {
if (type == NONE || !enabled) return;
u8x8->setFont(font);
u8x8->draw2x2Glyph(col, row, glyph);
}
uint8_t getCols() {
if (type==NONE || !enabled) return 0;
return u8x8->getCols();
@ -466,6 +342,7 @@ class FourLineDisplayUsermod : public Usermod {
* or if forceRedraw).
*/
void redraw(bool forceRedraw) {
bool needRedraw = false;
unsigned long now = millis();
if (type == NONE || !enabled) return;
@ -483,49 +360,49 @@ class FourLineDisplayUsermod : public Usermod {
// Check if values which are shown on display changed from the last time.
if (forceRedraw) {
knownHour = 99;
needRedraw = true;
clear();
} else if ((bri == 0 && powerON) || (bri > 0 && !powerON)) { //trigger power icon
powerON = !powerON;
drawStatusIcons();
lastRedraw = millis();
return;
} else if (knownnightlight != nightlightActive) { //trigger moon icon
knownnightlight = nightlightActive;
drawStatusIcons();
if (knownnightlight) overlay(PSTR(" Timer On"), 3000, 6);
lastRedraw = millis();
if (knownnightlight) {
String timer = PSTR("Timer On");
center(timer,LINE_BUFFER_SIZE-1);
overlay(timer.c_str(), 2500, 6);
//lastRedraw = millis();
}
return;
} else if (wificonnected != interfacesInited) { //trigger wifi icon
wificonnected = interfacesInited;
drawStatusIcons();
lastRedraw = millis();
return;
} 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);
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) { knownBrightness = bri; }
else if (!displayTurnedOff) { 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) {
// Nothing to change.
// Turn off display after 1 minutes with no change.
if (sleepMode && !displayTurnedOff && (now - lastRedraw > screenTimeout)) {
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();
@ -536,14 +413,10 @@ class FourLineDisplayUsermod : public Usermod {
return;
}
needRedraw = false;
lastRedraw = millis();
lastRedraw = now;
if (displayTurnedOff) {
// Turn the display back on
sleepOrClock(false);
clear();
}
wakeDisplay();
// Update last known values.
knownBrightness = bri;
@ -579,7 +452,7 @@ class FourLineDisplayUsermod : public Usermod {
char lineBuffer[4];
sprintf_P(lineBuffer, PSTR("%-3d"), brightness100);
drawString(1, lineHeight, lineBuffer);
lastRedraw = millis();
//lastRedraw = millis();
}
}
@ -590,7 +463,7 @@ class FourLineDisplayUsermod : public Usermod {
char lineBuffer[4];
sprintf_P(lineBuffer, PSTR("%-3d"), fxspeed100);
drawString(5, lineHeight, lineBuffer);
lastRedraw = millis();
//lastRedraw = millis();
}
}
@ -601,30 +474,34 @@ class FourLineDisplayUsermod : public Usermod {
char lineBuffer[4];
sprintf_P(lineBuffer, PSTR("%-3d"), fxintensity100);
drawString(9, lineHeight, lineBuffer);
lastRedraw = millis();
//lastRedraw = millis();
}
}
void draw2x2GlyphIcons() {
if (lineHeight == 2) {
drawGlyph(1, 0, 1, u8x8_font_benji_custom_icons_2x2, true);//brightness icon
drawGlyph(5, 0, 2, u8x8_font_benji_custom_icons_2x2, true);//speed icon
drawGlyph(9, 0, 3, u8x8_font_benji_custom_icons_2x2, true);//intensity icon
drawGlyph(14, 2*lineHeight, 4, u8x8_font_benji_custom_icons_2x2, true);//palette icon
drawGlyph(14, 3*lineHeight, 5, u8x8_font_benji_custom_icons_2x2, true);//effect icon
drawGlyph( 1, 0, 1, u8x8_4LineDisplay_WLED_icons_2x2, true); //brightness icon
drawGlyph( 5, 0, 2, u8x8_4LineDisplay_WLED_icons_2x2, true); //speed icon
drawGlyph( 9, 0, 3, u8x8_4LineDisplay_WLED_icons_2x2, true); //intensity icon
drawGlyph(14, 2*lineHeight, 4, u8x8_4LineDisplay_WLED_icons_2x2, true); //palette icon
drawGlyph(14, 3*lineHeight, 5, u8x8_4LineDisplay_WLED_icons_2x2, true); //effect icon
} else {
drawGlyph(2, 0, 69, u8x8_font_open_iconic_weather_1x1); //brightness icon
drawGlyph(6, 0, 72, u8x8_font_open_iconic_play_1x1); //speed icon
drawGlyph(10, 0, 78, u8x8_font_open_iconic_thing_1x1); //intensity icon
drawGlyph(15, 2*lineHeight, 4, u8x8_font_benji_custom_icons_1x1); //palette icon
drawGlyph(15, 3*lineHeight, 70, u8x8_font_open_iconic_thing_1x1); //effect icon
drawGlyph( 2, 0, 1, u8x8_4LineDisplay_WLED_icons_1x1); //brightness icon
drawGlyph( 6, 0, 2, u8x8_4LineDisplay_WLED_icons_1x1); //speed icon
drawGlyph(10, 0, 3, u8x8_4LineDisplay_WLED_icons_1x1); //intensity icon
if (markLineNum!=2) drawGlyph(0, 2, 4, u8x8_4LineDisplay_WLED_icons_1x1); //palette icon
if (markLineNum!=3) drawGlyph(0, 3, 5, u8x8_4LineDisplay_WLED_icons_1x1); //effect icon
}
}
void drawStatusIcons() {
drawGlyph(14, 0, 80 + (wificonnected?0:1), u8x8_font_open_iconic_embedded_1x1, true); // wifi icon
drawGlyph(15, 0, 78 + (bri > 0 ? 0 : 3), u8x8_font_open_iconic_embedded_1x1, true); // power icon
drawGlyph(13, 0, 66 + (nightlightActive?0:4), u8x8_font_open_iconic_weather_1x1, true); // moon icon for nighlight mode
uint8_t col = 15;
uint8_t row = 0;
drawGlyph(col, row, (wificonnected ? 20 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // wifi icon
if (lineHeight==2) { col--; } else { row++; }
drawGlyph(col, row, (bri > 0 ? 9 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // power icon
if (lineHeight==2) { col--; } else { row++; }
drawGlyph(col, row, (nightlightActive ? 6 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // moon icon for nighlight mode
}
/**
@ -639,19 +516,18 @@ class FourLineDisplayUsermod : public Usermod {
//Draw the arrow for the current setting beiong changed
void drawArrow() {
if (markColNum != 255 && markLineNum !=255) drawGlyph(markColNum, markLineNum*lineHeight, 69, u8x8_font_open_iconic_play_1x1);
if (markColNum != 255 && markLineNum !=255) drawGlyph(markColNum, markLineNum*lineHeight, 21, u8x8_4LineDisplay_WLED_icons_1x1);
}
//Display the current effect or palette (desiredEntry)
// on the appropriate line (row).
void showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row) {
char lineBuffer[LINE_BUFFER_SIZE];
char lineBuffer[MAX_JSON_CHARS];
knownMode = effectCurrent;
knownPalette = effectPalette;
if (overlayUntil == 0) {
// Find the mode name in JSON
uint8_t printedChars = extractModeName(inputEffPal, qstring, lineBuffer, LINE_BUFFER_SIZE-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];
@ -707,48 +583,119 @@ 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);
//lastRedraw = millis();
return true;
}
return false;
}
/**
* Allows you to show one line and a glyph as overlay for a
* period of time.
* Allows you to show one line and a glyph as overlay for a period of time.
* Clears the screen and prints.
* Used in Rotary Encoder usermod.
*/
void overlay(const char* line1, long showHowLong, byte glyphType) {
if (displayTurnedOff) {
// Turn the display back on
sleepOrClock(false);
if (!wakeDisplay()) clear();
// Print the overlay
if (glyphType>0 && glyphType<255) {
if (lineHeight == 2) drawGlyph(5, 0, glyphType, u8x8_4LineDisplay_WLED_icons_6x6, true);
else drawGlyph(7, lineHeight, glyphType, u8x8_4LineDisplay_WLED_icons_2x2, true);
}
if (line1) {
String buf = line1;
center(buf, getCols());
drawString(0, (glyphType<255?3:0)*lineHeight, buf.c_str());
}
overlayUntil = millis() + showHowLong;
}
/**
* Allows you to show Akemi WLED logo overlay for a period of time.
* Clears the screen and prints.
*/
void overlayLogo(long showHowLong) {
// Turn the display back on
if (!wakeDisplay()) clear();
// 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);
if (lineHeight == 2) {
//add a bit of randomness
switch (millis()%3) {
case 0:
//WLED
draw2x2Glyph( 0, 2, 1, u8x8_wled_logo_2x2);
draw2x2Glyph( 4, 2, 2, u8x8_wled_logo_2x2);
draw2x2Glyph( 8, 2, 3, u8x8_wled_logo_2x2);
draw2x2Glyph(12, 2, 4, u8x8_wled_logo_2x2);
break;
case 1:
//WLED Akemi
drawGlyph( 2, 2, 1, u8x8_wled_logo_akemi_4x4, true);
drawGlyph( 6, 2, 2, u8x8_wled_logo_akemi_4x4, true);
drawGlyph(10, 2, 3, u8x8_wled_logo_akemi_4x4, true);
break;
case 2:
//Akemi
//draw2x2Glyph( 4, 0, 12, u8x8_4LineDisplay_WLED_icons_4x4);
drawGlyph( 5, 0, 12, u8x8_4LineDisplay_WLED_icons_6x6, true);
drawString(6, 6, "WLED");
break;
}
} else {
if (line1) drawString(0, 2*(lineHeight-1), line1);
switch (millis()%3) {
case 0:
//WLED
draw2x2Glyph( 0, 0, 1, u8x8_wled_logo_2x2);
draw2x2Glyph( 4, 0, 2, u8x8_wled_logo_2x2);
draw2x2Glyph( 8, 0, 3, u8x8_wled_logo_2x2);
draw2x2Glyph(12, 0, 4, u8x8_wled_logo_2x2);
break;
case 1:
//WLED Akemi
drawGlyph( 2, 0, 1, u8x8_wled_logo_akemi_4x4);
drawGlyph( 6, 0, 2, u8x8_wled_logo_akemi_4x4);
drawGlyph(10, 0, 3, u8x8_wled_logo_akemi_4x4);
break;
case 2:
//Akemi
//drawGlyph( 6, 0, 12, u8x8_4LineDisplay_WLED_icons_4x4);
draw2x2Glyph( 6, 0, 12, u8x8_4LineDisplay_WLED_icons_2x2);
break;
}
}
overlayUntil = millis() + showHowLong;
}
/**
* Allows you to show two lines as overlay for a period of time.
* Clears the screen and prints.
* Used in Auto Save usermod
*/
void overlay(const char* line1, const char* line2, long showHowLong) {
// Turn the display back on
if (!wakeDisplay()) clear();
// Print the overlay
if (line1) {
String buf = line1;
center(buf, getCols());
drawString(0, 1*lineHeight, buf.c_str());
}
if (line2) {
String buf = line2;
center(buf, getCols());
drawString(0, 2*lineHeight, buf.c_str());
}
overlayUntil = millis() + showHowLong;
}
void networkOverlay(const char* line1, long showHowLong) {
if (displayTurnedOff) {
// Turn the display back on
sleepOrClock(false);
}
if (!wakeDisplay()) clear();
// Print the overlay
clear();
// First row string
if (line1) {
String l1 = line1;
l1.trim();
@ -785,15 +732,15 @@ class FourLineDisplayUsermod : public Usermod {
*/
void sleepOrClock(bool enabled) {
if (enabled) {
displayTurnedOff = true;
if (clockMode) {
knownMinute = knownHour = 99;
showTime();
} else
setPowerSave(1);
displayTurnedOff = true;
} else {
setPowerSave(0);
displayTurnedOff = false;
setPowerSave(0);
}
}
@ -819,29 +766,27 @@ class FourLineDisplayUsermod : public Usermod {
if (AmPmHour > 11) { AmPmHour -= 12; isitAM = false; }
if (AmPmHour == 0) { AmPmHour = 12; }
}
drawStatusIcons(); //icons power, wifi, timer, etc
if (knownHour != hourCurrent) {
// only update date when hour changes
sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(month(localTime)), day(localTime));
draw2x2String(2, lineHeight==1 ? 0 : lineHeight, lineBuffer); // adjust for 8 line displays, draw month and day
}
sprintf_P(lineBuffer,PSTR("%2d:%02d"), (useAMPM ? AmPmHour : hourCurrent), minuteCurrent);
draw2x2String(2, lineHeight*2, lineBuffer); //draw hour, min. blink ":" depending on odd/even seconds
if (useAMPM) drawString(12, lineHeight*2, (isitAM ? "AM" : "PM"), true); //draw am/pm if using 12 time
drawStatusIcons(); //icons power, wifi, timer, etc
knownMinute = minuteCurrent;
knownHour = hourCurrent;
} else {
if (secondCurrent == lastSecond) return;
}
if (showSeconds && !useAMPM) {
if (showSeconds) {
lastSecond = secondCurrent;
draw2x2String(6, lineHeight*2, secondCurrent%2 ? " " : ":");
sprintf_P(lineBuffer, PSTR("%02d"), secondCurrent);
drawString(12 + (lineHeight%2), lineHeight*2+1, lineBuffer, true); // even with double sized rows print seconds in 1 line
drawString(12, lineHeight*2+1, lineBuffer, true); // even with double sized rows print seconds in 1 line
}
}
@ -896,7 +841,7 @@ class FourLineDisplayUsermod : public Usermod {
top["help4Type"] = F("1=SSD1306,2=SH1106,3=SSD1306_128x64,4=SSD1305,5=SSD1305_128x64,6=SSD1306_SPI,7=SSD1306_SPI_128x64"); // help for Settings page
top[FPSTR(_flip)] = (bool) flip;
top[FPSTR(_contrast)] = contrast;
top[FPSTR(_refreshRate)] = refreshRate/1000;
top[FPSTR(_refreshRate)] = refreshRate;
top[FPSTR(_screenTimeOut)] = screenTimeout/1000;
top[FPSTR(_sleepMode)] = (bool) sleepMode;
top[FPSTR(_clockMode)] = (bool) clockMode;
@ -930,7 +875,8 @@ class FourLineDisplayUsermod : public Usermod {
for (byte i=0; i<5; i++) newPin[i] = top["pin"][i] | ioPin[i];
flip = top[FPSTR(_flip)] | flip;
contrast = top[FPSTR(_contrast)] | contrast;
refreshRate = (top[FPSTR(_refreshRate)] | refreshRate/1000) * 1000;
refreshRate = top[FPSTR(_refreshRate)] | refreshRate;
refreshRate = min(5000, max(250, (int)refreshRate));
screenTimeout = (top[FPSTR(_screenTimeOut)] | screenTimeout/1000) * 1000;
sleepMode = top[FPSTR(_sleepMode)] | sleepMode;
clockMode = top[FPSTR(_clockMode)] | clockMode;
@ -964,14 +910,15 @@ class FourLineDisplayUsermod : public Usermod {
setup();
needsRedraw |= true;
}
/*if (!(type == SSD1306_SPI || type == SSD1306_SPI64))*/ u8x8->setBusClock(ioFrequency); // can be used for SPI too
u8x8->setBusClock(ioFrequency); // can be used for SPI too
setContrast(contrast);
setFlipMode(flip);
knownHour = 99;
if (needsRedraw && !wakeDisplay()) redraw(true);
else overlayLogo(3500);
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_showSeconds)].isNull();
return !top[FPSTR(_refreshRate)].isNull();
}
/*
@ -987,7 +934,7 @@ class FourLineDisplayUsermod : public Usermod {
const char FourLineDisplayUsermod::_name[] PROGMEM = "4LineDisplay";
const char FourLineDisplayUsermod::_enabled[] PROGMEM = "enabled";
const char FourLineDisplayUsermod::_contrast[] PROGMEM = "contrast";
const char FourLineDisplayUsermod::_refreshRate[] PROGMEM = "refreshRateSec";
const char FourLineDisplayUsermod::_refreshRate[] PROGMEM = "refreshRate-ms";
const char FourLineDisplayUsermod::_screenTimeOut[] PROGMEM = "screenTimeOutSec";
const char FourLineDisplayUsermod::_flip[] PROGMEM = "flip";
const char FourLineDisplayUsermod::_sleepMode[] PROGMEM = "sleepMode";

View File

@ -47,7 +47,7 @@
// The last UI state, remove color and saturation option if diplay not active(too many options)
#ifdef USERMOD_FOUR_LINE_DISPLAY
#define LAST_UI_STATE 6
#define LAST_UI_STATE 7
#else
#define LAST_UI_STATE 4
#endif
@ -115,17 +115,20 @@ static int re_qstringCmp(const void *ap, const void *bp) {
class RotaryEncoderUIUsermod : public Usermod {
private:
int fadeAmount = 5; // Amount to change every step (brightness)
unsigned long currentTime;
int8_t fadeAmount = 5; // Amount to change every step (brightness)
unsigned long loopTime;
unsigned long buttonHoldTime;
unsigned long buttonPressedTime = 0;
unsigned long buttonWaitTime = 0;
bool buttonPressedBefore = false;
bool buttonLongPressed = false;
int8_t pinA = ENCODER_DT_PIN; // DT from encoder
int8_t pinB = ENCODER_CLK_PIN; // CLK from encoder
int8_t pinC = ENCODER_SW_PIN; // SW from encoder
unsigned char select_state = 0; // 0: brightness, 1: effect, 2: effect speed
unsigned char button_state = HIGH;
unsigned char prev_button_state = HIGH;
bool networkShown = false;
unsigned char select_state = 0; // 0: brightness, 1: effect, 2: effect speed, ...
uint16_t currentHue1 = 16; // default boot color
byte currentSat1 = 255;
@ -157,6 +160,9 @@ private:
uint8_t knownMode = 0;
uint8_t knownPalette = 0;
byte presetHigh = 0;
byte presetLow = 0;
bool initDone = false;
bool enabled = true;
@ -166,6 +172,8 @@ private:
static const char _DT_pin[];
static const char _CLK_pin[];
static const char _SW_pin[];
static const char _presetHigh[];
static const char _presetLow[];
/**
* Sort the modes and palettes to the index arrays
@ -266,8 +274,7 @@ public:
pinMode(pinA, INPUT_PULLUP);
pinMode(pinB, INPUT_PULLUP);
pinMode(pinC, INPUT_PULLUP);
currentTime = millis();
loopTime = currentTime;
loopTime = millis();
if (!initDone) sortModesAndPalettes();
@ -307,7 +314,8 @@ public:
*/
void loop()
{
currentTime = millis(); // get the current elapsed time
if (!enabled || strip.isUpdating()) return;
unsigned long currentTime = millis(); // get the current elapsed time
// Initialize effectCurrentIndex and effectPaletteIndex to
// current state. We do it here as (at least) effectCurrent
@ -323,58 +331,48 @@ public:
if (currentTime >= (loopTime + 2)) // 2ms since last check of encoder = 500Hz
{
button_state = digitalRead(pinC);
if (prev_button_state != button_state)
{
if (button_state == HIGH && (millis()-buttonHoldTime < 3000))
{
prev_button_state = button_state;
loopTime = currentTime; // Updates loopTime
bool buttonPressed = !digitalRead(pinC); //0=pressed, 1=released
if (buttonPressed) {
if (!buttonPressedBefore) buttonPressedTime = currentTime;
buttonPressedBefore = true;
if (currentTime-buttonPressedTime > 3000) {
if (!buttonLongPressed) displayNetworkInfo(); //long press for network info
buttonLongPressed = true;
}
} else if (!buttonPressed && buttonPressedBefore) {
bool doublePress = buttonWaitTime;
buttonWaitTime = 0;
if (!buttonLongPressed) {
if (doublePress) {
toggleOnOff();
lampUdated();
} else {
buttonWaitTime = currentTime;
}
}
buttonLongPressed = false;
buttonPressedBefore = false;
}
if (buttonWaitTime && currentTime-buttonWaitTime>350 && !buttonPressedBefore) {
buttonWaitTime = 0;
char newState = select_state + 1;
if (newState > LAST_UI_STATE) newState = 0;
bool changedState = true;
if (newState > LAST_UI_STATE || (newState == 7 && presetHigh==0 && presetLow == 0)) newState = 0;
if (display != nullptr) {
switch (newState) {
case 0:
changedState = changeState(PSTR("Brightness"), 1, 0, 1);
break;
case 1:
changedState = changeState(PSTR("Speed"), 1, 4, 2);
break;
case 2:
changedState = changeState(PSTR("Intensity"), 1 ,8, 3);
break;
case 3:
changedState = changeState(PSTR("Color Palette"), 2, 0, 4);
break;
case 4:
changedState = changeState(PSTR("Effect"), 3, 0, 5);
break;
case 5:
changedState = changeState(PSTR("Main Color"), 255, 255, 7);
break;
case 6:
changedState = changeState(PSTR("Saturation"), 255, 255, 8);
break;
case 0: changedState = changeState(PSTR("Brightness"), 1, 0, 1); break; //1 = sun
case 1: changedState = changeState(PSTR("Speed"), 1, 4, 2); break; //2 = skip forward
case 2: changedState = changeState(PSTR("Intensity"), 1, 8, 3); break; //3 = fire
case 3: changedState = changeState(PSTR("Color Palette"), 2, 0, 4); break; //4 = custom palette
case 4: changedState = changeState(PSTR("Effect"), 3, 0, 5); break; //5 = puzzle piece
case 5: changedState = changeState(PSTR("Main Color"), 255, 255, 7); break; //7 = brush
case 6: changedState = changeState(PSTR("Saturation"), 255, 255, 8); break; //8 = contrast
case 7: changedState = changeState(PSTR("Preset"), 255, 255, 11); break; //11 = heart
}
}
if (changedState) {
select_state = newState;
}
}
else
{
prev_button_state = button_state;
networkShown = false;
if (!prev_button_state) buttonHoldTime = millis();
}
}
if (!prev_button_state && (millis()-buttonHoldTime > 3000) && !networkShown) {
displayNetworkInfo(); //long press for network info
loopTime = currentTime; // Updates loopTime
return;
if (changedState) select_state = newState;
}
Enc_A = digitalRead(pinA); // Read encoder pins
@ -384,65 +382,37 @@ public:
if (Enc_B == LOW) //changes to LOW so that then encoder registers a change at the very end of a pulse
{ // B is high so clockwise
switch(select_state) {
case 0:
changeBrightness(true);
break;
case 1:
changeEffectSpeed(true);
break;
case 2:
changeEffectIntensity(true);
break;
case 3:
changePalette(true);
break;
case 4:
changeEffect(true);
break;
case 5:
changeHue(true);
break;
case 6:
changeSat(true);
break;
case 0: changeBrightness(true); break;
case 1: changeEffectSpeed(true); break;
case 2: changeEffectIntensity(true); break;
case 3: changePalette(true); break;
case 4: changeEffect(true); break;
case 5: changeHue(true); break;
case 6: changeSat(true); break;
case 7: changePreset(true); break;
}
}
else if (Enc_B == HIGH)
{ // B is low so counter-clockwise
switch(select_state) {
case 0:
changeBrightness(false);
break;
case 1:
changeEffectSpeed(false);
break;
case 2:
changeEffectIntensity(false);
break;
case 3:
changePalette(false);
break;
case 4:
changeEffect(false);
break;
case 5:
changeHue(false);
break;
case 6:
changeSat(false);
break;
case 0: changeBrightness(false); break;
case 1: changeEffectSpeed(false); break;
case 2: changeEffectIntensity(false); break;
case 3: changePalette(false); break;
case 4: changeEffect(false); break;
case 5: changeHue(false); break;
case 6: changeSat(false); break;
case 7: changePreset(false); break;
}
}
}
Enc_A_prev = Enc_A; // Store value of A for next time
loopTime = currentTime; // Updates loopTime
}
}
void displayNetworkInfo() {
#ifdef USERMOD_FOUR_LINE_DISPLAY
display->networkOverlay(PSTR("NETWORK INFO"), 10000);
networkShown = true;
#endif
}
@ -468,12 +438,10 @@ public:
if (display != nullptr) {
if (display->wakeDisplay()) {
// Throw away wake up input
display->redraw(true);
return false;
}
String line = stateName;
//line.trim();
display->center(line, display->getCols());
display->overlay(line.c_str(), 750, glyph);
display->overlay(stateName, 750, glyph);
display->setMarkLine(markedLine, markedCol);
}
#endif
@ -491,12 +459,12 @@ public:
void changeBrightness(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
// Throw away wake up input
return;
}
#endif
if (increase) bri = (bri + fadeAmount <= 255) ? (bri + fadeAmount) : 255;
else bri = (bri - fadeAmount >= 0) ? (bri - fadeAmount) : 0;
bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 0);
lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY
display->updateBrightness();
@ -507,6 +475,7 @@ public:
void changeEffect(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
// Throw away wake up input
return;
}
@ -523,6 +492,7 @@ public:
void changeEffectSpeed(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
// Throw away wake up input
return;
}
@ -538,6 +508,7 @@ public:
void changeEffectIntensity(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
// Throw away wake up input
return;
}
@ -553,6 +524,7 @@ public:
void changePalette(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
// Throw away wake up input
return;
}
@ -569,13 +541,13 @@ public:
void changeHue(bool increase){
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
// Throw away wake up input
return;
}
#endif
if (increase) { if (currentHue1<256) currentHue1 += 4; else currentHue1 = 0; }
else { if (currentHue1>3) currentHue1 -= 4; else currentHue1 = 256; }
colorHStoRGB(currentHue1*255, currentSat1, col);
currentHue1 = max(min((increase ? currentHue1+fadeAmount : currentHue1-fadeAmount), 255), 0);
colorHStoRGB(currentHue1*256, currentSat1, col);
strip.applyToAllSelected = true;
strip.setColor(0, colorFromRgbw(col));
lampUdated();
@ -587,6 +559,7 @@ public:
void changeSat(bool increase){
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
// Throw away wake up input
return;
}
@ -601,6 +574,29 @@ public:
#endif
}
void changePreset(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
// Throw away wake up input
return;
}
#endif
if (presetHigh && presetLow && presetHigh > presetLow) {
String apireq = F("win&PL=~");
if (!increase) apireq += '-';
apireq += F("&P1=");
apireq += presetLow;
apireq += F("&P2=");
apireq += presetHigh;
handleSet(nullptr, apireq, false);
lampUdated();
}
#ifdef USERMOD_FOUR_LINE_DISPLAY
display->updateRedrawTime();
#endif
}
/*
* 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.
@ -652,6 +648,8 @@ public:
top[FPSTR(_DT_pin)] = pinA;
top[FPSTR(_CLK_pin)] = pinB;
top[FPSTR(_SW_pin)] = pinC;
top[FPSTR(_presetLow)] = presetLow;
top[FPSTR(_presetHigh)] = presetHigh;
DEBUG_PRINTLN(F("Rotary Encoder config saved."));
}
@ -672,6 +670,11 @@ public:
int8_t newCLKpin = top[FPSTR(_CLK_pin)] | pinB;
int8_t newSWpin = top[FPSTR(_SW_pin)] | pinC;
presetHigh = top[FPSTR(_presetHigh)] | presetHigh;
presetLow = top[FPSTR(_presetLow)] | presetLow;
presetHigh = MIN(250,MAX(0,presetHigh));
presetLow = MIN(250,MAX(0,presetLow));
enabled = top[FPSTR(_enabled)] | enabled;
DEBUG_PRINT(FPSTR(_name));
@ -699,7 +702,7 @@ public:
}
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_enabled)].isNull();
return !top[FPSTR(_presetHigh)].isNull();
}
/*
@ -718,3 +721,5 @@ const char RotaryEncoderUIUsermod::_enabled[] PROGMEM = "enabled";
const char RotaryEncoderUIUsermod::_DT_pin[] PROGMEM = "DT-pin";
const char RotaryEncoderUIUsermod::_CLK_pin[] PROGMEM = "CLK-pin";
const char RotaryEncoderUIUsermod::_SW_pin[] PROGMEM = "SW-pin";
const char RotaryEncoderUIUsermod::_presetHigh[] PROGMEM = "preset-high";
const char RotaryEncoderUIUsermod::_presetLow[] PROGMEM = "preset-low";

View File

@ -356,7 +356,6 @@ uint8_t WS2812FX::getTargetFps() {
void WS2812FX::setTargetFps(uint8_t fps) {
if (fps > 0 && fps <= 120) _targetFps = fps;
//_targetFps = min(max((int)fps,1),120);
_frametime = 1000 / _targetFps;
}

View File

@ -210,6 +210,8 @@ void handleAnalog(uint8_t b)
void handleButton()
{
static unsigned long lastRead = 0UL;
bool analog = false;
unsigned long now = millis();
for (uint8_t b=0; b<WLED_MAX_BUTTONS; b++) {
#ifdef ESP8266
@ -220,8 +222,8 @@ void handleButton()
if (usermods.handleButton(b)) continue; // did usermod handle buttons
if ((buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) && millis() - lastRead > 250) { // button is not a button but a potentiometer
if (b+1 == WLED_MAX_BUTTONS) lastRead = millis();
if ((buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) && now - lastRead > 250) { // button is not a button but a potentiometer
analog = true;
handleAnalog(b); continue;
}
@ -233,21 +235,21 @@ void handleButton()
//momentary button logic
if (isButtonPressed(b)) { //pressed
if (!buttonPressedBefore[b]) buttonPressedTime[b] = millis();
if (!buttonPressedBefore[b]) buttonPressedTime[b] = now;
buttonPressedBefore[b] = true;
if (millis() - buttonPressedTime[b] > WLED_LONG_PRESS) { //long press
if (now - buttonPressedTime[b] > WLED_LONG_PRESS) { //long press
if (!buttonLongPressed[b]) longPressAction(b);
else if (b) { //repeatable action (~3 times per s) on button > 0
longPressAction(b);
buttonPressedTime[b] = millis() - WLED_LONG_REPEATED_ACTION; //300ms
buttonPressedTime[b] = now - WLED_LONG_REPEATED_ACTION; //300ms
}
buttonLongPressed[b] = true;
}
} else if (!isButtonPressed(b) && buttonPressedBefore[b]) { //released
long dur = millis() - buttonPressedTime[b];
long dur = now - buttonPressedTime[b];
if (dur < WLED_DEBOUNCE_THRESHOLD) {buttonPressedBefore[b] = false; continue;} //too short "press", debounce
bool doublePress = buttonWaitTime[b]; //did we have a short press before?
buttonWaitTime[b] = 0;
@ -261,7 +263,7 @@ void handleButton()
if (doublePress) {
doublePressAction(b);
} else {
buttonWaitTime[b] = millis();
buttonWaitTime[b] = now;
}
}
}
@ -270,11 +272,12 @@ void handleButton()
}
//if 350ms elapsed since last short press release it is a short press
if (buttonWaitTime[b] && millis() - buttonWaitTime[b] > WLED_DOUBLE_PRESS && !buttonPressedBefore[b]) {
if (buttonWaitTime[b] && now - buttonWaitTime[b] > WLED_DOUBLE_PRESS && !buttonPressedBefore[b]) {
buttonWaitTime[b] = 0;
shortPressAction(b);
}
}
if (analog) lastRead = now;
}
void handleIO()

View File

@ -89,7 +89,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(strip.cctBlending, hw_led[F("cb")]);
Bus::setCCTBlend(strip.cctBlending);
strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS
//strip.setTargetFps(hw_led["fps"].isNull() ? WLED_FPS : hw_led["fps"].as<byte>());
JsonArray ins = hw_led["ins"];
@ -382,8 +381,15 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
if (act) timerWeekday[it]++;
}
if (it<8) {
CJSON(timerMonth[it], timer[F("mon")]);
CJSON(timerDay[it], timer[F("day")]);
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++;
}
@ -745,8 +751,12 @@ void serializeConfig() {
timers_ins0["macro"] = timerMacro[i];
timers_ins0[F("dow")] = timerWeekday[i] >> 1;
if (i<8) {
timers_ins0[F("mon")] = timerMonth[i];
timers_ins0[F("day")] = timerDay[i];
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];
}
}

View File

@ -537,7 +537,6 @@ img {
#gwrap .sliderdisplay { background: linear-gradient(90deg, #000 0%, #0f0); }
#bwrap .sliderdisplay { background: linear-gradient(90deg, #000 0%, #00f); }
#wwrap .sliderdisplay { background: linear-gradient(90deg, #000 0%, #fff); }
/*#vwrap .sliderdisplay { background: linear-gradient(90deg, #000 0%, #fff); }*/
#kwrap .sliderdisplay { background: linear-gradient(90deg, #ff8f1f 0%, #fff 50%, #cbdbff); }
#wbal .sliderdisplay { background: linear-gradient(90deg, #ff8f1f 0%, #fff 50%, #cbdbff); }

View File

@ -15,7 +15,7 @@ var csel = 0;
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;
var pcMode = false, pcModeA = false, lastw = 0, wW;
var tr = 7;
var d = document;
var palettesData;
@ -198,15 +198,15 @@ function onLoad()
fetch((loc?`http://${locip}`:'.') + "/holidays.json", { // may be loaded from external source
method: 'get'
})
.then(res => {
.then((res)=>{
//if (!res.ok) showErrorToast();
return res.json();
})
.then(json => {
.then((json)=>{
if (Array.isArray(json)) hol = json;
//TODO: do some parsing first
})
.catch(function (error) {
.catch((e)=>{
console.log("holidays.json does not contain array of holidays. Defaults loaded.");
})
.finally(()=>{
@ -421,9 +421,8 @@ function loadPresets(callback = null)
pJson = json;
populatePresets();
})
.catch(function (error) {
showToast(error, true);
console.log(error);
.catch((e)=>{
showToast(e, true);
presetError(false);
})
.finally(()=>{
@ -438,17 +437,17 @@ function loadPalettes(callback = null)
fetch(url, {
method: 'get'
})
.then(res => {
.then((res)=>{
if (!res.ok) showErrorToast();
return res.json();
})
.then(json => {
.then((json)=>{
clearErrorToast();
lJson = Object.entries(json);
populatePalettes();
})
.catch(function (error) {
showToast(error, true);
.catch((e)=>{
showToast(e, true);
presetError(false);
})
.finally(()=>{
@ -464,17 +463,17 @@ function loadFX(callback = null)
fetch(url, {
method: 'get'
})
.then(res => {
.then((res)=>{
if (!res.ok) showErrorToast();
return res.json();
})
.then(json => {
.then((json)=>{
clearErrorToast();
eJson = Object.entries(json);
populateEffects();
})
.catch(function (error) {
showToast(error, true);
.catch((e)=>{
showToast(e, true);
presetError(false);
})
.finally(()=>{
@ -490,20 +489,20 @@ function loadFXData(callback = null)
fetch(url, {
method: 'get'
})
.then(res => {
.then((res)=>{
if (!res.ok) showErrorToast();
return res.json();
})
.then(json => {
.then((json)=>{
clearErrorToast();
fxdata = json||[];
// add default value for Solid
fxdata.shift()
fxdata.unshift("@;!;");
})
.catch(function (error) {
.catch((e)=>{
fxdata = [];
showToast(error, true);
showToast(e, true);
})
.finally(()=>{
if (callback) callback();
@ -910,17 +909,16 @@ function loadNodes()
fetch(url, {
method: 'get'
})
.then(res => {
.then((res)=>{
if (!res.ok) showToast('Could not load Node list!', true);
return res.json();
})
.then(json => {
.then((json)=>{
clearErrorToast();
populateNodes(lastinfo, json);
})
.catch(function (error) {
showToast(error, true);
console.log(error);
.catch((e)=>{
showToast(e, true);
});
}
@ -1074,8 +1072,8 @@ function cmpP(a, b)
function makeWS() {
if (ws) return;
ws = new WebSocket('ws://'+(loc?locip:window.location.hostname)+'/ws');
ws.onmessage = function(event) {
var json = JSON.parse(event.data);
ws.onmessage = (e)=>{
var json = JSON.parse(e.data);
if (json.leds) return; //liveview packet
clearTimeout(jsonTimeout);
jsonTimeout = null;
@ -1095,11 +1093,12 @@ function makeWS() {
displayRover(i, s);
readState(s);
};
ws.onclose = function(event) {
ws.onclose = (e)=>{
gId('connind').style.backgroundColor = "var(--c-r)";
ws = null;
if (lastinfo.ws > -1) setTimeout(makeWS,500); //retry WS connection
}
ws.onopen = function(event) {
ws.onopen = (e)=>{
ws.send("{'v':true}");
reqsLegal = true;
clearErrorToast();
@ -1236,7 +1235,7 @@ function setSliderAndColorControl(idx, applyDef=false)
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);
slOnOff[i] = slOnOff[i].substring(0,dPos);
}
if (slOnOff.length>i && slOnOff[i]!="!") label.innerHTML = slOnOff[i];
else if (i==0) label.innerHTML = "Effect speed";
@ -1389,9 +1388,9 @@ function requestJson(command=null)
readState(s);
reqsLegal = true;
})
.catch(function (error) {
showToast(error, true);
console.log(error);
.catch((e)=>{
showToast(e, true);
console.log(e);
});
}
@ -2335,12 +2334,12 @@ function move(e)
}
function showNodes() {
gId('buttonNodes').style.display = (lastinfo.ndc > 0 && (w > 797 || (w > 539 && w < 720))) ? "block":"none";
gId('buttonNodes').style.display = (lastinfo.ndc > 0 && (wW > 797 || (wW > 539 && wW < 720))) ? "block":"none";
}
function size()
{
w = window.innerWidth;
wW = window.innerWidth;
showNodes();
var h = gId('top').clientHeight;
sCol('--th', h + "px");
@ -2357,8 +2356,8 @@ function togglePcMode(fromB = false)
localStorage.setItem('pcm', pcModeA);
pcMode = pcModeA;
}
if (w < 1250 && !pcMode) return;
if (!fromB && ((w < 1250 && lastw < 1250) || (w >= 1250 && lastw >= 1250))) return;
if (wW < 1250 && !pcMode) return;
if (!fromB && ((wW < 1250 && lastw < 1250) || (wW >= 1250 && lastw >= 1250))) return;
openTab(0, true);
if (w < 1250) {pcMode = false;}
else if (pcModeA && !fromB) pcMode = pcModeA;
@ -2367,7 +2366,7 @@ function togglePcMode(fromB = false)
gId('bot').style.height = (pcMode && !cfg.comp.pcmbot) ? "0":"auto";
sCol('--bh', gId('bot').clientHeight + "px");
_C.style.width = (pcMode)?'100%':'400%';
lastw = w;
lastw = wW;
}
function mergeDeep(target, ...sources)

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>
</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>

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");
@ -23,11 +24,14 @@
{
return d.getElementById(s);
}
function gN(s) {
return d.getElementsByName(s)[0];
}
function expand(o,i)
{
o.classList.toggle("exp");
var t = gId("WD"+i);
t.style.display = t.style.display!=="none" ? "none" : "";
o.innerHTML = t.style.display==="none" ? "&#128467;" : "&#x2715;";
}
function Cs()
{
@ -50,73 +54,52 @@
}
function BTa()
{
var ih="<tr><th>En.</th><th>Hour</th><th>Minute</th><th>Preset</th><th></th></tr>";
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><button type="button" class="sml" onclick="expand(this,${i})">&gt;</button></td></tr>`;
ih+=`<tr><td colspan=5><div id="WD${i}" style="display:none;">`;
ih+=`<input name="X${i}" id="TMX${i}" type="checkbox" onchange="tglDOW(${i},this.checked)"><label for="TMX${i}">Use DOW</label>`;
ih+=`<table id="TT${i}" style="width:100%;"><tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr><tr>`
<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><div id="TD${i}">Date:
<select id="TMM${i}" name="M${i}" onchange="chkMon(this,${i})">
<option value="0">Every month</option>
<option value="13">Odd month</option>
<option value="14">Even month</option>
<option value="1">Jan</option>
<option value="2">Feb</option>
<option value="3">Mar</option>
<option value="4">Apr</option>
<option value="5">May</option>
<option value="6">Jun</option>
<option value="7">Jul</option>
<option value="8">Aug</option>
<option value="9">Sep</option>
<option value="10">Oct</option>
<option value="11">Nov</option>
<option value="12">Dec</option>
</select><select id="TMD${i}" name="D${i}">`;
for (j=1;j<32;j++) ih+=`<option value="${j}">${j}</option>`;
ih+="</select></div><hr></div></td></tr>";
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><button type="button" class="sml" onclick="expand(this,8)">&gt;</button></td></tr><tr><td colspan=5>`;
ih+=`<div id="WD8"style="display:none;"><table style="width:100%;"><tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr><tr>`;
<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>Sunrise<input name="H9" value="255" type="hidden"></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><button type="button" class="sml" onclick="expand(this,9)">&gt;</button></td></tr><tr><td colspan=5>`;
ih+=`<div id="WD9" style="display:none;"><table style="width:100%;"><tr><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr><tr>`;
<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 tglDOW(i,b)
{
gId("TT"+i).style.display = b ? "table" : "none";
gId("TD"+i).style.display = !b ? "block" : "none";
}
function chkMon(o,i)
{
const m31 = [1,3,5,7,8,10,12,0,13,14];
if (!m31.includes(parseInt(o.value)) && parseInt(gId("TMD"+i).value)>30) gId("TMD"+i).value="30";
if (parseInt(o.value)===2 && parseInt(gId("TMD"+i).value)>28) gId("TMD"+i).value="28";
}
function FC()
{
for(j=0;j<8;j++)
for(i=0;i<10;i++)
{
for(i=0;i<10;i++) gId("W"+i+j).checked=gId("W"+i).value>>j&1;
tglDOW(j,gId('TMX'+j).checked);
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()
@ -154,7 +137,6 @@
//values injected by server while sending HTML
}
</script>
<!--script src="http://192.168.70.61/settings/settings.js?p=5"></script-->
<style>@import url("style.css");</style>
</head>
<body onload="S()">
@ -218,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>
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>

View File

@ -102,7 +102,7 @@ button:hover {
.label {
margin: 0;
padding: 8px 0 2px 0;
padding: 6px 0 0;
}
#namelabel {
@ -135,6 +135,7 @@ button:hover {
line-height: 1;
display: inline-block;
margin: -2px 0 4px 0;
text-shadow: -1px -1px 0 var(--c-3), 1px -1px 0 var(--c-3), -1px 1px 0 var(--c-3), 1px 1px 0 var(--c-3);
}
.huge {
@ -343,7 +344,7 @@ button:hover {
}
#info table .btn, #nodes table .btn {
margin: 0;
width: 230px;
width: 180px;
}
#info div, #nodes div {
width: 490px;
@ -378,14 +379,11 @@ img {
.sliderwrap {
height: 30px;
width: 225px;
width: 250px;
position: relative;
margin: 4px 0;
}
#rwrap .sliderwrap,
#gwrap .sliderwrap,
#bwrap .sliderwrap,
#wwrap .sliderwrap,
#wbal .sliderwrap {
#Colors .sliderwrap {
width: 260px;
margin: 10px 0 0;
}
@ -393,18 +391,14 @@ img {
.sliderdisplay {
content:'';
position: absolute;
top: 13px; left: 8px; right: 8px;
height: 4px;
top: 10px; left: 8px; right: 8px;
height: 8px;
background: var(--c-4);
border-radius: 16px;
pointer-events: none;
z-index: -1;
}
#rwrap .sliderdisplay,
#gwrap .sliderdisplay,
#bwrap .sliderdisplay,
#wwrap .sliderdisplay,
#wbal .sliderdisplay {
#Colors .sliderdisplay {
height: 28px;
top: 0; bottom: 0;
left: 0; right: 0;
@ -414,6 +408,7 @@ img {
#gwrap .sliderdisplay { background: linear-gradient(90deg, #000 0%, #0f0); }
#bwrap .sliderdisplay { background: linear-gradient(90deg, #000 0%, #00f); }
#wwrap .sliderdisplay { background: linear-gradient(90deg, #000 0%, #fff); }
#kwrap .sliderdisplay { background: linear-gradient(90deg, #ff8f1f 0%, #fff 50%, #cbdbff); }
#wbal .sliderdisplay { background: linear-gradient(90deg, #ff8f1f 0%, #fff 50%, #d4e0ff); }
.sliderbubble {
@ -448,11 +443,7 @@ input[type=range] {
background-color: transparent;
cursor: pointer;
}
#rwrap input[type=range],
#gwrap input[type=range],
#bwrap input[type=range],
#wwrap input[type=range],
#wbal input[type=range] {
#Colors input[type=range] {
width: 252px;
margin: 0;
}
@ -463,46 +454,55 @@ input[type=range]::-webkit-slider-runnable-track {
background: transparent;
}
input[type=range]::-webkit-slider-thumb {
height: 16px;
width: 16px;
border: 2px solid #000;
height: 20px;
width: 20px;
border-radius: 50%;
background: var(--c-f);
cursor: pointer;
-webkit-appearance: none;
margin-top: 7px;
margin-top: 4px;
}
input[type=range]::-moz-range-track {
width: 100%;
height: 30px;
background-color: rgba(0, 0, 0, 0);
background-color: var(--c-0);
}
input[type=range]::-moz-range-thumb {
border: 0px solid rgba(0, 0, 0, 0);
height: 16px;
width: 16px;
border: 2px solid var(--c-3);
height: 20px;
width: 20px;
border-radius: 50%;
background: var(--c-f);
transform: translateY(5px);
}
#rwrap input[type=range]::-webkit-slider-thumb,
#gwrap input[type=range]::-webkit-slider-thumb,
#bwrap input[type=range]::-webkit-slider-thumb,
#wwrap input[type=range]::-webkit-slider-thumb,
#wbal input[type=range]::-webkit-slider-thumb {
height: 18px;
width: 18px;
#Colors input[type=range]::-webkit-slider-thumb {
border: 2px solid #000;
margin-top: 5px;
}
#rwrap input[type=range]::-moz-range-thumb,
#gwrap input[type=range]::-moz-range-thumb,
#bwrap input[type=range]::-moz-range-thumb,
#wwrap input[type=range]::-moz-range-thumb,
#wbal input[type=range]::-moz-range-thumb {
#Colors input[type=range]::-moz-range-thumb {
border: 2px solid var(--c-1);
}
#wwrap, #wbal {
display: block;
#Presets .list {
max-height: 215px;
overflow-y: scroll;
overflow-x: hidden;
width: 280px;
margin: 0 0 0 20px;
-ms-overflow-style: none;
scrollbar-width: none; /* Firefox */
}
/* Hide scrollbar for Chrome, Safari and Opera */
#Presets .list::-webkit-scrollbar {
display: none;
}
#Segments .sliderwrap{
width: 225px;
}
#picker, #rgbwrap, #kwrap, #vwrap, #wwrap, #wbal {
display: none;
}
.hd {
@ -516,11 +516,6 @@ input[type=range]::-moz-range-thumb {
#picker {
width: 260px;
display: none;
}
#rgbwrap, #wwrap, #wbal {
display: none;
}
#picker, #csl, #segcont {
@ -553,6 +548,7 @@ input[type=range]::-moz-range-thumb {
width: 42px;
height: 42px;
margin: 4px;
padding: 0;
}
#fxBtn, #palBtn {
@ -632,6 +628,7 @@ input[type=number]::-webkit-outer-spin-button {
width: 44px;
height: 44px;
margin: 5px;
padding: 0;
}
.xxs-w {
@ -639,6 +636,7 @@ input[type=number]::-webkit-outer-spin-button {
margin: 2px;
width: 50px;
height: 50px;
padding: 0;
}
.qcs, .xxs {
@ -652,7 +650,6 @@ input[type=number]::-webkit-outer-spin-button {
.pwr {
color: var(--c-6);
/*transform: translate(2px, 3px);*/
cursor: pointer;
}
@ -668,8 +665,8 @@ input[type=number]::-webkit-outer-spin-button {
}
.schkl {
padding: 2px 2px 2px 16px;
margin: 0;
width: 24px;
top: -2px;
}
.check input, .radio input {
@ -808,20 +805,17 @@ input[type=number]::-webkit-outer-spin-button {
position: absolute;
width: 284px;
z-index: 1;
height: 266px;
height: 260px;
overflow-y: scroll;
overflow-x: hidden;
padding: 0 18px;
margin-top: 10px;
-ms-overflow-style: none;
scrollbar-width: none; /* Firefox */
}
.fndIcn { /* needed for magnifier SVG, can be removed when magnifier is in Wicons font */
width: 24px;
height: 24px;
stroke: var(--c-e);
stroke-width: 3px;
fill-opacity: 0;
margin-top: 1px;
/* Hide scrollbar for Chrome, Safari and Opera */
.dd-content::-webkit-scrollbar {
display: none;
}
.fnd {
@ -832,39 +826,44 @@ input[type=number]::-webkit-outer-spin-button {
margin: 0 auto;
}
div.fnd div {
.search-icon {
position: absolute;
top: 10px;
left: 13px;
pointer-events: none;
width: 24px;
height: 24px;
margin-top: -1px;
z-index: 1;
}
div.fnd span {
.clear-icon {
position: absolute;
display: none;
top: 10px;
right: 13px;
cursor: pointer;
margin-top: -1px;
z-index: 1;
}
input[type="text"].fnd {
input[type=text].fnd {
display: block;
width: 100%;
box-sizing: border-box;
padding: 8px 44px 8px 44px;
padding: 8px 48px 8px 48px;
margin: 5px auto 0;
text-align: left;
border-radius: 20px;
border-radius: 25px;
background-color: var(--c-2);
border: 1px solid var(--c-4);
}
input[type="text"].fnd:focus {
input[type=text].fnd:focus {
background-color: var(--c-4);
}
input[type="text"].fnd:not(:placeholder-shown), input[type="text"].fnd:hover {
input[type=text].fnd:not(:placeholder-shown), input[type=text].fnd:hover {
background-color: var(--c-3);
}

View File

@ -7,10 +7,47 @@
<meta content="yes" name="apple-mobile-web-app-capable">
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAGACGAAAAFgAAAIlQTkcNChoKAAAADUlIRFIAAAAQAAAAEAgGAAAAH/P/YQAAAE1JREFUOI1j/P//PwOxgNGeAUMxE9G6cQCKDWAhpADZ2f8PMjBS3QW08QK20KaZC2gfC9hCnqouoNgARgY7zMxAyNlUdQHlXiAlO2MDAD63EVqNHAe0AAAAAElFTkSuQmCC"/>
<title>WLED</title>
<script>function feedback(){}</script>
<script>
function feedback(){}
// instead of including [script src="iro.js"][/script] and [script src="rangetouch.js"][/script]
// (which would be inlined by nodeJS inliner during minimization and compression) we need to load them dynamically
// the following is needed to load iro.js and rangetouch.js as consecutive requests to allow ESP8266
// to keep up with requests (if requests happent too fast some may not get processed)
// it will also call onLoad() after last is loaded (it was removed from [body onload="onLoad()"]).
var h = document.getElementsByTagName('head')[0];
var l = document.createElement('script');
l.type = 'application/javascript';
l.src = 'iro.js';
l.addEventListener('load', (e) => {
// after iro is loaded initialize global variable
cpick = new iro.ColorPicker("#picker", {
width: 260,
wheelLightness: false,
wheelAngle: 270,
wheelDirection: "clockwise",
layout: [{
component: iro.ui.Wheel,
options: {}
}]
});
cpick.on("input:end", () => {setColor(1);});
cpick.on("color:change", () => {updatePSliders()});
var l = document.createElement('script');
l.type = 'application/javascript';
l.src = 'rangetouch.js';
l.addEventListener('load', (e) => {
// after rangetouch is loaded initialize global variable
ranges = RangeTouch.setup('input[type="range"]', {});
onLoad(); // start processing UI
});
//h.appendChild(l); // if this fires too quickly for ESP8266 use next line
setTimeout(function(){h.appendChild(l)},50);
});
setTimeout(function(){h.appendChild(l)},50);
</script>
<link rel="stylesheet" href="simple.css">
</head>
<body onload="onLoad()">
<body>
<div id="cv" class="overlay">Loading WLED UI...</div>
<noscript><div class="overlay" style="opacity:1;">Sorry, WLED UI needs JavaScript!</div></noscript>
@ -24,7 +61,7 @@
<button id="buttonNodes" onclick="toggleNodes()"><i class="icons">&#xe22d;</i><p class="tab-label">Nodes</p></button></div>
<button onclick="window.location.href='/settings';"><i class="icons">&#xe0a2;</i><p class="tab-label">Config</p></button>
<button id="buttonCP" onclick="tglCP()"><i class="icons">&#xe2b3;</i><p class="tab-label">Expand</p></button>
<button id="buttonBri" onclick="tglBri()"><i class="icons">&#xe2a6;</i><p class="tab-label">Brightness</p></button>
<!--button id="buttonBri" onclick="tglBri()"><i class="icons">&#xe2a6;</i><p class="tab-label">Brightness</p></button-->
</div>
<div id="briwrap">
<p class="label hd">Global brightness</p>
@ -42,6 +79,13 @@
<div class ="container">
<div class="tabcontent">
<div id="QuickLoad" class="center">
<p class="label h">Quick Load</p>
<div id="pql"></div>
</div>
<div id="QCS" class="center">
<p class="label h">Solid color</p>
<div id="qcs-w" class="center">
<div class="qcs" onclick="pC('#ff0000');" title="Red" style="background-color:#ff0000;"></div>
<div class="qcs" onclick="pC('#ffa000');" title="Orange" style="background-color:#ffa000;"></div>
@ -55,15 +99,27 @@
<div class="qcs" onclick="pC('#08ff00');" title="Green" style="background-color:#08ff00;"></div>
<div class="qcs" onclick="pC('rnd');" title="Random" style="background:linear-gradient(to right, red, orange, yellow, green, blue, purple);transform: translateY(-11px);">R</div>
</div>
<div id="csl" class="center" style="display: none;">
<button class="xxs btn" onclick="selectSlot(0);">1</button>
<button class="xxs btn" onclick="selectSlot(1);">2</button>
<button class="xxs btn" onclick="selectSlot(2);">3</button>
</div>
<div id="picker" class="center"></div>
<div id="Colors" class="center">
<div id="vwrap">
<!--p class="label h">Value</p-->
<div class="sliderwrap il">
<input id="sliderV" class="noslide" oninput="fromV()" onchange="setColor(0)" max="100" min="0" type="range" value="100" step="any" />
<div class="sliderdisplay"></div>
</div><br>
</div>
<div id="kwrap">
<!--p class="label h">Temperature</p-->
<div class="sliderwrap il">
<input id="sliderK" class="noslide" oninput="fromK()" onchange="setColor(0)" max="10091" min="1900" type="range" value="6550" />
<div class="sliderdisplay"></div>
</div>
</div>
<div id="rgbwrap" class="center">
<p class="label h">RGB channels</p>
<div id="rwrap" class="il">
<div class="sliderwrap il">
<input id="sliderR" onchange="fromRgb()" max="255" min="0" type="range" value="128" />
@ -83,37 +139,41 @@
</div>
</div><br>
</div>
<div id="wwrap" class="center">
<p class="label hd">White channel</p>
<p class="label h">White channel</p>
<div class="sliderwrap il">
<input id="sliderW" onchange="setColor(0)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
</div>
<div id="wbal">
<p class="labels hd">White balance</p>
<p class="label h">White balance</p>
<div class="sliderwrap il">
<input id="sliderA" onchange="setBalance(this.value)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
</div>
</div>
<div id="Slots" class="center">
<p class="label h">Color slots</p>
<div id="csl" class="center" style="display: none;">
<button class="xxs btn" onclick="selectSlot(0);">1</button>
<button class="xxs btn" onclick="selectSlot(1);">2</button>
<button class="xxs btn" onclick="selectSlot(2);">3</button>
</div>
</div>
<div id="Segments" class="center">
<div id="segcont"></div>
</div>
<div id="QuickLoad" class="center">
<p class="label h">Quick Load</p>
<div id="pql"></div>
</div>
<div id="Presets" class="center" style="display:none;">
<div id="Presets" class="center">
<p class="label h">Presets</p>
<div class="fnd">
<input type="text" class="fnd" placeholder="Search" oninput="search(this,'pcont')" onfocus="search(this)" />
<span onclick="clean(this)" class="icons">&#xe38f;</span>
<div class="icons"><svg xmlns='http://www.w3.org/2000/svg' class='fndIcn'><circle cx='8' cy='8' r='6' /><line x1='12' y1='12' x2='24' y2='12' transform='rotate(45,12,12)' /></svg></div>
<i class="icons clear-icon" onclick="clean(this);">&#xe38f;</i>
<i class="icons search-icon">&#xe0a1;</i>
</div>
<div id="pcont" class="list"></div>
</div>
@ -142,8 +202,8 @@
<div id="fxDropdown" class="dd-content">
<div class="fnd">
<input type="text" class="fnd" placeholder="Search" oninput="search(this,'fxlist')" onfocus="search(this)" />
<span onclick="clean(this)" class="icons">&#xe38f;</span>
<div class="icons"><svg xmlns='http://www.w3.org/2000/svg' class='fndIcn'><circle cx='8' cy='8' r='6' /><line x1='12' y1='12' x2='24' y2='12' transform='rotate(45,12,12)' /></svg></div>
<i class="icons clear-icon" onclick="clean(this);">&#xe38f;</i>
<i class="icons search-icon">&#xe0a1;</i>
</div>
<div id="fxlist" class="list">
<div class="lstI" data-id="0" onClick="setX(0)"><a href="#0" onClick="setX(0)">Solid</a></div>
@ -152,8 +212,8 @@
<div id="palDropdown" class="dd-content">
<div class="fnd">
<input type="text" class="fnd" placeholder="Search" oninput="search(this,'pallist')" onfocus="search(this)" />
<span onclick="clean(this)" class="icons">&#xe38f;</span>
<div class="icons"><svg xmlns='http://www.w3.org/2000/svg' class='fndIcn'><circle cx='8' cy='8' r='6' /><line x1='12' y1='12' x2='24' y2='12' transform='rotate(45,12,12)' /></svg></div>
<i class="icons clear-icon" onclick="clean(this);">&#xe38f;</i>
<i class="icons search-icon">&#xe0a1;</i>
</div>
<div id="pallist" class="list">
<div class="lstI" data-id="0" onClick="setPalette(0)"><a href="#0" onClick="setPalette(0)">Default</a><div class="lstIprev"></div></div>

View File

@ -1,7 +1,7 @@
//page js
var loc = false, locip;
var noNewSegs = false;
var isOn = false, isInfo = false, isNodes = false, isRgbw = false;
var isOn = false, isInfo = false, isNodes = false, isRgbw = false, cct = false;
var whites = [0,0,0];
var selColors;
var powered = [true];
@ -11,16 +11,15 @@ var csel = 0;
var currentPreset = -1;
var lastUpdate = 0;
var segCount = 0, ledCount = 0, lowestUnused = 0, maxSeg = 0, lSeg = 0;
var lastw = 0;
var tr = 7;
var d = document;
const ranges = RangeTouch.setup('input[type="range"]', {});
var palettesData;
var fxdata = [];
var pJson = {}, eJson = {}, lJson = {};
var pN = "", pI = 0, pNum = 0;
var pmt = 1, pmtLS = 0, pmtLast = 0;
var lastinfo = {};
var ws;
var ws, cpick, ranges;
var cfg = {
theme:{base:"dark", bg:{url:""}, alpha:{bg:0.6,tab:0.8}, color:{bg:""}},
comp :{colors:{picker: true, rgb: false, quick: true, hex: false}, labels:true, pcmbot:false, pid:true, seglen:false}
@ -33,30 +32,6 @@ var hol = [
[2024,2,31,2,"https://aircoookie.github.io/easter.png"]
];
var cpick = new iro.ColorPicker("#picker", {
width: 260,
wheelLightness: false,
wheelAngle: 90,
layout: [
{
component: iro.ui.Wheel,
options: {}
},
{
component: iro.ui.Slider,
options: { sliderType: 'value' }
}/*,
{
component: iro.ui.Slider,
options: {
sliderType: 'kelvin',
minTemperature: 2100,
maxTemperature: 10000
}
}*/
]
});
function handleVisibilityChange() {if (!d.hidden && new Date () - lastUpdate > 3000) requestJson();}
function sCol(na, col) {d.documentElement.style.setProperty(na, col);}
function gId(c) {return d.getElementById(c);}
@ -71,7 +46,8 @@ function applyCfg()
if (bg) sCol('--c-1', bg);
var ccfg = cfg.comp.colors;
//gId('picker').style.display = "none"; // ccfg.picker ? "block":"none";
gId('rgbwrap').style.display = ccfg.rgb ? "block":"none";
//gId('vwrap').style.display = "none"; // ccfg.picker ? "block":"none";
//gId('rgbwrap').style.display = ccfg.rgb ? "block":"none";
gId('qcs-w').style.display = ccfg.quick ? "block":"none";
var l = cfg.comp.labels; //l = false;
var e = d.querySelectorAll('.tab-label');
@ -200,14 +176,14 @@ async function onLoad()
fetch((loc?`http://${locip}`:'.') + "/holidays.json", {
method: 'get'
})
.then(res => {
.then((res)=>{
return res.json();
})
.then(json => {
.then((json)=>{
if (Array.isArray(json)) hol = json;
//TODO: do some parsing first
})
.catch(function(error){
.catch((e)=>{
console.log("holidays.json does not contain array of holidays. Defaults loaded.");
})
.finally(()=>{
@ -228,7 +204,7 @@ async function onLoad()
var cd = gId('csl').children;
for (var i = 0; i < cd.length; i++) cd[i].style.backgroundColor = "rgb(0, 0, 0)";
selectSlot(0);
cpick.on("input:end", function() {
cpick.on("input:end", ()=>{
setColor(1);
});
pmtLS = localStorage.getItem('wledPmt');
@ -237,13 +213,12 @@ async function onLoad()
loadPalettes(()=>{
loadPalettesData(redrawPalPrev);
loadFX(()=>{
loadFXData();
loadPresets(()=>{
//if (isObj(lastinfo) && isEmpty(lastinfo)) loadInfo(requestJson); // if not filled by WS
requestJson();
});
});
});
//updateUI(true);
d.addEventListener("visibilitychange", handleVisibilityChange, false);
size();
@ -264,7 +239,8 @@ function showToast(text, error = false)
x.className = error ? "error":"show";
clearTimeout(timeout);
x.style.animation = 'none';
timeout = setTimeout(function(){ x.classList.remove("show"); }, 2900);
timeout = setTimeout(()=>{ x.classList.remove("show"); }, 2900);
if (error) console.log(text);
}
function showErrorToast()
@ -425,6 +401,34 @@ function loadFX(callback = null)
});
}
function loadFXData(callback = null)
{
var url = (loc?`http://${locip}`:'') + '/json/fxdata';
fetch(url, {
method: 'get'
})
.then(res => {
if (!res.ok) showErrorToast();
return res.json();
})
.then(json => {
clearErrorToast();
fxdata = json||[];
// add default value for Solid
fxdata.shift()
fxdata.unshift("@;!;");
})
.catch(function (error) {
fxdata = [];
showToast(error, true);
})
.finally(()=>{
if (callback) callback();
updateUI();
});
}
var pQL = [];
function populateQL()
{
@ -479,40 +483,7 @@ function parseInfo() {
syncTglRecv = li.str;
maxSeg = li.leds.maxseg;
pmt = li.fs.pmt;
}
function loadInfo(callback=null)
{
var url = (loc?`http://${locip}`:'') + '/json/info';
var useWs = (ws && ws.readyState === WebSocket.OPEN);
if (useWs) {
ws.send('{"v":true}');
return;
}
fetch(url, {
method: 'get'
})
.then(res => {
if (!res.ok) showToast('Could not load Info!', true);
return res.json();
})
.then(json => {
clearErrorToast();
lastinfo = json;
parseInfo();
showNodes();
if (isInfo) populateInfo(json);
updateUI();
reqsLegal = true;
if (!ws && lastinfo.ws > -1) setTimeout(makeWS,500);
})
.catch(function (error) {
showToast(error, true);
console.log(error);
})
.finally(()=>{
if (callback) callback();
});
cct = li.leds.cct;
}
function populateInfo(i)
@ -538,7 +509,7 @@ function populateInfo(i)
if (i.ver.includes("-bl")) vcn = "Ryujin";
if (i.cn) vcn = i.cn;
cn += `v${i.ver} "${vcn}"<br><br><table class="infot">
cn += `v${i.ver} "${vcn}"<br><br><table>
${urows}
${inforow("Build",i.vid)}
${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")}
@ -577,19 +548,21 @@ function populateSegments(s)
<input type="checkbox" id="seg${i}sel" onchange="selSeg(${i})" ${inst.sel ? "checked":""}>
<span class="checkmark schk"></span>
</label>
<div class="il">
<i class="icons slider-icon pwr ${powered[i] ? "act":""}" id="seg${i}pwr" onclick="setSegPwr(${i})" title="${inst.n}">&#xe08f;</i>
<div id="sliderSeg${i}Bri" class="sliderwrap il">
<input id="seg${i}bri" class="noslide" onchange="setSegBri(${i})" oninput="updateTrail(this)" max="255" min="1" type="range" value="${inst.bri}" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
</div>
</div>`;
}
if (gId('buttonBri').className !== 'active') tglBri(true);
//if (gId('buttonBri').className !== 'active') tglBri(true);
} else {
tglBri(false);
//tglBri(false);
}
gId('buttonBri').style.display = (segCount > 1) ? "block" : "none";
//gId('buttonBri').style.display = (segCount > 1) ? "block" : "none";
gId('segcont').innerHTML = cn;
for (var i = 0; i < segCount && i<4; i++) updateTrail(gId(`seg${i}bri`));
}
@ -666,9 +639,12 @@ function populateEffects()
effects.sort((a,b) => (a.name).localeCompare(b.name));
effects.unshift({
"id": 0,
"name": "Solid",
"name": "Solid@;!;0"
});
for (let i = 0; i < effects.length; i++) {
// WLEDSR: add slider and color control to setEffect (used by requestjson)
if (effects[i].name.indexOf("Reserved") < 0) {
var posAt = effects[i].name.indexOf("@");
var extra = '';
if (posAt > 0)
@ -676,11 +652,15 @@ function populateEffects()
else
posAt = 999;
html += generateListItemHtml(
'fx',
effects[i].id,
effects[i].name.substr(0,posAt),
'setEffect'
'setEffect',
'','',
extra
);
}
}
gId('fxlist').innerHTML=html;
}
@ -702,6 +682,7 @@ function populatePalettes()
var html = "";
for (let i = 0; i < palettes.length; i++) {
html += generateListItemHtml(
'palette',
palettes[i].id,
palettes[i].name,
'setPalette',
@ -779,11 +760,19 @@ function generateOptionItemHtml(id, name)
return `<option value="${id}">${name}</option>`;
}
function generateListItemHtml(id, name, clickAction, extraHtml = '')
function generateListItemHtml(listName, id, name, clickAction, extraHtml = '', extraClass = '', extraPar = '')
{
return `<div class="lstI c" data-id="${id}" onClick="${clickAction}(${id})"><span class="lstIname">${name}</span>${extraHtml}</div>`;
return `<div class="lstI ${extraClass}" data-id="${id}" data-opt="${extraPar}" onClick="${clickAction}(${id})">
<div class="lstIcontent">
<span class="lstIname">
${name}
</span>
</div>
${extraHtml}
</div>`;
}
//update the 'sliderdisplay' background div of a slider for a visual indication of slider position
function updateTrail(e)
{
if (e==null) return;
@ -791,15 +780,17 @@ function updateTrail(e)
var perc = e.value * 100 / max;
perc = parseInt(perc);
if (perc < 50) perc += 2;
e.parentNode.getElementsByClassName('sliderdisplay')[0].style.background = `linear-gradient(90deg, var(--c-f) ${perc}%, var(--c-4) ${perc}%)`;
var bubble = e.parentNode.parentNode.getElementsByTagName('output')[0];
if (bubble) bubble.innerHTML = e.value;
var val = `linear-gradient(90deg, var(--c-f) ${perc}%, var(--c-4) ${perc}%)`;
e.parentNode.getElementsByClassName('sliderdisplay')[0].style.background = val;
var b = e.parentNode.parentNode.getElementsByTagName('output')[0];
if (b) b.innerHTML = e.value;
}
//rangetouch slider function
function toggleBubble(e)
{
var bubble = e.target.parentNode.parentNode.getElementsByTagName('output')[0];
bubble.classList.toggle('sliderbubbleshow');
var b = e.target.parentNode.parentNode.getElementsByTagName('output')[0];
b.classList.toggle('sliderbubbleshow');
}
function updatePA()
@ -832,18 +823,22 @@ function updateUI()
sel = 0;
if (eJson && eJson.length) {
for (var i=0; i<eJson.length; i++) if (eJson[i].id == selectedFx) {sel = i; break;}
gId('fxBtn').innerHTML = '<i class="icons">&#xe0e8;</i> ' + eJson[sel].name;
var posAt = eJson[sel].name.indexOf("@");
if (posAt<=0) posAt=999;
gId('fxBtn').innerHTML = '<i class="icons">&#xe0e8;</i> ' + eJson[sel].name.substr(0,posAt);
}
updateTrail(gId('sliderBri'));
updateTrail(gId('sliderSpeed'));
updateTrail(gId('sliderIntensity'));
if (isRgbw) gId('wwrap').style.display = "block";
gId("wbal").style.display = (lastinfo.leds.cct) ? "block":"none";
updatePA(true);
gId('wwrap').style.display = (isRgbw) ? "block":"none";
gId("wbal").style.display = (cct) ? "block":"none";
gId('kwrap').style.display = (cct) ? "none":"block";
updatePA();
redrawPalPrev();
updateRgb();
updatePSliders();
var l = cfg.comp.labels; //l = false;
var e = d.querySelectorAll('.label');
@ -859,10 +854,10 @@ function cmpP(a, b)
}
function makeWS() {
if (ws || lastinfo.ws<0) return;
if (ws) return;
ws = new WebSocket('ws://'+(loc?locip:window.location.hostname)+'/ws');
ws.onmessage = function(event) {
var json = JSON.parse(event.data);
ws.onmessage = (e)=>{
var json = JSON.parse(e.data);
if (json.leds) return; //liveview packet
clearTimeout(jsonTimeout);
jsonTimeout = null;
@ -874,18 +869,18 @@ function makeWS() {
if (i) {
lastinfo = i;
parseInfo();
showNodes();
if (isInfo) populateInfo(i);
} else
i = lastinfo;
var s = json.state ? json.state : json;
readState(s);
};
ws.onclose = function(event) {
ws.onclose = (e)=>{
gId('connind').style.backgroundColor = "var(--c-r)";
ws = null;
if (lastinfo.ws > -1) setTimeout(makeWS,500);
}
ws.onopen = function(event) {
ws.onopen = (e)=>{
ws.send("{'v':true}");
reqsLegal = true;
clearErrorToast();
@ -938,7 +933,11 @@ function readState(s,command=false)
gId('sliderSpeed').value = i.sx;
gId('sliderIntensity').value = i.ix;
/*
gId('sliderC1').value = i.f1x ? i.f1x : 0;
gId('sliderC2').value = i.f2x ? i.f2x : 0;
gId('sliderC3').value = i.f3x ? i.f3x : 0;
*/
if (s.error && s.error != 0) {
var errstr = "";
switch (s.error) {
@ -981,11 +980,6 @@ function requestJson(command=null)
if (command) {
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) {
var tn = parseInt(t.value*10);
if (tn != tr) command.transition = tn;
}
req = JSON.stringify(command);
if (req.length > 1000) useWs = false; //do not send very long requests over websocket
};
@ -1042,8 +1036,9 @@ function toggleInfo()
{
if (isNodes) toggleNodes();
isInfo = !isInfo;
if (isInfo) requestJson(); // loadInfo();
if (isInfo) requestJson();
gId('info').style.transform = (isInfo) ? "translateY(0px)":"translateY(100%)";
gId('buttonI').className = (isInfo) ? "active":"";
}
function toggleNodes()
@ -1052,8 +1047,9 @@ function toggleNodes()
isNodes = !isNodes;
if (isNodes) loadNodes();
gId('nodes').style.transform = (isNodes) ? "translateY(0px)":"translateY(100%)";
gId('buttonNodes').className = (isNodes) ? "active":"";
}
/*
function tglBri(b=null)
{
if (b===null) b = gId(`briwrap`).style.display === "block";
@ -1061,17 +1057,18 @@ function tglBri(b=null)
gId('buttonBri').className = !b ? "active":"";
size();
}
*/
function tglCP()
{
var p = gId('buttonCP').className === "active";
gId('buttonCP').className = !p ? "active":"";
gId('picker').style.display = !p ? "block":"none";
gId('vwrap').style.display = !p ? "block":"none";
gId('rgbwrap').style.display = !p ? "block":"none";
var csl = gId(`csl`).style.display === "block";
gId('csl').style.display = !csl ? "block":"none";
var ps = gId(`Presets`).style.display === "block";
gId('Presets').style.display = !ps ? "block":"none";
var csl = gId('Slots').style.display === "block";
gId('Slots').style.display = !csl ? "block":"none";
//var ps = gId(`Presets`).style.display === "block";
//gId('Presets').style.display = !ps ? "block":"none";
}
function tglCs(i)
@ -1124,7 +1121,7 @@ function setSegBri(s)
requestJson(obj);
}
function setEffect(ind = null)
function setEffect(ind = 0)
{
tglFxDropdown();
var obj = {"seg": {"fx": parseInt(ind)}};
@ -1174,14 +1171,12 @@ function selectSlot(b)
{
csel = b;
var cd = gId('csl').children;
for (let i = 0; i < cd.length; i++) {
cd[i].classList.remove('xxs-w');
}
cd[csel].classList.add('xxs-w');
cpick.color.set(cd[csel].style.backgroundColor);
gId('sliderW').value = whites[csel];
for (let i = 0; i < cd.length; i++) cd[i].classList.remove('xxs-w');
cd[b].classList.add('xxs-w');
setPicker(cd[b].style.backgroundColor);
gId('sliderW').value = whites[b];
redrawPalPrev();
updateRgb();
updatePSliders();
}
var lasth = 0;
@ -1195,16 +1190,49 @@ function pC(col)
} while (Math.abs(col.h - lasth) < 50);
lasth = col.h;
}
cpick.color.set(col);
setPicker(col);
setColor(0);
}
function updateRgb()
{
function updatePSliders() {
//update RGB sliders
var col = cpick.color.rgb;
gId('sliderR').value = col.r;
gId('sliderG').value = col.g;
gId('sliderB').value = col.b;
//update hex field
var str = cpick.color.hexString.substring(1);
var w = whites[csel];
if (w > 0) str += w.toString(16);
//update value slider
var v = gId('sliderV');
v.value = cpick.color.value;
//background color as if color had full value
var hsv = {"h":cpick.color.hue,"s":cpick.color.saturation,"v":100};
var c = iro.Color.hsvToRgb(hsv);
var cs = 'rgb('+c.r+','+c.g+','+c.b+')';
v.nextElementSibling.style.backgroundImage = `linear-gradient(90deg, #000 0%, ${cs})`;
//update Kelvin slider
gId('sliderK').value = cpick.color.kelvin;
}
function setPicker(rgb) {
var c = new iro.Color(rgb);
if (c.value > 0) cpick.color.set(c);
else cpick.color.setChannel('hsv', 'v', 0);
}
function fromV()
{
cpick.color.setChannel('hsv', 'v', d.getElementById('sliderV').value);
}
function fromK()
{
cpick.color.set({ kelvin: d.getElementById('sliderK').value });
}
function fromRgb()
@ -1212,7 +1240,7 @@ function fromRgb()
var r = gId('sliderR').value;
var g = gId('sliderG').value;
var b = gId('sliderB').value;
cpick.color.set(`rgb(${r},${g},${b})`);
setPicker(`rgb(${r},${g},${b})`);
setColor(0);
}
@ -1241,7 +1269,7 @@ function setBalance(b)
}
var hc = 0;
setInterval(function(){if (!isInfo) return; hc+=18; if (hc>300) hc=0; if (hc>200)hc=306; if (hc==144) hc+=36; if (hc==108) hc+=18;
setInterval(()=>{if (!isInfo) return; hc+=18; if (hc>300) hc=0; if (hc>200)hc=306; if (hc==144) hc+=36; if (hc==108) hc+=18;
gId('heart').style.color = `hsl(${hc}, 100%, 50%)`;}, 910);
function openGH() { window.open("https://github.com/Aircoookie/WLED/wiki"); }
@ -1275,7 +1303,7 @@ function loadPalettesData(callback = null)
}
palettesData = {};
getPalettesData(0, function() {
getPalettesData(0, ()=>{
localStorage.setItem(lsKey, JSON.stringify({
p: palettesData,
vid: lastinfo.vid
@ -1294,18 +1322,17 @@ function getPalettesData(page, callback)
"Content-type": "application/json; charset=UTF-8"
}
})
.then(res => {
.then((res)=>{
if (!res.ok) showErrorToast();
return res.json();
})
.then(json => {
.then((json)=>{
palettesData = Object.assign({}, palettesData, json.p);
if (page < json.m) setTimeout(function() { getPalettesData(page + 1, callback); }, 50);
if (page < json.m) setTimeout(()=>{ getPalettesData(page + 1, callback); }, 50);
else callback();
})
.catch(function(error) {
showToast(error, true);
console.log(error);
.catch((e)=>{
showToast(e, true);
});
}
@ -1390,7 +1417,6 @@ function move(e)
function size()
{
w = window.innerWidth;
var h = gId('top').clientHeight;
sCol('--th', h + "px");
sCol("--tp", h - (gId(`briwrap`).style.display === "block" ? 0 : gId(`briwrap`).clientTop) + "px");

View File

@ -92,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;
@ -108,8 +111,12 @@ td {
.d5 {
width: 4rem !important;
}
.exp {
transform: rotate(90deg);
.cal {
font-size:1.5rem;
cursor:pointer
}
#TMT table {
width: 100%;
}
#toast {
opacity: 0;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -654,7 +654,7 @@ void setPaletteColors(JsonArray json, CRGBPalette16 palette)
for (int i = 0; i < 16; i++) {
JsonArray colors = json.createNestedArray();
CRGB color = palette[i];
colors.add((((float)i / (float)16) * 255));
colors.add(i<<4);
colors.add(color.red);
colors.add(color.green);
colors.add(color.blue);

View File

@ -168,11 +168,9 @@ void colorUpdated(int callMode)
void updateInterfaces(uint8_t callMode)
{
sendDataWs();
if (callMode == CALL_MODE_WS_SEND) {
lastInterfaceUpdate = millis();
return;
}
sendDataWs();
if (callMode == CALL_MODE_WS_SEND) return;
#ifndef WLED_DISABLE_ALEXA
if (espalexaDevice != nullptr && callMode != CALL_MODE_ALEXA) {
@ -185,7 +183,6 @@ void updateInterfaces(uint8_t callMode)
callMode != CALL_MODE_NO_NOTIFY) updateBlynk();
#endif
doPublishMqtt = true;
lastInterfaceUpdate = millis();
}

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
@ -334,11 +360,8 @@ void checkTimers()
&& (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)
&& ( (timerDay[i] == 0 && ((timerWeekday[i] >> weekdayMondayFirst()) & 0x01)) //timer should activate at current day of week
|| (timerDay[i] > 0 && timerDay[i]==day(localTime) &&
(timerMonth[i] == 0 || timerMonth[i]==month(localTime) || (timerMonth[i]==13 && month(localTime)%2) || (timerMonth[i]==14 && !(month(localTime)%2)))
)
)
&& ((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();

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;

View File

@ -39,9 +39,17 @@ void handlePresets()
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);
//HTTP API commands
const char* httpwin = fdo["win"];
if (httpwin) {
String apireq = "win&";
apireq += httpwin;
handleSet(nullptr, apireq, false);
} else {
fdo.remove("ps"); //remove load request for presets to prevent recursive crash
deserializeState(fdo, CALL_MODE_NO_NOTIFY, presetToApply);
}
#if defined(ARDUINO_ARCH_ESP32)
//Aircoookie recommended not to delete buffer
@ -54,7 +62,11 @@ void handlePresets()
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
effectChanged = true; //force UDP notification
colorUpdated(callModeToApply);
updateInterfaces(callModeToApply);
presetToApply = 0; //clear request for preset
callModeToApply = 0;
}

View File

@ -339,7 +339,7 @@ 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
k[1] = i+48;//ascii 0,1,2,3,...
k[0] = 'H'; //timer hours
timerHours[i] = request->arg(k).toInt();
k[0] = 'N'; //minutes
@ -349,16 +349,15 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
k[0] = 'W'; //weekdays
timerWeekday[i] = request->arg(k).toInt();
if (i<8) {
k[0] = 'X'; //DOW
if (!request->hasArg(k)) {
k[0] = 'M'; //month
timerMonth[i] = request->arg(k).toInt();
k[0] = 'D'; //day
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();
} else {
timerMonth[i] = 0;
timerDay[i] = 0;
}
k[0] = 'E'; //end day
timerDayEnd[i] = request->arg(k).toInt();
}
}
}

View File

@ -8,7 +8,7 @@
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2201011
#define VERSION 2201093
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG
@ -508,10 +508,12 @@ 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 timerMonth[] _INIT_N(({0,0,0,0,0,0,0,0}));
WLED_GLOBAL byte timerDay[] _INIT_N(({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);

View File

@ -458,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
@ -550,10 +550,10 @@ void getSettingsJS(byte subPage, char* dest)
k[0] = 'T'; sappend('v',k,timerMacro[i]);
k[0] = 'W'; sappend('v',k,timerWeekday[i]);
if (i<8) {
k[0] = 'X'; sappend('c',k,timerDay[i]==0);
k[0] = 'M'; sappend('v',k,timerMonth[i]);
k[0] = 'D'; sappend('v',k,timerDay[i]?timerDay[i]:1);
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]);
}
}
}