Merge branch 'master' of https://github.com/aircoookie/WLED into dev

Conflicts:
	tools/cdata.js
	usermods/PIR_sensor_switch/readme.md
	usermods/Temperature/readme.md
	wled00/FX.h
	wled00/FX_fcn.cpp
	wled00/bus_manager.h
	wled00/bus_wrapper.h
	wled00/cfg.cpp
	wled00/const.h
	wled00/data/settings.htm
	wled00/data/settings_leds.htm
	wled00/data/settings_um.htm
	wled00/html_settings.h
	wled00/json.cpp
	wled00/mqtt.cpp
	wled00/set.cpp
	wled00/wled.cpp
	wled00/wled.h
	wled00/wled_eeprom.cpp
	wled00/wled_server.cpp
	wled00/xml.cpp
This commit is contained in:
Blaz Kristan 2021-05-18 15:45:34 +02:00
commit bfd7be543a
37 changed files with 1565 additions and 1271 deletions

View File

@ -2,6 +2,36 @@
### Builds after release 0.12.0 ### Builds after release 0.12.0
#### Build 2105171
- Always copy MQTT payloads to prevent non-0-terminated strings
- Updated ArduinoJson to 6.18.0
- Added experimental support for `{"on":"t"}` to toggle on/off state via JSON
#### Build 2105120
- Fixed possibility of non-0-terminated MQTT payloads
- Fixed two warnings regarding integer comparison
#### Build 2105112
- Usermod settings page no usermods message
- Lowered min speed for Drip effect
#### Build 2105111
- Fixed various Codacy code style and logic issues
#### Build 2105110
- Added Usermod settings page and configurable usermods (PR #1951)
- Added experimental `/json/cfg` endpoint for changing settings from JSON (see #1944, not part of official API)
#### Build 2105070
- Fixed not turning on after pressing "Off" on IR remote twice (#1950)
- Fixed OTA update file selection from Android app (TODO: file type verification in JS, since android can't deal with accept='.bin' attribute)
#### Build 2104220 #### Build 2104220
- Version bump to 0.12.1-b1 "Hikari" - Version bump to 0.12.1-b1 "Hikari"

View File

@ -114,9 +114,6 @@ build_unflags =
# enables all features for travis CI # enables all features for travis CI
build_flags_all_features = build_flags_all_features =
-D WLED_USE_ANALOG_LED
-D WLED_USE_H801
-D WLED_ENABLE_5CH_LEDS
-D WLED_ENABLE_ADALIGHT -D WLED_ENABLE_ADALIGHT
-D WLED_ENABLE_DMX -D WLED_ENABLE_DMX
-D WLED_ENABLE_MQTT -D WLED_ENABLE_MQTT
@ -289,7 +286,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k} board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA -D WLED_USE_ANALOG_LEDS build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
[env:esp8285_4CH_H801] [env:esp8285_4CH_H801]
board = esp8285 board = esp8285
@ -297,7 +294,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k} board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA -D WLED_USE_ANALOG_LEDS -D WLED_USE_H801 build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
[env:esp8285_5CH_H801] [env:esp8285_5CH_H801]
board = esp8285 board = esp8285
@ -305,7 +302,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k} board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA -D WLED_USE_ANALOG_LEDS -D WLED_USE_H801 -D WLED_ENABLE_5CH_LEDS build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
[env:d1_mini_5CH_Shojo_PCB] [env:d1_mini_5CH_Shojo_PCB]
board = d1_mini board = d1_mini
@ -313,7 +310,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages} platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m} board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266} -D WLED_USE_ANALOG_LEDS -D WLED_USE_SHOJO_PCB -D WLED_ENABLE_5CH_LEDS build_flags = ${common.build_flags_esp8266} -D WLED_USE_SHOJO_PCB
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# DEVELOPMENT BOARDS # DEVELOPMENT BOARDS
@ -405,6 +402,7 @@ build_flags = ${common.build_flags_esp32} ${common.debug_flags} ${common.build_f
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# codm pixel controller board configurations # codm pixel controller board configurations
# codm-controller-0.6 can also be used for the TYWE3S controller
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
[env:codm-controller-0.6] [env:codm-controller-0.6]

View File

@ -39,12 +39,8 @@ build_flags = ${common.build_flags_esp8266}
; PIN defines for 2 wire LEDs ; PIN defines for 2 wire LEDs
-D CLKPIN=0 -D CLKPIN=0
-D DATAPIN=2 -D DATAPIN=2
; to drive analog LED strips (aka 5050), uncomment the following ; to drive analog LED strips (aka 5050) hardware configuration is no longer necessary
; PWM pins 5,12,13,15 are used with Magic Home LED Controller (default) ; configure the settings in the UI as follows (hard):
-D WLED_USE_ANALOG_LEDS ; for the Magic Home LED Controller use PWM pins 5,12,13,15
; for the H801 controller (PINs 15,13,12,14 (W2 = 04)) uncomment this ; for the H801 controller use PINs 15,13,12,14 (W2 = 04)
; -D WLED_USE_H801 ; for the BW-LT11 controller use PINs 12,4,14,5
; for the BW-LT11 controller (PINs 12,4,14,5 ) uncomment this
; -D WLED_USE_BWLT11
; and to enable channel 5 for RGBW-CT led strips this
; -D WLED_USE_5CH_LEDS

View File

@ -344,6 +344,10 @@ const char PAGE_settings_dmx[] PROGMEM = R"=====()=====";
str str
.replace(/\<link rel="stylesheet".*\>/gms, "") .replace(/\<link rel="stylesheet".*\>/gms, "")
.replace(/\<style\>.*\<\/style\>/gms, "%CSS%%SCSS%") .replace(/\<style\>.*\<\/style\>/gms, "%CSS%%SCSS%")
.replace(
/function GetV().*\<\/script\>/gms,
"function GetV() {var d=document;\n"
),
} }
], ],
"wled00/html_settings.h" "wled00/html_settings.h"

232
tools/fps_test.htm Normal file
View File

