1st working usermod settings (Temperature).
Added color on pin conflicts in LEDs setting page.
This commit is contained in:
parent
9a6d709082
commit
13b3b2fd23
@ -333,6 +333,18 @@ const char PAGE_settings_dmx[] PROGMEM = R"=====()=====";
|
|||||||
"function GetV() {var d=document;\n"
|
"function GetV() {var d=document;\n"
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
file: "settings_um.htm",
|
||||||
|
name: "PAGE_settings_um",
|
||||||
|
prepend: "=====(",
|
||||||
|
append: ")=====",
|
||||||
|
method: "plaintext",
|
||||||
|
filter: "html-minify",
|
||||||
|
mangle: (str) =>
|
||||||
|
str
|
||||||
|
.replace(/\<link rel="stylesheet".*\>/gms, "")
|
||||||
|
.replace(/\<style\>.*\<\/style\>/gms, "%CSS%%SCSS%")
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"wled00/html_settings.h"
|
"wled00/html_settings.h"
|
||||||
);
|
);
|
||||||
|
@ -173,45 +173,48 @@ class UsermodTemperature : public Usermod {
|
|||||||
/**
|
/**
|
||||||
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
|
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
|
||||||
* Values in the state object may be modified by connected clients
|
* Values in the state object may be modified by connected clients
|
||||||
* Add "pin_Temperature" to json state. This can be used to check which GPIO pin usermod uses.
|
* Add "<usermodename>_<usermodparam>" to json state. This can be used to check which GPIO pin usermod uses.
|
||||||
*/
|
*/
|
||||||
void addToJsonState(JsonObject &root)
|
void addToJsonState(JsonObject &root)
|
||||||
{
|
{
|
||||||
root[F("pin_Temperature")] = temperaturePin;
|
//root[F("Temperature_pin")] = temperaturePin;
|
||||||
root[F("mode_Temperature")] = degC ? ("C") : ("F");
|
//root[F("Temperature_degC")] = degC ? ("C") : ("F");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
|
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
|
||||||
* Values in the state object may be modified by connected clients
|
* Values in the state object may be modified by connected clients
|
||||||
* Read "pin_Temperature" from json state and and change GPIO pin used.
|
* Read "<usermodname>_<usermodparam>" from json state and and change settings (i.e. GPIO pin) used.
|
||||||
*/
|
*/
|
||||||
void readFromJsonState(JsonObject &root) {
|
void readFromJsonState(JsonObject &root) {
|
||||||
if (root[F("pin_Temperature")] != nullptr) {
|
if (root[F("Temperature_pin")] != nullptr) {
|
||||||
int8_t pin = (int)root[F("pin_Temperature")];
|
int8_t pin = (int)root[F("Temperature_pin")];
|
||||||
// deallocate pin and release memory
|
if (pin != temperaturePin) {
|
||||||
pinManager.deallocatePin(temperaturePin);
|
// deallocate pin and release memory
|
||||||
delete sensor;
|
delete sensor;
|
||||||
delete oneWire;
|
delete oneWire;
|
||||||
// disable usermod
|
pinManager.deallocatePin(temperaturePin);
|
||||||
temperaturePin = -1;
|
// disable usermod
|
||||||
disabled = true;
|
temperaturePin = -1;
|
||||||
// check if pin is OK
|
disabled = true;
|
||||||
if (pin>=0 && pinManager.allocatePin(pin,false)) {
|
// check if pin is OK
|
||||||
// allocat memory
|
if (pin>=0 && pinManager.allocatePin(pin,false)) {
|
||||||
oneWire = new OneWire(pin);
|
// allocat memory
|
||||||
sensor = new DallasTemperature(oneWire);
|
oneWire = new OneWire(pin);
|
||||||
if (sensor) {
|
sensor = new DallasTemperature(oneWire);
|
||||||
temperaturePin = pin;
|
if (sensor) {
|
||||||
sensor->begin();
|
temperaturePin = pin;
|
||||||
disabled = !sensor->getAddress(sensorDeviceAddress, 0);
|
sensor->begin();
|
||||||
} else {
|
disabled = !sensor->getAddress(sensorDeviceAddress, 0);
|
||||||
pinManager.deallocatePin(pin);
|
} else {
|
||||||
|
pinManager.deallocatePin(pin);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (root[F("mode_Temperature")] != nullptr) {
|
if (root[F("Temperature_degC")] != nullptr) {
|
||||||
degC = (root[F("mode_Temperature")]==String(PSTR("C")));
|
String strDegC = root[F("Temperature_degC")]; // checkbox -> off or on
|
||||||
|
degC = (bool) (strDegC!="off"); // off is guaranteed to be present
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,9 +223,9 @@ class UsermodTemperature : public Usermod {
|
|||||||
*/
|
*/
|
||||||
void addToConfig(JsonObject &root) {
|
void addToConfig(JsonObject &root) {
|
||||||
// we add JSON object: {"Temperature": {"pin": 0, "degC": true}}
|
// we add JSON object: {"Temperature": {"pin": 0, "degC": true}}
|
||||||
JsonObject top = root.createNestedObject(F("Temperature"));
|
JsonObject top = root.createNestedObject(F("Temperature")); // usermodname
|
||||||
top[F("pin")] = temperaturePin;
|
top["pin"] = temperaturePin; // usermodparam
|
||||||
top[F("degC")] = degC;
|
top[F("degC")] = degC; // usermodparam
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -231,8 +234,8 @@ class UsermodTemperature : public Usermod {
|
|||||||
void readFromConfig(JsonObject &root) {
|
void readFromConfig(JsonObject &root) {
|
||||||
// we look for JSON object: {"Temperature": {"pin": 0, "degC": true}}
|
// we look for JSON object: {"Temperature": {"pin": 0, "degC": true}}
|
||||||
JsonObject top = root[F("Temperature")];
|
JsonObject top = root[F("Temperature")];
|
||||||
if (!top.isNull() && top[F("pin")] != nullptr) {
|
if (!top.isNull() && top["pin"] != nullptr) {
|
||||||
temperaturePin = (int)top[F("pin")];
|
temperaturePin = (int)top["pin"];
|
||||||
degC = top[F("degC")] != nullptr ? top[F("degC")] : true;
|
degC = top[F("degC")] != nullptr ? top[F("degC")] : true;
|
||||||
} else {
|
} else {
|
||||||
DEBUG_PRINTLN(F("No Temperature sensor config found. (Using defaults.)"));
|
DEBUG_PRINTLN(F("No Temperature sensor config found. (Using defaults.)"));
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
html {
|
html {
|
||||||
--h: 11.55vh;
|
--h: 10.55vh;
|
||||||
}
|
}
|
||||||
button {
|
button {
|
||||||
background: #333;
|
background: #333;
|
||||||
@ -18,7 +18,7 @@
|
|||||||
font-family: Verdana, Helvetica, sans-serif;
|
font-family: Verdana, Helvetica, sans-serif;
|
||||||
border: 0.3ch solid #333;
|
border: 0.3ch solid #333;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: 8vmin;
|
font-size: 6vmin;
|
||||||
height: var(--h);
|
height: var(--h);
|
||||||
width: 95%;
|
width: 95%;
|
||||||
margin-top: 2.4vh;
|
margin-top: 2.4vh;
|
||||||
@ -41,6 +41,7 @@
|
|||||||
<form action="/settings/ui"><button type="submit">User Interface</button></form>
|
<form action="/settings/ui"><button type="submit">User Interface</button></form>
|
||||||
<form action="/settings/sync"><button type="submit">Sync Interfaces</button></form>
|
<form action="/settings/sync"><button type="submit">Sync Interfaces</button></form>
|
||||||
<form action="/settings/time"><button type="submit">Time & Macros</button></form>
|
<form action="/settings/time"><button type="submit">Time & Macros</button></form>
|
||||||
|
<form action="/settings/um"><button type="submit">Usermods</button></form>
|
||||||
<form action="/settings/sec"><button type="submit">Security & Updates</button></form>
|
<form action="/settings/sec"><button type="submit">Security & Updates</button></form>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -163,6 +163,18 @@
|
|||||||
var lc=d.getElementsByName("LC"+LCs[i].name.substring(2))[0];
|
var lc=d.getElementsByName("LC"+LCs[i].name.substring(2))[0];
|
||||||
lc.max=maxPB;
|
lc.max=maxPB;
|
||||||
}
|
}
|
||||||
|
if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3" || nm=="L4" || nm=="RL" || nm=="BT" || nm=="IR" || nm=="AX")
|
||||||
|
if (LCs[i].value!="" && LCs[i].value!="-1") {
|
||||||
|
var p = [];
|
||||||
|
if (d.um_p && Array.isArray(d.um_p)) for (k=0;k<d.um_p.length;k++) p.push(d.um_p[k]);
|
||||||
|
for (j=0; j<LCs.length; j++) {
|
||||||
|
if (i==j) continue;
|
||||||
|
var n2 = LCs[j].name.substring(0,2);
|
||||||
|
if (n2=="L0" || n2=="L1" || n2=="L2" || n2=="L3" || n2=="L4" || n2=="RL" || n2=="BT" || n2=="IR" || n2=="AX")
|
||||||
|
if (LCs[j].value!="" && LCs[j].value!="-1") p.push(parseInt(LCs[j].value,10));
|
||||||
|
}
|
||||||
|
if (p.some((e)=>e==parseInt(LCs[i].value,10))) LCs[i].style.color="red"; else LCs[i].style.color="#fff";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d.getElementById("LC").readOnly) d.getElementsByName("LC")[0].value = sLC;
|
if (d.getElementById("LC").readOnly) d.getElementsByName("LC")[0].value = sLC;
|
||||||
|
125
wled00/data/settings_um.htm
Normal file
125
wled00/data/settings_um.htm
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head lang="en">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=500">
|
||||||
|
<title>UI Settings</title>
|
||||||
|
<script>
|
||||||
|
var d = document;
|
||||||
|
var umCfg = {};
|
||||||
|
var pins = [6,7,8,9,10,11];
|
||||||
|
var pinO = ["reserved","reserved","reserved","reserved","reserved","reserved"], owner;
|
||||||
|
var loc = false, locip;
|
||||||
|
function gId(s) { return d.getElementById(s); }
|
||||||
|
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 B() { window.open("/settings","_self"); }
|
||||||
|
function S() {
|
||||||
|
if (window.location.protocol == "file:") {
|
||||||
|
loc = true;
|
||||||
|
locip = localStorage.getItem('locIp');
|
||||||
|
if (!locip) {
|
||||||
|
locip = prompt("File Mode. Please enter WLED IP!");
|
||||||
|
localStorage.setItem('locIp', locip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ldS();
|
||||||
|
}
|
||||||
|
function check(o,k) {
|
||||||
|
if (o.type=="number" && o.name.substr(-4)=="_pin") {
|
||||||
|
for (var i=0; i<pins.length; i++) {
|
||||||
|
if (k==pinO[i]) continue;
|
||||||
|
if (o.value==pins[i]) { o.style.color="red"; break; } else o.style.color="#fff";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function getPins(o) {
|
||||||
|
if (isO(o)) {
|
||||||
|
for (const [k,v] of Object.entries(o)) {
|
||||||
|
if (isO(v)) {
|
||||||
|
owner = k;
|
||||||
|
getPins(v);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (k=="pin") {
|
||||||
|
if (Array.isArray(v)) {
|
||||||
|
for (var i=0; i<v.length; i++) if (v[i]>=0) { pins.push(v[i]); pinO.push(owner); }
|
||||||
|
} else {
|
||||||
|
if (v>=0) { pins.push(v); pinO.push(owner); }
|
||||||
|
}
|
||||||
|
} else if (Array.isArray(v)) {
|
||||||
|
for (var i=0; i<v.length; i++) getPins(v[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function ldS() {
|
||||||
|
var url = (loc?`http://${locip}`:'') + '/cfg.json';
|
||||||
|
fetch(url, {
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (!res.ok) gId('lserr').style.display = "inline";
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.then(json => {
|
||||||
|
umCfg = json.um;
|
||||||
|
getPins(json);
|
||||||
|
var urows="";
|
||||||
|
if (isO(umCfg)) {
|
||||||
|
for (const [k,o] of Object.entries(umCfg)) {
|
||||||
|
urows += `<hr><h3>${k}</h3>`;
|
||||||
|
if (isO(o)) {
|
||||||
|
for (const [s,v] of Object.entries(o)) {
|
||||||
|
var t,c;
|
||||||
|
switch (typeof v) {
|
||||||
|
case "boolean":
|
||||||
|
t = "checkbox"; c = v ? `checked value="on"` : ""; break;
|
||||||
|
case "number":
|
||||||
|
t = "number"; c = `value="${parseInt(v,10)}"`; break;
|
||||||
|
case "string":
|
||||||
|
t = "text"; c = `value="${v}"`; break;
|
||||||
|
default:
|
||||||
|
t = "text"; c = `value="${v}"`; break;
|
||||||
|
}
|
||||||
|
// https://stackoverflow.com/questions/11657123/posting-both-checked-and-unchecked-checkboxes
|
||||||
|
if (t=="checkbox") urows += `<input type="hidden" name="${k}_${s}" value="off">`;
|
||||||
|
urows += `${s}: <input type="${t}" name="${k}_${s}" ${c} oninput="check(this,'${k}')"><br>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gId("um").innerHTML = urows;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
gId('lserr').style.display = "inline"
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function svS(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
console.log(d.Sf);
|
||||||
|
if (d.Sf.checkValidity()) d.Sf.submit(); //https://stackoverflow.com/q/37323914
|
||||||
|
}
|
||||||
|
function GetV() {}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
@import url("style.css");
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onload="S()">
|
||||||
|
<form id="form_s" name="Sf" method="post" onsubmit="svS(event)">
|
||||||
|
<div class="toprow">
|
||||||
|
<div class="helpB"><button type="button" onclick="H()">?</button></div>
|
||||||
|
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><br>
|
||||||
|
<span id="lssuc" style="color:green; display:none">✔ Configuration saved!</span>
|
||||||
|
<span id="lserr" style="color:red; display:none">⚠ Could not load configuration.</span><hr>
|
||||||
|
</div>
|
||||||
|
<h2>Usermod Setup</h2>
|
||||||
|
<div id="um">Loading settings...</div>
|
||||||
|
<hr><button type="button" onclick="B()">Back</button><button type="submit">Save</button>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
File diff suppressed because one or more lines are too long
@ -32,7 +32,7 @@ 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
|
||||||
if (subPage <1 || subPage >7) return;
|
if (subPage <1 || subPage >8) return;
|
||||||
|
|
||||||
//WIFI SETTINGS
|
//WIFI SETTINGS
|
||||||
if (subPage == 1)
|
if (subPage == 1)
|
||||||
@ -408,6 +408,26 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
//USERMODS
|
||||||
|
if (subPage == 8)
|
||||||
|
{
|
||||||
|
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
|
||||||
|
JsonObject um = doc.createNestedObject(F("um"));
|
||||||
|
|
||||||
|
size_t args = request->args();
|
||||||
|
for (size_t i=0; i<args; i++) {
|
||||||
|
String name = request->argName(i);
|
||||||
|
String value = request->arg(i);
|
||||||
|
|
||||||
|
um.remove(name); // checkboxes get two fields (first is always "off", existence of second depends on checkmark and may be "on")
|
||||||
|
um[name] = value;
|
||||||
|
DEBUG_PRINT(name);
|
||||||
|
DEBUG_PRINT(" = ");
|
||||||
|
DEBUG_PRINTLN(value);
|
||||||
|
}
|
||||||
|
usermods.readFromJsonState(um);
|
||||||
|
}
|
||||||
|
|
||||||
if (subPage != 2 && (subPage != 6 || !doReboot)) serializeConfig(); //do not save if factory reset or LED settings (which are saved after LED re-init)
|
if (subPage != 2 && (subPage != 6 || !doReboot)) serializeConfig(); //do not save if factory reset or LED settings (which are saved after LED re-init)
|
||||||
if (subPage == 4) alexaInit();
|
if (subPage == 4) alexaInit();
|
||||||
}
|
}
|
||||||
|
@ -373,6 +373,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
|
|||||||
#ifdef WLED_ENABLE_DMX // include only if DMX is enabled
|
#ifdef WLED_ENABLE_DMX // include only if DMX is enabled
|
||||||
else if (url.indexOf("dmx") > 0) subPage = 7;
|
else if (url.indexOf("dmx") > 0) subPage = 7;
|
||||||
#endif
|
#endif
|
||||||
|
else if (url.indexOf("um") > 0) subPage = 8;
|
||||||
} else subPage = 255; //welcome page
|
} else subPage = 255; //welcome page
|
||||||
|
|
||||||
if (subPage == 1 && wifiLock && otaLock)
|
if (subPage == 1 && wifiLock && otaLock)
|
||||||
@ -394,6 +395,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
|
|||||||
case 5: strcpy_P(s, PSTR("Time")); break;
|
case 5: strcpy_P(s, PSTR("Time")); break;
|
||||||
case 6: strcpy_P(s, PSTR("Security")); strcpy_P(s2, PSTR("Rebooting, please wait ~10 seconds...")); break;
|
case 6: strcpy_P(s, PSTR("Security")); strcpy_P(s2, PSTR("Rebooting, please wait ~10 seconds...")); break;
|
||||||
case 7: strcpy_P(s, PSTR("DMX")); break;
|
case 7: strcpy_P(s, PSTR("DMX")); break;
|
||||||
|
case 8: strcpy_P(s, PSTR("Usermods")); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
strcat_P(s, PSTR(" settings saved."));
|
strcat_P(s, PSTR(" settings saved."));
|
||||||
@ -420,6 +422,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
|
|||||||
case 5: request->send_P(200, "text/html", PAGE_settings_time, settingsProcessor); break;
|
case 5: request->send_P(200, "text/html", PAGE_settings_time, settingsProcessor); break;
|
||||||
case 6: request->send_P(200, "text/html", PAGE_settings_sec , settingsProcessor); break;
|
case 6: request->send_P(200, "text/html", PAGE_settings_sec , settingsProcessor); break;
|
||||||
case 7: request->send_P(200, "text/html", PAGE_settings_dmx , settingsProcessor); break;
|
case 7: request->send_P(200, "text/html", PAGE_settings_dmx , settingsProcessor); break;
|
||||||
|
case 8: request->send_P(200, "text/html", PAGE_settings_um , settingsProcessor); break;
|
||||||
case 255: request->send_P(200, "text/html", PAGE_welcome); break;
|
case 255: request->send_P(200, "text/html", PAGE_welcome); break;
|
||||||
default: request->send_P(200, "text/html", PAGE_settings , settingsProcessor);
|
default: request->send_P(200, "text/html", PAGE_settings , settingsProcessor);
|
||||||
}
|
}
|
||||||
|
@ -274,9 +274,9 @@ void getSettingsJS(byte subPage, char* dest)
|
|||||||
} else if (!kv.value().isNull()) {
|
} else if (!kv.value().isNull()) {
|
||||||
// element is an JsonObject
|
// element is an JsonObject
|
||||||
JsonObject obj = kv.value();
|
JsonObject obj = kv.value();
|
||||||
if (obj[F("pin")] != nullptr) {
|
if (obj["pin"] != nullptr) {
|
||||||
if (i++) oappend(SET_F(","));
|
if (i++) oappend(SET_F(","));
|
||||||
oappendi((int)obj[F("pin")]);
|
oappendi((int)obj["pin"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user