Browse Source

WebUI fix change detection of input elements (#1986)

- use setOriginalsFromValues function right after elements had changed and
**only** with those elements
- add checkbox handling to setOriginalsFromValues to match hasChanged
- apply hasChanged after value is set to refresh originals after saving
- change parts that already set 'original' attribute manually

This was sort-of broken, because original attr never worked as intended. 
Also fix possible OOM for large WebUI images, since sending full config each time means that we need more memory to buffer and parse it.
master
Max Prokhorov 5 years ago
committed by GitHub
parent
commit
8e7854b04e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 22222 additions and 22029 deletions
  1. BIN
      code/espurna/data/index.all.html.gz
  2. BIN
      code/espurna/data/index.light.html.gz
  3. BIN
      code/espurna/data/index.lightfox.html.gz
  4. BIN
      code/espurna/data/index.rfbridge.html.gz
  5. BIN
      code/espurna/data/index.rfm69.html.gz
  6. BIN
      code/espurna/data/index.sensor.html.gz
  7. BIN
      code/espurna/data/index.small.html.gz
  8. BIN
      code/espurna/data/index.thermostat.html.gz
  9. +3
    -3
      code/espurna/scheduler.ino
  10. +3040
    -3015
      code/espurna/static/index.all.html.gz.h
  11. +2827
    -2830
      code/espurna/static/index.light.html.gz.h
  12. +2413
    -2389
      code/espurna/static/index.lightfox.html.gz.h
  13. +2442
    -2440
      code/espurna/static/index.rfbridge.html.gz.h
  14. +3906
    -3909
      code/espurna/static/index.rfm69.html.gz.h
  15. +2495
    -2498
      code/espurna/static/index.sensor.html.gz.h
  16. +2388
    -2391
      code/espurna/static/index.small.html.gz.h
  17. +2425
    -2428
      code/espurna/static/index.thermostat.html.gz.h
  18. +28
    -7
      code/gulpfile.js
  19. +234
    -98
      code/html/custom.js
  20. +21
    -21
      code/html/index.html

BIN
code/espurna/data/index.all.html.gz View File


BIN
code/espurna/data/index.light.html.gz View File


BIN
code/espurna/data/index.lightfox.html.gz View File


BIN
code/espurna/data/index.rfbridge.html.gz View File


BIN
code/espurna/data/index.rfm69.html.gz View File


BIN
code/espurna/data/index.sensor.html.gz View File


BIN
code/espurna/data/index.small.html.gz View File


BIN
code/espurna/data/index.thermostat.html.gz View File


+ 3
- 3
code/espurna/scheduler.ino View File

@ -32,10 +32,8 @@ void _schWebSocketOnConnected(JsonObject &root){
if (!relayCount()) return;
root["maxSchedules"] = SCHEDULER_MAX_SCHEDULES;
JsonObject &schedules = root.createNestedObject("schedules");
uint8_t size = 0;
schedules["max"] = SCHEDULER_MAX_SCHEDULES;
JsonArray& enabled = schedules.createNestedArray("schEnabled");
JsonArray& switch_ = schedules.createNestedArray("schSwitch");
@ -46,6 +44,8 @@ void _schWebSocketOnConnected(JsonObject &root){
JsonArray& utc = schedules.createNestedArray("schUTC");
JsonArray& weekdays = schedules.createNestedArray("schWDs");
uint8_t size = 0;
for (byte i = 0; i < SCHEDULER_MAX_SCHEDULES; i++) {
if (!hasSetting("schSwitch", i)) break;
++size;


+ 3040
- 3015
code/espurna/static/index.all.html.gz.h
File diff suppressed because it is too large
View File


+ 2827
- 2830
code/espurna/static/index.light.html.gz.h
File diff suppressed because it is too large
View File


+ 2413
- 2389
code/espurna/static/index.lightfox.html.gz.h
File diff suppressed because it is too large
View File


+ 2442
- 2440
code/espurna/static/index.rfbridge.html.gz.h
File diff suppressed because it is too large
View File


+ 3906
- 3909
code/espurna/static/index.rfm69.html.gz.h
File diff suppressed because it is too large
View File


+ 2495
- 2498
code/espurna/static/index.sensor.html.gz.h
File diff suppressed because it is too large
View File


+ 2388
- 2391
code/espurna/static/index.small.html.gz.h
File diff suppressed because it is too large
View File


+ 2425
- 2428
code/espurna/static/index.thermostat.html.gz.h
File diff suppressed because it is too large
View File


+ 28
- 7
code/gulpfile.js View File

@ -109,14 +109,35 @@ var htmllintReporter = function(filepath, issues) {
var buildWebUI = function(module) {
var modules = {'light': false, 'sensor': false, 'rfbridge': false, 'rfm69': false, 'thermostat': false};
// Declare some modules as optional to remove with
// removeIf(!name) ...code... endRemoveIf(!name) sections
// (via gulp-remove-code)
var modules = {
'light': false,
'sensor': false,
'rfbridge': false,
'rfm69': false,
'thermostat': false,
'lightfox': false
};
// Note: only build these when specified as module arg
var excludeAll = [
'rfm69',
'lightfox'
];
// 'all' to include all *but* excludeAll
// '<module>' to include a single module
// 'small' is the default state (all disabled)
if ('all' === module) {
modules['light'] = true;
modules['sensor'] = true;
modules['rfbridge'] = true;
modules['rfm69'] = false; // we will never be adding this except when building RFM69GW
modules['lightfox'] = false; // we will never be adding this except when building lightfox
modules['thermostat'] = true;
Object.keys(modules).
filter(function(key) {
return excludeAll.indexOf(key) < 0;
}).
forEach(function(key) {
modules[key] = true;
});
} else if ('small' !== module) {
modules[module] = true;
}


+ 234
- 98
code/html/custom.js View File

@ -2,7 +2,6 @@ var debug = false;
var websock;
var password = false;
var maxNetworks;
var maxSchedules;
var messages = [];
var free_size = 0;
@ -12,7 +11,7 @@ var numChanged = 0;
var numReboot = 0;
var numReconnect = 0;
var numReload = 0;
var conf_saved = false;
var configurationSaved = false;
var useWhite = false;
var useCCT = false;
@ -228,24 +227,46 @@ function validateForm(form) {
return validateFormPasswords(form) && validateFormHostname(form);
}
function getValue(element) {
if ($(element).attr("type") === "checkbox") {
return $(element).prop("checked") ? 1 : 0;
} else if ($(element).attr("type") === "radio") {
if (!$(element).prop("checked")) {
return null;
// Observe all group settings to selectively update originals based on the current data
var groupSettingsObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
// If any new elements are added, set "settings-target" element as changed to forcibly send the data
var targets = $(mutation.target).attr("data-settings-target");
if (targets !== undefined) {
mutation.addedNodes.forEach(function(node) {
var overrides = [];
targets.split(" ").forEach(function(target) {
var elem = $("[name='" + target + "']", node);
if (!elem.length) return;
var value = getValue(elem);
if ((value === null) || (value === elem[0].defaultValue)) {
overrides.push(elem);
}
});
setOriginalsFromValues($("input,select", node));
overrides.forEach(function(elem) {
elem.attr("hasChanged", "true");
if (elem.prop("tagName") === "SELECT") {
elem.prop("value", 0);
}
});
});
}
}
return $(element).val();
}
function addValue(data, name, value) {
// If anything was removed, forcibly send **all** of the group to avoid having any outdated keys
// TODO: hide instead of remove?
var changed = $(mutation.target).attr("hasChanged") === "true";
if (changed || mutation.removedNodes.length) {
$(mutation.target).attr("hasChanged", "true");
$("input,select", mutation.target.childNodes).attr("hasChanged", "true");
}
});
});
// These fields will always be a list of values
var is_group = [
// These fields will always be a list of values
function isGroupValue(value) {
var names = [
"ssid", "pass", "gw", "mask", "ip", "dns",
"schEnabled", "schSwitch","schAction","schType","schHour","schMinute","schWDs","schUTC",
"relayBoot", "relayPulse", "relayTime", "relayLastSch",
@ -257,24 +278,31 @@ function addValue(data, name, value) {
"node", "key", "topic",
"rpnRule", "rpnTopic", "rpnName"
];
return names.indexOf(value) >= 0;
}
function getValue(element) {
// join both adminPass 1 and 2
if (name.startsWith("adminPass")) {
name = "adminPass";
if ($(element).attr("type") === "checkbox") {
return $(element).prop("checked") ? 1 : 0;
} else if ($(element).attr("type") === "radio") {
if (!$(element).prop("checked")) {
return null;
}
}
// join all relayLastSch values
if (name.startsWith("relayLastSch")) {
name = "relayLastSch";
}
return $(element).val();
}
function addValue(data, name, value) {
if (name in data) {
if (!Array.isArray(data[name])) {
data[name] = [data[name]];
}
data[name].push(value);
} else if (is_group.indexOf(name) >= 0) {
} else if (isGroupValue(name)) {
data[name] = [value];
} else {
data[name] = value;
@ -282,25 +310,71 @@ function addValue(data, name, value) {
}
function getData(form) {
function getData(form, changed, cleanup) {
// Populate two sets of data, ones that had been changed and ones that stayed the same
var data = {};
var changed_data = [];
if (cleanup === undefined) {
cleanup = true;
}
if (changed === undefined) {
changed = true;
}
// Populate data
$("input,select", form).each(function() {
if ($(this).attr("data-settings-ignore") === "true") {
return;
}
var name = $(this).attr("name");
var real_name = $(this).attr("data-settings-real-name");
if (real_name !== undefined) {
name = real_name;
}
var value = getValue(this);
if (null !== value) {
var haschanged = ("true" === $(this).attr("hasChanged"));
var indexed = changed_data.indexOf(name) >= 0;
if ((haschanged || !changed) && !indexed) {
changed_data.push(name);
}
addValue(data, name, value);
}
});
// Post process
addValue(data, "schSwitch", 0xFF);
delete data["filename"];
delete data["rfbcode"];
// Finally, filter out only fields that had changed.
// Note: We need to preserve dynamic lists like schedules, wifi etc.
// so we don't accidentally break when user deletes entry in the middle
var resulting_data = {};
for (var value in data) {
if (changed_data.indexOf(value) >= 0) {
resulting_data[value] = data[value];
}
}
return data;
// Hack: clean-up leftover arrays.
// When empty, the receiving side will prune all keys greater than the current one.
if (cleanup) {
$(".group-settings").each(function() {
var haschanged = ("true" === $(this).attr("hasChanged"));
if (haschanged && !this.children.length) {
var targets = this.dataset.settingsTarget;
if (targets === undefined) return;
targets.split(" ").forEach(function(target) {
resulting_data[target] = [];
});
}
});
}
return resulting_data;
}
@ -335,6 +409,7 @@ function generateAPIKey() {
var apikey = randomString(16, {hex: true});
$("input[name='apiKey']")
.val(apikey)
.attr("original", "-".repeat(16))
.attr("haschanged", "true");
return false;
}
@ -359,7 +434,8 @@ function toggleVisiblePassword() {
}
function doGeneratePassword() {
$("input", $("#formPassword"))
var elems = $("input", $("#formPassword"));
elems
.val(generatePassword())
.attr("haschanged", "true")
.each(function() {
@ -444,27 +520,27 @@ function sendConfig(data) {
send(JSON.stringify({config: data}));
}
function setOriginalsFromValues(force) {
force = (true === force);
$("input,select").each(function() {
var initial = (undefined === $(this).attr("original"));
if (force || initial) {
var value;
if ($(this).attr("type") === "checkbox") {
value = $(this).prop("checked");
} else {
value = $(this).val();
}
$(this).attr("original", value);
hasChanged.call(this);
function setOriginalsFromValues(elems) {
if (typeof elems == "undefined") {
elems = $("input,select");
}
elems.each(function() {
var value;
if ($(this).attr("type") === "checkbox") {
value = $(this).prop("checked");
} else {
value = $(this).val();
}
$(this).attr("original", value);
hasChanged.call(this);
});
}
function resetOriginals() {
setOriginalsFromValues(true);
setOriginalsFromValues();
$(".group-settings").attr("haschanged", "false")
numReboot = numReconnect = numReload = 0;
conf_saved = false;
configurationSaved = false;
}
function doReload(milliseconds) {
@ -563,7 +639,7 @@ function doUpgrade() {
function doUpdatePassword() {
var form = $("#formPassword");
if (validateFormPasswords(form)) {
sendConfig(getData(form));
sendConfig(getData(form, true, false));
}
return false;
}
@ -632,7 +708,7 @@ function doCheckOriginals() {
}
function waitForSave(){
if (conf_saved == false) {
if (!configurationSaved) {
setTimeout(waitForSave, 1000);
} else {
doCheckOriginals();
@ -709,7 +785,7 @@ function doRestore() {
function doFactoryReset() {
var response = window.confirm("Are you sure you want to restore to factory settings?");
if (response === false) {
if (!response) {
return false;
}
sendAction("factory_reset", {});
@ -836,6 +912,7 @@ function createRelayList(data, container, template_name) {
var line = $(template).clone();
$("label", line).html("Switch #" + i);
$("input", line).attr("tabindex", 40 + i).val(data[i]);
setOriginalsFromValues($("input", line));
line.appendTo("#" + container);
}
@ -855,6 +932,7 @@ function createMagnitudeList(data, container, template_name) {
$("label", line).html(magnitudeType(data.type[i]) + " #" + parseInt(data.index[i], 10));
$("div.hint", line).html(magnitudes[i].description);
$("input", line).attr("tabindex", 40 + i).val(data.idx[i]);
setOriginalsFromValues($("input", line));
line.appendTo("#" + container);
}
@ -873,6 +951,7 @@ function addRPNRule() {
$(this).attr("tabindex", tabindex++);
});
$(line).find("button").on('click', delParent);
setOriginalsFromValues($("input", line));
line.appendTo("#rpnRules");
}
@ -884,6 +963,7 @@ function addRPNTopic() {
$(this).attr("tabindex", tabindex++);
});
$(line).find("button").on('click', delParent);
setOriginalsFromValues($("input", line));
line.appendTo("#rpnTopics");
}
@ -901,6 +981,7 @@ function addMapping() {
$(this).attr("tabindex", tabindex++);
});
$(line).find("button").on('click', delParent);
setOriginalsFromValues($("input", line));
line.appendTo("#mapping");
}
@ -910,6 +991,10 @@ function addMapping() {
// Wifi
// -----------------------------------------------------------------------------
function numNetworks() {
return $("#networks > div").length;
}
function delNetwork() {
var parent = $(this).parents(".pure-g");
$(parent).remove();
@ -920,14 +1005,18 @@ function moreNetwork() {
$(".more", parent).toggle();
}
function addNetwork() {
function addNetwork(values) {
var numNetworks = $("#networks > div").length;
if (numNetworks >= maxNetworks) {
var number = numNetworks();
if (number >= maxNetworks) {
alert("Max number of networks reached");
return null;
}
if (values === undefined) {
values = {};
}
var tabindex = 200 + numNetworks * 10;
var template = $("#networkTemplate").children();
var line = $(template).clone();
@ -938,6 +1027,11 @@ function addNetwork() {
$(".password-reveal", line).on("click", toggleVisiblePassword);
$(line).find(".button-del-network").on("click", delNetwork);
$(line).find(".button-more-network").on("click", moreNetwork);
Object.entries(values).forEach(function(kv) {
$("input[name='" + kv[0] + "']", line).val(kv[1]);
});
line.appendTo("#networks");
return line;
@ -948,6 +1042,15 @@ function addNetwork() {
// Relays scheduler
// -----------------------------------------------------------------------------
function numSchedules() {
return $("#schedules > div").length;
}
function maxSchedules() {
var value = $("#schedules").attr("data-settings-max");
return parseInt(value === undefined ? 0 : value, 10);
}
function delSchedule() {
var parent = $(this).parents(".pure-g");
$(parent).remove();
@ -958,18 +1061,23 @@ function moreSchedule() {
$("div.more", parent).toggle();
}
function addSchedule(event) {
function addSchedule(values) {
var numSchedules = $("#schedules > div").length;
if (numSchedules >= maxSchedules) {
var schedules = numSchedules();
if (schedules >= maxSchedules()) {
alert("Max number of schedules reached");
return null;
}
if (values === undefined) {
values = {};
}
var tabindex = 200 + numSchedules * 10;
var template = $("#scheduleTemplate").children();
var line = $(template).clone();
var type = (1 === event.data.schType) ? "switch" : "light";
var type = (1 === values.schType) ? "switch" : "light";
template = $("#" + type + "ActionTemplate").children();
$(line).find("#schActionDiv").append(template.clone());
@ -980,13 +1088,23 @@ function addSchedule(event) {
});
$(line).find(".button-del-schedule").on("click", delSchedule);
$(line).find(".button-more-schedule").on("click", moreSchedule);
$(line).find("input[name='schUTC']").prop("id", "schUTC" + (numSchedules + 1))
.next().prop("for", "schUTC" + (numSchedules + 1));
$(line).find("input[name='schEnabled']").prop("id", "schEnabled" + (numSchedules + 1))
.next().prop("for", "schEnabled" + (numSchedules + 1));
line.appendTo("#schedules");
var schUTC_id = "schUTC" + (schedules + 1);
$(line).find("input[name='schUTC']").prop("id", schUTC_id).next().prop("for", schUTC_id);
var schEnabled_id = "schEnabled" + (schedules + 1);
$(line).find("input[name='schEnabled']").prop("id", schEnabled_id).next().prop("for", schEnabled_id);
$(line).find("input[type='checkbox']").prop("checked", false);
Object.entries(values).forEach(function(kv) {
var key = kv[0], value = kv[1];
$("input[name='" + key + "']", line).val(value);
$("select[name='" + key + "']", line).prop("value", value);
$("input[type='checkbox'][name='" + key + "']", line).prop("checked", value);
});
line.appendTo("#schedules");
return line;
}
@ -1083,6 +1201,7 @@ function initRelayConfig(data) {
$("select[name='relayOnDisc']", line).val(data.on_disc[i]);
}
setOriginalsFromValues($("input,select", line));
line.appendTo("#relayConfig");
// Populate the relay SELECTs
@ -1333,8 +1452,8 @@ function addRfbNode() {
var status = true;
$("span", line).html(numNodes);
$(line).find("input").each(function() {
$(this).attr("data-id", numNodes);
$(this).attr("data-status", status ? 1 : 0);
$(this).data("id", numNodes);
$(this).attr("status", status ? 1 : 0);
status = !status;
});
$(line).find(".button-rfb-learn").on("click", rfbLearn);
@ -1378,6 +1497,7 @@ function initLightfox(data, relayCount) {
$(this).val(data[i]["relay"]);
status = !status;
});
setOriginalsFromValues($("input,select", $line));
$line.appendTo("#lightfoxNodes");
}
@ -1512,8 +1632,10 @@ function processData(data) {
var mapping = data.mapping[i];
Object.keys(mapping).forEach(function(key) {
var id = "input[name=" + key + "]";
if ($(id, line).length) $(id, line).val(mapping[key]).attr("original", mapping[key]);
if ($(id, line).length) $(id, line).val(mapping[key]);
});
setOriginalsFromValues($("input", line));
}
return;
}
@ -1535,7 +1657,9 @@ function processData(data) {
// fill in the blanks
var rule = data.rpnRules[i];
$("input", line).val(rule).attr("original", rule);
$("input", line).val(rule);
setOriginalsFromValues($("input", line));
}
return;
@ -1553,8 +1677,10 @@ function processData(data) {
// fill in the blanks
var topic = data.rpnTopics[i];
var name = data.rpnNames[i];
$("input[name='rpnTopic']", line).val(topic).attr("original", topic);
$("input[name='rpnName']", line).val(name).attr("original", name);
$("input[name='rpnTopic']", line).val(topic);
$("input[name='rpnName']", line).val(name);
setOriginalsFromValues($("input", line));
}
return;
@ -1650,13 +1776,7 @@ function processData(data) {
}
if ("wifi" === key) {
for (i in value) {
var wifi = value[i];
var nwk_line = addNetwork();
Object.keys(wifi).forEach(function(key) {
$("input[name='" + key + "']", nwk_line).val(wifi[key]);
});
}
value.forEach(addNetwork);
return;
}
@ -1681,22 +1801,17 @@ function processData(data) {
// Relays scheduler
// -----------------------------------------------------------------------------
if ("maxSchedules" === key) {
maxSchedules = parseInt(value, 10);
return;
}
if ("schedules" === key) {
$("#schedules").attr("data-settings-max", value.max);
for (var i=0; i<value.size; ++i) {
var sch_line = addSchedule({ data: {schType: value.schType[i] }});
// XXX: no
var sch_map = {};
Object.keys(value).forEach(function(key) {
if ("size" == key) return;
var sch_value = value[key][i];
$("input[name='" + key + "']", sch_line).val(sch_value);
$("select[name='" + key + "']", sch_line).prop("value", sch_value);
$("input[type='checkbox'][name='" + key + "']", sch_line).prop("checked", sch_value);
if ("max" == key) return;
sch_map[key] = value[key][i];
});
addSchedule(sch_map);
}
return;
}
@ -1724,8 +1839,11 @@ function processData(data) {
if ("ledConfig" === key) {
initLeds(value);
for (var i=0; i<value.length; ++i) {
$("select[name='ledMode'][data='" + i + "']").val(value[i].mode);
$("input[name='ledRelay'][data='" + i + "']").val(value[i].relay);
var mode = $("select[name='ledMode'][data='" + i + "']");
var relay = $("select[name='ledRelay'][data='" + i + "']");
mode.val(value[i].mode);
relay.val(value[i].relay);
setOriginalsFromValues($([mode,relay]));
}
return;
}
@ -1783,8 +1901,8 @@ function processData(data) {
// Messages
if ("message" === key) {
if (value == 8 && (numReboot > 0 || numReload > 0 || numReconnect > 0)){
conf_saved = true;
if (value == 8) {
configurationSaved = true;
}
window.alert(messages[value]);
return;
@ -1861,6 +1979,7 @@ function processData(data) {
// ---------------------------------------------------------------------
// Matching
// ---------------------------------------------------------------------
var elems = [];
var pre;
var post;
@ -1877,6 +1996,7 @@ function processData(data) {
post = input.attr("post") || "";
input.val(pre + value + post);
}
elems.push(input);
}
// Look for SPANs
@ -1886,11 +2006,13 @@ function processData(data) {
value.forEach(function(elem) {
span.append(elem);
span.append('</br>');
elems.push(span);
});
} else {
pre = span.attr("pre") || "";
post = span.attr("post") || "";
span.html(pre + value + post);
elems.push(span);
}
}
@ -1898,11 +2020,12 @@ function processData(data) {
var select = $("select[name='" + key + "']");
if (select.length > 0) {
select.val(value);
elems.push(select);
}
});
setOriginalsFromValues($(elems));
setOriginalsFromValues();
});
}
@ -1928,16 +2051,16 @@ function hasChanged() {
if ("reconnect" === action) { ++numReconnect; }
if ("reboot" === action) { ++numReboot; }
if ("reload" === action) { ++numReload; }
$(this).attr("hasChanged", true);
}
$(this).attr("hasChanged", true);
} else {
if (hasChanged) {
--numChanged;
if ("reconnect" === action) { --numReconnect; }
if ("reboot" === action) { --numReboot; }
if ("reload" === action) { --numReload; }
$(this).attr("hasChanged", false);
}
$(this).attr("hasChanged", false);
}
}
@ -2050,9 +2173,14 @@ $(function() {
$(".button-add-network").on("click", function() {
$(".more", addNetwork()).toggle();
});
$(".button-add-switch-schedule").on("click", { schType: 1 }, addSchedule);
$(".button-add-switch-schedule").on("click", function() {
addSchedule({schType: 1, schSwitch: -1});
});
<!-- removeIf(!light)-->
$(".button-add-light-schedule").on("click", { schType: 2 }, addSchedule);
$(".button-add-light-schedule").on("click", function() {
addSchedule({schType: 2, schSwitch: -1});
});
<!-- endRemoveIf(!light)-->
$(".button-add-rpnrule").on('click', addRPNRule);
@ -2083,8 +2211,16 @@ $(function() {
$("textarea").on("dblclick", function() { this.select(); });
resetOriginals();
$(".group-settings").each(function() {
groupSettingsObserver.observe(this, {childList: true});
});
// don't autoconnect when opening from filesystem
if (window.location.protocol === "file:") { return; }
if (window.location.protocol === "file:") {
return;
}
// Check host param in query string
var search = new URLSearchParams(window.location.search),


+ 21
- 21
code/html/index.html View File

@ -42,13 +42,13 @@
<fieldset>
<div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4" for="adminPass1">New Password</label>
<input class="pure-u-1 pure-u-lg-3-4" name="adminPass1" minlength="8" maxlength="63" type="password" tabindex="1" autocomplete="false" spellcheck="false" required />
<input class="pure-u-1 pure-u-lg-3-4" name="adminPass1" data-settings-real-name="adminPass" minlength="8" maxlength="63" type="password" tabindex="1" autocomplete="false" spellcheck="false" required />
<span class="no-select password-reveal"></span>
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4" for="adminPass2">Repeat password</label>
<input class="pure-u-1 pure-u-lg-3-4" name="adminPass2" minlength="8" maxlength="63" type="password" tabindex="2" autocomplete="false" spellcheck="false" required />
<input class="pure-u-1 pure-u-lg-3-4" name="adminPass2" data-settings-real-name="adminPass" minlength="8" maxlength="63" type="password" tabindex="2" autocomplete="false" spellcheck="false" required />
<span class="no-select password-reveal"></span>
</div>
</fieldset>
@ -589,10 +589,10 @@
<div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4">Admin password</label>
<input name="adminPass1" class="pure-u-1 pure-u-lg-3-4" placeholder="New password" minlength="8" maxlength="63" type="password" action="reboot" tabindex="11" autocomplete="false" spellcheck="false" />
<input name="adminPass1" class="pure-u-1 pure-u-lg-3-4" placeholder="New password" data-settings-real-name="adminPass" minlength="8" maxlength="63" type="password" action="reboot" tabindex="11" autocomplete="false" spellcheck="false" />
<span class="no-select password-reveal"></span>
<div class="pure-u-1 pure-u-lg-1-4"></div>
<input name="adminPass2" class="pure-u-1 pure-u-lg-3-4" placeholder="Repeat password" minlength="8" maxlength="63" type="password" action="reboot" tabindex="12" autocomplete="false" spellcheck="false" />
<input name="adminPass2" class="pure-u-1 pure-u-lg-3-4" placeholder="Repeat password" data-settings-real-name="adminPass" minlength="8" maxlength="63" type="password" action="reboot" tabindex="12" autocomplete="false" spellcheck="false" />
<span class="no-select password-reveal"></span>
<div class="pure-u-0 pure-u-lg-1-4"></div>
@ -753,7 +753,7 @@
<legend>Networks</legend>
<div id="networks"></div>
<div id="networks" class="group-settings" data-settings-target="ssid pass" ></div>
<button type="button" class="pure-button button-add-network">Add network</button>
@ -774,7 +774,7 @@
<fieldset>
<div id="schedules"></div>
<div id="schedules" class="group-settings" data-settings-target="schSwitch" ></div>
<button type="button" class="pure-button button-add-switch-schedule module module-relay">Add switch schedule</button>
<!-- removeIf(!light) -->
@ -812,7 +812,7 @@
<legend>Specific topics</legend>
<div id="mapping"></div>
<div id="mapping" class="group-settings" ></div>
<button type="button" class="pure-button button-add-mapping">Add</button>
@ -1236,10 +1236,10 @@
<div class="pure-u-1 hint">Set IDX to 0 to disable notifications from that component.</div>
</div>
<div id="dczRelays"></div>
<div id="dczRelays" class="group-settings" ></div>
<!-- removeIf(!sensor) -->
<div id="dczMagnitudes"></div>
<div id="dczMagnitudes" class="group-settings" ></div>
<!-- endRemoveIf(!sensor) -->
</fieldset>
@ -1348,10 +1348,10 @@
<div class="pure-u-1 hint">Enter the field number to send each data to, 0 disable notifications from that component.</div>
</div>
<div id="tspkRelays"></div>
<div id="tspkRelays" class="group-settings" ></div>
<!-- removeIf(!sensor) -->
<div id="tspkMagnitudes"></div>
<div id="tspkMagnitudes" class="group-settings" ></div>
<!-- endRemoveIf(!sensor) -->
</fieldset>
@ -1446,7 +1446,7 @@
<legend>Rules</legend>
<div id="rpnRules"></div>
<div id="rpnRules" class="group-settings" data-settings-target="rpnRule" ></div>
<button type="button" class="pure-button button-add-rpnrule">Add</button>
<legend>MQTT</legend>
@ -1456,7 +1456,7 @@
<div class="pure-u-1-3">Variable Name</div>
</div>
<div id="rpnTopics"></div>
<div id="rpnTopics" class="group-settings" data-settings-target="rpnTopic" ></div>
<button type="button" class="pure-button button-add-rpntopic">Add</button>
</fieldset>
@ -1840,12 +1840,12 @@
<div class="pure-u-1 pure-u-lg-3-4 hint more">Set when using a static IP</div>
<label class="pure-u-1 pure-u-lg-1-4 more">Network Mask</label>
<input class="pure-u-1 pure-u-lg-3-4 more" name="mask" type="text" action="reconnect" value="255.255.255.0" maxlength="15" tabindex="0" autocomplete="false" />
<input class="pure-u-1 pure-u-lg-3-4 more" name="mask" type="text" action="reconnect" value="" maxlength="15" tabindex="0" autocomplete="false" />
<div class="pure-u-0 pure-u-lg-1-4 more"></div>
<div class="pure-u-1 pure-u-lg-3-4 hint more">Usually 255.255.255.0 for /24 networks</div>
<label class="pure-u-1 pure-u-lg-1-4 more">DNS IP</label>
<input class="pure-u-1 pure-u-lg-3-4 more" name="dns" type="text" action="reconnect" value="8.8.8.8" maxlength="15" tabindex="0" autocomplete="false" />
<input class="pure-u-1 pure-u-lg-3-4 more" name="dns" type="text" action="reconnect" value="" maxlength="15" tabindex="0" autocomplete="false" />
<div class="pure-u-0 pure-u-lg-1-4 more"></div>
<div class="pure-u-1 pure-u-lg-3-4 hint more">Set the Domain Name Server IP to use when using a static IP</div>
@ -1862,7 +1862,7 @@
<label class="pure-u-1 pure-u-lg-1-4">When time is</label>
<div class="pure-u-1-4 pure-u-lg-1-5">
<input class="pure-u-2-3" name="schHour" type="number" min="0" step="1" max="23" value="0" />
<input class="pure-u-2-3" name="schHour" type="number" min="0" step="1" max="23" value="12" />
<div class="pure-u-1-4 hint center">&nbsp;h</div>
</div>
<div class="pure-u-1-4 pure-u-lg-1-5">
@ -1879,7 +1879,7 @@
<div class="pure-u-2-5 pure-u-lg-1-5">
<input class="pure-u-23-24 pure-u-lg-23-24" name="schWDs" type="text" maxlength="15" tabindex="0" value="1,2,3,4,5,6,7" />
</div>
<div class="pure-u-3-5 pure-u-lg-1-2 hint center">&nbsp;1 for Monday, 2 for Tuesday...</div>
<div class="pure-u-3-5 pure-u-lg-1-2 hint center">&nbsp;1 for Monday, 2 for Tuesday etc., comma separated</div>
<div id="schActionDiv" class="pure-u-1">
</div>
@ -1895,13 +1895,13 @@
<div id="switchActionTemplate" class="template">
<label class="pure-u-1 pure-u-lg-1-4">Action</label>
<div class="pure-u-1 pure-u-lg-1-5">
<select class="pure-u-1 pure-u-lg-23-24" name="schAction">
<select class="pure-u-1 pure-u-lg-23-24" name="schAction" haschanged="true">
<option value="0">Turn OFF</option>
<option value="1">Turn ON</option>
<option value="2">Toggle</option>
</select>
</div>
<select class="pure-u-1 pure-u-lg-1-5 isrelay" name="schSwitch"></select>
<select class="pure-u-1 pure-u-lg-1-5 isrelay" name="schSwitch" ></select>
<input type="hidden" name="schType" value="1">
</div>
@ -1911,7 +1911,7 @@
<div class="pure-u-1 pure-u-lg-1-5">
<input class="pure-u-2-3" name="schAction" type="number" min="0" step="1" max="255" value="0" />
</div>
<select class="pure-u-1 pure-u-lg-1-5 islight" name="schSwitch"></select>
<select class="pure-u-1 pure-u-lg-1-5 islight" name="schSwitch" ></select>
<input type="hidden" name="schType" value="2">
</div>
<!-- endRemoveIf(!light) -->
@ -1950,7 +1950,7 @@
</div>
<div class="pure-g module module-sch">
<div class="pure-u-1 pure-u-lg-1-4"><label>Restore last schedule</label></div>
<div><input name="relayLastSch" type="checkbox" /></div>
<div><input name="relayLastSch" data-settings-real-name="relayLastSch" type="checkbox" /></div>
</div>
<div class="pure-g module module-mqtt">
<div class="pure-u-1 pure-u-lg-1-4"><label>MQTT group</label></div>


Loading…
Cancel
Save