@ -0,0 +1,232 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>WLED frame rate test tool</title>
<style>
body {
background-color: #222;
color: #fff;
font-family: Helvetica, Verdana, sans-serif;
}
input {
background-color: #333;
color: #fff;
}
#ip {
width: 100px;
}
#secs {
width: 36px;
}
#csva {
position: absolute;
top: -100px; /*gtfo*/
}
button {
background-color: #333;
color: #fff;
}
table, th, td {
border: 1px solid #aaa;
border-collapse: collapse;
text-align: center;
}
.red {
color: #d20;
}
</style>
<script>
var gotfx = false, running = false;
var pos = 0, prev = 0, min = 999, max = 0, fpslist = [], names = [], names_checked = [];
var to;
function S() {
document.getElementById('ip').value = localStorage.getItem('locIpFps');
if (document.getElementById('ip').value) req(false);
}
function loadC() {
hide(false);
var list = localStorage.getItem('fpsFxSelection');
if (!list) return;
list = JSON.parse(list);
var chks = document.querySelectorAll('.fxcheck');
for (let i = 0; i < chks.length; i++) {
if (i < list.length) chks[i].checked = list[i];
}
}
function saveC() {
var list = [];
var chks = document.querySelectorAll('.fxcheck');
for (let i = 0; i < chks.length; i++) {
list.push(chks[i].checked);
}
localStorage.setItem('fpsFxSelection', JSON.stringify(list));
}
function setC(c) {
hide(false);
var chks = document.querySelectorAll('.fxcheck');
for (let i = 0; i < chks.length; i++) {
chks[i].checked = (c == 255);
}
if (c == 1 && chks.length > 100) {
chks[1].checked = true; //Blink
chks[15].checked = true; //Running
chks[16].checked = true; //Saw
chks[37].checked = true; //Running 2
chks[44].checked = true; //Tetrix
chks[63].checked = true; //Pride 2015
chks[74].checked = true; //Colortwinkles
chks[101].checked = true;//Pacifica
}
}
function hide(h) {
var trs = document.querySelectorAll('.trs');
var chks = document.querySelectorAll('.fxcheck');
for (let i = 0; i < trs.length; i++) {
trs[i].style.display = (h && !chks[i].checked) ? "none":"table-row";
}
}
function run(init) {
if (init) {
running = !running;
document.getElementById('runbtn').innerText = running ? 'Stop':'Run';
if (running) {pos = 0; prev = -1; min = 999; max = 0; fpslist = []; names_checked = []; hide(true);}
clearTimeout(to);
if (!running) {req({seg:{fx:0},v:true,stop:true}); return;}
}
if (!gotfx) {req(false); return;}
var chks = document.querySelectorAll('.fxcheck');
var fpsb = document.querySelectorAll('.fps');
if (prev >= 0) {pos++};
if (pos >= chks.length) {run(true); return;} //end
while (!chks[pos].checked) {
fpsb[pos].innerText = "-";
pos++;
if (pos >= chks.length) {run(true); return;} //end
}
names_checked.push(names[pos]);
var extra = {};
try {
extra = JSON.parse(document.getElementById('ej').value);
} catch (e) {
}
var cmd = {seg:{fx:pos},v:true};
Object.assign(cmd, extra);
req(cmd);
}
function req(command) {
var ip = document.getElementById('ip').value;
if (!ip) {alert("Please enter WLED IP"); return;}
if (ip != localStorage.getItem('locIpFps')) localStorage.setItem('locIpFps', document.getElementById('ip').value);
var url = command ? `http://${ip}/json/si` : `http://${ip}/json/effects`;
var type = command ? 'post':'get';
var req = undefined;
if (command)
{
req = JSON.stringify(command);
}
fetch
(url, {
method: type,
headers: {
"Content-type": "application/json; charset=UTF-8"
},
body: req
})
.then(res => {
if (!res.ok) {
alert('Data malfunction');
}
return res.json();
})
.then(json => {
if (!json) {
alert('Empty response'); return;
}
if (!command) {
names = json;
var tblc = '';
for (let i = 0; i < json.length; i++) {
tblc += `<tr class="trs"><td><input type="checkbox" class="fxcheck" /></td><td>${i}</td><td>${json[i]}</td><td class="fps"></td></tr>`
}
var tbl = `<table>
<tr>
<th>Test?</th><th>ID</th><th>Effect Name</th><th>FPS</th>
</tr>
${tblc}
</table>`;
document.getElementById('tablecon').innerHTML = tbl;
setC(1);
loadC();
gotfx = true;
document.getElementById('runbtn').innerText = "Run";
} else {
if (!json.info) return;
document.getElementById('leds').innerText = json.info.leds.count;
document.getElementById('seg').innerText = json.state.seg[0].len;
document.getElementById('bri').innerText = json.state.bri;
if (prev >= 0) {
var lastfps = parseInt(json.info.leds.fps); //previous FX
if (lastfps < min) min = lastfps;
if (lastfps > max) max = lastfps;
fpslist.push(lastfps);
var sum = 0;
for (let i = 0; i < fpslist.length; i++) {
sum += fpslist[i];
}
sum /= fpslist.length;
document.getElementById('fps_min').innerText = min;
document.getElementById('fps_max').innerText = max;
document.getElementById('fps_avg').innerText = Math.round(sum*10)/10;
var fpsb = document.querySelectorAll('.fps');
fpsb[prev].innerHTML = lastfps;
}
prev = pos;
var delay = parseInt(document.getElementById('secs').value)*1000;
delay = Math.min(Math.max(delay, 2000), 15000)
if (!command.stop) to = setTimeout(run,delay);
}
})
.catch(function (error) {
alert('Comms malfunction');
console.log(error);
});
}
function csv(n) {
var txt = "";
for (let i = 0; i < fpslist.length; i++) {
if (!n) txt += names_checked[i] + ',';
txt += fpslist[i]; txt += "\n";
}
document.getElementById('csva').value = txt;
var copyText = document.getElementById('csva');
copyText.select();
copyText.setSelectionRange(0, 999999);
document.execCommand("copy");
}
</script>
</head>
<body onload="S()">
<h2>Starship monitoring dashboard</h2>
(or rather just a WLED frame rate tester lol)<br><br>
IP: <input id="ip" /><br>
Time per effect: <input type=number id=secs value=5 max=15 min=2 />s<br>
Effects to test:
<button type="button" onclick="setC(255)">All</button>
<button type="button" onclick="setC(1)">Selection 1</button>
<button type="button" onclick="setC(0)">None</button>
<button type="button" onclick="loadC()">Get LS</button>
<button type="button" class="red" onclick="saveC()">Save to LS</button><br>
Extra JSON: <input id="ej" /><br>
<button type="button" onclick="run(true)" id="runbtn">Fetch FX list</button><br>
LEDs: <span id="leds">-</span>, Seg: <span id="seg">-</span>, Bri: <span id="bri">-</span><br>
FPS min: <span id="fps_min">-</span>, max: <span id="fps_max">-</span>, avg: <span id="fps_avg">-</span><br><br>
<div id="tablecon">
</div><br>
<button type="button" onclick="csv(false)">Copy csv to clipboard</button>
<button type="button" onclick="csv(true)">Copy csv (FPS only)</button>
<textarea id=csva></textarea>
</body>
</html>

View File

