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"
|
||||
),
|
||||
},
|
||||
{
|
||||
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"
|
||||
);
|
||||
|
@ -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).
|
||||
* 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)
|
||||
{
|
||||
root[F("pin_Temperature")] = temperaturePin;
|
||||
root[F("mode_Temperature")] = degC ? ("C") : ("F");
|
||||
//root[F("Temperature_pin")] = temperaturePin;
|
||||
//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).
|
||||
* 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) {
|
||||
if (root[F("pin_Temperature")] != nullptr) {
|
||||
int8_t pin = (int)root[F("pin_Temperature")];
|
||||
// deallocate pin and release memory
|
||||
pinManager.deallocatePin(temperaturePin);
|
||||
delete sensor;
|
||||
delete oneWire;
|
||||
// disable usermod
|
||||
temperaturePin = -1;
|
||||
disabled = true;
|
||||
// check if pin is OK
|
||||
if (pin>=0 && pinManager.allocatePin(pin,false)) {
|
||||
// allocat memory
|
||||
oneWire = new OneWire(pin);
|
||||
sensor = new DallasTemperature(oneWire);
|
||||
if (sensor) {
|
||||
temperaturePin = pin;
|
||||
sensor->begin();
|
||||
disabled = !sensor->getAddress(sensorDeviceAddress, 0);
|
||||
} else {
|
||||
pinManager.deallocatePin(pin);
|
||||
if (root[F("Temperature_pin")] != nullptr) {
|
||||
int8_t pin = (int)root[F("Temperature_pin")];
|
||||
if (pin != temperaturePin) {
|
||||
// deallocate pin and release memory
|
||||
delete sensor;
|
||||
delete oneWire;
|
||||
pinManager.deallocatePin(temperaturePin);
|
||||
// disable usermod
|
||||
temperaturePin = -1;
|
||||
disabled = true;
|
||||
// check if pin is OK
|
||||
if (pin>=0 && pinManager.allocatePin(pin,false)) {
|
||||
// allocat memory
|
||||
oneWire = new OneWire(pin);
|
||||
sensor = new DallasTemperature(oneWire);
|
||||
if (sensor) {
|
||||
temperaturePin = pin;
|
||||
sensor->begin();
|
||||
disabled = !sensor->getAddress(sensorDeviceAddress, 0);
|
||||
} else {
|
||||
pinManager.deallocatePin(pin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (root[F("mode_Temperature")] != nullptr) {
|
||||
degC = (root[F("mode_Temperature")]==String(PSTR("C")));
|
||||
if (root[F("Temperature_degC")] != nullptr) {
|
||||
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) {
|
||||
// we add JSON object: {"Temperature": {"pin": 0, "degC": true}}
|
||||
JsonObject top = root.createNestedObject(F("Temperature"));
|
||||
top[F("pin")] = temperaturePin;
|
||||
top[F("degC")] = degC;
|
||||
JsonObject top = root.createNestedObject(F("Temperature")); // usermodname
|
||||
top["pin"] = temperaturePin; // usermodparam
|
||||
top[F("degC")] = degC; // usermodparam
|
||||
}
|
||||
|
||||
/**
|
||||
@ -231,8 +234,8 @@ class UsermodTemperature : public Usermod {
|
||||
void readFromConfig(JsonObject &root) {
|
||||
// we look for JSON object: {"Temperature": {"pin": 0, "degC": true}}
|
||||
JsonObject top = root[F("Temperature")];
|
||||
if (!top.isNull() && top[F("pin")] != nullptr) {
|
||||
temperaturePin = (int)top[F("pin")];
|
||||
if (!top.isNull() && top["pin"] != nullptr) {
|
||||
temperaturePin = (int)top["pin"];
|
||||
degC = top[F("degC")] != nullptr ? top[F("degC")] : true;
|
||||
} else {
|
||||
DEBUG_PRINTLN(F("No Temperature sensor config found. (Using defaults.)"));
|
||||
|
@ -10,7 +10,7 @@
|
||||
margin: 0;
|
||||
}
|
||||
html {
|
||||
--h: 11.55vh;
|
||||
--h: 10.55vh;
|
||||
}
|
||||
button {
|
||||
background: #333;
|
||||
@ -18,7 +18,7 @@
|
||||
font-family: Verdana, Helvetica, sans-serif;
|
||||
border: 0.3ch solid #333;
|
||||
display: inline-block;
|
||||
font-size: 8vmin;
|
||||
font-size: 6vmin;
|
||||
height: var(--h);
|
||||
width: 95%;
|
||||
margin-top: 2.4vh;
|
||||
@ -41,6 +41,7 @@
|
||||
<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/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>
|
||||
</body>
|
||||
</html>
|
@ -163,6 +163,18 @@
|
||||
var lc=d.getElementsByName("LC"+LCs[i].name.substring(2))[0];
|
||||
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;
|
||||
|
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
|
||||
if (subPage <1 || subPage >7) return;
|
||||
if (subPage <1 || subPage >8) return;
|
||||
|
||||
//WIFI SETTINGS
|
||||
if (subPage == 1)
|
||||
@ -408,6 +408,26 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
}
|
||||
#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 == 4) alexaInit();
|
||||
}
|
||||
|
@ -373,6 +373,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
|
||||
#ifdef WLED_ENABLE_DMX // include only if DMX is enabled
|
||||
else if (url.indexOf("dmx") > 0) subPage = 7;
|
||||
#endif
|
||||
else if (url.indexOf("um") > 0) subPage = 8;
|
||||
} else subPage = 255; //welcome page
|
||||
|
||||
if (subPage == 1 && wifiLock && otaLock)
|
||||
@ -394,6 +395,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
|
||||
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 7: strcpy_P(s, PSTR("DMX")); break;
|
||||
case 8: strcpy_P(s, PSTR("Usermods")); break;
|
||||
}
|
||||
|
||||
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 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 8: request->send_P(200, "text/html", PAGE_settings_um , settingsProcessor); break;
|
||||
case 255: request->send_P(200, "text/html", PAGE_welcome); break;
|
||||
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()) {
|
||||
// element is an JsonObject
|
||||
JsonObject obj = kv.value();
|
||||
if (obj[F("pin")] != nullptr) {
|
||||
if (obj["pin"] != nullptr) {
|
||||
if (i++) oappend(SET_F(","));
|
||||
oappendi((int)obj[F("pin")]);
|
||||
oappendi((int)obj["pin"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user