@ -9,9 +9,7 @@ The LED strip is switched [using a relay](https://github.com/Aircoookie/WLED/wik
## Webinterface ## Webinterface
The info page in the web interface shows the items below The info page in the web interface shows the remaining time of the off timer.
- the remaining time of the off timer.
**I recommend to deactivate the sensor before an OTA update and activate it again afterwards**.
## Sensor connection ## Sensor connection
@ -65,6 +63,9 @@ void registerUsermods()
To query or change the PIR sensor state the methods `bool PIRsensorEnabled()` and `void EnablePIRsensor(bool enable)` are available. To query or change the PIR sensor state the methods `bool PIRsensorEnabled()` and `void EnablePIRsensor(bool enable)` are available.
When the PIR sensor state changes an MQTT message is broadcasted with topic `wled/deviceMAC/motion` and message `on` or `off`.
Usermod can also be configured to just send MQTT message and not change WLED state using settings page as well as responding to motion only during nighttime (assuming NTP and lattitude/longitude are set to determine sunrise/sunset times).
### There are two options to get access to the usermod instance: ### There are two options to get access to the usermod instance:
1. Include `usermod_PIR_sensor_switch.h` **before** you include the other usermod in `usermods_list.cpp' 1. Include `usermod_PIR_sensor_switch.h` **before** you include the other usermod in `usermods_list.cpp'

View File

@ -3,7 +3,7 @@
Based on the excellent `QuinLED_Dig_Uno_Temp_MQTT` by srg74 and 400killer! Based on the excellent `QuinLED_Dig_Uno_Temp_MQTT` by srg74 and 400killer!
This usermod will read from an attached DS18B20 temperature sensor (as available on the QuinLED Dig-Uno) This usermod will read from an attached DS18B20 temperature sensor (as available on the QuinLED Dig-Uno)
The temperature is displayed both in the Info section of the web UI as well as published to the `/temperature` MQTT topic if enabled. The temperature is displayed both in the Info section of the web UI as well as published to the `/temperature` MQTT topic if enabled.
This usermod will be expanded with support for different sensor types in the future. This usermod may be expanded with support for different sensor types in the future.
If temperature sensor is not detected during boot, this usermod will be disabled. If temperature sensor is not detected during boot, this usermod will be disabled.
@ -16,18 +16,19 @@ Copy the example `platformio_override.ini` to the root directory. This file sho
* `USERMOD_DALLASTEMPERATURE` - define this to have this user mod included wled00\usermods_list.cpp * `USERMOD_DALLASTEMPERATURE` - define this to have this user mod included wled00\usermods_list.cpp
* `USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 20 seconds * `USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 20 seconds
All parameters can be configured at runtime using Usermods settings page. All parameters can be configured at runtime using Usermods settings page, including pin, selection to display temerature in degrees Celsius or Farenheit mand measurement interval.
## Project link ## Project link
* [QuinLED-Dig-Uno](https://quinled.info/2018/09/15/quinled-dig-uno/) - Project link * [QuinLED-Dig-Uno](https://quinled.info/2018/09/15/quinled-dig-uno/) - Project link
* [Srg74-WLED-Wemos-shield](https://github.com/srg74/WLED-wemos-shield) - another great DIY WLED board
### PlatformIO requirements ### PlatformIO requirements
If you are using `platformio_override.ini`, you should be able to refresh the task list and see your custom task, for example `env:d1_mini_usermod_dallas_temperature_C`. If you are using `platformio_override.ini`, you should be able to refresh the task list and see your custom task, for example `env:d1_mini_usermod_dallas_temperature_C`.
If you are not using `platformio_override.ini`, you might have to uncomment `DallasTemperature@~3.8.0`,`OneWire@~2.3.5 under` `[common]` section in `platformio.ini`: If you are not using `platformio_override.ini`, you might have to uncomment `OneWire@~2.3.5 under` `[common]` section in `platformio.ini`:
```ini ```ini
# platformio.ini # platformio.ini
@ -41,8 +42,7 @@ default_envs = d1_mini
... ...
lib_deps = lib_deps =
... ...
#For Dallas sensor uncomment following 2 lines #For Dallas sensor uncomment following line
DallasTemperature@~3.8.0
OneWire@~2.3.5 OneWire@~2.3.5
... ...
``` ```

Binary file not shown.

View File

@ -34,7 +34,7 @@
*/ */
uint16_t WS2812FX::mode_static(void) { uint16_t WS2812FX::mode_static(void) {
fill(SEGCOLOR(0)); fill(SEGCOLOR(0));
return (SEGMENT.getOption(SEG_OPTION_TRANSITIONAL)) ? FRAMETIME : 380; //update faster if in transition return (SEGMENT.getOption(SEG_OPTION_TRANSITIONAL)) ? FRAMETIME : 350; //update faster if in transition
} }
@ -1436,8 +1436,8 @@ uint16_t WS2812FX::mode_tricolor_fade(void)
} }
byte stp = prog; // % 256 byte stp = prog; // % 256
uint32_t color = 0;
for(uint16_t i = 0; i < SEGLEN; i++) { for(uint16_t i = 0; i < SEGLEN; i++) {
uint32_t color;
if (stage == 2) { if (stage == 2) {
color = color_blend(color_from_palette(i, true, PALETTE_SOLID_WRAP, 2), color2, stp); color = color_blend(color_from_palette(i, true, PALETTE_SOLID_WRAP, 2), color2, stp);
} else if (stage == 1) { } else if (stage == 1) {
@ -3050,7 +3050,7 @@ uint16_t WS2812FX::mode_drip(void)
numDrops = 1 + (SEGMENT.intensity >> 6); // 255>>6 = 3 numDrops = 1 + (SEGMENT.intensity >> 6); // 255>>6 = 3
float gravity = -0.001 - (SEGMENT.speed/50000.0); float gravity = -0.0005 - (SEGMENT.speed/50000.0);
gravity *= SEGLEN; gravity *= SEGLEN;
int sourcedrop = 12; int sourcedrop = 12;

View File

@ -612,6 +612,7 @@ class WS2812FX {
bool bool
isRgbw = false, isRgbw = false,
isOffRefreshRequred = false, //periodic refresh is required for the strip to remain off.
gammaCorrectBri = false, gammaCorrectBri = false,
gammaCorrectCol = true, gammaCorrectCol = true,
applyToAllSelected = true, applyToAllSelected = true,

View File

@ -1,124 +0,0 @@
/*
Editor: https://www.visualmicro.com/
This file is for intellisense purpose only.
Visual micro (and the arduino ide) ignore this code during compilation. This code is automatically maintained by visualmicro, manual changes to this file will be overwritten
The contents of the _vm sub folder can be deleted prior to publishing a project
All non-arduino files created by visual micro and all visual studio project or solution files can be freely deleted and are not required to compile a sketch (do not delete your own code!).
Note: debugger breakpoints are stored in '.sln' or '.asln' files, knowledge of last uploaded breakpoints is stored in the upload.vmps.xml file. Both files are required to continue a previous debug session without needing to compile and upload again
Hardware: ESP32 Dev Module, Platform=esp32, Package=esp32
*/
#if defined(_VMICRO_INTELLISENSE)
#ifndef _VSARDUINO_H_
#define _VSARDUINO_H_
#define __ESP32_esp32__
#define __ESP32_ESP32__
#define ESP_PLATFORM
#define HAVE_CONFIG_H
#define GCC_NOT_5_2_0 0
#define WITH_POSIX
#define F_CPU 240000000L
#define ARDUINO 108011
#define ARDUINO_ESP32_DEV
#define ARDUINO_ARCH_ESP32
#define ESP32
#define CORE_DEBUG_LEVEL 0
#define __cplusplus 201103L
#define _Pragma(x)
#undef __cplusplus
#define __cplusplus 201103L
#define __STDC__
#define __ARM__
#define __arm__
#define __inline__
#define __asm__(...)
#define __extension__
#define __ATTR_PURE__
#define __ATTR_CONST__
#define __volatile__
#define __ASM
#define __INLINE
#define __attribute__(noinline)
//#define _STD_BEGIN
//#define EMIT
#define WARNING
#define _Lockit
#define __CLR_OR_THIS_CALL
#define C4005
#define _NEW
typedef bool _Bool;
typedef int _read;
typedef int _seek;
typedef int _write;
typedef int _close;
typedef int __cleanup;
//#define inline
#define __builtin_clz
#define __builtin_clzl
#define __builtin_clzll
#define __builtin_labs
#define __builtin_va_list
typedef int __gnuc_va_list;
#define __ATOMIC_ACQ_REL
#define __CHAR_BIT__
#define _EXFUN()
typedef unsigned char byte;
extern "C" void __cxa_pure_virtual() {;}
typedef long __INTPTR_TYPE__ ;
typedef long __UINTPTR_TYPE__ ;
typedef long __SIZE_TYPE__ ;
typedef long __PTRDIFF_TYPE__;
typedef long pthread_t;
typedef long pthread_key_t;
typedef long pthread_once_t;
typedef long pthread_mutex_t;
typedef long pthread_mutex_t;
typedef long pthread_cond_t;
#include "arduino.h"
#include <pins_arduino.h>
#define interrupts() sei()
#define noInterrupts() cli()
#define ESP_LOGI(tag, ...)
#include "wled00.ino"
#include "wled01_eeprom.ino"
#include "wled02_xml.ino"
#include "wled03_set.ino"
#include "wled04_file.ino"
#include "wled05_init.ino"
#include "wled06_usermod.ino"
#include "wled07_notify.ino"
#include "wled08_led.ino"
#include "wled09_button.ino"
#include "wled10_ntp.ino"
#include "wled11_ol.ino"
#include "wled12_alexa.ino"
#include "wled13_cronixie.ino"
#include "wled14_colors.ino"
#include "wled15_hue.ino"
#include "wled16_blynk.ino"
#include "wled17_mqtt.ino"
#include "wled18_server.ino"
#include "wled19_json.ino"
#include "wled20_ir.ino"
#endif
#endif

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -63,7 +63,7 @@ class Bus {
return _start; return _start;
} }
void setStart(uint16_t start) { inline void setStart(uint16_t start) {
_start = start; _start = start;
} }
@ -81,7 +81,7 @@ class Bus {
return false; return false;
} }
virtual uint8_t skipFirstLed() { virtual uint8_t skippedLeds() {
return 0; return 0;
} }
@ -184,10 +184,10 @@ class BusDigital : public Bus {
} }
inline bool isRgbw() { inline bool isRgbw() {
return _rgbw; return (_rgbw || _type == TYPE_SK6812_RGBW || _type == TYPE_TM1814);
} }
inline uint8_t skipFirstLed() { inline uint8_t skippedLeds() {
return _skip; return _skip;
} }
@ -451,6 +451,11 @@ class BusManager {
return Bus::isRgbw(type); return Bus::isRgbw(type);
} }
//Return true if the strip requires a refresh to stay off.
static bool isOffRefreshRequred(uint8_t type) {
return type == TYPE_TM1814;
}
private: private:
uint8_t numBusses = 0; uint8_t numBusses = 0;
Bus* busses[WLED_MAX_BUSSES]; Bus* busses[WLED_MAX_BUSSES];

View File

@ -12,24 +12,7 @@ void getStringFromJson(char* dest, const char* src, size_t len) {
if (src != nullptr) strlcpy(dest, src, len); if (src != nullptr) strlcpy(dest, src, len);
} }
void deserializeConfig() { bool deserializeConfig(JsonObject doc, bool fromFS) {
bool fromeep = false;
bool success = deserializeConfigSec();
if (!success) { //if file does not exist, try reading from EEPROM
deEEPSettings();
fromeep = true;
}
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
DEBUG_PRINTLN(F("Reading settings from /cfg.json..."));
success = readObjectFromFile("/cfg.json", nullptr, &doc);
if (!success) { //if file does not exist, try reading from EEPROM
if (!fromeep) deEEPSettings();
return;
}
//int rev_major = doc["rev"][0]; // 1 //int rev_major = doc["rev"][0]; // 1
//int rev_minor = doc["rev"][1]; // 0 //int rev_minor = doc["rev"][1]; // 0
@ -103,6 +86,8 @@ void deserializeConfig() {
CJSON(strip.rgbwMode, hw_led[F("rgbwm")]); CJSON(strip.rgbwMode, hw_led[F("rgbwm")]);
JsonArray ins = hw_led["ins"]; JsonArray ins = hw_led["ins"];
if (fromFS || !ins.isNull()) {
uint8_t s = 0; // bus iterator uint8_t s = 0; // bus iterator
strip.isRgbw = false; strip.isRgbw = false;
busses.removeAll(); busses.removeAll();
@ -112,6 +97,7 @@ void deserializeConfig() {
uint8_t pins[5] = {255, 255, 255, 255, 255}; uint8_t pins[5] = {255, 255, 255, 255, 255};
JsonArray pinArr = elm["pin"]; JsonArray pinArr = elm["pin"];
if (pinArr.size() == 0) continue; if (pinArr.size() == 0) continue;
pins[0] = pinArr[0];
uint8_t i = 0; uint8_t i = 0;
for (int p : pinArr) { for (int p : pinArr) {
pins[i++] = p; pins[i++] = p;
@ -129,7 +115,9 @@ void deserializeConfig() {
//RGBW mode is enabled if at least one of the strips is RGBW //RGBW mode is enabled if at least one of the strips is RGBW
// if ((bool)elm[F("rgbw")]) SET_BIT(ledType,7); else UNSET_BIT(ledType,7); // hack bit 7 to indicate RGBW (as an override if necessary) // if ((bool)elm[F("rgbw")]) SET_BIT(ledType,7); else UNSET_BIT(ledType,7); // hack bit 7 to indicate RGBW (as an override if necessary)
// strip.isRgbw |= (bool)elm[F("rgbw")]; // strip.isRgbw |= (bool)elm[F("rgbw")];
strip.isRgbw = (strip.isRgbw || Bus::isRgbw(ledType)); strip.isRgbw = (strip.isRgbw || BusManager::isRgbw(ledType));
//refresh is required to remain off if at least one of the strips requires the refresh.
strip.isOffRefreshRequred |= BusManager::isOffRefreshRequred(ledType);
s++; s++;
lC += length; lC += length;
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst); BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst);
@ -138,44 +126,50 @@ void deserializeConfig() {
DEBUG_PRINTLN(busses.getNumBusses()); DEBUG_PRINTLN(busses.getNumBusses());
if (mem <= MAX_LED_MEMORY && busses.getNumBusses() <= WLED_MAX_BUSSES) busses.add(bc); // finalization will be done in WLED::beginStrip() if (mem <= MAX_LED_MEMORY && busses.getNumBusses() <= WLED_MAX_BUSSES) busses.add(bc); // finalization will be done in WLED::beginStrip()
} }
//strip.finalizeInit();
}
if (lC > ledCount) ledCount = lC; // fix incorrect total length (honour analog setup) if (lC > ledCount) ledCount = lC; // fix incorrect total length (honour analog setup)
DEBUG_PRINTLN(F(" Done LEDs.")); DEBUG_PRINTLN(F(" Done LEDs."));
JsonObject hw_btn_ins_0 = hw[F("btn")][F("ins")][0]; JsonObject hw_btn_ins_0 = hw[F("btn")][F("ins")][0];
CJSON(buttonType, hw_btn_ins_0["type"]); CJSON(buttonType, hw_btn_ins_0["type"]);
int hw_btn_pin = hw_btn_ins_0["pin"][0]; int hw_btn_pin = hw_btn_ins_0[F("pin")][0] | -2; //-2 = not present in doc, keep current. -1 = disable
if (hw_btn_pin > -2) {
if (pinManager.allocatePin(hw_btn_pin,false)) { if (pinManager.allocatePin(hw_btn_pin,false)) {
btnPin = hw_btn_pin; btnPin = hw_btn_pin;
pinMode(btnPin, INPUT_PULLUP); pinMode(btnPin, INPUT_PULLUP);
} else { } else {
btnPin = -1; btnPin = -1;
} }
}
JsonArray hw_btn_ins_0_macros = hw_btn_ins_0[F("macros")]; JsonArray hw_btn_ins_0_macros = hw_btn_ins_0[F("macros")];
CJSON(macroButton, hw_btn_ins_0_macros[0]); CJSON(macroButton, hw_btn_ins_0_macros[0]);
CJSON(macroLongPress,hw_btn_ins_0_macros[1]); CJSON(macroLongPress,hw_btn_ins_0_macros[1]);
CJSON(macroDoublePress, hw_btn_ins_0_macros[2]); CJSON(macroDoublePress, hw_btn_ins_0_macros[2]);
//int hw_btn_ins_0_type = hw_btn_ins_0["type"]; // 0
#ifndef WLED_DISABLE_INFRARED #ifndef WLED_DISABLE_INFRARED
int hw_ir_pin = hw["ir"]["pin"] | -1; // 4 int hw_ir_pin = hw["ir"]["pin"] | -2; // 4
if (hw_ir_pin > -2) {
if (pinManager.allocatePin(hw_ir_pin,false)) { if (pinManager.allocatePin(hw_ir_pin,false)) {
irPin = hw_ir_pin; irPin = hw_ir_pin;
} else { } else {
irPin = -1; irPin = -1;
} }
}
#endif #endif
CJSON(irEnabled, hw["ir"]["type"]); CJSON(irEnabled, hw["ir"]["type"]);
JsonObject relay = hw[F("relay")]; JsonObject relay = hw[F("relay")];
int hw_relay_pin = relay["pin"] | -1; int hw_relay_pin = relay["pin"] | -2;
if (hw_relay_pin>=0 && pinManager.allocatePin(hw_relay_pin,true)) { if (hw_relay_pin > -2) {
if (pinManager.allocatePin(hw_relay_pin,true)) {
rlyPin = hw_relay_pin; rlyPin = hw_relay_pin;
pinMode(rlyPin, OUTPUT); pinMode(rlyPin, OUTPUT);
} else { } else {
rlyPin = -1; rlyPin = -1;
} }
}
if (relay.containsKey("rev")) { if (relay.containsKey("rev")) {
rlyMde = !relay["rev"]; rlyMde = !relay["rev"];
} }
@ -202,8 +196,9 @@ void deserializeConfig() {
JsonObject light_nl = light["nl"]; JsonObject light_nl = light["nl"];
CJSON(nightlightMode, light_nl[F("mode")]); CJSON(nightlightMode, light_nl[F("mode")]);
CJSON(nightlightDelayMinsDefault, light_nl["dur"]); byte prev = nightlightDelayMinsDefault;
nightlightDelayMins = nightlightDelayMinsDefault; CJSON(nightlightDelayMinsDefault, light_nl[F("dur")]);
if (nightlightDelayMinsDefault != prev) nightlightDelayMins = nightlightDelayMinsDefault;
CJSON(nightlightTargetBri, light_nl[F("tbri")]); CJSON(nightlightTargetBri, light_nl[F("tbri")]);
CJSON(macroNl, light_nl[F("macro")]); CJSON(macroNl, light_nl[F("macro")]);
@ -231,12 +226,14 @@ void deserializeConfig() {
JsonObject if_sync_recv = if_sync["recv"]; JsonObject if_sync_recv = if_sync["recv"];
CJSON(receiveNotificationBrightness, if_sync_recv["bri"]); CJSON(receiveNotificationBrightness, if_sync_recv["bri"]);
CJSON(receiveNotificationColor, if_sync_recv["col"]); CJSON(receiveNotificationColor, if_sync_recv["col"]);
CJSON(receiveNotificationEffects, if_sync_recv["fx"]); CJSON(receiveNotificationEffects, if_sync_recv[F("fx")]);
//! following line might be a problem if called after boot
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects); receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
JsonObject if_sync_send = if_sync["send"]; JsonObject if_sync_send = if_sync["send"];
prev = notifyDirectDefault;
CJSON(notifyDirectDefault, if_sync_send[F("dir")]); CJSON(notifyDirectDefault, if_sync_send[F("dir")]);
notifyDirect = notifyDirectDefault; if (notifyDirectDefault != prev) notifyDirect = notifyDirectDefault;
CJSON(notifyButton, if_sync_send[F("btn")]); CJSON(notifyButton, if_sync_send[F("btn")]);
CJSON(notifyAlexa, if_sync_send[F("va")]); CJSON(notifyAlexa, if_sync_send[F("va")]);
CJSON(notifyHue, if_sync_send[F("hue")]); CJSON(notifyHue, if_sync_send[F("hue")]);
@ -321,9 +318,10 @@ void deserializeConfig() {
CJSON(latitude, if_ntp[F("lt")]); CJSON(latitude, if_ntp[F("lt")]);
JsonObject ol = doc[F("ol")]; JsonObject ol = doc[F("ol")];
prev = overlayDefault;
CJSON(overlayDefault ,ol[F("clock")]); // 0 CJSON(overlayDefault ,ol[F("clock")]); // 0
CJSON(countdownMode, ol[F("cntdwn")]); CJSON(countdownMode, ol[F("cntdwn")]);
overlayCurrent = overlayDefault; if (prev != overlayDefault) overlayCurrent = overlayDefault;
CJSON(overlayMin, ol["min"]); CJSON(overlayMin, ol["min"]);
CJSON(overlayMax, ol[F("max")]); CJSON(overlayMax, ol[F("max")]);
@ -398,7 +396,32 @@ void deserializeConfig() {
DEBUG_PRINTLN(F("Starting usermod config.")); DEBUG_PRINTLN(F("Starting usermod config."));
JsonObject usermods_settings = doc["um"]; JsonObject usermods_settings = doc["um"];
usermods.readFromConfig(usermods_settings); if (!usermods_settings.isNull()) usermods.readFromConfig(usermods_settings);
if (fromFS) return false;
doReboot = doc[F("rb")] | doReboot;
return (doc["sv"] | true);
}
void deserializeConfigFromFS() {
bool fromeep = false;
bool success = deserializeConfigSec();
if (!success) { //if file does not exist, try reading from EEPROM
deEEPSettings();
fromeep = true;
}
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
DEBUG_PRINTLN(F("Reading settings from /cfg.json..."));
success = readObjectFromFile("/cfg.json", nullptr, &doc);
if (!success) { //if file does not exist, try reading from EEPROM
if (!fromeep) deEEPSettings();
return;
}
deserializeConfig(doc.as<JsonObject>(), true);
} }
void serializeConfig() { void serializeConfig() {
@ -481,7 +504,7 @@ void serializeConfig() {
for (uint8_t i = 0; i < nPins; i++) ins_pin.add(pins[i]); for (uint8_t i = 0; i < nPins; i++) ins_pin.add(pins[i]);
ins[F("order")] = bus->getColorOrder(); ins[F("order")] = bus->getColorOrder();
ins["rev"] = bus->reversed; ins["rev"] = bus->reversed;
ins[F("skip")] = bus->skipFirstLed(); ins[F("skip")] = bus->skippedLeds();
ins["type"] = bus->getType(); ins["type"] = bus->getType();
ins[F("rgbw")] = bus->isRgbw(); ins[F("rgbw")] = bus->isRgbw();
} }

View File

@ -10,18 +10,18 @@
margin: 0; margin: 0;
} }
html { html {
--h: 10.55vh; --h: 10.2vh;
} }
button { button {
background: #333; background: #333;
color: #fff; color: #fff;
font-family: Verdana, Helvetica, sans-serif; font-family: Verdana, Helvetica, sans-serif;
border: 0.3ch solid #333;
display: inline-block; display: inline-block;
border: 1px solid #333;
font-size: 6vmin; font-size: 6vmin;
height: var(--h); height: var(--h);
width: 95%; width: 95%;
margin-top: 2.4vh; margin-top: 2vh;
} }
</style> </style>
<script> <script>

View File

@ -181,9 +181,7 @@
} }
} }
function GetV() function GetV(){var d=document;}
{
}
</script> </script>
<style> <style>
@import url("style.css"); @import url("style.css");

View File

@ -3,14 +3,15 @@
<head lang="en"> <head lang="en">
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=500"> <meta name="viewport" content="width=500">
<title>UI Settings</title> <title>Usermod Settings</title>
<script> <script>
var d = document; var d = document;
var umCfg = {}; var umCfg = {};
var pins = [6,7,8,9,10,11]; var pins = [6,7,8,9,10,11];
var pinO = ["reserved","reserved","reserved","reserved","reserved","reserved"], owner; var pinO = ["rsvd","rsvd","rsvd","rsvd","rsvd","rsvd"], owner;
var loc = false, locip; var loc = false, locip;
var urows; var urows;
var numM = 0;
function gId(s) { return d.getElementById(s); } function gId(s) { return d.getElementById(s); }
function isO(i) { return (i && typeof i === 'object' && !Array.isArray(i)); } function isO(i) { return (i && typeof i === 'object' && !Array.isArray(i)); }
function H() { window.open("https://github.com/Aircoookie/WLED/wiki/Settings#usermod-settings"); } function H() { window.open("https://github.com/Aircoookie/WLED/wiki/Settings#usermod-settings"); }
@ -24,7 +25,9 @@
localStorage.setItem('locIp', locip); localStorage.setItem('locIp', locip);
} }
} }
ldS(); GetV();
if (numM > 0 || locip) ldS();
else gId("um").innerHTML = "No Usermods installed.";
} }
function check(o,k) { function check(o,k) {
var n = o.name.replace("[]","").substr(-3); var n = o.name.replace("[]","").substr(-3);
@ -99,11 +102,8 @@
urows += `<hr><h3>${k}</h3>`; urows += `<hr><h3>${k}</h3>`;
addField(k,'unknown',o); addField(k,'unknown',o);
} }
if (urows==="")
urows = "No Usermods configuration found.<br>Press <i>Save</i> to initialize defaults if usermods were compiled in.";
} else {
urows = "Usermods configuration not found.<br>Most likely no Usermods exist.<br>Press <i>Save</i> to try to initialize defaults.";
} }
if (urows==="") urows = "Usermods configuration not found.<br>Press <i>Save</i> to initialize defaults.";
gId("um").innerHTML = urows; gId("um").innerHTML = urows;
}) })
.catch(function (error) { .catch(function (error) {

View File

@ -43,7 +43,7 @@
Installed version: ##VERSION##<br> Installed version: ##VERSION##<br>
Download the latest binary: <a href="https://github.com/Aircoookie/WLED/releases" target="_blank"> Download the latest binary: <a href="https://github.com/Aircoookie/WLED/releases" target="_blank">
<img src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square"></a><br> <img src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square"></a><br>
<input type='file' class="bt" name='update' accept=".bin" required><br> <input type='file' class="bt" name='update' required><br> <!--should have accept='.bin', but it prevents file upload from android app-->
<input type='submit' class="bt" value='Update!' ><br> <input type='submit' class="bt" value='Update!' ><br>
<button type="button" class="bt" onclick="B()">Back</button></form> <button type="button" class="bt" onclick="B()">Back</button></form>
<div id="msg"><b>Updating...</b><br>Please do not close or refresh the page :)</div> <div id="msg"><b>Updating...</b><br>Please do not close or refresh the page :)</div>

View File

@ -26,7 +26,8 @@ void handleButton();
void handleIO(); void handleIO();
//cfg.cpp //cfg.cpp
void deserializeConfig(); bool deserializeConfig(JsonObject doc, bool fromFS = false);
void deserializeConfigFromFS();
bool deserializeConfigSec(); bool deserializeConfigSec();
void serializeConfig(); void serializeConfig();
void serializeConfigSec(); void serializeConfigSec();

View File

@ -55,13 +55,12 @@ bool bufferedFind(const char *target, bool fromStart = true) {
size_t targetLen = strlen(target); size_t targetLen = strlen(target);
size_t index = 0; size_t index = 0;
uint16_t bufsize = 0, count = 0;
byte buf[FS_BUFSIZE]; byte buf[FS_BUFSIZE];
if (fromStart) f.seek(0); if (fromStart) f.seek(0);
while (f.position() < f.size() -1) { while (f.position() < f.size() -1) {
bufsize = f.read(buf, FS_BUFSIZE); uint16_t bufsize = f.read(buf, FS_BUFSIZE);
count = 0; uint16_t count = 0;
while (count < bufsize) { while (count < bufsize) {
if(buf[count] != target[index]) if(buf[count] != target[index])
index = 0; // reset index if any char does not match index = 0; // reset index if any char does not match
@ -97,13 +96,12 @@ bool bufferedFindSpace(uint16_t targetLen, bool fromStart = true) {
if (!f || !f.size()) return false; if (!f || !f.size()) return false;
uint16_t index = 0; uint16_t index = 0;
uint16_t bufsize = 0, count = 0;
byte buf[FS_BUFSIZE]; byte buf[FS_BUFSIZE];
if (fromStart) f.seek(0); if (fromStart) f.seek(0);
while (f.position() < f.size() -1) { while (f.position() < f.size() -1) {
bufsize = f.read(buf, FS_BUFSIZE); uint16_t bufsize = f.read(buf, FS_BUFSIZE);
count = 0; uint16_t count = 0;
while (count < bufsize) { while (count < bufsize) {
if(buf[count] == ' ') { if(buf[count] == ' ') {
@ -140,13 +138,12 @@ bool bufferedFindObjectEnd() {
if (!f || !f.size()) return false; if (!f || !f.size()) return false;
uint16_t objDepth = 0; //num of '{' minus num of '}'. return once 0 uint16_t objDepth = 0; //num of '{' minus num of '}'. return once 0
uint16_t bufsize = 0, count = 0;
//size_t start = f.position(); //size_t start = f.position();
byte buf[FS_BUFSIZE]; byte buf[FS_BUFSIZE];
while (f.position() < f.size() -1) { while (f.position() < f.size() -1) {
bufsize = f.read(buf, FS_BUFSIZE); uint16_t bufsize = f.read(buf, FS_BUFSIZE);
count = 0; uint16_t count = 0;
while (count < bufsize) { while (count < bufsize) {
if (buf[count] == '{') objDepth++; if (buf[count] == '{') objDepth++;

View File

@ -45,10 +45,10 @@ action="/update" id="uf" enctype="multipart/form-data" onsubmit="U()">
Installed version: 0.12.2-bl3<br>Download the latest binary: <a Installed version: 0.12.2-bl3<br>Download the latest binary: <a
href="https://github.com/Aircoookie/WLED/releases" target="_blank"><img href="https://github.com/Aircoookie/WLED/releases" target="_blank"><img
src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square"> src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square">
</a><br><input type="file" class="bt" name="update" accept=".bin" required><br> </a><br><input type="file" class="bt" name="update" required><br><input
<input type="submit" class="bt" value="Update!"><br><button type="button" type="submit" class="bt" value="Update!"><br><button type="button" class="bt"
class="bt" onclick="B()">Back</button></form><div id="msg"><b>Updating...</b> onclick="B()">Back</button></form><div id="msg"><b>Updating...</b><br>
<br>Please do not close or refresh the page :)</div></body></html>)====="; Please do not close or refresh the page :)</div></body></html>)=====";
// Autogenerated from wled00/data/welcome.htm, do not edit!! // Autogenerated from wled00/data/welcome.htm, do not edit!!

View File

@ -12,7 +12,7 @@ const char PAGE_settingsCss[] PROGMEM = R"=====(<style>body{font-family:Verdana,
// Autogenerated from wled00/data/settings.htm, do not edit!! // Autogenerated from wled00/data/settings.htm, do not edit!!
const char PAGE_settings[] PROGMEM = R"=====(<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>WLED Settings const char PAGE_settings[] PROGMEM = R"=====(<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>WLED Settings
</title><style> </title><style>
body{text-align:center;background:#222;height:100px;margin:0}html{--h:10.55vh}button{background:#333;color:#fff;font-family:Verdana,Helvetica,sans-serif;border:.3ch solid #333;display:inline-block;font-size:6vmin;height:var(--h);width:95%%;margin-top:2.4vh} body{text-align:center;background:#222;height:100px;margin:0}html{--h:10.2vh}button{background:#333;color:#fff;font-family:Verdana,Helvetica,sans-serif;display:inline-block;border:1px solid #333;font-size:6vmin;height:var(--h);width:95%%;margin-top:2vh}
</style><script> </style><script>
function BB(){window.frameElement&&(document.getElementById("b").style.display="none",document.documentElement.style.setProperty("--h","13.86vh"))} function BB(){window.frameElement&&(document.getElementById("b").style.display="none",document.documentElement.style.setProperty("--h","13.86vh"))}
</script></head><body onload="BB()"><form action="/"><button type="submit" </script></head><body onload="BB()"><form action="/"><button type="submit"
@ -395,9 +395,9 @@ type="submit">Save & Reboot</button></form></body></html>)=====";
// Autogenerated from wled00/data/settings_um.htm, do not edit!! // Autogenerated from wled00/data/settings_um.htm, do not edit!!
const char PAGE_settings_um[] PROGMEM = R"=====(<!DOCTYPE html><html><head lang="en"><meta charset="utf-8"><meta const char PAGE_settings_um[] PROGMEM = R"=====(<!DOCTYPE html><html><head lang="en"><meta charset="utf-8"><meta
name="viewport" content="width=500"><title>UI Settings</title><script> name="viewport" content="width=500"><title>Usermod Settings</title><script>
var owner,locip,urows,d=document,umCfg={},pins=[6,7,8,9,10,11],pinO=["reserved","reserved","reserved","reserved","reserved","reserved"],loc=!1;function gId(e){return d.getElementById(e)}function isO(e){return e&&"object"==typeof e&&!Array.isArray(e)}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#usermod-settings")}function B(){window.open("/settings","_self")}function S(){"file:"==window.location.protocol&&(loc=!0,(locip=localStorage.getItem("locIp"))||(locip=prompt("File Mode. Please enter WLED IP!"),localStorage.setItem("locIp",locip))),ldS()}function check(e,o){var i=e.name.replace("[]","").substr(-3);if("number"==e.type&&"pin"==i.substr(0,3))for(var n=0;n<pins.length;n++)if(o!=pinO[n]){if(e.value==pins[n]||e.value<-1||e.value>39){e.style.color="red";break}e.style.color=e.value>33?"orange":"#fff"}}function getPins(e){if(isO(e))for(const[i,n]of Object.entries(e))if(isO(n))owner=i,getPins(n);else if("pin"==i.replace("[]","").substr(-3))if(Array.isArray(n))for(var o=0;o<n.length;o++)n[o]>=0&&(pins.push(n[o]),pinO.push(owner));else n>=0&&(pins.push(n),pinO.push(owner));else if(Array.isArray(n))for(o=0;o<n.length;o++)getPins(n[o])}function addField(e,o,i,n=!1){if(isO(i))for(const[o,n]of Object.entries(i))addField(e,o,n);else if(Array.isArray(i))for(var r=0;r<i.length;r++)addField(e,o,i[r],!0);else{var s,t;switch(typeof i){case"boolean":s="checkbox",t=i?'checked value="on"':"";break;case"number":s="number",t=`value="${parseInt(i,10)}"`;break;case"string":default:s="text",t=`value="${i}"`}"checkbox"==s&&(urows+=`<input type="hidden" name="${e}_${o}${n?"[]":""}" value="off">`),urows+=`${o}: <input type="${s}" name="${e}_${o}${n?"[]":""}" ${t} oninput="check(this,'${e}')"><br>`}}function ldS(){fetch((loc?"http://"+locip:"")+"/cfg.json",{method:"get"}).then(e=>(e.ok||(gId("lserr").style.display="inline"),e.json())).then(e=>{if(umCfg=e.um,getPins(e),urows="",isO(umCfg)){for(const[e,o]of Object.entries(umCfg))urows+=`<hr><h3>${e}</h3>`,addField(e,"unknown",o);""===urows&&(urows="No Usermods configuration found.<br>Press <i>Save</i> to initialize defaults if usermods were compiled in.")}else urows="Usermods configuration not found.<br>Most likely no Usermods exist.<br>Press <i>Save</i> to try to initialize defaults.";gId("um").innerHTML=urows}).catch((function(e){gId("lserr").style.display="inline",console.log(e)}))}function svS(e){e.preventDefault(),console.log(d.Sf),d.Sf.checkValidity()&&d.Sf.submit()}function GetV(){} var owner,locip,urows,d=document,umCfg={},pins=[6,7,8,9,10,11],pinO=["rsvd","rsvd","rsvd","rsvd","rsvd","rsvd"],loc=!1,numM=0;function gId(e){return d.getElementById(e)}function isO(e){return e&&"object"==typeof e&&!Array.isArray(e)}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#usermod-settings")}function B(){window.open("/settings","_self")}function S(){"file:"==window.location.protocol&&(loc=!0,(locip=localStorage.getItem("locIp"))||(locip=prompt("File Mode. Please enter WLED IP!"),localStorage.setItem("locIp",locip))),GetV(),numM>0||locip?ldS():gId("um").innerHTML="No Usermods installed."}function check(e,n){var o=e.name.replace("[]","").substr(-3);if("number"==e.type&&"pin"==o.substr(0,3))for(var i=0;i<pins.length;i++)if(n!=pinO[i]){if(e.value==pins[i]||e.value<-1||e.value>39){e.style.color="red";break}e.style.color=e.value>33?"orange":"#fff"}}function getPins(e){if(isO(e))for(const[o,i]of Object.entries(e))if(isO(i))owner=o,getPins(i);else if("pin"==o.replace("[]","").substr(-3))if(Array.isArray(i))for(var n=0;n<i.length;n++)i[n]>=0&&(pins.push(i[n]),pinO.push(owner));else i>=0&&(pins.push(i),pinO.push(owner));else if(Array.isArray(i))for(n=0;n<i.length;n++)getPins(i[n])}function addField(e,n,o,i=!1){if(isO(o))for(const[n,i]of Object.entries(o))addField(e,n,i);else if(Array.isArray(o))for(var t=0;t<o.length;t++)addField(e,n,o[t],!0);else{var r,s;switch(typeof o){case"boolean":r="checkbox",s=o?'checked value="on"':"";break;case"number":r="number",s=`value="${parseInt(o,10)}"`;break;case"string":default:r="text",s=`value="${o}"`}"checkbox"==r&&(urows+=`<input type="hidden" name="${e}_${n}${i?"[]":""}" value="off">`),urows+=`${n}: <input type="${r}" name="${e}_${n}${i?"[]":""}" ${s} oninput="check(this,'${e}')"><br>`}}function ldS(){fetch((loc?"http://"+locip:"")+"/cfg.json",{method:"get"}).then(e=>(e.ok||(gId("lserr").style.display="inline"),e.json())).then(e=>{if(umCfg=e.um,getPins(e),urows="",isO(umCfg))for(const[e,n]of Object.entries(umCfg))urows+=`<hr><h3>${e}</h3>`,addField(e,"unknown",n);""===urows&&(urows="Usermods configuration not found.<br>Press <i>Save</i> to initialize defaults."),gId("um").innerHTML=urows}).catch((function(e){gId("lserr").style.display="inline",console.log(e)}))}function svS(e){e.preventDefault(),console.log(d.Sf),d.Sf.checkValidity()&&d.Sf.submit()}function GetV() {var d=document;
</script>%CSS%%SCSS%</head><body onload="S()"><form %CSS%%SCSS%</head><body onload="S()"><form
id="form_s" name="Sf" method="post" onsubmit="svS(event)"><div class="toprow"> id="form_s" name="Sf" method="post" onsubmit="svS(event)"><div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div><button <div class="helpB"><button type="button" onclick="H()">?</button></div><button
type="button" onclick="B()">Back</button><button type="submit">Save</button><br> type="button" onclick="B()">Back</button><button type="submit">Save</button><br>

View File

@ -227,7 +227,7 @@ void decodeIR24(uint32_t code)
switch (code) { switch (code) {
case IR24_BRIGHTER : incBrightness(); break; case IR24_BRIGHTER : incBrightness(); break;
case IR24_DARKER : decBrightness(); break; case IR24_DARKER : decBrightness(); break;
case IR24_OFF : briLast = bri; bri = 0; break; case IR24_OFF : if (bri > 0) briLast = bri; bri = 0; break;
case IR24_ON : bri = briLast; break; case IR24_ON : bri = briLast; break;
case IR24_RED : colorFromUint32(COLOR_RED); break; case IR24_RED : colorFromUint32(COLOR_RED); break;
case IR24_REDDISH : colorFromUint32(COLOR_REDDISH); break; case IR24_REDDISH : colorFromUint32(COLOR_REDDISH); break;
@ -259,7 +259,7 @@ void decodeIR24OLD(uint32_t code)
switch (code) { switch (code) {
case IR24_OLD_BRIGHTER : incBrightness(); break; case IR24_OLD_BRIGHTER : incBrightness(); break;
case IR24_OLD_DARKER : decBrightness(); break; case IR24_OLD_DARKER : decBrightness(); break;
case IR24_OLD_OFF : briLast = bri; bri = 0; break; case IR24_OLD_OFF : if (bri > 0) briLast = bri; bri = 0; break;
case IR24_OLD_ON : bri = briLast; break; case IR24_OLD_ON : bri = briLast; break;
case IR24_OLD_RED : colorFromUint32(COLOR_RED); break; case IR24_OLD_RED : colorFromUint32(COLOR_RED); break;
case IR24_OLD_REDDISH : colorFromUint32(COLOR_REDDISH); break; case IR24_OLD_REDDISH : colorFromUint32(COLOR_REDDISH); break;
@ -292,7 +292,7 @@ void decodeIR24CT(uint32_t code)
switch (code) { switch (code) {
case IR24_CT_BRIGHTER : incBrightness(); break; case IR24_CT_BRIGHTER : incBrightness(); break;
case IR24_CT_DARKER : decBrightness(); break; case IR24_CT_DARKER : decBrightness(); break;
case IR24_CT_OFF : briLast = bri; bri = 0; break; case IR24_CT_OFF : if (bri > 0) briLast = bri; bri = 0; break;
case IR24_CT_ON : bri = briLast; break; case IR24_CT_ON : bri = briLast; break;
case IR24_CT_RED : colorFromUint32(COLOR_RED); break; case IR24_CT_RED : colorFromUint32(COLOR_RED); break;
case IR24_CT_REDDISH : colorFromUint32(COLOR_REDDISH); break; case IR24_CT_REDDISH : colorFromUint32(COLOR_REDDISH); break;
@ -327,7 +327,7 @@ void decodeIR40(uint32_t code)
switch (code) { switch (code) {
case IR40_BPLUS : incBrightness(); break; case IR40_BPLUS : incBrightness(); break;
case IR40_BMINUS : decBrightness(); break; case IR40_BMINUS : decBrightness(); break;
case IR40_OFF : briLast = bri; bri = 0; break; case IR40_OFF : if (bri > 0) briLast = bri; bri = 0; break;
case IR40_ON : bri = briLast; break; case IR40_ON : bri = briLast; break;
case IR40_RED : colorFromUint24(COLOR_RED); break; case IR40_RED : colorFromUint24(COLOR_RED); break;
case IR40_REDDISH : colorFromUint24(COLOR_REDDISH); break; case IR40_REDDISH : colorFromUint24(COLOR_REDDISH); break;
@ -384,7 +384,7 @@ void decodeIR44(uint32_t code)
switch (code) { switch (code) {
case IR44_BPLUS : incBrightness(); break; case IR44_BPLUS : incBrightness(); break;
case IR44_BMINUS : decBrightness(); break; case IR44_BMINUS : decBrightness(); break;
case IR44_OFF : briLast = bri; bri = 0; break; case IR44_OFF : if (bri > 0) briLast = bri; bri = 0; break;
case IR44_ON : bri = briLast; break; case IR44_ON : bri = briLast; break;
case IR44_RED : colorFromUint24(COLOR_RED); break; case IR44_RED : colorFromUint24(COLOR_RED); break;
case IR44_REDDISH : colorFromUint24(COLOR_REDDISH); break; case IR44_REDDISH : colorFromUint24(COLOR_REDDISH); break;
@ -447,7 +447,7 @@ void decodeIR21(uint32_t code)
switch (code) { switch (code) {
case IR21_BRIGHTER: incBrightness(); break; case IR21_BRIGHTER: incBrightness(); break;
case IR21_DARKER: decBrightness(); break; case IR21_DARKER: decBrightness(); break;
case IR21_OFF: briLast = bri; bri = 0; break; case IR21_OFF: if (bri > 0) briLast = bri; bri = 0; break;
case IR21_ON: bri = briLast; break; case IR21_ON: bri = briLast; break;
case IR21_RED: colorFromUint32(COLOR_RED); break; case IR21_RED: colorFromUint32(COLOR_RED); break;
case IR21_REDDISH: colorFromUint32(COLOR_REDDISH); break; case IR21_REDDISH: colorFromUint32(COLOR_REDDISH); break;

View File

@ -166,7 +166,7 @@ void deserializeSegment(JsonObject elem, byte it)
if (icol.isNull()) break; if (icol.isNull()) break;
byte sz = icol.size(); byte sz = icol.size();
if (sz == 0 && sz > 4) break; if (sz == 0 || sz > 4) break;
int rgbw[] = {0,0,0,0}; int rgbw[] = {0,0,0,0};
copyArray(icol, rgbw); copyArray(icol, rgbw);
@ -198,6 +198,8 @@ bool deserializeState(JsonObject root)
bool on = root["on"] | (bri > 0); bool on = root["on"] | (bri > 0);
if (!on != !bri) toggleOnOff(); if (!on != !bri) toggleOnOff();
if (root["on"].is<const char*>() && root["on"].as<const char*>()[0] == 't') toggleOnOff();
int tr = root[F("transition")] | -1; int tr = root[F("transition")] | -1;
if (tr >= 0) if (tr >= 0)
{ {
@ -813,6 +815,9 @@ void serveJson(AsyncWebServerRequest* request, uint8_t versionAPI)
request->send_P(200, "application/json", JSON_palette_names); request->send_P(200, "application/json", JSON_palette_names);
return; return;
} }
else if (url.indexOf("cfg") > 0 && handleFileRead(request, "/cfg.json")) {
return;
}
else if (url.length() > 6) { //not just /json else if (url.length() > 6) { //not just /json
request->send( 501, "application/json", F("{\"error\":\"Not implemented\"}")); request->send( 501, "application/json", F("{\"error\":\"Not implemented\"}"));
return; return;

View File

@ -52,23 +52,22 @@ void onMqttConnect(bool sessionPresent)
} }
void onMqttMessage(char* topic, char* payload0, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) { void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
DEBUG_PRINT(F("MQTT msg: ")); DEBUG_PRINT(F("MQTT msg: "));
DEBUG_PRINTLN(topic); DEBUG_PRINTLN(topic);
// paranoia check to avoid npe if no payload // paranoia check to avoid npe if no payload
if (payload0==nullptr) { if (payload==nullptr) {
DEBUG_PRINTLN(F("no payload -> leave")); DEBUG_PRINTLN(F("no payload -> leave"));
return; return;
} }
//make a copy of the payload to 0-terminate it
// payload is not always null terminated char* payloadStr = new char[len+1];
char *payload = new char[len+1]; if (payloadStr == nullptr) return; //no mem
if (payload==nullptr) return; // out of memory strncpy(payloadStr, payload, len);
strncpy(payload,payload0,len); payloadStr[len] = '\0';
payload[len] = '\0'; DEBUG_PRINTLN(payloadStr);
DEBUG_PRINTLN(payload);
size_t topicPrefixLen = strlen(mqttDeviceTopic); size_t topicPrefixLen = strlen(mqttDeviceTopic);
if (strncmp(topic, mqttDeviceTopic, topicPrefixLen) == 0) { if (strncmp(topic, mqttDeviceTopic, topicPrefixLen) == 0) {
@ -79,8 +78,8 @@ void onMqttMessage(char* topic, char* payload0, AsyncMqttClientMessageProperties
topic += topicPrefixLen; topic += topicPrefixLen;
} else { } else {
// Non-Wled Topic used here. Probably a usermod subscribed to this topic. // Non-Wled Topic used here. Probably a usermod subscribed to this topic.
usermods.onMqttMessage(topic, payload); usermods.onMqttMessage(topic, payloadStr);
delete[] payload; delete[] payloadStr;
return; return;
} }
} }
@ -88,26 +87,26 @@ void onMqttMessage(char* topic, char* payload0, AsyncMqttClientMessageProperties
//Prefix is stripped from the topic at this point //Prefix is stripped from the topic at this point
if (strcmp_P(topic, PSTR("/col")) == 0) { if (strcmp_P(topic, PSTR("/col")) == 0) {
colorFromDecOrHexString(col, payload); colorFromDecOrHexString(col, (char*)payloadStr);
colorUpdated(NOTIFIER_CALL_MODE_DIRECT_CHANGE); colorUpdated(NOTIFIER_CALL_MODE_DIRECT_CHANGE);
} else if (strcmp_P(topic, PSTR("/api")) == 0) { } else if (strcmp_P(topic, PSTR("/api")) == 0) {
if (payload[0] == '{') { //JSON API if (payload[0] == '{') { //JSON API
DynamicJsonDocument doc(JSON_BUFFER_SIZE); DynamicJsonDocument doc(JSON_BUFFER_SIZE);
deserializeJson(doc, payload); deserializeJson(doc, payloadStr);
deserializeState(doc.as<JsonObject>()); deserializeState(doc.as<JsonObject>());
} else { //HTTP API } else { //HTTP API
String apireq = "win&"; String apireq = "win&";
apireq += payload; apireq += (char*)payloadStr;
handleSet(nullptr, apireq); handleSet(nullptr, apireq);
} }
} else if (strlen(topic) != 0) { } else if (strlen(topic) != 0) {
// non standard topic, check with usermods // non standard topic, check with usermods
usermods.onMqttMessage(topic, payload); usermods.onMqttMessage(topic, payloadStr);
} else { } else {
// topmost topic (just wled/MAC) // topmost topic (just wled/MAC)
parseMQTTBriPayload(payload); parseMQTTBriPayload(payloadStr);
} }
delete[] payload; delete[] payloadStr;
} }
@ -132,7 +131,7 @@ void publishMqtt()
strcpy(subuf, mqttDeviceTopic); strcpy(subuf, mqttDeviceTopic);
strcat_P(subuf, PSTR("/status")); strcat_P(subuf, PSTR("/status"));
mqtt->publish(subuf, 0, true, "online"); // do not retain message mqtt->publish(subuf, 0, false, "online"); // do not retain message
char apires[1024]; char apires[1024];
XML_response(nullptr, apires); XML_response(nullptr, apires);

View File

@ -112,7 +112,7 @@ void updateTimezone() {
break; break;
} }
case TZ_AUSTRALIA_NORTHERN : { case TZ_AUSTRALIA_NORTHERN : {
tcrStandard = {First, Sun, Apr, 3, 570}; //ACST = UTC + 9.5 hours tcrDaylight = {First, Sun, Apr, 3, 570}; //ACST = UTC + 9.5 hours
tcrStandard = tcrDaylight; tcrStandard = tcrDaylight;
break; break;
} }

View File

@ -63,10 +63,9 @@ void _overlayAnalogClock()
} }
if (analogClock5MinuteMarks) if (analogClock5MinuteMarks)
{ {
int pix; for (byte i = 0; i <= 12; i++)
for (int i = 0; i <= 12; i++)
{ {
pix = analogClock12pixel + round((overlaySize / 12.0) *i); int pix = analogClock12pixel + round((overlaySize / 12.0) *i);
if (pix > overlayMax) pix -= overlaySize; if (pix > overlayMax) pix -= overlaySize;
strip.setPixelColor(pix, 0x00FFAA); strip.setPixelColor(pix, 0x00FFAA);
} }

View File

@ -19,14 +19,14 @@ uint16_t playlistEntryDur = 0;
void shufflePlaylist() { void shufflePlaylist() {
int currentIndex = playlistLen, randomIndex; int currentIndex = playlistLen;
PlaylistEntry temporaryValue, *entries = reinterpret_cast<PlaylistEntry*>(playlistEntries); PlaylistEntry temporaryValue, *entries = reinterpret_cast<PlaylistEntry*>(playlistEntries);
// While there remain elements to shuffle... // While there remain elements to shuffle...
while (currentIndex--) { while (currentIndex--) {
// Pick a random element... // Pick a random element...
randomIndex = random(0, currentIndex); int randomIndex = random(0, currentIndex);
// And swap it with the current element. // And swap it with the current element.
temporaryValue = entries[currentIndex]; temporaryValue = entries[currentIndex];
entries[currentIndex] = entries[randomIndex]; entries[currentIndex] = entries[randomIndex];

View File

@ -31,7 +31,7 @@ bool isAsterisksOnly(const char* str, byte maxLen)
void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
{ {
//0: menu 1: wifi 2: leds 3: ui 4: sync 5: time 6: sec 7: DMX //0: menu 1: wifi 2: leds 3: ui 4: sync 5: time 6: sec 7: DMX 8: usermods
if (subPage <1 || subPage >8) return; if (subPage <1 || subPage >8) return;
//WIFI SETTINGS //WIFI SETTINGS
@ -84,7 +84,6 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
if (btnPin>=0 && pinManager.isPinAllocated(btnPin)) pinManager.deallocatePin(btnPin); if (btnPin>=0 && pinManager.isPinAllocated(btnPin)) pinManager.deallocatePin(btnPin);
strip.isRgbw = false; strip.isRgbw = false;
uint8_t colorOrder, type, skip; uint8_t colorOrder, type, skip;
uint16_t length, start; uint16_t length, start;
uint8_t pins[5] = {255, 255, 255, 255, 255}; uint8_t pins[5] = {255, 255, 255, 255, 255};

File diff suppressed because it is too large Load Diff

View File

@ -220,7 +220,7 @@ void WLED::loop()
yield(); yield();
if (!offMode) if (!offMode || strip.isOffRefreshRequred)
strip.service(); strip.service();
#ifdef ESP8266 #ifdef ESP8266
else if (!noWifiSleep) else if (!noWifiSleep)
@ -370,7 +370,7 @@ void WLED::setup()
updateFSInfo(); updateFSInfo();
DEBUG_PRINTLN(F("Reading config")); DEBUG_PRINTLN(F("Reading config"));
deserializeConfig(); deserializeConfigFromFS();
/* /*
#if STATUSLED #if STATUSLED
bool lStatusLed = false; bool lStatusLed = false;

View File

@ -8,7 +8,7 @@
*/ */
// version code in format yymmddb (b = daily build) // version code in format yymmddb (b = daily build)
#define VERSION 2105161 #define VERSION 2105171
//uncomment this if you have a "my_config.h" file you'd like to use //uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG //#define WLED_USE_MY_CONFIG

View File

@ -321,7 +321,7 @@ void loadSettingsFromEEPROM()
notifyMacro = EEPROM.read(2201); notifyMacro = EEPROM.read(2201);
strip.rgbwMode = EEPROM.read(2203); strip.rgbwMode = EEPROM.read(2203);
//was skipFirstLed = EEPROM.read(2204); //skipFirstLed = EEPROM.read(2204);
if (EEPROM.read(2210) || EEPROM.read(2211) || EEPROM.read(2212)) if (EEPROM.read(2210) || EEPROM.read(2211) || EEPROM.read(2212))
{ {

View File

@ -85,6 +85,7 @@ void initServer()
AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/json", [](AsyncWebServerRequest *request) { AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/json", [](AsyncWebServerRequest *request) {
bool verboseResponse = false; bool verboseResponse = false;
uint8_t vAPI = 1; uint8_t vAPI = 1;
bool isConfig = false;
{ //scope JsonDocument so it releases its buffer { //scope JsonDocument so it releases its buffer
DynamicJsonDocument jsonBuffer(JSON_BUFFER_SIZE); DynamicJsonDocument jsonBuffer(JSON_BUFFER_SIZE);
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject)); DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject));
@ -92,6 +93,9 @@ void initServer()
if (error || root.isNull()) { if (error || root.isNull()) {
request->send(400, "application/json", F("{\"error\":9}")); return; request->send(400, "application/json", F("{\"error\":9}")); return;
} }
const String& url = request->url();
isConfig = url.indexOf("cfg") > -1;
if (!isConfig) {
if (root.containsKey("rev")) if (root.containsKey("rev"))
{ {
vAPI = root["rev"] | 1; vAPI = root["rev"] | 1;
@ -99,9 +103,16 @@ void initServer()
fileDoc = &jsonBuffer; // used for applying presets (presets.cpp) fileDoc = &jsonBuffer; // used for applying presets (presets.cpp)
verboseResponse = deserializeState(root); verboseResponse = deserializeState(root);
fileDoc = nullptr; fileDoc = nullptr;
} else {
verboseResponse = deserializeConfig(root); //use verboseResponse to determine whether cfg change should be saved immediately
}
}
if (verboseResponse) {
if (!isConfig) {
serveJson(request,vAPI); return; //if JSON contains "v"
} else {
serializeConfig(); //Save new settings to FS
} }
if (verboseResponse) { //if JSON contains "v"
serveJson(request,vAPI); return;
} }
request->send(200, "application/json", F("{\"success\":true}")); request->send(200, "application/json", F("{\"success\":true}"));
}); });

View File

@ -185,7 +185,7 @@ void getSettingsJS(byte subPage, char* dest)
obuf = dest; obuf = dest;
olen = 0; olen = 0;
if (subPage <1 || subPage >7) return; if (subPage <1 || subPage >8) return;
if (subPage == 1) if (subPage == 1)
{ {
@ -361,7 +361,7 @@ void getSettingsJS(byte subPage, char* dest)
sappend('v',co,bus->getColorOrder()); sappend('v',co,bus->getColorOrder());
sappend('v',ls,bus->getStart()); sappend('v',ls,bus->getStart());
sappend('c',cv,bus->reversed); sappend('c',cv,bus->reversed);
sappend('c',sl,bus->skipFirstLed()); sappend('c',sl,bus->skippedLeds());
// sappend('c',ew,bus->isRgbw()); // sappend('c',ew,bus->isRgbw());
} }
sappend('v',SET_F("MA"),strip.ablMilliampsMax); sappend('v',SET_F("MA"),strip.ablMilliampsMax);
@ -579,5 +579,13 @@ void getSettingsJS(byte subPage, char* dest)
sappend('i',SET_F("CH15"),DMXFixtureMap[14]); sappend('i',SET_F("CH15"),DMXFixtureMap[14]);
} }
#endif #endif
if (subPage == 8) //usermods
{
oappend(SET_F("numM="));
oappendi(usermods.getModCount());
oappend(";");
}
oappend(SET_F("}</script>")); oappend(SET_F("}</script>"));
} }