1
0
mirror of https://github.com/s00500/ESPUI.git synced 2024-06-01 18:43:26 +00:00

Compare commits

...

58 Commits

Author SHA1 Message Date
75bbece7b5
Merge pull request #299 from sunlord2k/sunlord2k-patch-2
Update ESPUI.cpp
2024-04-06 11:16:35 +02:00
efda9b7140
Merge pull request #298 from sunlord2k/sunlord2k-patch-1
Update ESPUI.h
2024-04-06 11:16:15 +02:00
sunlord2k
9ba3f5a940
Update ESPUI.cpp
Changed FILE_WRITE to FILE_WRITING for compatibility with SD.h for usage with SD cards
2024-04-05 12:10:10 +02:00
sunlord2k
84ff52d8cb
Update ESPUI.h
Changed FILE_WRITE to FILE_WRITING for compatibility with SD.h for usage of ESPUI with SD card
2024-04-05 12:09:01 +02:00
dd234135f2
Merge pull request #295 from MartinMueller2003/master
Added a File Display object that can be used to scroll through a text file on the UI
2024-02-26 13:00:48 +01:00
Martin
6724abfef3 removed temporary SSID and network name definitions. 2024-02-22 08:08:21 -05:00
Martin Mueller
a448fdd180
Update README.md 2024-02-21 15:42:11 -05:00
Martin
ba7fa33dfb dded missing shortcut 2024-02-21 15:39:32 -05:00
Martin
624663076f Added File Display 2024-02-21 15:32:17 -05:00
Martin
c43c2c7b72 Added File Display section 2024-02-21 15:31:46 -05:00
MartinMueller2003
e9fc63f86d Modified to use the ESPUI unified littlefs handle 2024-02-12 09:15:00 -05:00
MartinMueller2003
1ba92a3cd6 Fixes for ESP8266 builds 2024-02-12 09:13:37 -05:00
MartinMueller2003
da20c8b0a5 Converted to normalized littlefs definition 2024-02-12 09:09:51 -05:00
MartinMueller2003
707c2aa3bf Added write function
Exposed unified littlefs type for use by our users.
2024-02-12 09:09:05 -05:00
Martin
9efd0c27aa Auto Generated updates 2024-02-10 15:40:34 -05:00
Martin
6a18c59f31 Added code to create a file display dynamic test / demo. 2024-02-10 13:54:58 -05:00
Martin
6e281e302a Added ability to turn on ESPUI debugging 2024-02-10 13:52:56 -05:00
Martin
d2ccf83874 Changes to support processing a file to display 2024-02-10 13:52:06 -05:00
Martin
ea8de92246 Checkpoint adding fileDisplay object 2024-02-06 22:38:17 -05:00
aa9a62bbdf
Merge pull request #288 from TA1AUB/patch-1
Update Turkish Language
2024-01-24 16:38:44 +01:00
Abdullah YILMAZ
f3b105f143
Update Turkish Language
Update Turkish Language
2024-01-20 22:44:38 +03:00
Martin Mueller
7508e034be
Merge pull request #285 from MartinMueller2003/master
Made WebServer and WebSocket globally accusable via access functions.
2024-01-10 09:19:25 -05:00
Martin Mueller
5b36d79395
Merge branch 's00500:master' into master 2024-01-08 08:54:15 -05:00
MartinMueller2003
e4c6906723 Made the Webserver globally accessible. 2024-01-08 08:43:10 -05:00
dc2d5e1205
Merge pull request #277 from MartinMueller2003/master
Restored custom port support.
2023-12-16 11:04:25 +01:00
MartinMueller2003
d3c6df5cfa Restored custom port support. 2023-12-14 08:34:17 -05:00
MartinMueller2003
46ad206a49 Restored missing port identifier for custom port support. 2023-12-14 08:32:59 -05:00
8b64b185a4
Merge pull request #264 from MartinMueller2003/master
Changes to support keeping multiple Browser Clients in sync
2023-11-09 18:15:04 +01:00
MartinMueller2003
00841ce32d Changes to support keeping multiple Browser Clients in sync 2023-11-09 11:06:44 -05:00
85ccff0ee0
Merge pull request #257 from d-a-v/stringref
use more string references
2023-10-14 20:31:01 +02:00
0571f65f6f
Merge pull request #249 from d-a-v/lambda-with-examples
Proposal: use lambda functions
2023-10-14 20:30:20 +02:00
David Gauchard
542ac03656 Merge branch 'master' into lambda-with-examples 2023-10-06 16:45:17 +02:00
8f7cce59bf
Merge pull request #253 from MartinMueller2003/master
Added fragmentation of large control values
2023-10-06 16:04:33 +02:00
David Gauchard
ae5b6d5c0a use more string references 2023-09-22 22:37:50 +02:00
David Gauchard
62b4b56a4d Merge branch 'master' into lambda-with-examples 2023-09-22 22:18:34 +02:00
8a3865e42e
Merge pull request #254 from croghostrider/patch-1
Remove LITTLEFS library
2023-09-22 08:06:14 +02:00
croghostrider
0d9a874ba2
Update library.json 2023-09-20 12:59:25 +02:00
cf1f81bced
Merge pull request #252 from d-a-v/minorchangesagain
some more minor changes
2023-09-19 12:05:47 +02:00
David Gauchard
0b22328bd8 introduce lambda 2023-09-15 00:59:10 +02:00
croghostrider
09596a4e65
fix #196
since Arduino 2.0.0 is LITTLEFS not more required for ESP32.
2023-09-13 17:24:26 +02:00
Martin
8d61abc47d updated files to send to the esp fs 2023-09-12 17:49:38 -04:00
Martin
adf2ba369e Added fragment assembly reset if something happens to the socket. 2023-09-12 06:56:53 -04:00
David Gauchard
93d0bf7d27 some more minor changes before #249 2023-09-11 13:29:32 +02:00
Martin
794d54dc96 updated minify and .h versions. 2023-09-11 06:53:05 -04:00
Martin
1deddad091 Added changes made to ack section 2023-09-11 06:52:35 -04:00
Martin
999d465c79 Add fragmentation support 2023-09-10 17:09:00 -04:00
Martin
83dbb49697 Removed more debug output 2023-09-09 17:01:55 -04:00
Martin
290f0c6fc6 Restored offset to marshal call. 2023-09-09 17:01:36 -04:00
Martin
7e3de1c14f Aligned with latest main branch 2023-09-09 16:52:28 -04:00
Martin
136b182813 Removed debug messages 2023-09-09 16:50:34 -04:00
Martin
43b8cdbfb2 Merge branch 'master' of https://github.com/MartinMueller2003/ESPUI 2023-09-09 16:49:52 -04:00
Martin
488a6cb252 first working version of the fragmentation code. 2023-09-09 15:58:12 -04:00
c7a9438149
Merge pull request #251 from d-a-v/minorfixes
minor fixes for restoring warning-less emulation on host
2023-09-08 17:03:12 +02:00
David Gauchard
03c30fa75c more minor changes for #249 2023-09-06 22:50:42 +02:00
David Gauchard
68c251226d minor fixes for restoring warning-less emulation on host for debugging (using emuAsync, see main readme) 2023-09-06 22:35:41 +02:00
Martin
ec41deab01 Added ClientTransferContext_t to the marshal API 2023-09-03 09:30:31 -04:00
Martin
fae30398b2 Added a client transfer context for use by the control marshaling function 2023-09-03 09:26:36 -04:00
Martin
de09ddbe09 Turned off ESPUI_DEBUG by default 2023-09-03 09:20:12 -04:00
31 changed files with 1573 additions and 779 deletions

View File

@ -28,6 +28,7 @@ The Library runs on any kind of **ESP8266** and **ESP32** (NodeMCU, AI Thinker,
* [Slider](#slider) * [Slider](#slider)
* [Number Input](#number-input) * [Number Input](#number-input)
* [Text Input](#text-input) * [Text Input](#text-input)
* [File Display](#filedisplay)
* [Date, Time, Colour and Password Input](#date-time-colour-and-password-input) * [Date, Time, Colour and Password Input](#date-time-colour-and-password-input)
* [Select control](#select-control) * [Select control](#select-control)
* [Getting the Time](#getting-the-time) * [Getting the Time](#getting-the-time)
@ -62,6 +63,10 @@ The Library runs on any kind of **ESP8266** and **ESP32** (NodeMCU, AI Thinker,
- Time control by @iangray001 - Time control by @iangray001
- Vertical controls by @iangray001 - Vertical controls by @iangray001
- Time/date/password/color input types by @pcbbc - Time/date/password/color input types by @pcbbc
- Delayed response support @MartinMueller2003
- Fragmented control transfer @MartinMueller2003
- Extended Callback @MartinMueller2003
- Added a file display element @MartinMueller2003
## Roadmap ## Roadmap
@ -143,6 +148,7 @@ more program memory to work with.
- Separator - Separator
- Time - Time
- Graph (partial implementation) - Graph (partial implementation)
- File Display
## Documentation ## Documentation
@ -153,7 +159,9 @@ This section will explain in detail how the Library is to be used from the Ardui
<br><br> <br><br>
Alternativly you may use the extended callback funtion which provides three parameters to the callback function `myCallback(Control *sender, int eventname, void * UserParameter)`. The `UserParameter` is provided as part of the `ESPUI.addControl` method set and allows the user to define contextual information that is to be presented to the callback function in an unmodified form. Alternativly you may use the extended callback funtion which provides three parameters to the callback function `myCallback(Control *sender, int eventname, void * UserParameter)`. The `UserParameter` is provided as part of the `ESPUI.addControl` method set and allows the user to define contextual information that is to be presented to the callback function in an unmodified form.
<br><br> <br><br>
The below example creates a button and defines a lambda function to implicitly create an `ExtendedCallback` which then invokes a more specialized button callback handler. The example uses the `UserParameter` to hold the `this` pointer to an object instance, providing a mechanism for sending the event to a specific object without the need for a switch / map / lookup translation of the Sender Id to an object reference. It also possible to use a lambda function in the callback parameter. It also allows the user to define, in a more C++ way, contextual information in any form. This is shown by the [completeLambda](examples/completeLambda/completeLambda.ino) example.
<br><br>
The below example creates a button and defines a lambda function to invoke a more specialized button callback handler:
``` ```
void YourClassName::setup() void YourClassName::setup()
{ {
@ -163,26 +171,18 @@ void YourClassName::setup()
" Button Face Text ", " Button Face Text ",
ControlColor::None, ControlColor::None,
ParentElementId, ParentElementId,
[](Control *sender, int eventname, void* param) [&](Control *sender, int eventname)
{ {
if(param) myButtonCallback(sender, eventname); // class method
{ });
reinterpret_cast<YourClassName*>(param)->myButtonCallback(sender, eventname);
}
},
this); // <-Third parameter for the extended callback
// or // or
ButtonElementId = ESPUI.button( ButtonElementId = ESPUI.button(
" Button Face Text ", " Button Face Text ",
[](Control *sender, int eventname, void* param) [&](Control *sender, int eventname)
{ {
if(param) myButtonCallback(sender, eventname); // class method
{ });
reinterpret_cast<YourClassName*>(param)->myButtonCallback(sender, eventname);
}
},
this); // <-Third parameter for the extended callback
} }
``` ```
``` ```
@ -357,6 +357,19 @@ However even with a type set, user input should still be validated
because it is easy to bypass client-side checks. Never trust user input. because it is easy to bypass client-side checks. Never trust user input.
#### File Display
![filedisplay](docs/ui_fileDisplay.png)
The File Display control is used to upload a file from the ESP file system and display the contents on the UI. The content is Auto Scrolled
to the last line in the file. Syntax:
`fileDisplayId = ESPUI.fileDisplay("Filetest", ControlColor::Turquoise, FullyQualified FilePath);`
After updating the contents of the file, trigger a display update using:
`ESPUI.updateControl(fileDisplayId);`
#### Select control #### Select control
![option1](docs/ui_select1.png) ![option1](docs/ui_select1.png)

230
data/js/controls.js vendored
View File

@ -60,6 +60,11 @@ const UPDATE_SEPARATOR = 119;
const UI_TIME = 20; const UI_TIME = 20;
const UPDATE_TIME = 120; const UPDATE_TIME = 120;
const UI_FILEDISPLAY = 21;
const UPDATE_FILEDISPLAY = 121;
const UI_FRAGMENT = 98;
const UP = 0; const UP = 0;
const DOWN = 1; const DOWN = 1;
const LEFT = 2; const LEFT = 2;
@ -77,6 +82,8 @@ const C_ALIZARIN = 6;
const C_DARK = 7; const C_DARK = 7;
const C_NONE = 255; const C_NONE = 255;
var controlAssemblyArray = new Object();
var FragmentAssemblyTimer = new Array();
var graphData = new Array(); var graphData = new Array();
var hasAccel = false; var hasAccel = false;
var sliderContinuous = false; var sliderContinuous = false;
@ -190,6 +197,12 @@ function restart() {
} }
function conStatusError() { function conStatusError() {
FragmentAssemblyTimer.forEach(element => {
clearInterval(element);
});
FragmentAssemblyTimer = new Array();
controlAssemblyArray = new Array();
if (true === websockConnected) { if (true === websockConnected) {
websockConnected = false; websockConnected = false;
websock.close(); websock.close();
@ -210,17 +223,20 @@ function handleVisibilityChange() {
} }
function start() { function start() {
let location = window.location.hostname;
let port = window.location.port;
// let location = "192.168.10.219";
// let port = "";
document.addEventListener("visibilitychange", handleVisibilityChange, false); document.addEventListener("visibilitychange", handleVisibilityChange, false);
if ( if (
window.location.port != "" || port != "" ||
window.location.port != 80 || port != 80 ||
window.location.port != 443 port != 443
) { ) {
websock = new WebSocket( websock = new WebSocket( "ws://" + location + ":" + port + "/ws" );
"ws://" + window.location.hostname + ":" + window.location.port + "/ws"
);
} else { } else {
websock = new WebSocket("ws://" + window.location.hostname + "/ws"); websock = new WebSocket("ws://" + location + "/ws");
} }
// is the timer running? // is the timer running?
@ -241,33 +257,54 @@ function start() {
$("#conStatus").addClass("color-green"); $("#conStatus").addClass("color-green");
$("#conStatus").text("Connected"); $("#conStatus").text("Connected");
websockConnected = true; websockConnected = true;
FragmentAssemblyTimer.forEach(element => {
clearInterval(element);
});
FragmentAssemblyTimer = new Array();
controlAssemblyArray = new Array();
}; };
websock.onclose = function (evt) { websock.onclose = function (evt) {
// console.log("Close evt: '" + evt + "'");
// console.log("Close reason: '" + evt.reason + "'");
// console.log("Close code: '" + evt.code + "'");
console.log("websock close"); console.log("websock close");
conStatusError(); conStatusError();
FragmentAssemblyTimer.forEach(element => {
clearInterval(element);
});
FragmentAssemblyTimer = new Array();
controlAssemblyArray = new Array();
}; };
websock.onerror = function (evt) { websock.onerror = function (evt) {
console.log("websock Error"); console.log("websock Error");
console.log(evt); // console.log("Error evt: '" + evt + "'");
// console.log("Error data: '" + evt.data + "'");
restart(); restart();
FragmentAssemblyTimer.forEach(element => {
clearInterval(element);
});
FragmentAssemblyTimer = new Array();
controlAssemblyArray = new Array();
}; };
var handleEvent = function (evt) { var handleEvent = function (evt) {
console.log(evt); // console.log("handleEvent:Data evt: '" + evt + "'");
// console.log("handleEvent:Data data: '" + evt.data + "'");
try { try {
var data = JSON.parse(evt.data); var data = JSON.parse(evt.data);
} }
catch (Event) { catch (Event) {
console.error(Event); console.error(Event);
// start the update over again // console.info("start the update over again");
websock.send("uiok:" + 0); websock.send("uiok:" + 0);
return; return;
} }
var e = document.body; var e = document.body;
var center = ""; var center = "";
// console.info("data.type: '" + data.type + "'");
switch (data.type) { switch (data.type) {
case UI_INITIAL_GUI: case UI_INITIAL_GUI:
@ -279,7 +316,9 @@ function start() {
if (data.sliderContinuous) { if (data.sliderContinuous) {
sliderContinuous = data.sliderContinuous; sliderContinuous = data.sliderContinuous;
} }
// console.info("UI_INITIAL_GUI:data record: '" + data + "'");
data.controls.forEach(element => { data.controls.forEach(element => {
// console.info("element: '" + JSON.stringify(element) + "'");
var fauxEvent = { var fauxEvent = {
data: JSON.stringify(element), data: JSON.stringify(element),
}; };
@ -295,7 +334,9 @@ function start() {
break; break;
case UI_EXTEND_GUI: case UI_EXTEND_GUI:
// console.info("UI_EXTEND_GUI data record: '" + data + "'");
data.controls.forEach(element => { data.controls.forEach(element => {
// console.info("UI_EXTEND_GUI:element: '" + JSON.stringify(element) + "'");
var fauxEvent = { var fauxEvent = {
data: JSON.stringify(element), data: JSON.stringify(element),
}; };
@ -329,7 +370,7 @@ function start() {
if (data.visible) addToHTML(data); if (data.visible) addToHTML(data);
break; break;
/* /*
These elements must call additional functions after being added to the DOM These elements must call additional functions after being added to the DOM
*/ */
case UI_BUTTON: case UI_BUTTON:
@ -527,6 +568,14 @@ function start() {
} }
break; break;
case UI_FILEDISPLAY:
if (data.visible)
{
addToHTML(data);
FileDisplayUploadFile(data);
}
break;
/* /*
* Update messages change the value/style of a component without adding new HTML * Update messages change the value/style of a component without adding new HTML
*/ */
@ -601,6 +650,92 @@ function start() {
websock.send("time:" + rv + ":" + data.id); websock.send("time:" + rv + ":" + data.id);
break; break;
case UPDATE_FILEDISPLAY:
FileDisplayUploadFile(data);
break;
case UI_FRAGMENT:
let FragmentLen = data.length;
let FragementOffset = data.offset;
let NextFragmentOffset = FragementOffset + FragmentLen;
let Total = data.total;
let Arrived = (FragmentLen + FragementOffset);
let FragmentFinal = Total === Arrived;
// console.info("UI_FRAGMENT:FragmentLen '" + FragmentLen + "'");
// console.info("UI_FRAGMENT:FragementOffset '" + FragementOffset + "'");
// console.info("UI_FRAGMENT:NextFragmentOffset '" + NextFragmentOffset + "'");
// console.info("UI_FRAGMENT:Total '" + Total + "'");
// console.info("UI_FRAGMENT:Arrived '" + Arrived + "'");
// console.info("UI_FRAGMENT:FragmentFinal '" + FragmentFinal + "'");
if (!data.hasOwnProperty('control'))
{
console.error("UI_FRAGMENT:Missing control record, skipping control");
break;
}
let control = data.control;
StopFragmentAssemblyTimer(data.control.id);
// is this the first fragment?
if(0 === FragementOffset)
{
// console.info("Found first fragment");
controlAssemblyArray[control.id] = data;
// console.info("Value: " + controlAssemblyArray[control.id].control.value);
controlAssemblyArray[control.id].offset = NextFragmentOffset;
StartFragmentAssemblyTimer(control.id);
let TotalRequest = JSON.stringify({ 'id' : control.id, 'offset' : NextFragmentOffset });
websock.send("uifragmentok:" + 0 + ": " + TotalRequest + ":");
// console.info("asked for fragment 2");
break;
}
// not first fragment. are we assembling this control?
if("undefined" === typeof controlAssemblyArray[control.id])
{
// it looks like we missed the first fragment. Start the control over
console.error("Missing first fragment for control: " + control.id);
StartFragmentAssemblyTimer(control.id);
let TotalRequest = JSON.stringify({ 'id' : control.id, 'offset' : 0 });
websock.send("uifragmentok:" + 0 + ": " + TotalRequest + ":");
// console.info("asked for fragment 1");
break;
}
// is this the expected next fragment
if(FragementOffset !== controlAssemblyArray[control.id].offset)
{
console.error("Wrong next fragment. Expected: " + controlAssemblyArray[control.id].offset + " Got: " + FragementOffset);
StartFragmentAssemblyTimer(control.id);
let TotalRequest = JSON.stringify({ 'id' : control.id, 'offset' : controlAssemblyArray[control.id].length + controlAssemblyArray[control.id].offset });
websock.send("uifragmentok:" + 0 + ": " + TotalRequest + ":");
// console.info("asked for the expected fragment");
break;
}
// console.info("Add to existing fragment");
controlAssemblyArray[control.id].control.value += control.value;
controlAssemblyArray[control.id].offset = NextFragmentOffset;
// console.info("Value: " + controlAssemblyArray[control.id].control.value);
if(true === FragmentFinal)
{
var fauxEvent = {
data: JSON.stringify(controlAssemblyArray[control.id].control),
};
handleEvent(fauxEvent);
controlAssemblyArray[control.id] = null;
// console.info("Found last fragment");
}
else
{
// console.info("Ask for next fragment.");
StartFragmentAssemblyTimer(control.id);
let TotalRequest = JSON.stringify({ 'id' : control.id, 'offset' : NextFragmentOffset});
websock.send("uifragmentok:" + 0 + ": " + TotalRequest + ":");
}
break;
default: default:
console.error("Unknown type or event"); console.error("Unknown type or event");
break; break;
@ -650,6 +785,70 @@ function start() {
websock.onmessage = handleEvent; websock.onmessage = handleEvent;
} }
async function FileDisplayUploadFile(data)
{
let text = await downloadFile(data.value);
let ItemToUpdateId = "fd" + data.id;
// console.info("ItemToUpdateId: " + ItemToUpdateId);
// console.info(" text: " + text);
// populate the text object
$("#" + ItemToUpdateId).val(text);
$("#" + ItemToUpdateId).css("textAlign", "left");
$("#" + ItemToUpdateId).css("white-space", "nowrap");
$("#" + ItemToUpdateId).css("overflow", "scroll");
$("#" + ItemToUpdateId).css("overflow-y", "scroll");
$("#" + ItemToUpdateId).css("overflow-x", "scroll");
$("#" + ItemToUpdateId).scrollTop($("#" + ItemToUpdateId).val().length);
// scroll the page to the updated control
// $("#" + ItemToUpdateId).focus();
} // FileDisplayUploadFile
async function downloadFile(filename)
{
let response = await fetch(filename);
if(response.status != 200) {
throw new Error("File Read Server Error: '" + response.status + "'");
}
// read response stream as text
let text_data = await response.text();
return text_data;
} // downloadFile
function StartFragmentAssemblyTimer(Id)
{
StopFragmentAssemblyTimer(Id);
FragmentAssemblyTimer[Id] = setInterval(function(_Id)
{
// does the fragment assembly still exist?
if("undefined" !== typeof controlAssemblyArray[_Id])
{
if(null !== controlAssemblyArray[_Id])
{
// we have a valid control that is being assembled
// ask for the next part
let TotalRequest = JSON.stringify({ 'id' : controlAssemblyArray[_Id].control.id, 'offset' : controlAssemblyArray[_Id].offset});
websock.send("uifragmentok:" + 0 + ": " + TotalRequest + ":");
}
}
}, 1000, Id);
}
function StopFragmentAssemblyTimer(Id)
{
if("undefined" !== typeof FragmentAssemblyTimer[Id])
{
if(FragmentAssemblyTimer[Id])
{
clearInterval(FragmentAssemblyTimer[Id]);
}
}
}
function sliderchange(number) { function sliderchange(number) {
var val = $("#sl" + number).val(); var val = $("#sl" + number).val();
websock.send("slvalue:" + val + ":" + number); websock.send("slvalue:" + val + ":" + number);
@ -780,11 +979,13 @@ var addToHTML = function (data) {
case UI_GRAPH: case UI_GRAPH:
case UI_GAUGE: case UI_GAUGE:
case UI_ACCEL: case UI_ACCEL:
case UI_FILEDISPLAY:
html = "<div id='id" + data.id + "' " + panelStyle + " class='two columns " + panelwide + " card tcenter " + html = "<div id='id" + data.id + "' " + panelStyle + " class='two columns " + panelwide + " card tcenter " +
colorClass(data.color) + "'><h5>" + data.label + "</h5><hr/>" + colorClass(data.color) + "'><h5>" + data.label + "</h5><hr/>" +
elementHTML(data) + elementHTML(data) +
"</div>"; "</div>";
break; break;
case UI_SEPARATOR: case UI_SEPARATOR:
html = "<div id='id" + data.id + "' " + panelStyle + " class='sectionbreak columns'>" + html = "<div id='id" + data.id + "' " + panelStyle + " class='sectionbreak columns'>" +
"<h5>" + data.label + "</h5><hr/></div>"; "<h5>" + data.label + "</h5><hr/></div>";
@ -811,6 +1012,9 @@ var elementHTML = function (data) {
case UI_LABEL: case UI_LABEL:
return "<span id='l" + id + "' " + elementStyle + return "<span id='l" + id + "' " + elementStyle +
" class='label label-wrap'>" + data.value + "</span>"; " class='label label-wrap'>" + data.value + "</span>";
case UI_FILEDISPLAY:
return "<textarea id='fd" + id + "' rows='4' " + elementStyle +
" class='label label-wrap'>" + "</textarea>";
case UI_BUTTON: case UI_BUTTON:
return "<button id='btn" + id + "' " + elementStyle + return "<button id='btn" + id + "' " + elementStyle +
" onmousedown='buttonclick(" + id + ", true)'" + " onmousedown='buttonclick(" + id + ", true)'" +
@ -870,8 +1074,6 @@ var elementHTML = function (data) {
} }
} }
var processEnabled = function (data) { var processEnabled = function (data) {
//Handle the enabling and disabling of controls //Handle the enabling and disabling of controls
//Most controls can be disabled through the use of $("#<item>").prop("disabled", true) and CSS will style it accordingly //Most controls can be disabled through the use of $("#<item>").prop("disabled", true) and CSS will style it accordingly
@ -917,6 +1119,8 @@ var processEnabled = function (data) {
case UI_CPAD: case UI_CPAD:
case UPDATE_PAD: case UPDATE_PAD:
case UPDATE_CPAD: case UPDATE_CPAD:
case UI_FILEDISPLAY:
case UPDATE_FILEDISPLAY:
if (data.enabled) { if (data.enabled) {
$("#id" + data.id + " nav").removeClass('disabled'); $("#id" + data.id + " nav").removeClass('disabled');
} else { } else {

View File

@ -1,107 +1,136 @@
const UI_INITIAL_GUI=200;const UI_RELOAD=201;const UPDATE_OFFSET=100;const UI_EXTEND_GUI=210;const UI_TITEL=0;const UI_PAD=1;const UPDATE_PAD=101;const UI_CPAD=2;const UPDATE_CPAD=102;const UI_BUTTON=3;const UPDATE_BUTTON=103;const UI_LABEL=4;const UPDATE_LABEL=104;const UI_SWITCHER=5;const UPDATE_SWITCHER=105;const UI_SLIDER=6;const UPDATE_SLIDER=106;const UI_NUMBER=7;const UPDATE_NUMBER=107;const UI_TEXT_INPUT=8;const UPDATE_TEXT_INPUT=108;const UI_GRAPH=9;const ADD_GRAPH_POINT=10;const CLEAR_GRAPH=109;const UI_TAB=11;const UPDATE_TAB=111;const UI_SELECT=12;const UPDATE_SELECT=112;const UI_OPTION=13;const UPDATE_OPTION=113;const UI_MIN=14;const UPDATE_MIN=114;const UI_MAX=15;const UPDATE_MAX=115;const UI_STEP=16;const UPDATE_STEP=116;const UI_GAUGE=17;const UPDATE_GAUGE=117;const UI_ACCEL=18;const UPDATE_ACCEL=118;const UI_SEPARATOR=19;const UPDATE_SEPARATOR=119;const UI_TIME=20;const UPDATE_TIME=120;const UP=0;const DOWN=1;const LEFT=2;const RIGHT=3;const CENTER=4;const C_TURQUOISE=0;const C_EMERALD=1;const C_PETERRIVER=2;const C_WETASPHALT=3;const C_SUNFLOWER=4;const C_CARROT=5;const C_ALIZARIN=6;const C_DARK=7;const C_NONE=255;var graphData=new Array();var hasAccel=false;var sliderContinuous=false;function colorClass(colorId){colorId=Number(colorId);switch(colorId){case C_TURQUOISE:return"turquoise";case C_EMERALD:return"emerald";case C_PETERRIVER:return"peterriver";case C_WETASPHALT:return"wetasphalt";case C_SUNFLOWER:return"sunflower";case C_CARROT:return"carrot";case C_ALIZARIN:return"alizarin";case C_DARK:case C_NONE:return"dark";default:return"";}} const UI_INITIAL_GUI=200;const UI_RELOAD=201;const UPDATE_OFFSET=100;const UI_EXTEND_GUI=210;const UI_TITEL=0;const UI_PAD=1;const UPDATE_PAD=101;const UI_CPAD=2;const UPDATE_CPAD=102;const UI_BUTTON=3;const UPDATE_BUTTON=103;const UI_LABEL=4;const UPDATE_LABEL=104;const UI_SWITCHER=5;const UPDATE_SWITCHER=105;const UI_SLIDER=6;const UPDATE_SLIDER=106;const UI_NUMBER=7;const UPDATE_NUMBER=107;const UI_TEXT_INPUT=8;const UPDATE_TEXT_INPUT=108;const UI_GRAPH=9;const ADD_GRAPH_POINT=10;const CLEAR_GRAPH=109;const UI_TAB=11;const UPDATE_TAB=111;const UI_SELECT=12;const UPDATE_SELECT=112;const UI_OPTION=13;const UPDATE_OPTION=113;const UI_MIN=14;const UPDATE_MIN=114;const UI_MAX=15;const UPDATE_MAX=115;const UI_STEP=16;const UPDATE_STEP=116;const UI_GAUGE=17;const UPDATE_GAUGE=117;const UI_ACCEL=18;const UPDATE_ACCEL=118;const UI_SEPARATOR=19;const UPDATE_SEPARATOR=119;const UI_TIME=20;const UPDATE_TIME=120;const UI_FILEDISPLAY=21;const UPDATE_FILEDISPLAY=121;const UI_FRAGMENT=98;const UP=0;const DOWN=1;const LEFT=2;const RIGHT=3;const CENTER=4;const C_TURQUOISE=0;const C_EMERALD=1;const C_PETERRIVER=2;const C_WETASPHALT=3;const C_SUNFLOWER=4;const C_CARROT=5;const C_ALIZARIN=6;const C_DARK=7;const C_NONE=255;var controlAssemblyArray=new Object();var FragmentAssemblyTimer=new Array();var graphData=new Array();var hasAccel=false;var sliderContinuous=false;function colorClass(colorId){colorId=Number(colorId);switch(colorId){case C_TURQUOISE:return"turquoise";case C_EMERALD:return"emerald";case C_PETERRIVER:return"peterriver";case C_WETASPHALT:return"wetasphalt";case C_SUNFLOWER:return"sunflower";case C_CARROT:return"carrot";case C_ALIZARIN:return"alizarin";case C_DARK:case C_NONE:return"dark";default:return"";}}
var websock;var websockConnected=false;var WebSocketTimer=null;function requestOrientationPermission(){} var websock;var websockConnected=false;var WebSocketTimer=null;function requestOrientationPermission(){}
function saveGraphData(){localStorage.setItem("espuigraphs",JSON.stringify(graphData));} function saveGraphData(){localStorage.setItem("espuigraphs",JSON.stringify(graphData));}
function restoreGraphData(id){var savedData=localStorage.getItem("espuigraphs",graphData);if(savedData!=null){savedData=JSON.parse(savedData);let idData=savedData[id];return Array.isArray(idData)?idData:[];} function restoreGraphData(id){var savedData=localStorage.getItem("espuigraphs",graphData);if(savedData!=null){savedData=JSON.parse(savedData);let idData=savedData[id];return Array.isArray(idData)?idData:[];}
return[];} return[];}
function restart(){$(document).add("*").off();$("#row").html("");conStatusError();start();} function restart(){$(document).add("*").off();$("#row").html("");conStatusError();start();}
function conStatusError(){if(true===websockConnected){websockConnected=false;websock.close();$("#conStatus").removeClass("color-green");$("#conStatus").addClass("color-red");$("#conStatus").html("Error / No Connection &#8635;");$("#conStatus").off();$("#conStatus").on({click:restart,});}} function conStatusError(){FragmentAssemblyTimer.forEach(element=>{clearInterval(element);});FragmentAssemblyTimer=new Array();controlAssemblyArray=new Array();if(true===websockConnected){websockConnected=false;websock.close();$("#conStatus").removeClass("color-green");$("#conStatus").addClass("color-red");$("#conStatus").html("Error / No Connection &#8635;");$("#conStatus").off();$("#conStatus").on({click:restart,});}}
function handleVisibilityChange(){if(!websockConnected&&!document.hidden){restart();}} function handleVisibilityChange(){if(!websockConnected&&!document.hidden){restart();}}
function start(){document.addEventListener("visibilitychange",handleVisibilityChange,false);if(window.location.port!=""||window.location.port!=80||window.location.port!=443){websock=new WebSocket("ws://"+window.location.hostname+":"+window.location.port+"/ws");}else{websock=new WebSocket("ws://"+window.location.hostname+"/ws");} function start(){let location=window.location.hostname;let port=window.location.port;document.addEventListener("visibilitychange",handleVisibilityChange,false);if(port!=""||port!=80||port!=443){websock=new WebSocket("ws://"+location+":"+port+"/ws");}else{websock=new WebSocket("ws://"+location+"/ws");}
if(null===WebSocketTimer){WebSocketTimer=setInterval(function(){if(websock.readyState===3){restart();}},5000);} if(null===WebSocketTimer){WebSocketTimer=setInterval(function(){if(websock.readyState===3){restart();}},5000);}
websock.onopen=function(evt){console.log("websock open");$("#conStatus").addClass("color-green");$("#conStatus").text("Connected");websockConnected=true;};websock.onclose=function(evt){console.log("websock close");conStatusError();};websock.onerror=function(evt){console.log("websock Error");console.log(evt);restart();};var handleEvent=function(evt){console.log(evt);try{var data=JSON.parse(evt.data);} websock.onopen=function(evt){console.log("websock open");$("#conStatus").addClass("color-green");$("#conStatus").text("Connected");websockConnected=true;FragmentAssemblyTimer.forEach(element=>{clearInterval(element);});FragmentAssemblyTimer=new Array();controlAssemblyArray=new Array();};websock.onclose=function(evt){console.log("websock close");conStatusError();FragmentAssemblyTimer.forEach(element=>{clearInterval(element);});FragmentAssemblyTimer=new Array();controlAssemblyArray=new Array();};websock.onerror=function(evt){console.log("websock Error");restart();FragmentAssemblyTimer.forEach(element=>{clearInterval(element);});FragmentAssemblyTimer=new Array();controlAssemblyArray=new Array();};var handleEvent=function(evt){try{var data=JSON.parse(evt.data);}
catch(Event){console.error(Event);websock.send("uiok:"+0);return;} catch(Event){console.error(Event);websock.send("uiok:"+0);return;}
var e=document.body;var center="";switch(data.type){case UI_INITIAL_GUI:$("#row").html("");$("#tabsnav").html("");$("#tabscontent").html("");if(data.sliderContinuous){sliderContinuous=data.sliderContinuous;} var e=document.body;var center="";switch(data.type){case UI_INITIAL_GUI:$("#row").html("");$("#tabsnav").html("");$("#tabscontent").html("");if(data.sliderContinuous){sliderContinuous=data.sliderContinuous;}
data.controls.forEach(element=>{var fauxEvent={data:JSON.stringify(element),};handleEvent(fauxEvent);});if(data.totalcontrols>(data.controls.length-1)){websock.send("uiok:"+(data.controls.length-1));} data.controls.forEach(element=>{var fauxEvent={data:JSON.stringify(element),};handleEvent(fauxEvent);});if(data.totalcontrols>(data.controls.length-1)){websock.send("uiok:"+(data.controls.length-1));}
break;case UI_EXTEND_GUI:data.controls.forEach(element=>{var fauxEvent={data:JSON.stringify(element),};handleEvent(fauxEvent);});if(data.totalcontrols>data.startindex+(data.controls.length-1)){websock.send("uiok:"+(data.startindex+(data.controls.length-1)));} break;case UI_EXTEND_GUI:data.controls.forEach(element=>{var fauxEvent={data:JSON.stringify(element),};handleEvent(fauxEvent);});if(data.totalcontrols>data.startindex+(data.controls.length-1)){websock.send("uiok:"+(data.startindex+(data.controls.length-1)));}
break;case UI_RELOAD:window.location.reload();break;case UI_TITEL:document.title=data.label;$("#mainHeader").html(data.label);break;case UI_LABEL:case UI_NUMBER:case UI_TEXT_INPUT:case UI_SELECT:case UI_GAUGE:case UI_SEPARATOR:if(data.visible)addToHTML(data);break;case UI_BUTTON:if(data.visible){addToHTML(data);$("#btn"+data.id).on({touchstart:function(e){e.preventDefault();buttonclick(data.id,true);},touchend:function(e){e.preventDefault();buttonclick(data.id,false);},});} break;case UI_RELOAD:window.location.reload();break;case UI_TITEL:document.title=data.label;$("#mainHeader").html(data.label);break;case UI_LABEL:case UI_NUMBER:case UI_TEXT_INPUT:case UI_SELECT:case UI_GAUGE:case UI_SEPARATOR:if(data.visible)addToHTML(data);break;case UI_BUTTON:if(data.visible){addToHTML(data);$("#btn"+data.id).on({touchstart:function(e){e.preventDefault();buttonclick(data.id,true);},touchend:function(e){e.preventDefault();buttonclick(data.id,false);},});}
break;case UI_SWITCHER:if(data.visible){addToHTML(data);switcher(data.id,data.value);} break;case UI_SWITCHER:if(data.visible){addToHTML(data);switcher(data.id,data.value);}
break;case UI_CPAD:case UI_PAD:if(data.visible){addToHTML(data);$("#pf"+data.id).on({touchstart:function(e){e.preventDefault();padclick(UP,data.id,true);},touchend:function(e){e.preventDefault();padclick(UP,data.id,false);},});$("#pl"+data.id).on({touchstart:function(e){e.preventDefault();padclick(LEFT,data.id,true);},touchend:function(e){e.preventDefault();padclick(LEFT,data.id,false);},});$("#pr"+data.id).on({touchstart:function(e){e.preventDefault();padclick(RIGHT,data.id,true);},touchend:function(e){e.preventDefault();padclick(RIGHT,data.id,false);},});$("#pb"+data.id).on({touchstart:function(e){e.preventDefault();padclick(DOWN,data.id,true);},touchend:function(e){e.preventDefault();padclick(DOWN,data.id,false);},});$("#pc"+data.id).on({touchstart:function(e){e.preventDefault();padclick(CENTER,data.id,true);},touchend:function(e){e.preventDefault();padclick(CENTER,data.id,false);},});} break;case UI_CPAD:case UI_PAD:if(data.visible){addToHTML(data);$("#pf"+data.id).on({touchstart:function(e){e.preventDefault();padclick(UP,data.id,true);},touchend:function(e){e.preventDefault();padclick(UP,data.id,false);},});$("#pl"+data.id).on({touchstart:function(e){e.preventDefault();padclick(LEFT,data.id,true);},touchend:function(e){e.preventDefault();padclick(LEFT,data.id,false);},});$("#pr"+data.id).on({touchstart:function(e){e.preventDefault();padclick(RIGHT,data.id,true);},touchend:function(e){e.preventDefault();padclick(RIGHT,data.id,false);},});$("#pb"+data.id).on({touchstart:function(e){e.preventDefault();padclick(DOWN,data.id,true);},touchend:function(e){e.preventDefault();padclick(DOWN,data.id,false);},});$("#pc"+data.id).on({touchstart:function(e){e.preventDefault();padclick(CENTER,data.id,true);},touchend:function(e){e.preventDefault();padclick(CENTER,data.id,false);},});}
break;case UI_SLIDER:if(data.visible){addToHTML(data);rangeSlider(!sliderContinuous);} break;case UI_SLIDER:if(data.visible){addToHTML(data);rangeSlider(!sliderContinuous);}
break;case UI_TAB:if(data.visible){$("#tabsnav").append("<li><a onmouseup='tabclick("+data.id+")' href='#tab"+data.id+"'>"+data.value+"</a></li>");$("#tabscontent").append("<div id='tab"+data.id+"'></div>");tabs=$(".tabscontent").tabbedContent({loop:true}).data("api");$("a").filter(function(){return $(this).attr("href")==="#click-to-switch";}).on("click",function(e){var tab=prompt("Tab to switch to (number or id)?");if(!tabs.switchTab(tab)){alert("That tab does not exist :\\");} break;case UI_TAB:if(data.visible){$("#tabsnav").append("<li><a onmouseup='tabclick("+data.id+")' href='#tab"+data.id+"'>"+data.value+"</a></li>");$("#tabscontent").append("<div id='tab"+data.id+"'></div>");tabs=$(".tabscontent").tabbedContent({loop:true}).data("api");$("a").filter(function(){return $(this).attr("href")==="#click-to-switch";}).on("click",function(e){var tab=prompt("Tab to switch to (number or id)?");if(!tabs.switchTab(tab)){alert("That tab does not exist :\\");}
e.preventDefault();});} e.preventDefault();});}
break;case UI_OPTION:if(data.parentControl){var parent=$("#select"+data.parentControl);parent.append("<option id='option"+ break;case UI_OPTION:if(data.parentControl){var parent=$("#select"+data.parentControl);parent.append("<option id='option"+
data.id+ data.id+
"' value='"+ "' value='"+
data.value+ data.value+
"' "+ "' "+
data.selected+ data.selected+
">"+ ">"+
data.label+ data.label+
"</option>");} "</option>");}
break;case UI_MIN:if(data.parentControl){if($('#sl'+data.parentControl).length){$('#sl'+data.parentControl).attr("min",data.value);}else if($('#num'+data.parentControl).length){$('#num'+data.parentControl).attr("min",data.value);}} break;case UI_MIN:if(data.parentControl){if($('#sl'+data.parentControl).length){$('#sl'+data.parentControl).attr("min",data.value);}else if($('#num'+data.parentControl).length){$('#num'+data.parentControl).attr("min",data.value);}}
break;case UI_MAX:if(data.parentControl){if($('#sl'+data.parentControl).length){$('#sl'+data.parentControl).attr("max",data.value);}else if($('#text'+data.parentControl).length){$('#text'+data.parentControl).attr("maxlength",data.value);}else if($('#num'+data.parentControl).length){$('#num'+data.parentControl).attr("max",data.value);}} break;case UI_MAX:if(data.parentControl){if($('#sl'+data.parentControl).length){$('#sl'+data.parentControl).attr("max",data.value);}else if($('#text'+data.parentControl).length){$('#text'+data.parentControl).attr("maxlength",data.value);}else if($('#num'+data.parentControl).length){$('#num'+data.parentControl).attr("max",data.value);}}
break;case UI_STEP:if(data.parentControl){if($('#sl'+data.parentControl).length){$('#sl'+data.parentControl).attr("step",data.value);}else if($('#num'+data.parentControl).length){$('#num'+data.parentControl).attr("step",data.value);}} break;case UI_STEP:if(data.parentControl){if($('#sl'+data.parentControl).length){$('#sl'+data.parentControl).attr("step",data.value);}else if($('#num'+data.parentControl).length){$('#num'+data.parentControl).attr("step",data.value);}}
break;case UI_GRAPH:if(data.visible){addToHTML(data);graphData[data.id]=restoreGraphData(data.id);renderGraphSvg(graphData[data.id],"graph"+data.id);} break;case UI_GRAPH:if(data.visible){addToHTML(data);graphData[data.id]=restoreGraphData(data.id);renderGraphSvg(graphData[data.id],"graph"+data.id);}
break;case ADD_GRAPH_POINT:var ts=new Date().getTime();graphData[data.id].push({x:ts,y:data.value});saveGraphData();renderGraphSvg(graphData[data.id],"graph"+data.id);break;case CLEAR_GRAPH:graphData[data.id]=[];saveGraphData();renderGraphSvg(graphData[data.id],"graph"+data.id);break;case UI_ACCEL:if(hasAccel)break;hasAccel=true;if(data.visible){addToHTML(data);requestOrientationPermission();} break;case ADD_GRAPH_POINT:var ts=new Date().getTime();graphData[data.id].push({x:ts,y:data.value});saveGraphData();renderGraphSvg(graphData[data.id],"graph"+data.id);break;case CLEAR_GRAPH:graphData[data.id]=[];saveGraphData();renderGraphSvg(graphData[data.id],"graph"+data.id);break;case UI_ACCEL:if(hasAccel)break;hasAccel=true;if(data.visible){addToHTML(data);requestOrientationPermission();}
break;case UPDATE_LABEL:$("#l"+data.id).html(data.value);if(data.hasOwnProperty('elementStyle')){$("#l"+data.id).attr("style",data.elementStyle);} break;case UI_FILEDISPLAY:if(data.visible)
break;case UPDATE_SWITCHER:switcher(data.id,data.value=="0"?0:1);if(data.hasOwnProperty('elementStyle')){$("#sl"+data.id).attr("style",data.elementStyle);} {addToHTML(data);FileDisplayUploadFile(data);}
break;case UPDATE_SLIDER:$("#sl"+data.id).attr("value",data.value) break;case UPDATE_LABEL:$("#l"+data.id).html(data.value);if(data.hasOwnProperty('elementStyle')){$("#l"+data.id).attr("style",data.elementStyle);}
slider_move($("#sl"+data.id).parent().parent(),data.value,"100",false);if(data.hasOwnProperty('elementStyle')){$("#sl"+data.id).attr("style",data.elementStyle);} break;case UPDATE_SWITCHER:switcher(data.id,data.value=="0"?0:1);if(data.hasOwnProperty('elementStyle')){$("#sl"+data.id).attr("style",data.elementStyle);}
break;case UPDATE_NUMBER:$("#num"+data.id).val(data.value);if(data.hasOwnProperty('elementStyle')){$("#num"+data.id).attr("style",data.elementStyle);} break;case UPDATE_SLIDER:$("#sl"+data.id).attr("value",data.value)
break;case UPDATE_TEXT_INPUT:$("#text"+data.id).val(data.value);if(data.hasOwnProperty('elementStyle')){$("#text"+data.id).attr("style",data.elementStyle);} slider_move($("#sl"+data.id).parent().parent(),data.value,"100",false);if(data.hasOwnProperty('elementStyle')){$("#sl"+data.id).attr("style",data.elementStyle);}
if(data.hasOwnProperty('inputType')){$("#text"+data.id).attr("type",data.inputType);} break;case UPDATE_NUMBER:$("#num"+data.id).val(data.value);if(data.hasOwnProperty('elementStyle')){$("#num"+data.id).attr("style",data.elementStyle);}
break;case UPDATE_SELECT:$("#select"+data.id).val(data.value);if(data.hasOwnProperty('elementStyle')){$("#select"+data.id).attr("style",data.elementStyle);} break;case UPDATE_TEXT_INPUT:$("#text"+data.id).val(data.value);if(data.hasOwnProperty('elementStyle')){$("#text"+data.id).attr("style",data.elementStyle);}
break;case UPDATE_BUTTON:$("#btn"+data.id).val(data.value);$("#btn"+data.id).text(data.value);if(data.hasOwnProperty('elementStyle')){$("#btn"+data.id).attr("style",data.elementStyle);} if(data.hasOwnProperty('inputType')){$("#text"+data.id).attr("type",data.inputType);}
break;case UPDATE_PAD:case UPDATE_CPAD:break;case UPDATE_GAUGE:$("#gauge"+data.id).val(data.value);if(data.hasOwnProperty('elementStyle')){$("#gauge"+data.id).attr("style",data.elementStyle);} break;case UPDATE_SELECT:$("#select"+data.id).val(data.value);if(data.hasOwnProperty('elementStyle')){$("#select"+data.id).attr("style",data.elementStyle);}
break;case UPDATE_ACCEL:break;case UPDATE_TIME:var rv=new Date().toISOString();websock.send("time:"+rv+":"+data.id);break;default:console.error("Unknown type or event");break;} break;case UPDATE_BUTTON:$("#btn"+data.id).val(data.value);$("#btn"+data.id).text(data.value);if(data.hasOwnProperty('elementStyle')){$("#btn"+data.id).attr("style",data.elementStyle);}
if(data.type>=UI_TITEL&&data.type<UPDATE_OFFSET){processEnabled(data);} break;case UPDATE_PAD:case UPDATE_CPAD:break;case UPDATE_GAUGE:$("#gauge"+data.id).val(data.value);if(data.hasOwnProperty('elementStyle')){$("#gauge"+data.id).attr("style",data.elementStyle);}
if(data.type>=UPDATE_OFFSET&&data.type<UI_INITIAL_GUI){var element=$("#id"+data.id);if(data.hasOwnProperty('panelStyle')){$("#id"+data.id).attr("style",data.panelStyle);} break;case UPDATE_ACCEL:break;case UPDATE_TIME:var rv=new Date().toISOString();websock.send("time:"+rv+":"+data.id);break;case UPDATE_FILEDISPLAY:FileDisplayUploadFile(data);break;case UI_FRAGMENT:let FragmentLen=data.length;let FragementOffset=data.offset;let NextFragmentOffset=FragementOffset+FragmentLen;let Total=data.total;let Arrived=(FragmentLen+FragementOffset);let FragmentFinal=Total===Arrived;if(!data.hasOwnProperty('control'))
if(data.hasOwnProperty('visible')){if(data['visible']) {console.error("UI_FRAGMENT:Missing control record, skipping control");break;}
$("#id"+data.id).show();else let control=data.control;StopFragmentAssemblyTimer(data.control.id);if(0===FragementOffset)
$("#id"+data.id).hide();} {controlAssemblyArray[control.id]=data;controlAssemblyArray[control.id].offset=NextFragmentOffset;StartFragmentAssemblyTimer(control.id);let TotalRequest=JSON.stringify({'id':control.id,'offset':NextFragmentOffset});websock.send("uifragmentok:"+0+": "+TotalRequest+":");break;}
if(data.type==UPDATE_SLIDER){element.removeClass("slider-turquoise slider-emerald slider-peterriver slider-wetasphalt slider-sunflower slider-carrot slider-alizarin");element.addClass("slider-"+colorClass(data.color));}else{element.removeClass("turquoise emerald peterriver wetasphalt sunflower carrot alizarin");element.addClass(colorClass(data.color));} if("undefined"===typeof controlAssemblyArray[control.id])
processEnabled(data);} {console.error("Missing first fragment for control: "+control.id);StartFragmentAssemblyTimer(control.id);let TotalRequest=JSON.stringify({'id':control.id,'offset':0});websock.send("uifragmentok:"+0+": "+TotalRequest+":");break;}
$(".range-slider__range").each(function(){$(this)[0].value=$(this).attr("value");$(this).next().html($(this).attr("value"));});};websock.onmessage=handleEvent;} if(FragementOffset!==controlAssemblyArray[control.id].offset)
function sliderchange(number){var val=$("#sl"+number).val();websock.send("slvalue:"+val+":"+number);$(".range-slider__range").each(function(){$(this).attr("value",$(this)[0].value);});} {console.error("Wrong next fragment. Expected: "+controlAssemblyArray[control.id].offset+" Got: "+FragementOffset);StartFragmentAssemblyTimer(control.id);let TotalRequest=JSON.stringify({'id':control.id,'offset':controlAssemblyArray[control.id].length+controlAssemblyArray[control.id].offset});websock.send("uifragmentok:"+0+": "+TotalRequest+":");break;}
function numberchange(number){var val=$("#num"+number).val();websock.send("nvalue:"+val+":"+number);} controlAssemblyArray[control.id].control.value+=control.value;controlAssemblyArray[control.id].offset=NextFragmentOffset;if(true===FragmentFinal)
function textchange(number){var val=$("#text"+number).val();websock.send("tvalue:"+val+":"+number);} {var fauxEvent={data:JSON.stringify(controlAssemblyArray[control.id].control),};handleEvent(fauxEvent);controlAssemblyArray[control.id]=null;}
function tabclick(number){var val=$("#tab"+number).val();websock.send("tabvalue:"+val+":"+number);} else
function selectchange(number){var val=$("#select"+number).val();websock.send("svalue:"+val+":"+number);} {StartFragmentAssemblyTimer(control.id);let TotalRequest=JSON.stringify({'id':control.id,'offset':NextFragmentOffset});websock.send("uifragmentok:"+0+": "+TotalRequest+":");}
function buttonclick(number,isdown){if(isdown)websock.send("bdown:"+number);else websock.send("bup:"+number);} break;default:console.error("Unknown type or event");break;}
function padclick(type,number,isdown){if($("#id"+number+" nav").hasClass("disabled")){return;} if(data.type>=UI_TITEL&&data.type<UPDATE_OFFSET){processEnabled(data);}
switch(type){case CENTER:if(isdown)websock.send("pcdown:"+number);else websock.send("pcup:"+number);break;case UP:if(isdown)websock.send("pfdown:"+number);else websock.send("pfup:"+number);break;case DOWN:if(isdown)websock.send("pbdown:"+number);else websock.send("pbup:"+number);break;case LEFT:if(isdown)websock.send("pldown:"+number);else websock.send("plup:"+number);break;case RIGHT:if(isdown)websock.send("prdown:"+number);else websock.send("prup:"+number);break;}} if(data.type>=UPDATE_OFFSET&&data.type<UI_INITIAL_GUI){var element=$("#id"+data.id);if(data.hasOwnProperty('panelStyle')){$("#id"+data.id).attr("style",data.panelStyle);}
function switcher(number,state){if(state==null){if(!$("#sl"+number).hasClass("checked")){websock.send("sactive:"+number);$("#sl"+number).addClass("checked");}else{websock.send("sinactive:"+number);$("#sl"+number).removeClass("checked");}}else if(state==1){$("#sl"+number).addClass("checked");$("#sl"+number).prop("checked",true);}else if(state==0){$("#sl"+number).removeClass("checked");$("#sl"+number).prop("checked",false);}} if(data.hasOwnProperty('visible')){if(data['visible'])
var rangeSlider=function(isDiscrete){var range=$(".range-slider__range");var slidercb=function(){sliderchange($(this).attr("id").replace(/^\D+/g,""));};range.on({input:function(){$(this).next().html(this.value)}});range.each(function(){$(this).next().html(this.value);if($(this).attr("callbackSet")!="true"){if(!isDiscrete){$(this).on({input:slidercb});}else{$(this).on({change:slidercb});} $("#id"+data.id).show();else
$(this).attr("callbackSet","true");}});};var addToHTML=function(data){panelStyle=data.hasOwnProperty('panelStyle')?" style='"+data.panelStyle+"' ":"";panelwide=data.hasOwnProperty('wide')?"wide":"";if(!data.hasOwnProperty('parentControl')||$("#tab"+data.parentControl).length>0){var parent=data.hasOwnProperty('parentControl')?$("#tab"+data.parentControl):$("#row");var html="";switch(data.type){case UI_LABEL:case UI_BUTTON:case UI_SWITCHER:case UI_CPAD:case UI_PAD:case UI_SLIDER:case UI_NUMBER:case UI_TEXT_INPUT:case UI_SELECT:case UI_GRAPH:case UI_GAUGE:case UI_ACCEL:html="<div id='id"+data.id+"' "+panelStyle+" class='two columns "+panelwide+" card tcenter "+ $("#id"+data.id).hide();}
colorClass(data.color)+"'><h5>"+data.label+"</h5><hr/>"+ if(data.type==UPDATE_SLIDER){element.removeClass("slider-turquoise slider-emerald slider-peterriver slider-wetasphalt slider-sunflower slider-carrot slider-alizarin");element.addClass("slider-"+colorClass(data.color));}else{element.removeClass("turquoise emerald peterriver wetasphalt sunflower carrot alizarin");element.addClass(colorClass(data.color));}
elementHTML(data)+ processEnabled(data);}
"</div>";break;case UI_SEPARATOR:html="<div id='id"+data.id+"' "+panelStyle+" class='sectionbreak columns'>"+ $(".range-slider__range").each(function(){$(this)[0].value=$(this).attr("value");$(this).next().html($(this).attr("value"));});};websock.onmessage=handleEvent;}
"<h5>"+data.label+"</h5><hr/></div>";break;case UI_TIME:break;} async function FileDisplayUploadFile(data)
parent.append(html);}else{var parent=$("#id"+data.parentControl);parent.append(elementHTML(data));}} {let text=await downloadFile(data.value);let ItemToUpdateId="fd"+data.id;$("#"+ItemToUpdateId).val(text);$("#"+ItemToUpdateId).css("textAlign","left");$("#"+ItemToUpdateId).css("white-space","nowrap");$("#"+ItemToUpdateId).css("overflow","scroll");$("#"+ItemToUpdateId).css("overflow-y","scroll");$("#"+ItemToUpdateId).css("overflow-x","scroll");$("#"+ItemToUpdateId).scrollTop($("#"+ItemToUpdateId).val().length);}
var elementHTML=function(data){var id=data.id async function downloadFile(filename)
var elementStyle=data.hasOwnProperty('elementStyle')?" style='"+data.elementStyle+"' ":"";var inputType=data.hasOwnProperty('inputType')?" type='"+data.inputType+"' ":"";switch(data.type){case UI_LABEL:return"<span id='l"+id+"' "+elementStyle+ {let response=await fetch(filename);if(response.status!=200){throw new Error("File Read Server Error: '"+response.status+"'");}
" class='label label-wrap'>"+data.value+"</span>";case UI_BUTTON:return"<button id='btn"+id+"' "+elementStyle+ let text_data=await response.text();return text_data;}
" onmousedown='buttonclick("+id+", true)'"+ function StartFragmentAssemblyTimer(Id)
" onmouseup='buttonclick("+id+", false)'>"+ {StopFragmentAssemblyTimer(Id);FragmentAssemblyTimer[Id]=setInterval(function(_Id)
data.value+"</button>";case UI_SWITCHER:return"<label id='sl"+id+"' "+elementStyle+ {if("undefined"!==typeof controlAssemblyArray[_Id])
" class='switch "+(data.value=="1"?"checked":"")+ {if(null!==controlAssemblyArray[_Id])
(data.hasOwnProperty('vertical')?" vert-switcher ":"")+ {let TotalRequest=JSON.stringify({'id':controlAssemblyArray[_Id].control.id,'offset':controlAssemblyArray[_Id].offset});websock.send("uifragmentok:"+0+": "+TotalRequest+":");}}},1000,Id);}
"'>"+ function StopFragmentAssemblyTimer(Id)
"<div class='in'>"+ {if("undefined"!==typeof FragmentAssemblyTimer[Id])
"<input type='checkbox' id='s"+id+"' onClick='switcher("+id+",null)' "+ {if(FragmentAssemblyTimer[Id])
(data.value=="1"?"checked":"")+"/></div></label>";case UI_CPAD:case UI_PAD:return"<nav class='control'><ul>"+ {clearInterval(FragmentAssemblyTimer[Id]);}}}
"<li><a onmousedown='padclick(UP, "+id+", true)' "+ function sliderchange(number){var val=$("#sl"+number).val();websock.send("slvalue:"+val+":"+number);$(".range-slider__range").each(function(){$(this).attr("value",$(this)[0].value);});}
"onmouseup='padclick(UP, "+id+", false)' id='pf"+id+"'>&#9650;</a></li>"+ function numberchange(number){var val=$("#num"+number).val();websock.send("nvalue:"+val+":"+number);}
"<li><a onmousedown='padclick(RIGHT, "+id+", true)' "+ function textchange(number){var val=$("#text"+number).val();websock.send("tvalue:"+val+":"+number);}
"onmouseup='padclick(RIGHT, "+id+", false)' id='pr"+id+"'>&#9650;</a></li>"+ function tabclick(number){var val=$("#tab"+number).val();websock.send("tabvalue:"+val+":"+number);}
"<li><a onmousedown='padclick(LEFT, "+id+", true)' "+ function selectchange(number){var val=$("#select"+number).val();websock.send("svalue:"+val+":"+number);}
"onmouseup='padclick(LEFT, "+id+", false)' id='pl"+id+"'>&#9650;</a></li>"+ function buttonclick(number,isdown){if(isdown)websock.send("bdown:"+number);else websock.send("bup:"+number);}
"<li><a onmousedown='padclick(DOWN, "+id+", true)' "+ function padclick(type,number,isdown){if($("#id"+number+" nav").hasClass("disabled")){return;}
"onmouseup='padclick(DOWN, "+id+", false)' id='pb"+id+"'>&#9650;</a></li>"+ switch(type){case CENTER:if(isdown)websock.send("pcdown:"+number);else websock.send("pcup:"+number);break;case UP:if(isdown)websock.send("pfdown:"+number);else websock.send("pfup:"+number);break;case DOWN:if(isdown)websock.send("pbdown:"+number);else websock.send("pbup:"+number);break;case LEFT:if(isdown)websock.send("pldown:"+number);else websock.send("plup:"+number);break;case RIGHT:if(isdown)websock.send("prdown:"+number);else websock.send("prup:"+number);break;}}
"</ul>"+ function switcher(number,state){if(state==null){if(!$("#sl"+number).hasClass("checked")){websock.send("sactive:"+number);$("#sl"+number).addClass("checked");}else{websock.send("sinactive:"+number);$("#sl"+number).removeClass("checked");}}else if(state==1){$("#sl"+number).addClass("checked");$("#sl"+number).prop("checked",true);}else if(state==0){$("#sl"+number).removeClass("checked");$("#sl"+number).prop("checked",false);}}
(data.type==UI_CPAD?"<a class='confirm' onmousedown='padclick(CENTER,"+id+", true)' "+ var rangeSlider=function(isDiscrete){var range=$(".range-slider__range");var slidercb=function(){sliderchange($(this).attr("id").replace(/^\D+/g,""));};range.on({input:function(){$(this).next().html(this.value)}});range.each(function(){$(this).next().html(this.value);if($(this).attr("callbackSet")!="true"){if(!isDiscrete){$(this).on({input:slidercb});}else{$(this).on({change:slidercb});}
"onmouseup='padclick(CENTER, "+id+", false)' id='pc"+id+"'>OK</a>":"")+ $(this).attr("callbackSet","true");}});};var addToHTML=function(data){panelStyle=data.hasOwnProperty('panelStyle')?" style='"+data.panelStyle+"' ":"";panelwide=data.hasOwnProperty('wide')?"wide":"";if(!data.hasOwnProperty('parentControl')||$("#tab"+data.parentControl).length>0){var parent=data.hasOwnProperty('parentControl')?$("#tab"+data.parentControl):$("#row");var html="";switch(data.type){case UI_LABEL:case UI_BUTTON:case UI_SWITCHER:case UI_CPAD:case UI_PAD:case UI_SLIDER:case UI_NUMBER:case UI_TEXT_INPUT:case UI_SELECT:case UI_GRAPH:case UI_GAUGE:case UI_ACCEL:case UI_FILEDISPLAY:html="<div id='id"+data.id+"' "+panelStyle+" class='two columns "+panelwide+" card tcenter "+
"</nav>";case UI_SLIDER:return"<div class='range-slider "+ colorClass(data.color)+"'><h5>"+data.label+"</h5><hr/>"+
(data.hasOwnProperty('vertical')?" vert-slider ":"")+ elementHTML(data)+
"'>"+ "</div>";break;case UI_SEPARATOR:html="<div id='id"+data.id+"' "+panelStyle+" class='sectionbreak columns'>"+
"<input id='sl"+id+"' type='range' min='0' max='100' value='"+data.value+"' "+ "<h5>"+data.label+"</h5><hr/></div>";break;case UI_TIME:break;}
elementStyle+" class='range-slider__range'><span class='range-slider__value'>"+ parent.append(html);}else{var parent=$("#id"+data.parentControl);parent.append(elementHTML(data));}}
data.value+"</span></div>";case UI_NUMBER:return"<input style='color:black; "+data.elementStyle+"' id='num"+id+ var elementHTML=function(data){var id=data.id
"' type='number' value='"+data.value+"' onchange='numberchange("+id+")' />";case UI_TEXT_INPUT:return"<input "+inputType+"style='color:black; "+data.elementStyle+"' id='text"+id+ var elementStyle=data.hasOwnProperty('elementStyle')?" style='"+data.elementStyle+"' ":"";var inputType=data.hasOwnProperty('inputType')?" type='"+data.inputType+"' ":"";switch(data.type){case UI_LABEL:return"<span id='l"+id+"' "+elementStyle+
"' value='"+data.value+"' onchange='textchange("+id+")' />";case UI_SELECT:return"<select style='color:black; "+data.elementStyle+"' id='select"+id+ " class='label label-wrap'>"+data.value+"</span>";case UI_FILEDISPLAY:return"<textarea id='fd"+id+"' rows='4' "+elementStyle+
"' onchange='selectchange("+id+")' />";case UI_GRAPH:return"<figure id='graph"+id+"'><figcaption>"+data.label+"</figcaption></figure>";case UI_GAUGE:return"WILL BE A GAUGE <input style='color:black;' id='gauge"+id+ " class='label label-wrap'>"+"</textarea>";case UI_BUTTON:return"<button id='btn"+id+"' "+elementStyle+
"' type='number' value='"+data.value+"' onchange='numberchange("+id+")' />";case UI_ACCEL:return"ACCEL // Not implemented fully!<div class='accelerometer' id='accel"+id+ " onmousedown='buttonclick("+id+", true)'"+
"' ><div class='ball"+id+"'></div><pre class='accelerometeroutput"+id+"'></pre>";default:return"";}} " onmouseup='buttonclick("+id+", false)'>"+
var processEnabled=function(data){switch(data.type){case UI_SWITCHER:case UPDATE_SWITCHER:if(data.enabled){$("#sl"+data.id).removeClass('disabled');$("#s"+data.id).prop("disabled",false);}else{$("#sl"+data.id).addClass('disabled');$("#s"+data.id).prop("disabled",true);} data.value+"</button>";case UI_SWITCHER:return"<label id='sl"+id+"' "+elementStyle+
break;case UI_SLIDER:case UPDATE_SLIDER:$("#sl"+data.id).prop("disabled",!data.enabled);break;case UI_NUMBER:case UPDATE_NUMBER:$("#num"+data.id).prop("disabled",!data.enabled);break;case UI_TEXT_INPUT:case UPDATE_TEXT_INPUT:$("#text"+data.id).prop("disabled",!data.enabled);break;case UI_SELECT:case UPDATE_SELECT:$("#select"+data.id).prop("disabled",!data.enabled);break;case UI_BUTTON:case UPDATE_BUTTON:$("#btn"+data.id).prop("disabled",!data.enabled);break;case UI_PAD:case UI_CPAD:case UPDATE_PAD:case UPDATE_CPAD:if(data.enabled){$("#id"+data.id+" nav").removeClass('disabled');}else{$("#id"+data.id+" nav").addClass('disabled');} " class='switch "+(data.value=="1"?"checked":"")+
(data.hasOwnProperty('vertical')?" vert-switcher ":"")+
"'>"+
"<div class='in'>"+
"<input type='checkbox' id='s"+id+"' onClick='switcher("+id+",null)' "+
(data.value=="1"?"checked":"")+"/></div></label>";case UI_CPAD:case UI_PAD:return"<nav class='control'><ul>"+
"<li><a onmousedown='padclick(UP, "+id+", true)' "+
"onmouseup='padclick(UP, "+id+", false)' id='pf"+id+"'>&#9650;</a></li>"+
"<li><a onmousedown='padclick(RIGHT, "+id+", true)' "+
"onmouseup='padclick(RIGHT, "+id+", false)' id='pr"+id+"'>&#9650;</a></li>"+
"<li><a onmousedown='padclick(LEFT, "+id+", true)' "+
"onmouseup='padclick(LEFT, "+id+", false)' id='pl"+id+"'>&#9650;</a></li>"+
"<li><a onmousedown='padclick(DOWN, "+id+", true)' "+
"onmouseup='padclick(DOWN, "+id+", false)' id='pb"+id+"'>&#9650;</a></li>"+
"</ul>"+
(data.type==UI_CPAD?"<a class='confirm' onmousedown='padclick(CENTER,"+id+", true)' "+
"onmouseup='padclick(CENTER, "+id+", false)' id='pc"+id+"'>OK</a>":"")+
"</nav>";case UI_SLIDER:return"<div class='range-slider "+
(data.hasOwnProperty('vertical')?" vert-slider ":"")+
"'>"+
"<input id='sl"+id+"' type='range' min='0' max='100' value='"+data.value+"' "+
elementStyle+" class='range-slider__range'><span class='range-slider__value'>"+
data.value+"</span></div>";case UI_NUMBER:return"<input style='color:black; "+data.elementStyle+"' id='num"+id+
"' type='number' value='"+data.value+"' onchange='numberchange("+id+")' />";case UI_TEXT_INPUT:return"<input "+inputType+"style='color:black; "+data.elementStyle+"' id='text"+id+
"' value='"+data.value+"' onchange='textchange("+id+")' />";case UI_SELECT:return"<select style='color:black; "+data.elementStyle+"' id='select"+id+
"' onchange='selectchange("+id+")' />";case UI_GRAPH:return"<figure id='graph"+id+"'><figcaption>"+data.label+"</figcaption></figure>";case UI_GAUGE:return"WILL BE A GAUGE <input style='color:black;' id='gauge"+id+
"' type='number' value='"+data.value+"' onchange='numberchange("+id+")' />";case UI_ACCEL:return"ACCEL // Not implemented fully!<div class='accelerometer' id='accel"+id+
"' ><div class='ball"+id+"'></div><pre class='accelerometeroutput"+id+"'></pre>";default:return"";}}
var processEnabled=function(data){switch(data.type){case UI_SWITCHER:case UPDATE_SWITCHER:if(data.enabled){$("#sl"+data.id).removeClass('disabled');$("#s"+data.id).prop("disabled",false);}else{$("#sl"+data.id).addClass('disabled');$("#s"+data.id).prop("disabled",true);}
break;case UI_SLIDER:case UPDATE_SLIDER:$("#sl"+data.id).prop("disabled",!data.enabled);break;case UI_NUMBER:case UPDATE_NUMBER:$("#num"+data.id).prop("disabled",!data.enabled);break;case UI_TEXT_INPUT:case UPDATE_TEXT_INPUT:$("#text"+data.id).prop("disabled",!data.enabled);break;case UI_SELECT:case UPDATE_SELECT:$("#select"+data.id).prop("disabled",!data.enabled);break;case UI_BUTTON:case UPDATE_BUTTON:$("#btn"+data.id).prop("disabled",!data.enabled);break;case UI_PAD:case UI_CPAD:case UPDATE_PAD:case UPDATE_CPAD:case UI_FILEDISPLAY:case UPDATE_FILEDISPLAY:if(data.enabled){$("#id"+data.id+" nav").removeClass('disabled');}else{$("#id"+data.id+" nav").addClass('disabled');}
break;}} break;}}

28
data/js/graph.min.js vendored
View File

@ -1,15 +1,15 @@
function lineGraph(parent,xAccessor,yAccessor){const width=620;const height=420;const gutter=40;const pixelsPerTick=30;function numericTransformer(dataMin,dataMax,pxMin,pxMax){var dataDiff=dataMax-dataMin,pxDiff=pxMax-pxMin,dataRatio=pxDiff/dataDiff,coordRatio=dataDiff/pxDiff;return{toCoord:function(data){return(data-dataMin)*dataRatio+pxMin;},toData:function(coord){return(coord-pxMin)*coordRatio+dataMin;}};} function lineGraph(parent,xAccessor,yAccessor){const width=620;const height=420;const gutter=40;const pixelsPerTick=30;function numericTransformer(dataMin,dataMax,pxMin,pxMax){var dataDiff=dataMax-dataMin,pxDiff=pxMax-pxMin,dataRatio=pxDiff/dataDiff,coordRatio=dataDiff/pxDiff;return{toCoord:function(data){return(data-dataMin)*dataRatio+pxMin;},toData:function(coord){return(coord-pxMin)*coordRatio+dataMin;}};}
function axisRenderer(orientation,transform){var axisGroup=document.createElementNS("http://www.w3.org/2000/svg","g");var axisPath=document.createElementNS("http://www.w3.org/2000/svg","path");axisGroup.setAttribute("class",orientation+"-axis");var xMin=gutter;var xMax=width-gutter;var yMin=height-gutter;var yMax=gutter;if(orientation==="x"){axisPath.setAttribute("d","M "+xMin+" "+yMin+" L "+xMax+" "+yMin);for(var i=xMin;i<=xMax;i++){if((i-xMin)%(pixelsPerTick*3)===0&&i!==xMin){var text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=new Date(Math.floor(transform(i))).toLocaleTimeString();text.setAttribute("x",i);text.setAttribute("y",yMin);text.setAttribute("dy","1em");axisGroup.appendChild(text);}}}else{axisPath.setAttribute("d","M "+xMin+" "+yMin+" L "+xMin+" "+yMax);for(var i=yMax;i<=yMin;i++){if((i-yMin)%pixelsPerTick===0&&i!==yMin){var tickGroup=document.createElementNS("http://www.w3.org/2000/svg","g");var gridLine=document.createElementNS("http://www.w3.org/2000/svg","path");text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=Math.floor(transform(i));text.setAttribute("x",xMin);text.setAttribute("y",i);text.setAttribute("dx","-.5em");text.setAttribute("dy",".3em");gridLine.setAttribute("d","M "+xMin+" "+i+" L "+xMax+" "+i);tickGroup.appendChild(gridLine);tickGroup.appendChild(text);axisGroup.appendChild(tickGroup);}}} function axisRenderer(orientation,transform){var axisGroup=document.createElementNS("http://www.w3.org/2000/svg","g");var axisPath=document.createElementNS("http://www.w3.org/2000/svg","path");axisGroup.setAttribute("class",orientation+"-axis");var xMin=gutter;var xMax=width-gutter;var yMin=height-gutter;var yMax=gutter;if(orientation==="x"){axisPath.setAttribute("d","M "+xMin+" "+yMin+" L "+xMax+" "+yMin);for(var i=xMin;i<=xMax;i++){if((i-xMin)%(pixelsPerTick*3)===0&&i!==xMin){var text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=new Date(Math.floor(transform(i))).toLocaleTimeString();text.setAttribute("x",i);text.setAttribute("y",yMin);text.setAttribute("dy","1em");axisGroup.appendChild(text);}}}else{axisPath.setAttribute("d","M "+xMin+" "+yMin+" L "+xMin+" "+yMax);for(var i=yMax;i<=yMin;i++){if((i-yMin)%pixelsPerTick===0&&i!==yMin){var tickGroup=document.createElementNS("http://www.w3.org/2000/svg","g");var gridLine=document.createElementNS("http://www.w3.org/2000/svg","path");text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=Math.floor(transform(i));text.setAttribute("x",xMin);text.setAttribute("y",i);text.setAttribute("dx","-.5em");text.setAttribute("dy",".3em");gridLine.setAttribute("d","M "+xMin+" "+i+" L "+xMax+" "+i);tickGroup.appendChild(gridLine);tickGroup.appendChild(text);axisGroup.appendChild(tickGroup);}}}
axisGroup.appendChild(axisPath);parent.appendChild(axisGroup);} axisGroup.appendChild(axisPath);parent.appendChild(axisGroup);}
function lineRenderer(xAccessor,yAccessor,xTransform,yTransform){var line=document.createElementNS("http://www.w3.org/2000/svg","path");xAccessor.reset();yAccessor.reset();if(!xAccessor.hasNext()||!yAccessor.hasNext()){return;} function lineRenderer(xAccessor,yAccessor,xTransform,yTransform){var line=document.createElementNS("http://www.w3.org/2000/svg","path");xAccessor.reset();yAccessor.reset();if(!xAccessor.hasNext()||!yAccessor.hasNext()){return;}
var pathString="M "+xTransform(xAccessor.next())+" "+yTransform(yAccessor.next());while(xAccessor.hasNext()&&yAccessor.hasNext()){pathString+=" L "+ var pathString="M "+xTransform(xAccessor.next())+" "+yTransform(yAccessor.next());while(xAccessor.hasNext()&&yAccessor.hasNext()){pathString+=" L "+
xTransform(xAccessor.next())+ xTransform(xAccessor.next())+
" "+ " "+
yTransform(yAccessor.next());} yTransform(yAccessor.next());}
line.setAttribute("class","series");line.setAttribute("d",pathString);parent.appendChild(line);} line.setAttribute("class","series");line.setAttribute("d",pathString);parent.appendChild(line);}
function pointRenderer(xAccessor,yAccessor,xTransform,yTransform){var pointGroup=document.createElementNS("http://www.w3.org/2000/svg","g");pointGroup.setAttribute("class","data-points");xAccessor.reset();yAccessor.reset();if(!xAccessor.hasNext()||!yAccessor.hasNext()){return;} function pointRenderer(xAccessor,yAccessor,xTransform,yTransform){var pointGroup=document.createElementNS("http://www.w3.org/2000/svg","g");pointGroup.setAttribute("class","data-points");xAccessor.reset();yAccessor.reset();if(!xAccessor.hasNext()||!yAccessor.hasNext()){return;}
while(xAccessor.hasNext()&&yAccessor.hasNext()){var xDataValue=xAccessor.next();var x=xTransform(xDataValue);var yDataValue=yAccessor.next();var y=yTransform(yDataValue);var circle=document.createElementNS("http://www.w3.org/2000/svg","circle");circle.setAttribute("cx",x);circle.setAttribute("cy",y);circle.setAttribute("r","4");var text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=Math.floor(xDataValue)+" / "+Math.floor(yDataValue);text.setAttribute("x",x);text.setAttribute("y",y);text.setAttribute("dx","1em");text.setAttribute("dy","-.7em");pointGroup.appendChild(circle);pointGroup.appendChild(text);} while(xAccessor.hasNext()&&yAccessor.hasNext()){var xDataValue=xAccessor.next();var x=xTransform(xDataValue);var yDataValue=yAccessor.next();var y=yTransform(yDataValue);var circle=document.createElementNS("http://www.w3.org/2000/svg","circle");circle.setAttribute("cx",x);circle.setAttribute("cy",y);circle.setAttribute("r","4");var text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=Math.floor(xDataValue)+" / "+Math.floor(yDataValue);text.setAttribute("x",x);text.setAttribute("y",y);text.setAttribute("dx","1em");text.setAttribute("dy","-.7em");pointGroup.appendChild(circle);pointGroup.appendChild(text);}
parent.appendChild(pointGroup);} parent.appendChild(pointGroup);}
xTransform=numericTransformer(xAccessor.min(),xAccessor.max(),0+gutter,width-gutter);yTransform=numericTransformer(yAccessor.min(),yAccessor.max(),height-gutter,0+gutter);axisRenderer("x",xTransform.toData);axisRenderer("y",yTransform.toData);lineRenderer(xAccessor,yAccessor,xTransform.toCoord,yTransform.toCoord);pointRenderer(xAccessor,yAccessor,xTransform.toCoord,yTransform.toCoord);} xTransform=numericTransformer(xAccessor.min(),xAccessor.max(),0+gutter,width-gutter);yTransform=numericTransformer(yAccessor.min(),yAccessor.max(),height-gutter,0+gutter);axisRenderer("x",xTransform.toData);axisRenderer("y",yTransform.toData);lineRenderer(xAccessor,yAccessor,xTransform.toCoord,yTransform.toCoord);pointRenderer(xAccessor,yAccessor,xTransform.toCoord,yTransform.toCoord);}
function renderGraphSvg(dataArray,renderId){var figure=document.getElementById(renderId);while(figure.hasChildNodes()){figure.removeChild(figure.lastChild);} function renderGraphSvg(dataArray,renderId){var figure=document.getElementById(renderId);while(figure.hasChildNodes()){figure.removeChild(figure.lastChild);}
var svg=document.createElementNS("http://www.w3.org/2000/svg","svg");svg.setAttribute("viewBox","0 0 640 440");svg.setAttribute("preserveAspectRatio","xMidYMid meet");lineGraph(svg,(function(data,min,max){var i=0;return{hasNext:function(){return i<data.length;},next:function(){return data[i++].x;},reset:function(){i=0;},min:function(){return min;},max:function(){return max;}};})(dataArray,Math.min.apply(Math,dataArray.map(function(o){return o.x;})),Math.max.apply(Math,dataArray.map(function(o){return o.x;}))),(function(data,min,max){var i=0;return{hasNext:function(){return i<data.length;},next:function(){return data[i++].y;},reset:function(){i=0;},min:function(){return min;},max:function(){return max;}};})(dataArray,Math.min.apply(Math,dataArray.map(function(o){return o.y;})),Math.max.apply(Math,dataArray.map(function(o){return o.y;}))));figure.appendChild(svg);} var svg=document.createElementNS("http://www.w3.org/2000/svg","svg");svg.setAttribute("viewBox","0 0 640 440");svg.setAttribute("preserveAspectRatio","xMidYMid meet");lineGraph(svg,(function(data,min,max){var i=0;return{hasNext:function(){return i<data.length;},next:function(){return data[i++].x;},reset:function(){i=0;},min:function(){return min;},max:function(){return max;}};})(dataArray,Math.min.apply(Math,dataArray.map(function(o){return o.x;})),Math.max.apply(Math,dataArray.map(function(o){return o.x;}))),(function(data,min,max){var i=0;return{hasNext:function(){return i<data.length;},next:function(){return data[i++].y;},reset:function(){i=0;},min:function(){return min;},max:function(){return max;}};})(dataArray,Math.min.apply(Math,dataArray.map(function(o){return o.y;})),Math.max.apply(Math,dataArray.map(function(o){return o.y;}))));figure.appendChild(svg);}

BIN
docs/ui_fileDisplay.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -30,6 +30,7 @@
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <ESP8266mDNS.h> #include <ESP8266mDNS.h>
#include <umm_malloc/umm_heap_select.h> #include <umm_malloc/umm_heap_select.h>
#ifndef CORE_MOCK
#ifndef MMU_IRAM_HEAP #ifndef MMU_IRAM_HEAP
#warning Try MMU option '2nd heap shared' in 'tools' IDE menu (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#option-summary) #warning Try MMU option '2nd heap shared' in 'tools' IDE menu (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#option-summary)
#warning use decorators: { HeapSelectIram doAllocationsInIRAM; ESPUI.addControl(...) ... } (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#how-to-select-heap) #warning use decorators: { HeapSelectIram doAllocationsInIRAM; ESPUI.addControl(...) ... } (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#how-to-select-heap)
@ -39,6 +40,7 @@
#error on ESP8266 and ESPUI, you must define OOM debug option when developping #error on ESP8266 and ESPUI, you must define OOM debug option when developping
#endif #endif
#endif #endif
#endif
//Settings //Settings
#define SLOW_BOOT 0 #define SLOW_BOOT 0
@ -396,7 +398,8 @@ void extendedCallback(Control* sender, int type, void* param)
Serial.print(sender->label); Serial.print(sender->label);
Serial.print("' = "); Serial.print("' = ");
Serial.println(sender->value); Serial.println(sender->value);
Serial.println(String("param = ") + String((int)param)); Serial.print("param = ");
Serial.println((long)param);
} }
void setup() { void setup() {
@ -443,6 +446,7 @@ void loop() {
#if !defined(ESP32) #if !defined(ESP32)
((void (*)())0xf00fdead)(); ((void (*)())0xf00fdead)();
#endif #endif
break;
default: default:
Serial.print('#'); Serial.print('#');
break; break;

View File

@ -1 +1,4 @@
// placeholder // placeholder
#if CORE_MOCK
#include "completeExample.cpp"
#endif

View File

@ -0,0 +1,544 @@
/**
* @file completeExample.cpp
* @author Ian Gray @iangray1000
*
* This is an example GUI to show off all of the features of ESPUI.
* This can be built using the Arduino IDE, or PlatformIO.
*
* ---------------------------------------------------------------------------------------
* If you just want to see examples of the ESPUI code, jump down to the setUpUI() function
* ---------------------------------------------------------------------------------------
*
* When this program boots, it will load an SSID and password from the EEPROM.
* The SSID is a null-terminated C string stored at EEPROM addresses 0-31
* The password is a null-terminated C string stored at EEPROM addresses 32-95.
* If these credentials do not work for some reason, the ESP will create an Access
* Point wifi with the SSID HOSTNAME (defined below). You can then connect and use
* the controls on the "Wifi Credentials" tab to store credentials into the EEPROM.
*
* Version with lambdas. Comparing to version with only callbacks:
* diff -u ../completeExample/completeExample.cpp completeLambda.ino|less
*
*/
#include <Arduino.h>
#include <EEPROM.h>
#include <ESPUI.h>
#if defined(ESP32)
#include <WiFi.h>
#include <ESPmDNS.h>
#else
// esp8266
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <umm_malloc/umm_heap_select.h>
#ifndef CORE_MOCK
#ifndef MMU_IRAM_HEAP
#warning Try MMU option '2nd heap shared' in 'tools' IDE menu (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#option-summary)
#warning use decorators: { HeapSelectIram doAllocationsInIRAM; ESPUI.addControl(...) ... } (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#how-to-select-heap)
#warning then check http://<ip>/heap
#endif // MMU_IRAM_HEAP
#ifndef DEBUG_ESP_OOM
#error on ESP8266 and ESPUI, you must define OOM debug option when developping
#endif
#endif
#endif
//Settings
#define SLOW_BOOT 0
#define HOSTNAME "ESPUITest"
#define FORCE_USE_HOTSPOT 0
//Function Prototypes
void connectWifi();
void setUpUI();
void textCallback(Control *sender, int type);
void generalCallback(Control *sender, int type);
void randomString(char *buf, int len);
void paramCallback(Control* sender, int type, int param);
//UI handles
uint16_t wifi_ssid_text, wifi_pass_text;
uint16_t mainLabel, mainSwitcher, mainSlider, mainText, mainNumber, mainScrambleButton, mainTime;
uint16_t styleButton, styleLabel, styleSwitcher, styleSlider, styleButton2, styleLabel2, styleSlider2;
uint16_t graph;
volatile bool updates = false;
// This is the main function which builds our GUI
void setUpUI() {
#ifdef ESP8266
{ HeapSelectIram doAllocationsInIRAM;
#endif
//Turn off verbose debugging
ESPUI.setVerbosity(Verbosity::Quiet);
//Make sliders continually report their position as they are being dragged.
ESPUI.sliderContinuous = true;
//This GUI is going to be a tabbed GUI, so we are adding most controls using ESPUI.addControl
//which allows us to set a parent control. If we didn't need tabs we could use the simpler add
//functions like:
// ESPUI.button()
// ESPUI.label()
/*
* Tab: Basic Controls
* This tab contains all the basic ESPUI controls, and shows how to read and update them at runtime.
*-----------------------------------------------------------------------------------------------------------*/
auto maintab = ESPUI.addControl(Tab, "", "Basic controls");
ESPUI.addControl(Separator, "General controls", "", None, maintab);
ESPUI.addControl(Button, "Button", "Button 1", Alizarin, maintab, [](Control *sender, int type){ paramCallback(sender, type, 19); });
mainLabel = ESPUI.addControl(Label, "Label", "Label text", Emerald, maintab, generalCallback);
mainSwitcher = ESPUI.addControl(Switcher, "Switcher", "", Sunflower, maintab, generalCallback);
//Sliders default to being 0 to 100, but if you want different limits you can add a Min and Max control
mainSlider = ESPUI.addControl(Slider, "Slider", "200", Turquoise, maintab, generalCallback);
ESPUI.addControl(Min, "", "10", None, mainSlider);
ESPUI.addControl(Max, "", "400", None, mainSlider);
//These are the values for the selector's options. (Note that they *must* be declared static
//so that the storage is allocated in global memory and not just on the stack of this function.)
static String optionValues[] {"Value 1", "Value 2", "Value 3", "Value 4", "Value 5"};
auto mainselector = ESPUI.addControl(Select, "Selector", "Selector", Wetasphalt, maintab, generalCallback);
for(auto const& v : optionValues) {
ESPUI.addControl(Option, v.c_str(), v, None, mainselector);
}
mainText = ESPUI.addControl(Text, "Text Input", "Initial value", Alizarin, maintab, generalCallback);
//Number inputs also accept Min and Max components, but you should still validate the values.
mainNumber = ESPUI.addControl(Number, "Number Input", "42", Emerald, maintab, generalCallback);
ESPUI.addControl(Min, "", "10", None, mainNumber);
ESPUI.addControl(Max, "", "50", None, mainNumber);
ESPUI.addControl(Separator, "Updates", "", None, maintab);
//This button will update all the updatable controls on this tab to random values
mainScrambleButton = ESPUI.addControl(Button, "Scramble Values", "Scramble Values", Carrot, maintab,
//This callback updates the "values" of a bunch of controls
[](Control *sender, int type) {
static char rndString1[10];
static char rndString2[20];
static bool scText = false;
if(type == B_UP) { //Button callbacks generate events for both UP and DOWN.
//Generate some random text
randomString(rndString1, 10);
randomString(rndString2, 20);
//Set the various controls to random value to show how controls can be updated at runtime
ESPUI.updateLabel(mainLabel, String(rndString1));
ESPUI.updateSwitcher(mainSwitcher, ESPUI.getControl(mainSwitcher)->value.toInt() ? false : true);
ESPUI.updateSlider(mainSlider, random(10, 400));
ESPUI.updateText(mainText, String(rndString2));
ESPUI.updateNumber(mainNumber, random(100000));
ESPUI.updateButton(mainScrambleButton, scText ? "Scrambled!" : "Scrambled.");
scText = !scText;
}
});
ESPUI.addControl(Switcher, "Constant updates", "0", Carrot, maintab,
[](Control *sender, int type) {
updates = (sender->value.toInt() > 0);
});
mainTime = ESPUI.addControl(Time, "", "", None, 0, generalCallback);
ESPUI.addControl(Button, "Get Time", "Get Time", Carrot, maintab,
[](Control *sender, int type) {
if(type == B_UP) {
ESPUI.updateTime(mainTime);
}
});
ESPUI.addControl(Separator, "Control Pads", "", None, maintab);
ESPUI.addControl(Pad, "Normal", "", Peterriver, maintab, generalCallback);
ESPUI.addControl(PadWithCenter, "With center", "", Peterriver, maintab, generalCallback);
/*
* Tab: Colours
* This tab shows all the basic colours
*-----------------------------------------------------------------------------------------------------------*/
auto colourtab = ESPUI.addControl(Tab, "", "Colours");
ESPUI.addControl(Button, "Alizarin", "Alizarin", Alizarin, colourtab, generalCallback);
ESPUI.addControl(Button, "Turquoise", "Turquoise", Turquoise, colourtab, generalCallback);
ESPUI.addControl(Button, "Emerald", "Emerald", Emerald, colourtab, generalCallback);
ESPUI.addControl(Button, "Peterriver", "Peterriver", Peterriver, colourtab, generalCallback);
ESPUI.addControl(Button, "Wetasphalt", "Wetasphalt", Wetasphalt, colourtab, generalCallback);
ESPUI.addControl(Button, "Sunflower", "Sunflower", Sunflower, colourtab, generalCallback);
ESPUI.addControl(Button, "Carrot", "Carrot", Carrot, colourtab, generalCallback);
ESPUI.addControl(Button, "Dark", "Dark", Dark, colourtab, generalCallback);
/*
* Tab: Styled controls
* This tab shows off how inline CSS styles can be applied to elements and panels in order
* to customise the look of the UI.
*-----------------------------------------------------------------------------------------------------------*/
auto styletab = ESPUI.addControl(Tab, "", "Styled controls");
styleButton = ESPUI.addControl(Button, "Styled Button", "Button", Alizarin, styletab, generalCallback);
styleLabel = ESPUI.addControl(Label, "Styled Label", "This is a label", Alizarin, styletab, generalCallback);
styleSwitcher = ESPUI.addControl(Switcher, "Styled Switcher", "1", Alizarin, styletab, generalCallback);
styleSlider = ESPUI.addControl(Slider, "Styled Slider", "0", Alizarin, styletab, generalCallback);
//This button will randomise the colours of the above controls to show updating of inline styles
ESPUI.addControl(Button, "Randomise Colours", "Randomise Colours", Sunflower, styletab,
//This callback generates and applies inline styles to a bunch of controls to change their colour.
//The styles created are of the form:
// "border-bottom: #999 3px solid; background-color: #aabbcc;"
// "background-color: #aabbcc;"
[](Control *sender, int type) {
//Declare space for style strings. These have to be static so that they are always available
//to the websocket layer. If we'd not made them static they'd be allocated on the heap and
//will be unavailable when we leave this function.
static char stylecol1[60], stylecol2[30];
if(type == B_UP) {
//Generate two random HTML hex colour codes, and print them into CSS style rules
sprintf(stylecol1, "border-bottom: #999 3px solid; background-color: #%06X;", (unsigned int) random(0x0, 0xFFFFFF));
sprintf(stylecol2, "background-color: #%06X;", (unsigned int) random(0x0, 0xFFFFFF));
//Apply those styles to various elements to show how controls react to styling
ESPUI.setPanelStyle(styleButton, stylecol1);
ESPUI.setElementStyle(styleButton, stylecol2);
ESPUI.setPanelStyle(styleLabel, stylecol1);
ESPUI.setElementStyle(styleLabel, stylecol2);
ESPUI.setPanelStyle(styleSwitcher, stylecol1);
ESPUI.setElementStyle(styleSwitcher, stylecol2);
ESPUI.setPanelStyle(styleSlider, stylecol1);
ESPUI.setElementStyle(styleSlider, stylecol2);
}
});
ESPUI.addControl(Separator, "Other styling examples", "", None, styletab);
styleButton2 = ESPUI.addControl(Button, "Styled Button", "Button", Alizarin, styletab, generalCallback);
ESPUI.setPanelStyle(styleButton2, "background: linear-gradient(90deg, rgba(131,58,180,1) 0%, rgba(253,29,29,1) 50%, rgba(252,176,69,1) 100%); border-bottom: #555;");
ESPUI.setElementStyle(styleButton2, "border-radius: 2em; border: 3px solid black; width: 30%; background-color: #8df;");
styleSlider2 = ESPUI.addControl(Slider, "Styled Slider", "0", Dark, styletab, generalCallback);
ESPUI.setElementStyle(styleSlider2, "background: linear-gradient(to right, red, orange, yellow, green, blue);");
styleLabel2 = ESPUI.addControl(Label, "Styled Label", "This is a label", Dark, styletab, generalCallback);
ESPUI.setElementStyle(styleLabel2, "text-shadow: 3px 3px #74b1ff, 6px 6px #c64ad7; font-size: 60px; font-variant-caps: small-caps; background-color: unset; color: #c4f0bb; -webkit-text-stroke: 1px black;");
/*
* Tab: Grouped controls
* This tab shows how multiple control can be grouped into the same panel through the use of the
* parentControl value. This also shows how to add labels to grouped controls, and how to use vertical controls.
*-----------------------------------------------------------------------------------------------------------*/
auto grouptab = ESPUI.addControl(Tab, "", "Grouped controls");
//The parent of this button is a tab, so it will create a new panel with one control.
auto groupbutton = ESPUI.addControl(Button, "Button Group", "Button A", Dark, grouptab, generalCallback);
//However the parent of this button is another control, so therefore no new panel is
//created and the button is added to the existing panel.
ESPUI.addControl(Button, "", "Button B", Alizarin, groupbutton, generalCallback);
ESPUI.addControl(Button, "", "Button C", Alizarin, groupbutton, generalCallback);
//Sliders can be grouped as well
//To label each slider in the group, we are going add additional labels and give them custom CSS styles
//We need this CSS style rule, which will remove the label's background and ensure that it takes up the entire width of the panel
String clearLabelStyle = "background-color: unset; width: 100%;";
//First we add the main slider to create a panel
auto groupsliders = ESPUI.addControl(Slider, "Slider Group", "10", Dark, grouptab, generalCallback);
//Then we add a label and set its style to the clearLabelStyle. Here we've just given it the name "A"
ESPUI.setElementStyle(ESPUI.addControl(Label, "", "A", None, groupsliders), clearLabelStyle);
//We can now continue to add additional sliders and labels
ESPUI.addControl(Slider, "", "20", None, groupsliders, generalCallback);
ESPUI.setElementStyle(ESPUI.addControl(Label, "", "B", None, groupsliders), clearLabelStyle);
ESPUI.addControl(Slider, "", "30", None, groupsliders, generalCallback);
ESPUI.setElementStyle(ESPUI.addControl(Label, "", "C", None, groupsliders), clearLabelStyle);
//We can also usefully group switchers.
auto groupswitcher = ESPUI.addControl(Switcher, "Switcher Group", "0", Dark, grouptab, generalCallback);
ESPUI.addControl(Switcher, "", "1", Sunflower, groupswitcher, generalCallback);
ESPUI.addControl(Switcher, "", "0", Sunflower, groupswitcher, generalCallback);
ESPUI.addControl(Switcher, "", "1", Sunflower, groupswitcher, generalCallback);
//To label these switchers we need to first go onto a "new line" below the line of switchers
//To do this we add an empty label set to be clear and full width (with our clearLabelStyle)
ESPUI.setElementStyle(ESPUI.addControl(Label, "", "", None, groupswitcher), clearLabelStyle);
//We will now need another label style. This one sets its width to the same as a switcher (and turns off the background)
String switcherLabelStyle = "width: 60px; margin-left: .3rem; margin-right: .3rem; background-color: unset;";
//We can now just add the styled labels.
ESPUI.setElementStyle(ESPUI.addControl(Label, "", "A", None, groupswitcher), switcherLabelStyle);
ESPUI.setElementStyle(ESPUI.addControl(Label, "", "B", None, groupswitcher), switcherLabelStyle);
ESPUI.setElementStyle(ESPUI.addControl(Label, "", "C", None, groupswitcher), switcherLabelStyle);
ESPUI.setElementStyle(ESPUI.addControl(Label, "", "D", None, groupswitcher), switcherLabelStyle);
//You can mix and match different control types, but the results might sometimes
//need additional styling to lay out nicely.
auto grouplabel = ESPUI.addControl(Label, "Mixed Group", "Main label", Dark, grouptab);
auto grouplabel2 = ESPUI.addControl(Label, "", "Secondary label", Emerald, grouplabel);
ESPUI.addControl(Button, "", "Button D", Alizarin, grouplabel, generalCallback);
ESPUI.addControl(Switcher, "", "1", Sunflower, grouplabel, generalCallback);
ESPUI.setElementStyle(grouplabel2, "font-size: x-large; font-family: serif;");
//Some controls can even support vertical orientation, currently Switchers and Sliders
ESPUI.addControl(Separator, "Vertical controls", "", None, grouptab);
auto vertgroupswitcher = ESPUI.addControl(Switcher, "Vertical Switcher Group", "0", Dark, grouptab, generalCallback);
ESPUI.setVertical(vertgroupswitcher);
//On the following lines we wrap the value returned from addControl and send it straight to setVertical
ESPUI.setVertical(ESPUI.addControl(Switcher, "", "0", None, vertgroupswitcher, generalCallback));
ESPUI.setVertical(ESPUI.addControl(Switcher, "", "0", None, vertgroupswitcher, generalCallback));
ESPUI.setVertical(ESPUI.addControl(Switcher, "", "0", None, vertgroupswitcher, generalCallback));
//The mechanism for labelling vertical switchers is the same as we used above for horizontal ones
ESPUI.setElementStyle(ESPUI.addControl(Label, "", "", None, vertgroupswitcher), clearLabelStyle);
ESPUI.setElementStyle(ESPUI.addControl(Label, "", "A", None, vertgroupswitcher), switcherLabelStyle);
ESPUI.setElementStyle(ESPUI.addControl(Label, "", "B", None, vertgroupswitcher), switcherLabelStyle);
ESPUI.setElementStyle(ESPUI.addControl(Label, "", "C", None, vertgroupswitcher), switcherLabelStyle);
ESPUI.setElementStyle(ESPUI.addControl(Label, "", "D", None, vertgroupswitcher), switcherLabelStyle);
auto vertgroupslider = ESPUI.addControl(Slider, "Vertical Slider Group", "15", Dark, grouptab, generalCallback);
ESPUI.setVertical(vertgroupslider);
ESPUI.setVertical(ESPUI.addControl(Slider, "", "25", None, vertgroupslider, generalCallback));
ESPUI.setVertical(ESPUI.addControl(Slider, "", "35", None, vertgroupslider, generalCallback));
ESPUI.setVertical(ESPUI.addControl(Slider, "", "45", None, vertgroupslider, generalCallback));
//The mechanism for labelling vertical sliders is the same as we used above for switchers
ESPUI.setElementStyle(ESPUI.addControl(Label, "", "", None, vertgroupslider), clearLabelStyle);
ESPUI.setElementStyle(ESPUI.addControl(Label, "", "A", None, vertgroupslider), switcherLabelStyle);
ESPUI.setElementStyle(ESPUI.addControl(Label, "", "B", None, vertgroupslider), switcherLabelStyle);
ESPUI.setElementStyle(ESPUI.addControl(Label, "", "C", None, vertgroupslider), switcherLabelStyle);
ESPUI.setElementStyle(ESPUI.addControl(Label, "", "D", None, vertgroupslider), switcherLabelStyle);
//Note that combining vertical and horizontal sliders is going to result in very messy layout!
/*
* Tab: Example UI
* An example UI for the documentation
*-----------------------------------------------------------------------------------------------------------*/
auto exampletab = ESPUI.addControl(Tab, "Example", "Example");
ESPUI.addControl(Separator, "Control and Status", "", None, exampletab);
ESPUI.addControl(Switcher, "Power", "1", Alizarin, exampletab, generalCallback);
ESPUI.addControl(Label, "Status", "System status: OK", Wetasphalt, exampletab, generalCallback);
ESPUI.addControl(Separator, "Settings", "", None, exampletab);
ESPUI.addControl(PadWithCenter, "Attitude Control", "", Dark, exampletab, generalCallback);
auto examplegroup1 = ESPUI.addControl(Button, "Activate Features", "Feature A", Carrot, exampletab, generalCallback);
ESPUI.addControl(Button, "Activate Features", "Feature B", Carrot, examplegroup1, generalCallback);
ESPUI.addControl(Button, "Activate Features", "Feature C", Carrot, examplegroup1, generalCallback);
ESPUI.addControl(Slider, "Value control", "45", Peterriver, exampletab, generalCallback);
/*
* Tab: WiFi Credentials
* You use this tab to enter the SSID and password of a wifi network to autoconnect to.
*-----------------------------------------------------------------------------------------------------------*/
auto wifitab = ESPUI.addControl(Tab, "", "WiFi Credentials");
wifi_ssid_text = ESPUI.addControl(Text, "SSID", "", Alizarin, wifitab, textCallback);
//Note that adding a "Max" control to a text control sets the max length
ESPUI.addControl(Max, "", "32", None, wifi_ssid_text);
wifi_pass_text = ESPUI.addControl(Text, "Password", "", Alizarin, wifitab, textCallback);
ESPUI.addControl(Max, "", "64", None, wifi_pass_text);
ESPUI.addControl(Button, "Save", "Save", Peterriver, wifitab,
[](Control *sender, int type) {
if(type == B_UP) {
Serial.println("Saving credentials to EPROM...");
Serial.println(ESPUI.getControl(wifi_ssid_text)->value);
Serial.println(ESPUI.getControl(wifi_pass_text)->value);
unsigned int i;
EEPROM.begin(100);
for(i = 0; i < ESPUI.getControl(wifi_ssid_text)->value.length(); i++) {
EEPROM.write(i, ESPUI.getControl(wifi_ssid_text)->value.charAt(i));
if(i==30) break; //Even though we provided a max length, user input should never be trusted
}
EEPROM.write(i, '\0');
for(i = 0; i < ESPUI.getControl(wifi_pass_text)->value.length(); i++) {
EEPROM.write(i + 32, ESPUI.getControl(wifi_pass_text)->value.charAt(i));
if(i==94) break; //Even though we provided a max length, user input should never be trusted
}
EEPROM.write(i + 32, '\0');
EEPROM.end();
}
});
//Finally, start up the UI.
//This should only be called once we are connected to WiFi.
ESPUI.begin(HOSTNAME);
#ifdef ESP8266
} // HeapSelectIram
#endif
}
//Most elements in this test UI are assigned this generic callback which prints some
//basic information. Event types are defined in ESPUI.h
void generalCallback(Control *sender, int type) {
Serial.print("CB: id(");
Serial.print(sender->id);
Serial.print(") Type(");
Serial.print(type);
Serial.print(") '");
Serial.print(sender->label);
Serial.print("' = ");
Serial.println(sender->value);
}
// Most elements in this test UI are assigned this generic callback which prints some
// basic information. Event types are defined in ESPUI.h
// The extended param can be used to pass additional information
void paramCallback(Control* sender, int type, int param)
{
Serial.print("CB: id(");
Serial.print(sender->id);
Serial.print(") Type(");
Serial.print(type);
Serial.print(") '");
Serial.print(sender->label);
Serial.print("' = ");
Serial.println(sender->value);
Serial.print("param = ");
Serial.println(param);
}
void setup() {
randomSeed(0);
Serial.begin(115200);
while(!Serial);
if(SLOW_BOOT) delay(5000); //Delay booting to give time to connect a serial monitor
connectWifi();
#if defined(ESP32)
WiFi.setSleep(false); //For the ESP32: turn off sleeping to increase UI responsivness (at the cost of power use)
#endif
setUpUI();
}
void loop() {
static long unsigned lastTime = 0;
//Send periodic updates if switcher is turned on
if(updates && millis() > lastTime + 500) {
static uint16_t sliderVal = 10;
//Flick this switcher on and off
ESPUI.updateSwitcher(mainSwitcher, ESPUI.getControl(mainSwitcher)->value.toInt() ? false : true);
sliderVal += 10;
if(sliderVal > 400) sliderVal = 10;
//Sliders, numbers, and labels can all be updated at will
ESPUI.updateSlider(mainSlider, sliderVal);
ESPUI.updateNumber(mainNumber, random(100000));
ESPUI.updateLabel(mainLabel, String(sliderVal));
lastTime = millis();
}
//Simple debug UART interface
if(Serial.available()) {
switch(Serial.read()) {
case 'w': //Print IP details
Serial.println(WiFi.localIP());
break;
case 'W': //Reconnect wifi
connectWifi();
break;
case 'C': //Force a crash (for testing exception decoder)
#if !defined(ESP32)
((void (*)())0xf00fdead)();
#endif
break;
default:
Serial.print('#');
break;
}
}
#if !defined(ESP32)
//We don't need to call this explicitly on ESP32 but we do on 8266
MDNS.update();
#endif
}
//Utilities
//
//If you are here just to see examples of how to use ESPUI, you can ignore the following functions
//------------------------------------------------------------------------------------------------
void readStringFromEEPROM(String& buf, int baseaddress, int size) {
buf.reserve(size);
for (int i = baseaddress; i < baseaddress+size; i++) {
char c = EEPROM.read(i);
buf += c;
if(!c) break;
}
}
void connectWifi() {
int connect_timeout;
#if defined(ESP32)
WiFi.setHostname(HOSTNAME);
#else
WiFi.hostname(HOSTNAME);
#endif
Serial.println("Begin wifi...");
//Load credentials from EEPROM
if(!(FORCE_USE_HOTSPOT)) {
yield();
EEPROM.begin(100);
String stored_ssid, stored_pass;
readStringFromEEPROM(stored_ssid, 0, 32);
readStringFromEEPROM(stored_pass, 32, 96);
EEPROM.end();
//Try to connect with stored credentials, fire up an access point if they don't work.
#if defined(ESP32)
WiFi.begin(stored_ssid.c_str(), stored_pass.c_str());
#else
WiFi.begin(stored_ssid, stored_pass);
#endif
connect_timeout = 28; //7 seconds
while (WiFi.status() != WL_CONNECTED && connect_timeout > 0) {
delay(250);
Serial.print(".");
connect_timeout--;
}
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println(WiFi.localIP());
Serial.println("Wifi started");
if (!MDNS.begin(HOSTNAME)) {
Serial.println("Error setting up MDNS responder!");
}
} else {
Serial.println("\nCreating access point...");
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(IPAddress(192, 168, 1, 1), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0));
WiFi.softAP(HOSTNAME);
connect_timeout = 20;
do {
delay(250);
Serial.print(",");
connect_timeout--;
} while(connect_timeout);
}
}
void textCallback(Control *sender, int type) {
//This callback is needed to handle the changed values, even though it doesn't do anything itself.
}
void randomString(char *buf, int len) {
for(auto i = 0; i < len-1; i++)
buf[i] = random(0, 26) + 'A';
buf[len-1] = '\0';
}

View File

@ -11,15 +11,17 @@ DNSServer dnsServer;
// esp8266 // esp8266
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <umm_malloc/umm_heap_select.h> #include <umm_malloc/umm_heap_select.h>
#ifndef CORE_MOCK
#ifndef MMU_IRAM_HEAP #ifndef MMU_IRAM_HEAP
#warning Try MMU option '2nd heap shared' in 'tools' IDE menu (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#option-summary) #warning Try MMU option '2nd heap shared' in 'tools' IDE menu (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#option-summary)
#warning use decorators: { HeapSelectIram doAllocationsInIRAM; ESPUI.addControl(...) ... } (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#how-to-select-heap) #warning use decorators: { HeapSelectIram doAllocationsInIRAM; ESPUI.addControl(...) ... } (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#how-to-select-heap)
#warning then check http://<ip>/heap #warning then check http://<ip>/heap
#endif // MMU_IRAM_HEAP #endif // MMU_IRAM_HEAP
#ifndef DEBUG_ESP_OOM #if !defined(DEBUG_ESP_OOM)
#error on ESP8266 and ESPUI, you must define OOM debug option when developping #error on ESP8266 and ESPUI, you must define OOM debug option when developping
#endif #endif
#endif #endif
#endif
const char* ssid = "ESPUI"; const char* ssid = "ESPUI";
const char* password = "espui"; const char* password = "espui";
@ -67,7 +69,8 @@ void buttonCallback(Control* sender, int type)
void buttonExample(Control* sender, int type, void* param) void buttonExample(Control* sender, int type, void* param)
{ {
Serial.println(String("param: ") + String(long(param))); Serial.print("param: ");
Serial.println((long)param);
switch (type) switch (type)
{ {
case B_DOWN: case B_DOWN:

View File

@ -11,6 +11,7 @@ DNSServer dnsServer;
// esp8266 // esp8266
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <umm_malloc/umm_heap_select.h> #include <umm_malloc/umm_heap_select.h>
#ifndef CORE_MOCK
#ifndef MMU_IRAM_HEAP #ifndef MMU_IRAM_HEAP
#warning Try MMU option '2nd heap shared' in 'tools' IDE menu (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#option-summary) #warning Try MMU option '2nd heap shared' in 'tools' IDE menu (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#option-summary)
#warning use decorators: { HeapSelectIram doAllocationsInIRAM; ESPUI.addControl(...) ... } (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#how-to-select-heap) #warning use decorators: { HeapSelectIram doAllocationsInIRAM; ESPUI.addControl(...) ... } (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#how-to-select-heap)
@ -20,6 +21,7 @@ DNSServer dnsServer;
#error on ESP8266 and ESPUI, you must define OOM debug option when developping #error on ESP8266 and ESPUI, you must define OOM debug option when developping
#endif #endif
#endif #endif
#endif
const char* ssid = "ESPUI"; const char* ssid = "ESPUI";
const char* password = "espui"; const char* password = "espui";
@ -72,7 +74,8 @@ void buttonCallback(Control* sender, int type)
void buttonExample(Control* sender, int type, void* param) void buttonExample(Control* sender, int type, void* param)
{ {
Serial.println(String("param: ") + String(long(param))); Serial.print("param: ");
Serial.println((long)param);
switch (type) switch (type)
{ {
case B_DOWN: case B_DOWN:

View File

@ -11,6 +11,7 @@ DNSServer dnsServer;
// esp8266 // esp8266
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <umm_malloc/umm_heap_select.h> #include <umm_malloc/umm_heap_select.h>
#ifndef CORE_MOCK
#ifndef MMU_IRAM_HEAP #ifndef MMU_IRAM_HEAP
#warning Try MMU option '2nd heap shared' in 'tools' IDE menu (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#option-summary) #warning Try MMU option '2nd heap shared' in 'tools' IDE menu (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#option-summary)
#warning use decorators: { HeapSelectIram doAllocationsInIRAM; ESPUI.addControl(...) ... } (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#how-to-select-heap) #warning use decorators: { HeapSelectIram doAllocationsInIRAM; ESPUI.addControl(...) ... } (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#how-to-select-heap)
@ -20,6 +21,7 @@ DNSServer dnsServer;
#error on ESP8266 and ESPUI, you must define OOM debug option when developping #error on ESP8266 and ESPUI, you must define OOM debug option when developping
#endif #endif
#endif #endif
#endif
const char* ssid = "ESPUI"; const char* ssid = "ESPUI";
const char* password = "espui"; const char* password = "espui";
@ -66,7 +68,8 @@ void buttonCallback(Control* sender, int type)
void buttonExample(Control* sender, int type, void* param) void buttonExample(Control* sender, int type, void* param)
{ {
Serial.println(String("param: ") + String(long(param))); Serial.print("param: ");
Serial.println((long)param);
switch (type) switch (type)
{ {
case B_DOWN: case B_DOWN:

View File

@ -38,6 +38,25 @@
"LANG_ESPUI_SWITCH_SWITCH": "UI 开关", "LANG_ESPUI_SWITCH_SWITCH": "UI 开关",
"LANG_ESPUI_PAD_PAD": "UI 面板", "LANG_ESPUI_PAD_PAD": "UI 面板",
"LANG_ESPUI_PAD_CENTER": "中间按钮?" "LANG_ESPUI_PAD_CENTER": "中间按钮?"
},
"tr-TR": {
"keys": {
"LANG_SUBCATERGORY_ESPUI": "Kullanıcı Arayüzü",
"LANG_ESPUI_ESPUI_TITLE": "Başlık",
"LANG_ESPUI_ESPUI_HOTSPOT": "Wifi Erişim Noktası Kodunu Etkinleştir",
"LANG_ESPUI_ESPUI_TOOLTIP": "ESP32/ESP8266'da bir web arayüzü oluşturur",
"LANG_ESPUI_BUTTON_BUTTON": "Kullanıcı Arayüzü Düğmesi",
"LANG_ESPUI_NAME": "ad",
"LANG_ESPUI_COLOR": "Renk",
"LANG_ESPUI_TEXT": "Metin",
"LANG_ESPUI_BUTTON_TOOLTIP": "Bir web arayüzü düğmesi",
"LANG_ESPUI_LABEL_LABEL": "Kullanıcı Arayüzü Etiketi",
"LANG_ESPUI_LABEL": "Etiket",
"LANG_ESPUI_LABEL_TOOLTIP": "Kodunuzdan güncelleyebileceğiniz bir web arayüzü etiketi",
"LANG_ESPUI_STATE": "Durum",
"LANG_ESPUI_SWITCH_SWITCH": "Kullanıcı Arayüzü Anahtarı",
"LANG_ESPUI_PAD_PAD": "UI Pad",
"LANG_ESPUI_PAD_CENTER": "Orta düğme?"
} }
} }
} }

View File

@ -24,11 +24,6 @@
"name": "ArduinoJson", "name": "ArduinoJson",
"authors": "Benoit Blanchon", "authors": "Benoit Blanchon",
"frameworks": "arduino" "frameworks": "arduino"
},
{
"name": "LittleFS_esp32",
"authors": "lorol",
"frameworks": "arduino"
} }
], ],
"version": "2.2.3", "version": "2.2.3",

View File

@ -32,12 +32,20 @@ lib_ignore =
[env:esp8266] [env:esp8266]
platform = espressif8266 platform = espressif8266
board = nodemcuv2 board = nodemcuv2
upload_port = COM8
monitor_port = COM8
monitor_speed = 115200
[env:esp32] [env:esp32]
platform = espressif32 platform = espressif32
board = esp32dev board = esp32dev
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
board_build.flash_mode = dout board_build.flash_mode = dout
;build_flags =
; -D DEBUG_ESPUI
lib_deps = lib_deps =
${env.lib_deps} ${env.lib_deps}
me-no-dev/AsyncTCP me-no-dev/AsyncTCP
upload_port = COM9
monitor_port = COM9
monitor_speed = 115200

View File

@ -16,10 +16,12 @@ const char* password = "espui";
const char* hostname = "espui"; const char* hostname = "espui";
int statusLabelId; String DisplayTestFileName = "/FileName.txt";
int graphId; int statusLabelId = Control::noParent;
int millisLabelId; int graphId = Control::noParent;
int testSwitchId; int millisLabelId = Control::noParent;
int testSwitchId = Control::noParent;
int fileDisplayId = Control::noParent;
void numberCall(Control* sender, int type) void numberCall(Control* sender, int type)
{ {
@ -238,6 +240,7 @@ void setup(void)
ESPUI.slider("Slider two", &slider, ControlColor::None, 100); ESPUI.slider("Slider two", &slider, ControlColor::None, 100);
ESPUI.text("Text Test:", &textCall, ControlColor::Alizarin, "a Text Field"); ESPUI.text("Text Test:", &textCall, ControlColor::Alizarin, "a Text Field");
ESPUI.number("Numbertest", &numberCall, ControlColor::Alizarin, 5, 0, 10); ESPUI.number("Numbertest", &numberCall, ControlColor::Alizarin, 5, 0, 10);
fileDisplayId = ESPUI.fileDisplay("Filetest", ControlColor::Turquoise, DisplayTestFileName);
graphId = ESPUI.graph("Graph Test", ControlColor::Wetasphalt); graphId = ESPUI.graph("Graph Test", ControlColor::Wetasphalt);
@ -258,7 +261,15 @@ void setup(void)
* password, for example begin("ESPUI Control", "username", "password") * password, for example begin("ESPUI Control", "username", "password")
*/ */
ESPUI.sliderContinuous = true; ESPUI.sliderContinuous = true;
ESPUI.begin("ESPUI Control"); ESPUI.prepareFileSystem();
ESPUI.beginLITTLEFS("ESPUI Control");
// create a text file
ESPUI.writeFile("/DisplayFile.txt", "Test Line\n");
// these files are used by browsers to auto config a connection.
ESPUI.writeFile("/wpad.dat", " ");
ESPUI.writeFile("/connecttest.txt", " ");
} }
void loop(void) void loop(void)
@ -268,7 +279,6 @@ void loop(void)
static long oldTime = 0; static long oldTime = 0;
static bool testSwitchState = false; static bool testSwitchState = false;
delay(10); delay(10);
return;
if (millis() - oldTime > 5000) if (millis() - oldTime > 5000)
{ {
@ -279,6 +289,21 @@ void loop(void)
testSwitchState = !testSwitchState; testSwitchState = !testSwitchState;
ESPUI.updateSwitcher(testSwitchId, testSwitchState); ESPUI.updateSwitcher(testSwitchId, testSwitchState);
// update the file Display file.
File testFile = ESPUI.EspuiLittleFS.open(String("/") + DisplayTestFileName, "a");
uint32_t filesize = testFile.size();
String TestLine = String("Current Time = ") + String(millis()) + "\n";
if(filesize < 1000)
{
testFile.write((const uint8_t*)TestLine.c_str(), TestLine.length());
ESPUI.updateControl(fileDisplayId);
TestLine += String("filesize: ") + String(filesize);
// Serial.println(TestLine);
}
testFile.close();
oldTime = millis(); oldTime = millis();
} }
} }

View File

@ -76,16 +76,7 @@ void listDir(const char* dirname, uint8_t levels)
} }
#endif #endif
#if defined(ESP32) File root = ESPUI.EspuiLittleFS.open(dirname);
#if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION_MINOR >= 4) || ESP_IDF_VERSION_MAJOR > 4
File root = LittleFS.open(dirname);
#else
File root = LITTLEFS.open(dirname);
#endif
#else
File root = LittleFS.open(dirname);
#endif
if (!root) if (!root)
{ {
#if defined(DEBUG_ESPUI) #if defined(DEBUG_ESPUI)
@ -160,7 +151,7 @@ void listDir(const char* dirname, uint8_t levels)
} }
#endif #endif
Dir dir = LittleFS.openDir(dirname); Dir dir = ESPUI.EspuiLittleFS.openDir(dirname);
while (dir.next()) while (dir.next())
{ {
@ -199,63 +190,33 @@ void listDir(const char* dirname, uint8_t levels)
void ESPUIClass::list() void ESPUIClass::list()
{ {
#if defined(ESP32) if (!EspuiLittleFS.begin())
#if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION_MINOR >= 4) || ESP_IDF_VERSION_MAJOR > 4
if (!LittleFS.begin())
#else
if (!LITTLEFS.begin())
#endif
{ {
Serial.println(F("LITTLEFS Mount Failed")); Serial.println(F("Espui LittleFS Mount Failed"));
return; return;
} }
#else
if (!LittleFS.begin())
{
Serial.println(F("LittleFS Mount Failed"));
return;
}
#endif
listDir("/", 1); listDir("/", 1);
#if defined(ESP32) #if defined(ESP32)
Serial.print(F("Total KB: ")); Serial.print(F("Total KB: "));
#if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION_MINOR >= 4) || ESP_IDF_VERSION_MAJOR > 4 Serial.println(EspuiLittleFS.totalBytes() / 1024);
Serial.println(LittleFS.totalBytes() / 1024);
#else
Serial.println(LITTLEFS.totalBytes() / 1024);
#endif
Serial.print(F("Used KB: ")); Serial.print(F("Used KB: "));
#if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION_MINOR >= 4) || ESP_IDF_VERSION_MAJOR > 4 Serial.println(EspuiLittleFS.usedBytes() / 1024);
Serial.println(LittleFS.usedBytes() / 1024);
#else
Serial.println(LITTLEFS.usedBytes() / 1024);
#endif
#else #else
FSInfo fs_info; FSInfo fs_info;
LittleFS.info(fs_info); EspuiLittleFS.info(fs_info);
Serial.print(F("Total KB: ")); Serial.print(F("Total KB: "));
Serial.println(fs_info.totalBytes / 1024); Serial.println(fs_info.totalBytes / 1024);
Serial.print(F("Used KB: ")); Serial.print(F("Used KB: "));
Serial.println(fs_info.usedBytes / 1024); Serial.println(fs_info.usedBytes / 1024);
#endif // !defined(ESP32)
#endif
} }
void deleteFile(const char* path) void deleteFile(const char* path)
{ {
#if defined(ESP32) bool exists = ESPUI.EspuiLittleFS.exists(path);
#if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION_MINOR >= 4) || ESP_IDF_VERSION_MAJOR > 4
bool exists = LittleFS.exists(path);
#else
bool exists = LITTLEFS.exists(path);
#endif
#else
bool exists = LittleFS.exists(path);
#endif
if (!exists) if (!exists)
{ {
#if defined(DEBUG_ESPUI) #if defined(DEBUG_ESPUI)
@ -275,15 +236,7 @@ void deleteFile(const char* path)
} }
#endif #endif
#if defined(ESP32) bool didRemove = ESPUI.EspuiLittleFS.remove(path);
#if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION_MINOR >= 4) || ESP_IDF_VERSION_MAJOR > 4
bool didRemove = LittleFS.remove(path);
#else
bool didRemove = LITTLEFS.remove(path);
#endif
#else
bool didRemove = LittleFS.remove(path);
#endif
if (didRemove) if (didRemove)
{ {
#if defined(DEBUG_ESPUI) #if defined(DEBUG_ESPUI)
@ -304,7 +257,7 @@ void deleteFile(const char* path)
} }
} }
void writeFile(const char* path, const char* data) void ESPUIClass::writeFile(const char* path, const char* data)
{ {
#if defined(DEBUG_ESPUI) #if defined(DEBUG_ESPUI)
if (ESPUI.verbosity) if (ESPUI.verbosity)
@ -313,15 +266,7 @@ void writeFile(const char* path, const char* data)
} }
#endif #endif
#if defined(ESP32) File file = EspuiLittleFS.open(path, FILE_WRITING);
#if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION_MINOR >= 4) || ESP_IDF_VERSION_MAJOR > 4
File file = LittleFS.open(path, FILE_WRITE);
#else
File file = LITTLEFS.open(path, FILE_WRITE);
#endif
#else
File file = LittleFS.open(path, FILE_WRITE);
#endif
if (!file) if (!file)
{ {
#if defined(DEBUG_ESPUI) #if defined(DEBUG_ESPUI)
@ -335,48 +280,26 @@ void writeFile(const char* path, const char* data)
} }
#if defined(ESP32) #if defined(ESP32)
if (file.print(data)) if (file.print(data))
{
#if defined(DEBUG_ESPUI)
if (ESPUI.verbosity)
{
Serial.println(F("File written"));
}
#endif
}
else
{
#if defined(DEBUG_ESPUI)
if (ESPUI.verbosity)
{
Serial.println(F("Write failed"));
}
#endif
}
#else #else
if (file.print(FPSTR(data))) if (file.print(FPSTR(data)))
#endif // !defined(ESP32)
{ {
#if defined(DEBUG_ESPUI) #if defined(DEBUG_ESPUI)
if (ESPUI.verbosity) if (ESPUI.verbosity)
{ {
Serial.println(F("File written")); Serial.println(F("File written"));
} }
#endif
} }
else else
{ {
#if defined(DEBUG_ESPUI)
if (ESPUI.verbosity) if (ESPUI.verbosity)
{ {
Serial.println(F("Write failed")); Serial.println(F("Write failed"));
} }
#endif #endif
} }
#endif
file.close(); file.close();
} }
@ -394,18 +317,17 @@ void ESPUIClass::prepareFileSystem(bool format)
#endif #endif
#if defined(ESP32) #if defined(ESP32)
#if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION_MINOR >= 4) || ESP_IDF_VERSION_MAJOR > 4 if (!EspuiLittleFS.begin(false)) // Test for an already formatted LittleFS by a mount failure
if (!LittleFS.begin(false)) // Test for an already formatted LittleFS by a mount failure
#else
if (!LITTLEFS.begin(false)) // Test for an already formatted LittleFS by a mount failure
#endif
{ {
#if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION_MINOR >= 4) || ESP_IDF_VERSION_MAJOR > 4 if (!EspuiLittleFS.begin(true)) // Attempt to format LittleFS
if (!LittleFS.begin(true)) // Attempt to format LittleFS
#else
if (!LITTLEFS.begin(true)) // Attempt to format LittleFS
#endif
{ {
#else
if (!EspuiLittleFS.begin()) // Test for an already formatted LittleFS by a mount failure
{
if (EspuiLittleFS.format()) // Attempt to format LittleFS
{
#endif // !defined(ESP32)
#if defined(DEBUG_ESPUI) #if defined(DEBUG_ESPUI)
if (verbosity) if (verbosity)
{ {
@ -417,11 +339,8 @@ void ESPUIClass::prepareFileSystem(bool format)
} }
else if (format) else if (format)
{ {
#if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION_MINOR >= 4) || ESP_IDF_VERSION_MAJOR > 4 EspuiLittleFS.format();
LittleFS.format();
#else
LITTLEFS.format();
#endif
#if defined(DEBUG_ESPUI) #if defined(DEBUG_ESPUI)
if (verbosity) if (verbosity)
{ {
@ -436,51 +355,6 @@ void ESPUIClass::prepareFileSystem(bool format)
listDir("/", 1); listDir("/", 1);
Serial.println(F("LittleFS Mount ESP32 Done")); Serial.println(F("LittleFS Mount ESP32 Done"));
} }
#endif
#else
if (!LittleFS.begin()) // Test for an already formatted LittleFS by a mount failure
{
if (LittleFS.format()) // Attempt to format LittleFS
{
#if defined(DEBUG_ESPUI)
if (verbosity)
{
Serial.println(F("LittleFS Formatted"));
}
#endif
}
else
{
#if defined(DEBUG_ESPUI)
if (verbosity)
{
Serial.println(F("LittleFS Mount Failed"));
}
#endif
return;
}
}
else if (format)
{
LittleFS.format();
#if defined(DEBUG_ESPUI)
if (verbosity)
{
Serial.println(F("LittleFS Formatted"));
}
#endif
}
#if defined(DEBUG_ESPUI)
if (verbosity)
{
listDir("/", 1);
Serial.println(F("LittleFS Mount ESP8266 Done"));
}
#endif
#endif #endif
deleteFile("/index.htm"); deleteFile("/index.htm");
@ -503,31 +377,18 @@ void ESPUIClass::prepareFileSystem(bool format)
// Now write // Now write
#ifdef ESP32 #ifdef ESP32
#if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION_MINOR >= 4) || ESP_IDF_VERSION_MAJOR > 4
writeFile("/index.htm", HTML_INDEX); writeFile("/index.htm", HTML_INDEX);
LittleFS.mkdir("/css"); EspuiLittleFS.mkdir("/css");
writeFile("/css/style.css", CSS_STYLE); writeFile("/css/style.css", CSS_STYLE);
writeFile("/css/normalize.css", CSS_NORMALIZE); writeFile("/css/normalize.css", CSS_NORMALIZE);
LittleFS.mkdir("/js"); EspuiLittleFS.mkdir("/js");
writeFile("/js/zepto.min.js", JS_ZEPTO); writeFile("/js/zepto.min.js", JS_ZEPTO);
writeFile("/js/controls.js", JS_CONTROLS); writeFile("/js/controls.js", JS_CONTROLS);
writeFile("/js/slider.js", JS_SLIDER); writeFile("/js/slider.js", JS_SLIDER);
writeFile("/js/graph.js", JS_GRAPH); writeFile("/js/graph.js", JS_GRAPH);
writeFile("/js/tabbedcontent.js", JS_TABBEDCONTENT); writeFile("/js/tabbedcontent.js", JS_TABBEDCONTENT);
#else
writeFile("/index.htm", HTML_INDEX);
LITTLEFS.mkdir("/css");
writeFile("/css/style.css", CSS_STYLE);
writeFile("/css/normalize.css", CSS_NORMALIZE);
LITTLEFS.mkdir("/js");
writeFile("/js/zepto.min.js", JS_ZEPTO);
writeFile("/js/controls.js", JS_CONTROLS);
writeFile("/js/slider.js", JS_SLIDER);
writeFile("/js/graph.js", JS_GRAPH);
writeFile("/js/tabbedcontent.js", JS_TABBEDCONTENT);
#endif
#else #else
writeFile("/index.htm", HTML_INDEX); writeFile("/index.htm", HTML_INDEX);
@ -560,15 +421,7 @@ void ESPUIClass::prepareFileSystem(bool format)
#endif #endif
#if defined(ESP32) EspuiLittleFS.end();
#if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION_MINOR >= 4) || ESP_IDF_VERSION_MAJOR > 4
LittleFS.end();
#else
LITTLEFS.end();
#endif
#else
LittleFS.end();
#endif
} }
// Handle Websockets Communication // Handle Websockets Communication
@ -601,10 +454,13 @@ void ESPUIClass::onWsEvent(
// Serial.println("ESPUIClass::OnWsEvent:Create new client."); // Serial.println("ESPUIClass::OnWsEvent:Create new client.");
MapOfClients[client->id()] = new ESPUIclient(client); MapOfClients[client->id()] = new ESPUIclient(client);
} }
MapOfClients[client->id()]->onWsEvent(type, arg, data, len);
}
ClearControlUpdateFlags(); if(MapOfClients[client->id()]->onWsEvent(type, arg, data, len))
{
// Serial.println("ESPUIClass::OnWsEvent:notify the clients that they need to be updated.");
NotifyClients(ESPUIclient::UpdateNeeded);
}
}
return; return;
} }
@ -627,27 +483,25 @@ uint16_t ESPUIClass::addControl(ControlType type, const char* label, const Strin
uint16_t ESPUIClass::addControl( uint16_t ESPUIClass::addControl(
ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl) ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl)
{ {
return addControl(type, label, value, color, parentControl, nullptr); return addControl(type, label, value, color, parentControl, new Control(type, label, nullptr, value, color, true, parentControl));
} }
uint16_t ESPUIClass::addControl(ControlType type, const char* label, const String& value, ControlColor color, uint16_t ESPUIClass::addControl(ControlType type, const char* label, const String& value, ControlColor color,
uint16_t parentControl, void (*callback)(Control*, int)) uint16_t parentControl, std::function<void(Control*, int)> callback)
{ {
uint16_t id = addControl(type, label, value, color, parentControl, nullptr, nullptr); uint16_t id = addControl(type, label, value, color, parentControl);
// set the original style callback // set the original style callback
getControl(id)->callback = callback; getControl(id)->callback = callback;
return id; return id;
} }
uint16_t ESPUIClass::addControl(ControlType type, const char* label, const String& value, ControlColor color, uint16_t ESPUIClass::addControl(
uint16_t parentControl, void (*callback)(Control*, int, void*), void* UserData) ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl, Control* control)
{ {
#ifdef ESP32 #ifdef ESP32
xSemaphoreTake(ControlsSemaphore, portMAX_DELAY); xSemaphoreTake(ControlsSemaphore, portMAX_DELAY);
#endif // def ESP32 #endif // def ESP32
Control* control = new Control(type, label, callback, UserData, value, color, true, parentControl);
if (controls == nullptr) if (controls == nullptr)
{ {
controls = control; controls = control;
@ -753,70 +607,37 @@ uint16_t ESPUIClass::graph(const char* label, ControlColor color)
} }
uint16_t ESPUIClass::slider( uint16_t ESPUIClass::slider(
const char* label, void (*callback)(Control*, int), ControlColor color, int value, int min, int max) const char* label, std::function<void(Control*, int)> callback, ControlColor color, int value, int min, int max)
{
uint16_t id = slider(label, nullptr, color, value, min, max, nullptr);
getControl(id)->callback = callback;
return id;
}
uint16_t ESPUIClass::slider(const char* label, void (*callback)(Control*, int, void*), ControlColor color, int value,
int min, int max, void* userData)
{ {
uint16_t sliderId uint16_t sliderId
= addControl(ControlType::Slider, label, String(value), color, Control::noParent, callback, userData); = addControl(ControlType::Slider, label, String(value), color, Control::noParent, callback);
addControl(ControlType::Min, label, String(min), ControlColor::None, sliderId); addControl(ControlType::Min, label, String(min), ControlColor::None, sliderId);
addControl(ControlType::Max, label, String(max), ControlColor::None, sliderId); addControl(ControlType::Max, label, String(max), ControlColor::None, sliderId);
return sliderId; return sliderId;
} }
uint16_t ESPUIClass::button(const char* label, void (*callback)(Control*, int), ControlColor color, const String& value) uint16_t ESPUIClass::button(const char* label, std::function<void(Control*, int)> callback, ControlColor color, const String& value)
{ {
return addControl(ControlType::Button, label, value, color, Control::noParent, callback); return addControl(ControlType::Button, label, value, color, Control::noParent, callback);
} }
uint16_t ESPUIClass::button( uint16_t ESPUIClass::switcher(const char* label, std::function<void(Control*, int)> callback, ControlColor color, bool startState)
const char* label, void (*callback)(Control*, int, void*), ControlColor color, const String& value, void* UserData)
{
return addControl(ControlType::Button, label, value, color, Control::noParent, callback, UserData);
}
uint16_t ESPUIClass::switcher(const char* label, void (*callback)(Control*, int), ControlColor color, bool startState)
{ {
return addControl(ControlType::Switcher, label, startState ? "1" : "0", color, Control::noParent, callback); return addControl(ControlType::Switcher, label, startState ? "1" : "0", color, Control::noParent, callback);
} }
uint16_t ESPUIClass::switcher( uint16_t ESPUIClass::pad(const char* label, std::function<void(Control*, int)> callback, ControlColor color)
const char* label, void (*callback)(Control*, int, void*), ControlColor color, bool startState, void* UserData)
{
return addControl(
ControlType::Switcher, label, startState ? "1" : "0", color, Control::noParent, callback, UserData);
}
uint16_t ESPUIClass::pad(const char* label, void (*callback)(Control*, int), ControlColor color)
{ {
return addControl(ControlType::Pad, label, "", color, Control::noParent, callback); return addControl(ControlType::Pad, label, "", color, Control::noParent, callback);
} }
uint16_t ESPUIClass::pad(const char* label, void (*callback)(Control*, int, void*), ControlColor color, void* UserData) uint16_t ESPUIClass::padWithCenter(const char* label, std::function<void(Control*, int)> callback, ControlColor color)
{
return addControl(ControlType::Pad, label, "", color, Control::noParent, callback, UserData);
}
uint16_t ESPUIClass::padWithCenter(const char* label, void (*callback)(Control*, int), ControlColor color)
{ {
return addControl(ControlType::PadWithCenter, label, "", color, Control::noParent, callback); return addControl(ControlType::PadWithCenter, label, "", color, Control::noParent, callback);
} }
uint16_t ESPUIClass::padWithCenter(
const char* label, void (*callback)(Control*, int, void*), ControlColor color, void* UserData)
{
return addControl(ControlType::PadWithCenter, label, "", color, Control::noParent, callback, UserData);
}
uint16_t ESPUIClass::number( uint16_t ESPUIClass::number(
const char* label, void (*callback)(Control*, int), ControlColor color, int number, int min, int max) const char* label, std::function<void(Control*, int)> callback, ControlColor color, int number, int min, int max)
{ {
uint16_t numberId = addControl(ControlType::Number, label, String(number), color, Control::noParent, callback); uint16_t numberId = addControl(ControlType::Number, label, String(number), color, Control::noParent, callback);
addControl(ControlType::Min, label, String(min), ControlColor::None, numberId); addControl(ControlType::Min, label, String(min), ControlColor::None, numberId);
@ -824,16 +645,6 @@ uint16_t ESPUIClass::number(
return numberId; return numberId;
} }
uint16_t ESPUIClass::number(const char* label, void (*callback)(Control*, int, void*), ControlColor color, int number,
int min, int max, void* UserData)
{
uint16_t numberId
= addControl(ControlType::Number, label, String(number), color, Control::noParent, callback, UserData);
addControl(ControlType::Min, label, String(min), ControlColor::None, numberId);
addControl(ControlType::Max, label, String(max), ControlColor::None, numberId);
return numberId;
}
uint16_t ESPUIClass::gauge(const char* label, ControlColor color, int number, int min, int max) uint16_t ESPUIClass::gauge(const char* label, ControlColor color, int number, int min, int max)
{ {
uint16_t numberId = addControl(ControlType::Gauge, label, String(number), color, Control::noParent); uint16_t numberId = addControl(ControlType::Gauge, label, String(number), color, Control::noParent);
@ -847,28 +658,21 @@ uint16_t ESPUIClass::separator(const char* label)
return addControl(ControlType::Separator, label, "", ControlColor::Alizarin, Control::noParent, nullptr); return addControl(ControlType::Separator, label, "", ControlColor::Alizarin, Control::noParent, nullptr);
} }
uint16_t ESPUIClass::accelerometer(const char* label, void (*callback)(Control*, int), ControlColor color) uint16_t ESPUIClass::fileDisplay(const char* label, ControlColor color, String filename)
{
return addControl(ControlType::FileDisplay, label, filename, color, Control::noParent);
}
uint16_t ESPUIClass::accelerometer(const char* label, std::function<void(Control*, int)> callback, ControlColor color)
{ {
return addControl(ControlType::Accel, label, "", color, Control::noParent, callback); return addControl(ControlType::Accel, label, "", color, Control::noParent, callback);
} }
uint16_t ESPUIClass::accelerometer( uint16_t ESPUIClass::text(const char* label, std::function<void(Control*, int)> callback, ControlColor color, const String& value)
const char* label, void (*callback)(Control*, int, void*), ControlColor color, void* UserData)
{
return addControl(ControlType::Accel, label, "", color, Control::noParent, callback, UserData);
}
uint16_t ESPUIClass::text(const char* label, void (*callback)(Control*, int), ControlColor color, const String& value)
{ {
return addControl(ControlType::Text, label, value, color, Control::noParent, callback); return addControl(ControlType::Text, label, value, color, Control::noParent, callback);
} }
uint16_t ESPUIClass::text(
const char* label, void (*callback)(Control*, int, void*), ControlColor color, const String& value, void* UserData)
{
return addControl(ControlType::Text, label, value, color, Control::noParent, callback, UserData);
}
Control* ESPUIClass::getControl(uint16_t id) Control* ESPUIClass::getControl(uint16_t id)
{ {
#ifdef ESP32 #ifdef ESP32
@ -911,12 +715,22 @@ void ESPUIClass::updateControl(Control* control, int)
{ {
return; return;
} }
// tel the control it has been updated // tell the control it has been updated
control->HasBeenUpdated(); control->SetControlChangedId(ESPUI.GetNextControlChangeId());
NotifyClients(ClientUpdateType_t::UpdateNeeded); NotifyClients(ClientUpdateType_t::UpdateNeeded);
} }
void ESPUIClass::setPanelStyle(uint16_t id, String style, int clientId) uint32_t ESPUIClass::GetNextControlChangeId()
{
if(uint32_t(-1) == ControlChangeID)
{
// force a reload which resets the counters
jsonReload();
}
return ++ControlChangeID;
}
void ESPUIClass::setPanelStyle(uint16_t id, const String& style, int clientId)
{ {
Control* control = getControl(id); Control* control = getControl(id);
if (control) if (control)
@ -926,7 +740,7 @@ void ESPUIClass::setPanelStyle(uint16_t id, String style, int clientId)
} }
} }
void ESPUIClass::setElementStyle(uint16_t id, String style, int clientId) void ESPUIClass::setElementStyle(uint16_t id, const String& style, int clientId)
{ {
Control* control = getControl(id); Control* control = getControl(id);
if (control) if (control)
@ -936,7 +750,7 @@ void ESPUIClass::setElementStyle(uint16_t id, String style, int clientId)
} }
} }
void ESPUIClass::setInputType(uint16_t id, String type, int clientId) void ESPUIClass::setInputType(uint16_t id, const String& type, int clientId)
{ {
Control* control = getControl(id); Control* control = getControl(id);
if (control) if (control)
@ -1183,30 +997,6 @@ void ESPUIClass::NotifyClients(ClientUpdateType_t newState)
} }
} }
void ESPUIClass::ClearControlUpdateFlags()
{
bool CanClearUpdateFlags = true;
for (auto& CurrentClient : MapOfClients)
{
if (!CurrentClient.second->IsSyncronized())
{
CanClearUpdateFlags = false;
break;
}
}
if (CanClearUpdateFlags)
{
Control* control = controls;
while (nullptr != control)
{
control->HasBeenSynchronized();
control = control->next;
}
}
}
void ESPUIClass::jsonReload() void ESPUIClass::jsonReload()
{ {
for (auto& CurrentClient : MapOfClients) for (auto& CurrentClient : MapOfClients)
@ -1240,15 +1030,7 @@ void ESPUIClass::beginLITTLEFS(const char* _title, const char* username, const c
server = new AsyncWebServer(port); server = new AsyncWebServer(port);
ws = new AsyncWebSocket("/ws"); ws = new AsyncWebSocket("/ws");
#if defined(ESP32) bool fsBegin = EspuiLittleFS.begin();
#if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION_MINOR >= 4) || ESP_IDF_VERSION_MAJOR > 4
bool fsBegin = LittleFS.begin();
#else
bool fsBegin = LITTLEFS.begin();
#endif
#else
bool fsBegin = LittleFS.begin();
#endif
if (!fsBegin) if (!fsBegin)
{ {
#if defined(DEBUG_ESPUI) #if defined(DEBUG_ESPUI)
@ -1269,15 +1051,7 @@ void ESPUIClass::beginLITTLEFS(const char* _title, const char* username, const c
} }
#endif #endif
#if defined(ESP32) bool indexExists = EspuiLittleFS.exists("/index.htm");
#if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION_MINOR >= 4) || ESP_IDF_VERSION_MAJOR > 4
bool indexExists = LittleFS.exists("/index.htm");
#else
bool indexExists = LITTLEFS.exists("/index.htm");
#endif
#else
bool indexExists = LittleFS.exists("/index.htm");
#endif
if (!indexExists) if (!indexExists)
{ {
#if defined(DEBUG_ESPUI) #if defined(DEBUG_ESPUI)
@ -1301,27 +1075,11 @@ void ESPUIClass::beginLITTLEFS(const char* _title, const char* username, const c
{ {
ws->setAuthentication(basicAuthUsername, basicAuthPassword); ws->setAuthentication(basicAuthUsername, basicAuthPassword);
} }
#if defined(ESP32) server->serveStatic("/", EspuiLittleFS, "/").setDefaultFile("index.htm").setAuthentication(username, password);
#if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION_MINOR >= 4) || ESP_IDF_VERSION_MAJOR > 4
server->serveStatic("/", LittleFS, "/").setDefaultFile("index.htm").setAuthentication(username, password);
#else
server->serveStatic("/", LITTLEFS, "/").setDefaultFile("index.htm").setAuthentication(username, password);
#endif
#else
server->serveStatic("/", LittleFS, "/").setDefaultFile("index.htm").setAuthentication(username, password);
#endif
} }
else else
{ {
#if defined(ESP32) server->serveStatic("/", EspuiLittleFS, "/").setDefaultFile("index.htm");
#if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION_MINOR >= 4) || ESP_IDF_VERSION_MAJOR > 4
server->serveStatic("/", LittleFS, "/").setDefaultFile("index.htm");
#else
server->serveStatic("/", LITTLEFS, "/").setDefaultFile("index.htm");
#endif
#else
server->serveStatic("/", LittleFS, "/").setDefaultFile("index.htm");
#endif
} }
// Heap for general Servertest // Heap for general Servertest
@ -1493,12 +1251,21 @@ void ESPUIClass::begin(const char* _title, const char* username, const char* pas
server->onNotFound([this](AsyncWebServerRequest* request) { server->onNotFound([this](AsyncWebServerRequest* request) {
if (captivePortal) if (captivePortal)
{ {
AsyncResponseStream *response = request->beginResponseStream("text/html");
String responseText;
responseText.reserve(1024);
responseText += F("<!DOCTYPE html><html><head><title>Captive Portal</title></head><body>");
responseText += ("<p>If site does not re-direct click here <a href='http://" + WiFi.softAPIP().toString() + "'>this link</a></p>");
responseText += ("</body></html><head><meta http-equiv=\"Refresh\" content=\"0; URL='http://" + WiFi.softAPIP().toString() + "'\" /></head>");
response->write(responseText.c_str(), responseText.length());
request->send(response);
request->redirect("/"); request->redirect("/");
} }
else else
{ {
request->send(404); request->send(404);
} }
yield();
}); });
server->begin(); server->begin();

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
// comment out to turn off debug output // comment out to turn off debug output
#define DEBUG_ESPUI true // #define DEBUG_ESPUI true
#define WS_AUTHENTICATION false #define WS_AUTHENTICATION false
#include <Arduino.h> #include <Arduino.h>
@ -34,7 +34,7 @@
#include <ESPAsyncTCP.h> #include <ESPAsyncTCP.h>
#include <Hash.h> #include <Hash.h>
#define FILE_WRITE "w" #define FILE_WRITING "w"
#endif #endif
@ -87,16 +87,13 @@ enum Verbosity : uint8_t
class ESPUIClass class ESPUIClass
{ {
public: public:
#ifdef ESP32
ESPUIClass() ESPUIClass()
{ {
#ifdef ESP32
ControlsSemaphore = xSemaphoreCreateMutex(); ControlsSemaphore = xSemaphoreCreateMutex();
xSemaphoreGive(ControlsSemaphore); xSemaphoreGive(ControlsSemaphore);
}
SemaphoreHandle_t ControlsSemaphore = NULL;
#endif // def ESP32 #endif // def ESP32
}
unsigned int jsonUpdateDocumentSize = 2000; unsigned int jsonUpdateDocumentSize = 2000;
#ifdef ESP8266 #ifdef ESP8266
unsigned int jsonInitialDocumentSize = 2000; unsigned int jsonInitialDocumentSize = 2000;
@ -120,38 +117,25 @@ public:
void prepareFileSystem(bool format = true); // Initially preps the filesystem and loads a lot of void prepareFileSystem(bool format = true); // Initially preps the filesystem and loads a lot of
// stuff into LITTLEFS // stuff into LITTLEFS
void list(); // Lists LITTLEFS directory void list(); // Lists LITTLEFS directory
void writeFile(const char* path, const char* data);
uint16_t addControl(ControlType type, const char* label); uint16_t addControl(ControlType type, const char* label);
uint16_t addControl(ControlType type, const char* label, const String& value); uint16_t addControl(ControlType type, const char* label, const String& value);
uint16_t addControl(ControlType type, const char* label, const String& value, ControlColor color); uint16_t addControl(ControlType type, const char* label, const String& value, ControlColor color);
uint16_t addControl(ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl); uint16_t addControl(ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl);
uint16_t addControl(ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl, void (*callback)(Control*, int)); uint16_t addControl(ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl, std::function<void(Control*, int)> callback);
uint16_t addControl(ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl, void (*callback)(Control*, int, void *), void* UserData);
bool removeControl(uint16_t id, bool force_rebuild_ui = false); bool removeControl(uint16_t id, bool force_rebuild_ui = false);
// create Elements // create Elements
// Create Event Button // Create Event Button
uint16_t button(const char* label, void (*callback)(Control*, int), ControlColor color, const String& value = ""); uint16_t button(const char* label, std::function<void(Control*, int)> callback, ControlColor color, const String& value = "");
uint16_t button(const char* label, void (*callback)(Control*, int, void*), ControlColor color, const String& value, void* UserData); uint16_t switcher(const char* label, std::function<void(Control*, int)> callback, ControlColor color, bool startState = false); // Create Toggle Button
uint16_t pad(const char* label, std::function<void(Control*, int)> callback, ControlColor color); // Create Pad Control
uint16_t switcher(const char* label, void (*callback)(Control*, int), ControlColor color, bool startState = false); // Create Toggle Button uint16_t padWithCenter(const char* label, std::function<void(Control*, int)> callback, ControlColor color); // Create Pad Control with Centerbutton
uint16_t switcher(const char* label, void (*callback)(Control*, int, void*), ControlColor color, bool startState, void* UserData); // Create Toggle Button uint16_t slider(const char* label, std::function<void(Control*, int)> callback, ControlColor color, int value, int min = 0, int max = 100); // Create Slider Control
uint16_t number(const char* label, std::function<void(Control*, int)> callback, ControlColor color, int value, int min = 0, int max = 100); // Create a Number Input Control
uint16_t pad(const char* label, void (*callback)(Control*, int), ControlColor color); // Create Pad Control uint16_t text(const char* label, std::function<void(Control*, int)> callback, ControlColor color, const String& value = ""); // Create a Text Input Control
uint16_t pad(const char* label, void (*callback)(Control*, int, void*), ControlColor color, void* UserData); // Create Pad Control
uint16_t padWithCenter(const char* label, void (*callback)(Control*, int), ControlColor color); // Create Pad Control with Centerbutton
uint16_t padWithCenter(const char* label, void (*callback)(Control*, int, void*), ControlColor color, void* UserData); // Create Pad Control with Centerbutton
uint16_t slider(const char* label, void (*callback)(Control*, int), ControlColor color, int value, int min = 0, int max = 100); // Create Slider Control
uint16_t slider(const char* label, void (*callback)(Control*, int, void*), ControlColor color, int value, int min, int max, void* UserData); // Create Slider Control
uint16_t number(const char* label, void (*callback)(Control*, int), ControlColor color, int value, int min = 0, int max = 100); // Create a Number Input Control
uint16_t number(const char* label, void (*callback)(Control*, int, void*), ControlColor color, int value, int min, int max, void* UserData); // Create a Number Input Control
uint16_t text(const char* label, void (*callback)(Control*, int), ControlColor color, const String& value = ""); // Create a Text Input Control
uint16_t text(const char* label, void (*callback)(Control*, int, void*), ControlColor color, const String& value, void* UserData); // Create a Text Input Control
// Output only // Output only
uint16_t label(const char* label, ControlColor color, uint16_t label(const char* label, ControlColor color,
@ -160,10 +144,10 @@ public:
uint16_t gauge(const char* label, ControlColor color, int value, int min = 0, uint16_t gauge(const char* label, ControlColor color, int value, int min = 0,
int max = 100); // Create Gauge display int max = 100); // Create Gauge display
uint16_t separator(const char* label); //Create separator uint16_t separator(const char* label); //Create separator
uint16_t fileDisplay(const char* label, ControlColor color, String filename);
// Input only // Input only
uint16_t accelerometer(const char* label, void (*callback)(Control*, int), ControlColor color); uint16_t accelerometer(const char* label, std::function<void(Control*, int)> callback, ControlColor color);
uint16_t accelerometer(const char* label, void (*callback)(Control*, int, void*), ControlColor color, void* UserData);
// Update Elements // Update Elements
@ -194,9 +178,9 @@ public:
void clearGraph(uint16_t id, int clientId = -1); void clearGraph(uint16_t id, int clientId = -1);
void addGraphPoint(uint16_t id, int nValue, int clientId = -1); void addGraphPoint(uint16_t id, int nValue, int clientId = -1);
void setPanelStyle(uint16_t id, String style, int clientId = -1); void setPanelStyle(uint16_t id, const String& style, int clientId = -1);
void setElementStyle(uint16_t id, String style, int clientId = -1); void setElementStyle(uint16_t id, const String& style, int clientId = -1);
void setInputType(uint16_t id, String type, int clientId = -1); void setInputType(uint16_t id, const String& type, int clientId = -1);
void setPanelWide(uint16_t id, bool wide); void setPanelWide(uint16_t id, bool wide);
void setVertical(uint16_t id, bool vert = true); void setVertical(uint16_t id, bool vert = true);
@ -211,14 +195,69 @@ public:
void jsonDom(uint16_t startidx, AsyncWebSocketClient* client = nullptr, bool Updating = false); void jsonDom(uint16_t startidx, AsyncWebSocketClient* client = nullptr, bool Updating = false);
Verbosity verbosity = Verbosity::Quiet; Verbosity verbosity = Verbosity::Quiet;
AsyncWebServer* server; uint32_t GetNextControlChangeId();
// emulate former extended callback API by using an intermediate lambda (no deprecation)
uint16_t addControl(ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl, std::function<void(Control*, int, void*)> callback, void* userData)
{
return addControl(type, label, value, color, parentControl, [callback, userData](Control* sender, int type){ callback(sender, type, userData); });
}
uint16_t button(const char* label, std::function<void(Control*, int, void*)> callback, ControlColor color, const String& value, void* userData)
{
return button(label, [callback, userData](Control* sender, int type){ callback(sender, type, userData); }, color, value);
}
uint16_t switcher(const char* label, std::function<void(Control*, int, void*)> callback, ControlColor color, bool startState, void* userData)
{
return switcher(label, [callback, userData](Control* sender, int type){ callback(sender, type, userData); }, color, startState);
}
uint16_t pad(const char* label, std::function<void(Control*, int, void*)> callback, ControlColor color, void* userData)
{
return pad(label, [callback, userData](Control* sender, int type){ callback(sender, type, userData); }, color);
}
uint16_t padWithCenter(const char* label, std::function<void(Control*, int, void*)> callback, ControlColor color, void* userData)
{
return padWithCenter(label, [callback, userData](Control* sender, int type){ callback(sender, type, userData); }, color);
}
uint16_t slider(const char* label, std::function<void(Control*, int, void*)> callback, ControlColor color, int value, int min, int max, void* userData)
{
return slider(label, [callback, userData](Control* sender, int type){ callback(sender, type, userData); }, color, value, min, max);
}
uint16_t number(const char* label, std::function<void(Control*, int, void*)> callback, ControlColor color, int value, int min, int max, void* userData)
{
return number(label, [callback, userData](Control* sender, int type){ callback(sender, type, userData); }, color, value, min, max);
}
uint16_t text(const char* label, std::function<void(Control*, int, void*)> callback, ControlColor color, const String& value, void* userData)
{
return text(label, [callback, userData](Control* sender, int type){ callback(sender, type, userData); } , color, value);
}
uint16_t accelerometer(const char* label, std::function<void(Control*, int, void*)> callback, ControlColor color, void* userData)
{
return accelerometer(label, [callback, userData](Control* sender, int type){ callback(sender, type, userData); }, color);
}
AsyncWebServer* WebServer() {return server;}
AsyncWebSocket* WebSocket() {return ws;}
#if defined(ESP32)
# if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION_MINOR >= 4) || ESP_IDF_VERSION_MAJOR > 4
fs::LittleFSFS & EspuiLittleFS = LittleFS;
#else
fs::LITTLEFSFS & EspuiLittleFS = LITTLEFS;
# endif
#else
fs::FS & EspuiLittleFS = LittleFS;
#endif
protected: protected:
friend class ESPUIclient; friend class ESPUIclient;
friend class ESPUIcontrol; friend class ESPUIcontrol;
#ifdef ESP32
SemaphoreHandle_t ControlsSemaphore = NULL;
#endif // def ESP32
void RemoveToBeDeletedControls(); void RemoveToBeDeletedControls();
AsyncWebServer* server;
AsyncWebSocket* ws; AsyncWebSocket* ws;
const char* basicAuthUsername = nullptr; const char* basicAuthUsername = nullptr;
@ -226,15 +265,17 @@ protected:
bool basicAuth = true; bool basicAuth = true;
uint16_t controlCount = 0; uint16_t controlCount = 0;
uint16_t addControl(ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl, Control* control);
#define ClientUpdateType_t ESPUIclient::ClientUpdateType_t #define ClientUpdateType_t ESPUIclient::ClientUpdateType_t
void NotifyClients(ClientUpdateType_t newState); void NotifyClients(ClientUpdateType_t newState);
void NotifyClient(uint32_t WsClientId, ClientUpdateType_t newState); void NotifyClient(uint32_t WsClientId, ClientUpdateType_t newState);
void ClearControlUpdateFlags();
bool SendJsonDocToWebSocket(ArduinoJson::DynamicJsonDocument& document, uint16_t clientId); bool SendJsonDocToWebSocket(ArduinoJson::DynamicJsonDocument& document, uint16_t clientId);
std::map<uint32_t, ESPUIclient*> MapOfClients; std::map<uint32_t, ESPUIclient*> MapOfClients;
uint32_t ControlChangeID = 0;
}; };
extern ESPUIClass ESPUI; extern ESPUIClass ESPUI;

View File

@ -100,7 +100,7 @@ bool ESPUIclient::SendClientNotification(ClientUpdateType_t value)
{ {
if(!CanSend()) if(!CanSend())
{ {
// Serial.println(F("ESPUIclient::NotifyClient")); // Serial.println(F("ESPUIclient::SendClientNotification:CannotSend"));
break; break;
} }
@ -124,42 +124,12 @@ void ESPUIclient::NotifyClient(ClientUpdateType_t newState)
{ {
SetState(newState); SetState(newState);
pCurrentFsmState->NotifyClient(); pCurrentFsmState->NotifyClient();
#ifdef OldWay
do // once
{
// Serial.println(String("ESPUIclient::NotifyClient: State: ") + String(int(newState)));
SetState(newState);
if (HasBeenNotified)
{
// do not need to do anything
break;
}
if(TransferIsInprogress)
{
// record that a notification was needed while we were transfering data to the client
DelayedNotification = true;
break;
}
DelayedNotification = false;
if (SendJsonDocToWebSocket(document))
{
HasBeenNotified = true;
}
} while (false);
return HasBeenNotified;
#endif // def OldWay
} }
// Handle Websockets Communication // Handle Websockets Communication
void ESPUIclient::onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t len) bool ESPUIclient::onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t len)
{ {
bool Response = false;
// Serial.println(String("ESPUIclient::OnWsEvent: type: ") + String(type)); // Serial.println(String("ESPUIclient::OnWsEvent: type: ") + String(type));
switch (type) switch (type)
@ -228,8 +198,24 @@ void ESPUIclient::onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t
if (cmd.equals(F("uiok"))) if (cmd.equals(F("uiok")))
{ {
// Serial.println(F("ESPUIclient::OnWsEvent:WS_EVT_DATA:uiok:ProcessAck"));
pCurrentFsmState->ProcessAck(id); // Serial.println(String(F("ESPUIclient::OnWsEvent:WS_EVT_DATA:uiok:ProcessAck:")) + pCurrentFsmState->GetStateName());
pCurrentFsmState->ProcessAck(id, emptyString);
break;
}
if (cmd.equals(F("uifragmentok")))
{
// Serial.println(String(F("ESPUIclient::OnWsEvent:WS_EVT_DATA:uiok:uifragmentok:")) + pCurrentFsmState->GetStateName() + ":ProcessAck");
if(!emptyString.equals(value))
{
// Serial.println(String(F("ESPUIclient::OnWsEvent:WS_EVT_DATA:uiok:uifragmentok:")) + pCurrentFsmState->GetStateName() + ":ProcessAck:value:'" + value + "'");
pCurrentFsmState->ProcessAck(uint16_t(-1), value);
}
else
{
Serial.println(F("ERROR:ESPUIclient::OnWsEvent:WS_EVT_DATA:uifragmentok:ProcessAck:Fragment Header is missing"));
}
break; break;
} }
@ -239,6 +225,7 @@ void ESPUIclient::onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t
break; break;
} }
// Serial.println(F("WS_EVT_DATA:Process Control"));
Control* control = ESPUI.getControl(id); Control* control = ESPUI.getControl(id);
if (nullptr == control) if (nullptr == control)
{ {
@ -251,6 +238,8 @@ void ESPUIclient::onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t
break; break;
} }
control->onWsEvent(cmd, value); control->onWsEvent(cmd, value);
// notify other clients of change
Response = true;
break; break;
} }
@ -260,6 +249,8 @@ void ESPUIclient::onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t
break; break;
} }
} // end switch } // end switch
return Response;
} }
/* /*
@ -269,7 +260,8 @@ client will acknowledge receipt by requesting the next chunk.
*/ */
uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex, uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex,
DynamicJsonDocument & rootDoc, DynamicJsonDocument & rootDoc,
bool InUpdateMode) bool InUpdateMode,
String FragmentRequestString)
{ {
#ifdef ESP32 #ifdef ESP32
xSemaphoreTake(ESPUI.ControlsSemaphore, portMAX_DELAY); xSemaphoreTake(ESPUI.ControlsSemaphore, portMAX_DELAY);
@ -283,8 +275,61 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex,
// Follow the list until control points to the startindex'th node // Follow the list until control points to the startindex'th node
Control* control = ESPUI.controls; Control* control = ESPUI.controls;
uint32_t currentIndex = 0; uint32_t currentIndex = 0;
uint32_t DataOffset = 0;
JsonArray items = rootDoc[F("controls")]; JsonArray items = rootDoc[F("controls")];
bool SingleControl = false;
if(!emptyString.equals(FragmentRequestString))
{
// Serial.println(F("prepareJSONChunk:Fragmentation:Got Header (1)"));
// Serial.println(String("prepareJSONChunk:startindex: ") + String(startindex));
// Serial.println(String("prepareJSONChunk:currentIndex: ") + String(currentIndex));
// Serial.println(String("prepareJSONChunk:FragmentRequestString: '") + FragmentRequestString + "'");
// this is actually a fragment or directed update request
// parse the string we got from the UI and try to update that specific
// control.
DynamicJsonDocument FragmentRequest(FragmentRequestString.length() * 3);
if(0 >= FragmentRequest.capacity())
{
Serial.println(F("ERROR:prepareJSONChunk:Fragmentation:Could not allocate memory for a fragmentation request. Skipping Response"));
break;
}
size_t FragmentRequestStartOffset = FragmentRequestString.indexOf("{");
DeserializationError error = deserializeJson(FragmentRequest, FragmentRequestString.substring(FragmentRequestStartOffset));
if(DeserializationError::Ok != error)
{
Serial.println(F("ERROR:prepareJSONChunk:Fragmentation:Could not extract json from the fragment request"));
break;
}
if(!FragmentRequest.containsKey(F("id")))
{
Serial.println(F("ERROR:prepareJSONChunk:Fragmentation:Request does not contain a control ID"));
break;
}
uint16_t ControlId = uint16_t(FragmentRequest[F("id")]);
if(!FragmentRequest.containsKey(F("offset")))
{
Serial.println(F("ERROR:prepareJSONChunk:Fragmentation:Request does not contain a starting offset"));
break;
}
DataOffset = uint16_t(FragmentRequest[F("offset")]);
control = ESPUI.getControlNoLock(ControlId);
if(nullptr == control)
{
Serial.println(String(F("ERROR:prepareJSONChunk:Fragmentation:Requested control: ")) + String(ControlId) + F(" does not exist"));
break;
}
// Serial.println(F("prepareJSONChunk:Fragmentation:disable the control search operation"));
currentIndex = 1;
startindex = 0;
SingleControl = true;
}
// find a control to send
while ((startindex > currentIndex) && (nullptr != control)) while ((startindex > currentIndex) && (nullptr != control))
{ {
// only count active controls // only count active controls
@ -293,7 +338,7 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex,
if(InUpdateMode) if(InUpdateMode)
{ {
// In update mode we only count the controls that have been updated. // In update mode we only count the controls that have been updated.
if(control->IsUpdated()) if(control->NeedsSync(CurrentSyncID))
{ {
++currentIndex; ++currentIndex;
} }
@ -320,16 +365,16 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex,
while (nullptr != control) while (nullptr != control)
{ {
// skip deleted controls or controls that have not been updated // skip deleted controls or controls that have not been updated
if (control->ToBeDeleted()) if (control->ToBeDeleted() && !SingleControl)
{ {
// Serial.println(String("prepareJSONChunk: Ignoring Deleted control: ") + String(control->id)); // Serial.println(String("prepareJSONChunk: Ignoring Deleted control: ") + String(control->id));
control = control->next; control = control->next;
continue; continue;
} }
if(InUpdateMode) if(InUpdateMode && !SingleControl)
{ {
if(control->IsUpdated()) if(control->NeedsSync(CurrentSyncID))
{ {
// dont skip this control // dont skip this control
} }
@ -343,7 +388,7 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex,
JsonObject item = items.createNestedObject(); JsonObject item = items.createNestedObject();
elementcount++; elementcount++;
control->MarshalControl(item, InUpdateMode); control->MarshalControl(item, InUpdateMode, DataOffset);
if (rootDoc.overflowed() || (ESPUI.jsonChunkNumberMax > 0 && (elementcount % ESPUI.jsonChunkNumberMax) == 0)) if (rootDoc.overflowed() || (ESPUI.jsonChunkNumberMax > 0 && (elementcount % ESPUI.jsonChunkNumberMax) == 0))
{ {
@ -351,6 +396,7 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex,
if (1 == elementcount) if (1 == elementcount)
{ {
Serial.println(String(F("ERROR: prepareJSONChunk: Control ")) + String(control->id) + F(" is too large to be sent to the browser.")); Serial.println(String(F("ERROR: prepareJSONChunk: Control ")) + String(control->id) + F(" is too large to be sent to the browser."));
// Serial.println(String(F("ERROR: prepareJSONChunk: value: ")) + control->value);
rootDoc.clear(); rootDoc.clear();
item = items.createNestedObject(); item = items.createNestedObject();
control->MarshalErrorMessage(item); control->MarshalErrorMessage(item);
@ -366,7 +412,11 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex,
} }
// exit the loop // exit the loop
control = nullptr; control = nullptr;
}
else if (SingleControl)
{
// Serial.println("prepareJSONChunk: exit loop");
control = nullptr;
} }
else else
{ {
@ -404,8 +454,7 @@ CLIENT: controls.js:handleEvent()
etc. etc.
Returns true if all controls have been sent (aka: Done) Returns true if all controls have been sent (aka: Done)
*/ */
bool ESPUIclient::SendControlsToClient(uint16_t startidx, bool ESPUIclient::SendControlsToClient(uint16_t startidx, ClientUpdateType_t TransferMode, String FragmentRequest)
ClientUpdateType_t TransferMode)
{ {
bool Response = false; bool Response = false;
// Serial.println(String("ESPUIclient:SendControlsToClient:startidx: ") + String(startidx)); // Serial.println(String("ESPUIclient:SendControlsToClient:startidx: ") + String(startidx));
@ -417,9 +466,9 @@ bool ESPUIclient::SendControlsToClient(uint16_t startidx,
break; break;
} }
if (startidx >= ESPUI.controlCount) else if ((startidx >= ESPUI.controlCount) && (emptyString.equals(FragmentRequest)))
{ {
// Serial.println("ESPUIclient:SendControlsToClient: No more controls to send."); // Serial.println(F("ERROR:ESPUIclient:SendControlsToClient: No more controls to send."));
Response = true; Response = true;
break; break;
} }
@ -427,17 +476,19 @@ bool ESPUIclient::SendControlsToClient(uint16_t startidx,
DynamicJsonDocument document(ESPUI.jsonInitialDocumentSize); DynamicJsonDocument document(ESPUI.jsonInitialDocumentSize);
FillInHeader(document); FillInHeader(document);
document[F("startindex")] = startidx; document[F("startindex")] = startidx;
document[F("totalcontrols")] = 65534; // ESPUI.controlCount; document[F("totalcontrols")] = uint16_t(-1); // ESPUI.controlCount;
if(0 == startidx) if(0 == startidx)
{ {
// Serial.println("ESPUIclient:SendControlsToClient: Tell client we are starting a transfer of controls."); // Serial.println("ESPUIclient:SendControlsToClient: Tell client we are starting a transfer of controls.");
document["type"] = (ClientUpdateType_t::RebuildNeeded == TransferMode) ? UI_INITIAL_GUI : UI_EXTEND_GUI; document["type"] = (ClientUpdateType_t::RebuildNeeded == TransferMode) ? UI_INITIAL_GUI : UI_EXTEND_GUI;
CurrentSyncID = NextSyncID;
NextSyncID = ESPUI.GetNextControlChangeId();
} }
// Serial.println(String("ESPUIclient:SendControlsToClient:type: ") + String((uint32_t)document["type"])); // Serial.println(String("ESPUIclient:SendControlsToClient:type: ") + String((uint32_t)document["type"]));
// Serial.println("ESPUIclient:SendControlsToClient: Build Controls."); // Serial.println("ESPUIclient:SendControlsToClient: Build Controls.");
if(prepareJSONChunk(startidx, document, ClientUpdateType_t::UpdateNeeded == TransferMode)) if(prepareJSONChunk(startidx, document, ClientUpdateType_t::UpdateNeeded == TransferMode, FragmentRequest))
{ {
#if defined(DEBUG_ESPUI) #if defined(DEBUG_ESPUI)
if (ESPUI.verbosity >= Verbosity::VerboseJSON) if (ESPUI.verbosity >= Verbosity::VerboseJSON)

View File

@ -44,19 +44,24 @@ protected:
bool CanSend(); bool CanSend();
void FillInHeader(ArduinoJson::DynamicJsonDocument& document); void FillInHeader(ArduinoJson::DynamicJsonDocument& document);
uint32_t prepareJSONChunk(uint16_t startindex, DynamicJsonDocument& rootDoc, bool InUpdateMode); uint32_t prepareJSONChunk(uint16_t startindex, DynamicJsonDocument& rootDoc, bool InUpdateMode, String value);
bool SendControlsToClient(uint16_t startidx, ClientUpdateType_t TransferMode); bool SendControlsToClient(uint16_t startidx, ClientUpdateType_t TransferMode, String FragmentRequest);
bool SendClientNotification(ClientUpdateType_t value); bool SendClientNotification(ClientUpdateType_t value);
private:
uint32_t CurrentSyncID = 0;
uint32_t NextSyncID = 0;
public: public:
ESPUIclient(AsyncWebSocketClient * _client); ESPUIclient(AsyncWebSocketClient * _client);
ESPUIclient(const ESPUIclient & source); ESPUIclient(const ESPUIclient & source);
virtual ~ESPUIclient(); virtual ~ESPUIclient();
void NotifyClient(ClientUpdateType_t value); void NotifyClient(ClientUpdateType_t value);
void onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t len); bool onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t len);
bool IsSyncronized(); bool IsSyncronized();
uint32_t id() { return client->id(); } uint32_t id() { return client->id(); }
void SetState(ClientUpdateType_t value); void SetState(ClientUpdateType_t value);
bool SendJsonDocToWebSocket(ArduinoJson::DynamicJsonDocument& document); bool SendJsonDocToWebSocket(ArduinoJson::DynamicJsonDocument& document);
}; };

View File

@ -58,12 +58,20 @@ bool fsm_EspuiClient_state_Idle::NotifyClient()
return Response; return Response;
} }
void fsm_EspuiClient_state_Idle::ProcessAck(uint16_t) void fsm_EspuiClient_state_Idle::ProcessAck(uint16_t ControlIndex, String FragmentRequestString)
{ {
// This is an unexpected request for control data from the browser if(!emptyString.equals(FragmentRequestString))
// treat it as if it was a rebuild operation {
// Serial.println(F("fsm_EspuiClient_state_Idle: ProcessAck")); // Serial.println(F("fsm_EspuiClient_state_Idle::ProcessAck:Fragmentation:Got fragment Header"));
Parent->NotifyClient(ClientUpdateType_t::RebuildNeeded); Parent->SendControlsToClient(ControlIndex, ClientUpdateType_t::UpdateNeeded, FragmentRequestString);
}
else
{
// This is an unexpected request for control data from the browser
// treat it as if it was a rebuild operation
// Serial.println(F("fsm_EspuiClient_state_Idle: ProcessAck:Error: Rebuild"));
Parent->NotifyClient(ClientUpdateType_t::RebuildNeeded);
}
} }
//---------------------------------------------- //----------------------------------------------
@ -75,10 +83,10 @@ bool fsm_EspuiClient_state_SendingUpdate::NotifyClient()
return true; /* Ignore request */ return true; /* Ignore request */
} }
void fsm_EspuiClient_state_SendingUpdate::ProcessAck(uint16_t ControlIndex) void fsm_EspuiClient_state_SendingUpdate::ProcessAck(uint16_t ControlIndex, String FragmentRequest)
{ {
// Serial.println(F("fsm_EspuiClient_state_SendingUpdate: ProcessAck")); // Serial.println(F("fsm_EspuiClient_state_SendingUpdate: ProcessAck"));
if(Parent->SendControlsToClient(ControlIndex, ClientUpdateType_t::UpdateNeeded)) if(Parent->SendControlsToClient(ControlIndex, ClientUpdateType_t::UpdateNeeded, FragmentRequest))
{ {
// No more data to send. Go back to idle or start next request // No more data to send. Go back to idle or start next request
Parent->fsm_EspuiClient_state_Idle_imp.Init(); Parent->fsm_EspuiClient_state_Idle_imp.Init();
@ -89,19 +97,53 @@ void fsm_EspuiClient_state_SendingUpdate::ProcessAck(uint16_t ControlIndex)
//---------------------------------------------- //----------------------------------------------
//---------------------------------------------- //----------------------------------------------
//---------------------------------------------- //----------------------------------------------
void fsm_EspuiClient_state_Rebuilding::Init()
{
// Serial.println(String("fsm_EspuiClient_state:Init: ") + GetStateName());
Parent->CurrentSyncID = 0;
Parent->NextSyncID = 0;
Parent->pCurrentFsmState = this;
}
bool fsm_EspuiClient_state_Rebuilding::NotifyClient() bool fsm_EspuiClient_state_Rebuilding::NotifyClient()
{ {
// Serial.println(F("fsm_EspuiClient_state_Rebuilding: NotifyClient")); // Serial.println(F("fsm_EspuiClient_state_Rebuilding: NotifyClient"));
return true; /* Ignore request */ return true; /* Ignore request */
} }
void fsm_EspuiClient_state_Rebuilding::ProcessAck(uint16_t ControlIndex) void fsm_EspuiClient_state_Rebuilding::ProcessAck(uint16_t ControlIndex, String FragmentRequest)
{ {
// Serial.println(F("fsm_EspuiClient_state_Rebuilding: ProcessAck")); // Serial.println(F("fsm_EspuiClient_state_Rebuilding: ProcessAck"));
if(Parent->SendControlsToClient(ControlIndex, ClientUpdateType_t::RebuildNeeded)) if(Parent->SendControlsToClient(ControlIndex, ClientUpdateType_t::RebuildNeeded, FragmentRequest))
{ {
// No more data to send. Go back to idle or start next request // No more data to send. Go back to idle or start next request
Parent->fsm_EspuiClient_state_Idle_imp.Init(); Parent->fsm_EspuiClient_state_Idle_imp.Init();
Parent->fsm_EspuiClient_state_Idle_imp.NotifyClient(); Parent->fsm_EspuiClient_state_Idle_imp.NotifyClient();
} }
} }
//----------------------------------------------
//----------------------------------------------
//----------------------------------------------
void fsm_EspuiClient_state_Reloading::Init()
{
// Serial.println(String("fsm_EspuiClient_state:Init: ") + GetStateName());
Parent->CurrentSyncID = 0;
Parent->NextSyncID = 0;
Parent->pCurrentFsmState = this;
}
void fsm_EspuiClient_state_Reloading::ProcessAck(uint16_t ControlIndex, String FragmentRequestString)
{
if(!emptyString.equals(FragmentRequestString))
{
// Serial.println(F("fsm_EspuiClient_state_Reloading::ProcessAck:Fragmentation:Got fragment Header"));
Parent->SendControlsToClient(ControlIndex, ClientUpdateType_t::UpdateNeeded, FragmentRequestString);
}
}
bool fsm_EspuiClient_state_Reloading::NotifyClient()
{
// Serial.println(F("fsm_EspuiClient_state_Reloading: NotifyClient"));
return true; /* Ignore request */
}

View File

@ -20,7 +20,7 @@ public:
void Init(); void Init();
virtual bool NotifyClient() = 0; virtual bool NotifyClient() = 0;
virtual void ProcessAck(uint16_t id) = 0; virtual void ProcessAck(uint16_t id, String FragmentRequest) = 0;
virtual String GetStateName () = 0; virtual String GetStateName () = 0;
void SetParent(ESPUIclient * value) { Parent = value; } void SetParent(ESPUIclient * value) { Parent = value; }
@ -36,7 +36,7 @@ public:
virtual ~fsm_EspuiClient_state_Idle() {} virtual ~fsm_EspuiClient_state_Idle() {}
virtual bool NotifyClient(); virtual bool NotifyClient();
virtual void ProcessAck(uint16_t id); virtual void ProcessAck(uint16_t id, String FragmentRequest);
String GetStateName() { return String(F("Idle")); } String GetStateName() { return String(F("Idle")); }
}; // fsm_EspuiClient_state_Idle }; // fsm_EspuiClient_state_Idle
@ -48,7 +48,7 @@ public:
virtual ~fsm_EspuiClient_state_SendingUpdate() {} virtual ~fsm_EspuiClient_state_SendingUpdate() {}
virtual bool NotifyClient(); virtual bool NotifyClient();
virtual void ProcessAck(uint16_t id); virtual void ProcessAck(uint16_t id, String FragmentRequest);
String GetStateName() { return String(F("Sending Update")); } String GetStateName() { return String(F("Sending Update")); }
}; // fsm_EspuiClient_state_SendingUpdate }; // fsm_EspuiClient_state_SendingUpdate
@ -59,8 +59,9 @@ public:
fsm_EspuiClient_state_Rebuilding() {} fsm_EspuiClient_state_Rebuilding() {}
virtual ~fsm_EspuiClient_state_Rebuilding() {} virtual ~fsm_EspuiClient_state_Rebuilding() {}
void Init();
virtual bool NotifyClient(); virtual bool NotifyClient();
virtual void ProcessAck(uint16_t id); virtual void ProcessAck(uint16_t id, String FragmentRequest);
String GetStateName() { return String(F("Sending Rebuild")); } String GetStateName() { return String(F("Sending Rebuild")); }
}; // fsm_EspuiClient_state_Rebuilding }; // fsm_EspuiClient_state_Rebuilding
@ -71,8 +72,9 @@ public:
fsm_EspuiClient_state_Reloading() {} fsm_EspuiClient_state_Reloading() {}
virtual ~fsm_EspuiClient_state_Reloading() {} virtual ~fsm_EspuiClient_state_Reloading() {}
virtual bool NotifyClient() { return false; } void Init();
virtual void ProcessAck(uint16_t) {} virtual bool NotifyClient();
virtual void ProcessAck(uint16_t id, String FragmentRequest);
String GetStateName() { return String(F("Reloading")); } String GetStateName() { return String(F("Reloading")); }
}; // fsm_EspuiClient_state_Reloading }; // fsm_EspuiClient_state_Reloading

View File

@ -3,13 +3,11 @@
static uint16_t idCounter = 0; static uint16_t idCounter = 0;
static const String ControlError = "*** ESPUI ERROR: Could not transfer control ***"; static const String ControlError = "*** ESPUI ERROR: Could not transfer control ***";
Control::Control(ControlType type, const char* label, void (*callback)(Control*, int, void*), void* UserData, Control::Control(ControlType type, const char* label, std::function<void(Control*, int)> callback,
const String& value, ControlColor color, bool visible, uint16_t parentControl) const String& value, ControlColor color, bool visible, uint16_t parentControl)
: type(type), : type(type),
label(label), label(label),
callback(nullptr), callback(callback),
extendedCallback(callback),
user(UserData),
value(value), value(value),
color(color), color(color),
visible(visible), visible(visible),
@ -20,6 +18,7 @@ Control::Control(ControlType type, const char* label, void (*callback)(Control*,
next(nullptr) next(nullptr)
{ {
id = ++idCounter; id = ++idCounter;
ControlChangeID = 1;
} }
Control::Control(const Control& Control) Control::Control(const Control& Control)
@ -27,13 +26,12 @@ Control::Control(const Control& Control)
id(Control.id), id(Control.id),
label(Control.label), label(Control.label),
callback(Control.callback), callback(Control.callback),
extendedCallback(Control.extendedCallback),
user(Control.user),
value(Control.value), value(Control.value),
color(Control.color), color(Control.color),
visible(Control.visible), visible(Control.visible),
parentControl(Control.parentControl), parentControl(Control.parentControl),
next(Control.next) next(Control.next),
ControlChangeID(Control.ControlChangeID)
{ } { }
void Control::SendCallback(int type) void Control::SendCallback(int type)
@ -42,22 +40,51 @@ void Control::SendCallback(int type)
{ {
callback(this, type); callback(this, type);
} }
if (extendedCallback)
{
extendedCallback(this, type, user);
}
} }
void Control::DeleteControl() void Control::DeleteControl()
{ {
ControlSyncState = ControlSyncState_t::deleted; _ToBeDeleted = true;
extendedCallback = nullptr;
callback = nullptr; callback = nullptr;
} }
void Control::MarshalControl(JsonObject & item, bool refresh) void Control::MarshalControl(JsonObject & _item, bool refresh, uint32_t StartingOffset)
{ {
JsonObject & item = _item;
uint32_t length = value.length();
uint32_t maxLength = uint32_t(double(ESPUI.jsonInitialDocumentSize) * 0.75);
if((length > maxLength) || StartingOffset)
{
/*
Serial.println(String("MarshalControl:Start Fragment Processing"));
Serial.println(String("MarshalControl:id: ") + String(id));
Serial.println(String("MarshalControl:length: ") + String(length));
Serial.println(String("MarshalControl:StartingOffset: ") + String(StartingOffset));
Serial.println(String("MarshalControl:maxLength: ") + String(maxLength));
if(0 == StartingOffset)
{
Serial.println(String("MarshalControl: New control to fragement. ID: ") + String(id));
}
else
{
Serial.println(String("MarshalControl: Next fragement. ID: ") + String(id));
}
*/
// indicate that no additional controls should be sent
_item[F("type")] = uint32_t(ControlType::Fragment);
_item[F("id")] = id;
length = min((length - StartingOffset), maxLength);
// Serial.println(String("MarshalControl:Final length: ") + String(length));
_item[F("offset")] = StartingOffset;
_item[F("length")] = length;
_item[F("total")] = value.length();
item = _item.createNestedObject(F("control"));
}
item[F("id")] = id; item[F("id")] = id;
ControlType TempType = (ControlType::Password == type) ? ControlType::Text : type; ControlType TempType = (ControlType::Password == type) ? ControlType::Text : type;
if(refresh) if(refresh)
@ -68,8 +95,9 @@ void Control::MarshalControl(JsonObject & item, bool refresh)
{ {
item[F("type")] = uint32_t(TempType); item[F("type")] = uint32_t(TempType);
} }
item[F("label")] = label; item[F("label")] = label;
item[F ("value")] = (ControlType::Password == type) ? F ("--------") : value; item[F ("value")] = (ControlType::Password == type) ? F ("--------") : value.substring(StartingOffset, length + StartingOffset);
item[F("visible")] = visible; item[F("visible")] = visible;
item[F("color")] = (int)color; item[F("color")] = (int)color;
item[F("enabled")] = enabled; item[F("enabled")] = enabled;
@ -124,6 +152,8 @@ void Control::onWsEvent(String & cmd, String& data)
{ {
do // once do // once
{ {
// Serial.println(String(F("Control::onWsEvent")));
SetControlChangedId(ESPUI.GetNextControlChangeId());
if (!HasCallback()) if (!HasCallback())
{ {
#if defined(DEBUG_ESPUI) #if defined(DEBUG_ESPUI)
@ -250,3 +280,4 @@ void Control::onWsEvent(String & cmd, String& data)
} }
} while (false); } while (false);
} }

View File

@ -2,6 +2,7 @@
#include <Arduino.h> #include <Arduino.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <functional>
enum ControlType : uint8_t enum ControlType : uint8_t
{ {
@ -29,7 +30,9 @@ enum ControlType : uint8_t
Accel, Accel,
Separator, Separator,
Time, Time,
FileDisplay,
Fragment = 98,
Password = 99, Password = 99,
UpdateOffset = 100, UpdateOffset = 100,
}; };
@ -53,9 +56,7 @@ public:
ControlType type; ControlType type;
uint16_t id; // just mirroring the id here for practical reasons uint16_t id; // just mirroring the id here for practical reasons
const char* label; const char* label;
void (*callback)(Control*, int); std::function<void(Control*, int)> callback;
void (*extendedCallback)(Control*, int, void*);
void* user;
String value; String value;
ControlColor color; ControlColor color;
bool visible; bool visible;
@ -72,8 +73,7 @@ public:
Control(ControlType type, Control(ControlType type,
const char* label, const char* label,
void (*callback)(Control*, int, void*), std::function<void(Control*, int)> callback,
void* UserData,
const String& value, const String& value,
ControlColor color, ControlColor color,
bool visible, bool visible,
@ -82,24 +82,19 @@ public:
Control(const Control& Control); Control(const Control& Control);
void SendCallback(int type); void SendCallback(int type);
bool HasCallback() { return ((nullptr != callback) || (nullptr != extendedCallback)); } bool HasCallback() { return (nullptr != callback); }
void MarshalControl(ArduinoJson::JsonObject& item, bool refresh); void MarshalControl(ArduinoJson::JsonObject& item, bool refresh, uint32_t DataOffset);
void MarshalErrorMessage(ArduinoJson::JsonObject& item); void MarshalErrorMessage(ArduinoJson::JsonObject& item);
bool ToBeDeleted() { return (ControlSyncState_t::deleted == ControlSyncState); }
void DeleteControl(); void DeleteControl();
bool IsUpdated() { return ControlSyncState_t::synchronized != ControlSyncState; }
void HasBeenUpdated() { ControlSyncState = ControlSyncState_t::updated; }
void HasBeenSynchronized() {ControlSyncState = ControlSyncState_t::synchronized;}
void onWsEvent(String& cmd, String& data); void onWsEvent(String& cmd, String& data);
inline bool ToBeDeleted() { return _ToBeDeleted; }
inline bool NeedsSync(uint32_t lastControlChangeID) {return (false == _ToBeDeleted) && (lastControlChangeID < ControlChangeID);}
void SetControlChangedId(uint32_t value) {ControlChangeID = value;}
private: private:
enum ControlSyncState_t bool _ToBeDeleted = false;
{ uint32_t ControlChangeID = 0;
synchronized = 0, String OldValue = emptyString;
updated,
deleted,
};
ControlSyncState_t ControlSyncState = ControlSyncState_t::synchronized;
}; };
#define UI_TITLE ControlType::Title #define UI_TITLE ControlType::Title

File diff suppressed because one or more lines are too long

View File

@ -1,19 +1,19 @@
const char JS_GRAPH[] PROGMEM = R"=====( const char JS_GRAPH[] PROGMEM = R"=====(
function lineGraph(parent,xAccessor,yAccessor){const width=620;const height=420;const gutter=40;const pixelsPerTick=30;function numericTransformer(dataMin,dataMax,pxMin,pxMax){var dataDiff=dataMax-dataMin,pxDiff=pxMax-pxMin,dataRatio=pxDiff/dataDiff,coordRatio=dataDiff/pxDiff;return{toCoord:function(data){return(data-dataMin)*dataRatio+pxMin;},toData:function(coord){return(coord-pxMin)*coordRatio+dataMin;}};} function lineGraph(parent,xAccessor,yAccessor){const width=620;const height=420;const gutter=40;const pixelsPerTick=30;function numericTransformer(dataMin,dataMax,pxMin,pxMax){var dataDiff=dataMax-dataMin,pxDiff=pxMax-pxMin,dataRatio=pxDiff/dataDiff,coordRatio=dataDiff/pxDiff;return{toCoord:function(data){return(data-dataMin)*dataRatio+pxMin;},toData:function(coord){return(coord-pxMin)*coordRatio+dataMin;}};}
function axisRenderer(orientation,transform){var axisGroup=document.createElementNS("http://www.w3.org/2000/svg","g");var axisPath=document.createElementNS("http://www.w3.org/2000/svg","path");axisGroup.setAttribute("class",orientation+"-axis");var xMin=gutter;var xMax=width-gutter;var yMin=height-gutter;var yMax=gutter;if(orientation==="x"){axisPath.setAttribute("d","M "+xMin+" "+yMin+" L "+xMax+" "+yMin);for(var i=xMin;i<=xMax;i++){if((i-xMin)%(pixelsPerTick*3)===0&&i!==xMin){var text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=new Date(Math.floor(transform(i))).toLocaleTimeString();text.setAttribute("x",i);text.setAttribute("y",yMin);text.setAttribute("dy","1em");axisGroup.appendChild(text);}}}else{axisPath.setAttribute("d","M "+xMin+" "+yMin+" L "+xMin+" "+yMax);for(var i=yMax;i<=yMin;i++){if((i-yMin)%pixelsPerTick===0&&i!==yMin){var tickGroup=document.createElementNS("http://www.w3.org/2000/svg","g");var gridLine=document.createElementNS("http://www.w3.org/2000/svg","path");text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=Math.floor(transform(i));text.setAttribute("x",xMin);text.setAttribute("y",i);text.setAttribute("dx","-.5em");text.setAttribute("dy",".3em");gridLine.setAttribute("d","M "+xMin+" "+i+" L "+xMax+" "+i);tickGroup.appendChild(gridLine);tickGroup.appendChild(text);axisGroup.appendChild(tickGroup);}}} function axisRenderer(orientation,transform){var axisGroup=document.createElementNS("http://www.w3.org/2000/svg","g");var axisPath=document.createElementNS("http://www.w3.org/2000/svg","path");axisGroup.setAttribute("class",orientation+"-axis");var xMin=gutter;var xMax=width-gutter;var yMin=height-gutter;var yMax=gutter;if(orientation==="x"){axisPath.setAttribute("d","M "+xMin+" "+yMin+" L "+xMax+" "+yMin);for(var i=xMin;i<=xMax;i++){if((i-xMin)%(pixelsPerTick*3)===0&&i!==xMin){var text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=new Date(Math.floor(transform(i))).toLocaleTimeString();text.setAttribute("x",i);text.setAttribute("y",yMin);text.setAttribute("dy","1em");axisGroup.appendChild(text);}}}else{axisPath.setAttribute("d","M "+xMin+" "+yMin+" L "+xMin+" "+yMax);for(var i=yMax;i<=yMin;i++){if((i-yMin)%pixelsPerTick===0&&i!==yMin){var tickGroup=document.createElementNS("http://www.w3.org/2000/svg","g");var gridLine=document.createElementNS("http://www.w3.org/2000/svg","path");text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=Math.floor(transform(i));text.setAttribute("x",xMin);text.setAttribute("y",i);text.setAttribute("dx","-.5em");text.setAttribute("dy",".3em");gridLine.setAttribute("d","M "+xMin+" "+i+" L "+xMax+" "+i);tickGroup.appendChild(gridLine);tickGroup.appendChild(text);axisGroup.appendChild(tickGroup);}}}
axisGroup.appendChild(axisPath);parent.appendChild(axisGroup);} axisGroup.appendChild(axisPath);parent.appendChild(axisGroup);}
function lineRenderer(xAccessor,yAccessor,xTransform,yTransform){var line=document.createElementNS("http://www.w3.org/2000/svg","path");xAccessor.reset();yAccessor.reset();if(!xAccessor.hasNext()||!yAccessor.hasNext()){return;} function lineRenderer(xAccessor,yAccessor,xTransform,yTransform){var line=document.createElementNS("http://www.w3.org/2000/svg","path");xAccessor.reset();yAccessor.reset();if(!xAccessor.hasNext()||!yAccessor.hasNext()){return;}
var pathString="M "+xTransform(xAccessor.next())+" "+yTransform(yAccessor.next());while(xAccessor.hasNext()&&yAccessor.hasNext()){pathString+=" L "+ var pathString="M "+xTransform(xAccessor.next())+" "+yTransform(yAccessor.next());while(xAccessor.hasNext()&&yAccessor.hasNext()){pathString+=" L "+
xTransform(xAccessor.next())+ xTransform(xAccessor.next())+
" "+ " "+
yTransform(yAccessor.next());} yTransform(yAccessor.next());}
line.setAttribute("class","series");line.setAttribute("d",pathString);parent.appendChild(line);} line.setAttribute("class","series");line.setAttribute("d",pathString);parent.appendChild(line);}
function pointRenderer(xAccessor,yAccessor,xTransform,yTransform){var pointGroup=document.createElementNS("http://www.w3.org/2000/svg","g");pointGroup.setAttribute("class","data-points");xAccessor.reset();yAccessor.reset();if(!xAccessor.hasNext()||!yAccessor.hasNext()){return;} function pointRenderer(xAccessor,yAccessor,xTransform,yTransform){var pointGroup=document.createElementNS("http://www.w3.org/2000/svg","g");pointGroup.setAttribute("class","data-points");xAccessor.reset();yAccessor.reset();if(!xAccessor.hasNext()||!yAccessor.hasNext()){return;}
while(xAccessor.hasNext()&&yAccessor.hasNext()){var xDataValue=xAccessor.next();var x=xTransform(xDataValue);var yDataValue=yAccessor.next();var y=yTransform(yDataValue);var circle=document.createElementNS("http://www.w3.org/2000/svg","circle");circle.setAttribute("cx",x);circle.setAttribute("cy",y);circle.setAttribute("r","4");var text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=Math.floor(xDataValue)+" / "+Math.floor(yDataValue);text.setAttribute("x",x);text.setAttribute("y",y);text.setAttribute("dx","1em");text.setAttribute("dy","-.7em");pointGroup.appendChild(circle);pointGroup.appendChild(text);} while(xAccessor.hasNext()&&yAccessor.hasNext()){var xDataValue=xAccessor.next();var x=xTransform(xDataValue);var yDataValue=yAccessor.next();var y=yTransform(yDataValue);var circle=document.createElementNS("http://www.w3.org/2000/svg","circle");circle.setAttribute("cx",x);circle.setAttribute("cy",y);circle.setAttribute("r","4");var text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=Math.floor(xDataValue)+" / "+Math.floor(yDataValue);text.setAttribute("x",x);text.setAttribute("y",y);text.setAttribute("dx","1em");text.setAttribute("dy","-.7em");pointGroup.appendChild(circle);pointGroup.appendChild(text);}
parent.appendChild(pointGroup);} parent.appendChild(pointGroup);}
xTransform=numericTransformer(xAccessor.min(),xAccessor.max(),0+gutter,width-gutter);yTransform=numericTransformer(yAccessor.min(),yAccessor.max(),height-gutter,0+gutter);axisRenderer("x",xTransform.toData);axisRenderer("y",yTransform.toData);lineRenderer(xAccessor,yAccessor,xTransform.toCoord,yTransform.toCoord);pointRenderer(xAccessor,yAccessor,xTransform.toCoord,yTransform.toCoord);} xTransform=numericTransformer(xAccessor.min(),xAccessor.max(),0+gutter,width-gutter);yTransform=numericTransformer(yAccessor.min(),yAccessor.max(),height-gutter,0+gutter);axisRenderer("x",xTransform.toData);axisRenderer("y",yTransform.toData);lineRenderer(xAccessor,yAccessor,xTransform.toCoord,yTransform.toCoord);pointRenderer(xAccessor,yAccessor,xTransform.toCoord,yTransform.toCoord);}
function renderGraphSvg(dataArray,renderId){var figure=document.getElementById(renderId);while(figure.hasChildNodes()){figure.removeChild(figure.lastChild);} function renderGraphSvg(dataArray,renderId){var figure=document.getElementById(renderId);while(figure.hasChildNodes()){figure.removeChild(figure.lastChild);}
var svg=document.createElementNS("http://www.w3.org/2000/svg","svg");svg.setAttribute("viewBox","0 0 640 440");svg.setAttribute("preserveAspectRatio","xMidYMid meet");lineGraph(svg,(function(data,min,max){var i=0;return{hasNext:function(){return i<data.length;},next:function(){return data[i++].x;},reset:function(){i=0;},min:function(){return min;},max:function(){return max;}};})(dataArray,Math.min.apply(Math,dataArray.map(function(o){return o.x;})),Math.max.apply(Math,dataArray.map(function(o){return o.x;}))),(function(data,min,max){var i=0;return{hasNext:function(){return i<data.length;},next:function(){return data[i++].y;},reset:function(){i=0;},min:function(){return min;},max:function(){return max;}};})(dataArray,Math.min.apply(Math,dataArray.map(function(o){return o.y;})),Math.max.apply(Math,dataArray.map(function(o){return o.y;}))));figure.appendChild(svg);} var svg=document.createElementNS("http://www.w3.org/2000/svg","svg");svg.setAttribute("viewBox","0 0 640 440");svg.setAttribute("preserveAspectRatio","xMidYMid meet");lineGraph(svg,(function(data,min,max){var i=0;return{hasNext:function(){return i<data.length;},next:function(){return data[i++].x;},reset:function(){i=0;},min:function(){return min;},max:function(){return max;}};})(dataArray,Math.min.apply(Math,dataArray.map(function(o){return o.x;})),Math.max.apply(Math,dataArray.map(function(o){return o.x;}))),(function(data,min,max){var i=0;return{hasNext:function(){return i<data.length;},next:function(){return data[i++].y;},reset:function(){i=0;},min:function(){return min;},max:function(){return max;}};})(dataArray,Math.min.apply(Math,dataArray.map(function(o){return o.y;})),Math.max.apply(Math,dataArray.map(function(o){return o.y;}))));figure.appendChild(svg);}
)====="; )=====";
const uint8_t JS_GRAPH_GZIP[1280] PROGMEM = { 31,139,8,0,22,146,123,99,2,255,205,87,95,111,219,54,16,127,247,167,112,4,52,16,99,89,86,27,175,3,170,240,33,109,135,174,64,18,20,77,48,96,24,246,192,73,180,76,76,150,4,138,182,69,184,254,238,59,146,162,36,59,82,134,58,41,182,135,56,34,239,47,239,126,119,71,46,214,89,36,88,158,141,83,150,209,79,156,20,75,183,32,156,102,194,171,174,163,136,150,101,206,61,105,191,208,46,202,179,82,140,183,44,22,75,252,246,77,16,154,245,146,178,100,41,240,188,217,72,214,66,80,142,231,118,93,176,138,166,229,23,202,31,88,244,55,190,12,194,133,53,155,173,87,148,179,232,129,147,172,92,228,28,22,110,76,4,185,101,153,167,255,147,202,43,42,181,130,95,82,161,221,134,240,177,34,124,100,139,5,174,57,166,86,162,168,244,182,102,157,26,49,69,250,74,192,20,54,196,153,21,246,162,60,231,177,33,217,189,153,225,9,57,21,107,158,237,68,254,65,241,188,179,206,106,207,208,206,80,245,194,90,70,23,141,157,137,182,27,238,61,145,127,132,189,86,88,219,107,164,245,202,248,136,46,90,87,38,181,194,112,191,15,247,163,38,74,164,98,229,87,154,197,148,67,124,114,206,32,63,138,59,243,132,141,155,137,140,226,251,196,243,117,129,227,60,130,200,102,194,143,56,37,130,254,146,82,181,186,187,119,157,165,16,197,187,217,108,187,221,250,219,75,63,231,201,236,77,16,4,179,114,147,56,158,147,56,40,180,138,190,16,72,242,137,122,10,144,5,85,141,63,126,73,197,181,16,156,253,181,22,212,117,162,148,148,165,227,117,78,50,113,166,138,185,54,175,194,130,13,136,234,53,169,176,70,221,180,179,43,21,151,193,222,225,54,48,215,107,182,232,134,11,99,236,84,14,218,217,211,29,121,21,131,227,183,99,103,162,172,79,28,248,144,230,227,70,239,145,170,217,67,33,132,220,85,182,24,214,217,102,87,88,49,132,108,50,65,59,176,233,178,169,206,236,43,247,0,249,23,151,8,92,8,206,207,217,25,214,130,38,107,130,86,226,212,64,43,89,8,154,250,231,179,44,163,252,215,135,219,27,156,209,237,24,208,71,221,91,117,202,69,10,248,114,27,168,184,12,33,228,139,252,38,143,72,74,31,216,138,222,67,8,178,196,173,213,28,6,165,114,60,214,75,144,142,103,98,209,67,139,129,232,188,166,171,3,8,144,162,0,8,127,88,178,52,118,149,12,2,144,239,33,56,244,180,124,52,123,208,22,58,249,144,58,15,87,88,234,188,180,249,208,190,190,58,108,68,77,46,100,155,11,216,127,145,10,74,56,139,111,160,167,62,179,130,94,30,27,67,144,24,72,126,53,148,99,57,4,140,24,164,156,169,255,147,78,255,16,56,252,75,77,182,81,250,183,204,179,227,50,84,166,109,170,14,144,101,53,14,209,13,242,6,80,105,37,52,52,71,253,76,22,172,40,52,131,242,17,209,106,104,219,183,154,173,77,251,238,153,171,94,213,204,63,79,62,28,182,244,244,249,24,106,44,250,156,66,156,161,206,229,163,29,40,146,179,150,111,73,202,59,136,147,139,190,125,59,147,143,119,237,20,131,51,42,23,149,25,211,66,176,201,90,115,132,246,176,126,102,36,77,205,182,12,242,152,33,220,66,32,169,219,227,203,249,121,175,43,173,245,9,54,32,25,61,233,192,72,121,48,122,210,133,253,40,125,140,201,122,102,57,37,220,87,168,26,84,105,47,110,91,119,122,1,146,106,104,118,176,81,228,44,19,167,130,67,11,63,187,93,181,90,6,142,172,175,58,154,171,252,193,112,250,222,228,235,203,129,186,103,253,70,210,53,197,199,217,54,151,7,220,197,67,195,108,136,178,21,150,125,194,18,119,145,114,36,28,49,30,165,39,87,167,145,134,128,154,143,227,216,171,222,59,68,83,179,119,128,198,65,243,188,158,65,63,116,120,116,34,9,69,61,131,162,234,16,187,145,26,152,43,131,151,138,225,161,242,250,201,145,50,245,127,214,244,14,156,187,149,103,162,53,72,174,111,36,163,158,154,109,37,20,67,11,38,220,243,120,105,17,184,98,153,139,188,206,154,84,176,14,38,230,102,234,117,111,179,80,65,79,42,149,71,74,229,145,210,131,59,112,99,194,76,185,166,181,232,160,55,90,125,243,60,57,230,81,241,127,204,243,29,243,203,175,31,77,135,106,244,86,29,250,151,80,212,233,159,92,171,211,47,215,251,77,162,95,101,215,156,19,233,25,194,231,216,52,137,5,75,214,188,83,170,9,21,117,37,188,151,159,99,183,97,174,199,143,97,87,157,70,67,224,46,143,105,169,250,77,189,207,233,42,223,80,131,142,122,11,90,165,208,27,168,158,138,80,70,167,214,158,250,69,33,252,30,193,124,195,232,246,125,174,42,33,24,7,227,183,243,96,60,159,7,189,156,133,106,196,124,67,175,203,130,70,66,63,45,65,10,174,83,241,239,240,55,94,81,42,234,9,102,222,252,160,193,115,15,222,185,30,64,205,91,217,39,55,195,129,125,21,215,221,183,125,216,218,246,61,102,87,74,208,79,105,150,136,37,188,128,179,126,54,197,244,7,220,204,255,244,43,96,210,19,163,203,165,76,237,149,245,30,209,149,126,89,131,87,125,52,184,247,171,87,51,234,96,64,119,36,16,82,229,156,74,253,26,242,26,42,20,79,209,158,57,111,20,229,202,47,132,106,97,82,157,34,140,254,131,104,202,255,105,52,229,115,162,169,133,225,74,86,87,89,183,45,3,102,161,214,254,1,223,145,42,131,193,18,0,0 }; const uint8_t JS_GRAPH_GZIP[1280] PROGMEM = { 31,139,8,0,3,199,199,101,2,255,205,87,95,111,219,54,16,127,247,167,112,4,52,16,99,89,86,27,175,3,170,240,33,109,135,174,64,18,20,77,48,96,24,246,192,73,180,76,76,150,4,138,182,69,184,254,238,59,146,162,36,59,82,134,58,41,182,135,56,34,239,47,239,126,119,71,46,214,89,36,88,158,141,83,150,209,79,156,20,75,183,32,156,102,194,171,174,163,136,150,101,206,61,105,191,208,46,202,179,82,140,183,44,22,75,252,246,77,16,154,245,146,178,100,41,240,188,217,72,214,66,80,142,231,118,93,176,138,166,229,23,202,31,88,244,55,190,12,194,133,53,155,173,87,148,179,232,129,147,172,92,228,28,22,110,76,4,185,101,153,167,255,147,202,43,42,181,130,95,82,161,221,134,240,177,34,124,100,139,5,174,57,166,86,162,168,244,182,102,157,26,49,69,250,74,192,20,54,196,153,21,246,162,60,231,177,33,217,189,153,225,9,57,21,107,158,237,68,254,65,241,188,179,206,106,207,208,206,80,245,194,90,70,23,141,157,137,182,27,238,61,145,127,132,189,86,88,219,107,164,245,202,248,136,46,90,87,38,181,194,112,191,15,247,163,38,74,164,98,229,87,154,197,148,67,124,114,206,32,63,138,59,243,132,141,155,137,140,226,251,196,243,117,129,227,60,130,200,102,194,143,56,37,130,254,146,82,181,186,187,119,157,165,16,197,187,217,108,187,221,250,219,75,63,231,201,236,77,16,4,179,114,147,56,158,147,56,40,180,138,190,16,72,242,137,122,10,144,5,85,141,63,126,73,197,181,16,156,253,181,22,212,117,162,148,148,165,227,117,78,50,113,166,138,185,54,175,194,130,13,136,234,53,169,176,70,221,180,179,43,21,151,193,222,225,54,48,215,107,182,232,134,11,99,236,84,14,218,217,211,29,121,21,131,227,183,99,103,162,172,79,28,248,144,230,227,70,239,145,170,217,67,33,132,220,85,182,24,214,217,102,87,88,49,132,108,50,65,59,176,233,178,169,206,236,43,247,0,249,23,151,8,92,8,206,207,217,25,214,130,38,107,130,86,226,212,64,43,89,8,154,250,231,179,44,163,252,215,135,219,27,156,209,237,24,208,71,221,91,117,202,69,10,248,114,27,168,184,12,33,228,139,252,38,143,72,74,31,216,138,222,67,8,178,196,173,213,28,6,165,114,60,214,75,144,142,103,98,209,67,139,129,232,188,166,171,3,8,144,162,0,8,127,88,178,52,118,149,12,2,144,239,33,56,244,180,124,52,123,208,22,58,249,144,58,15,87,88,234,188,180,249,208,190,190,58,108,68,77,46,100,155,11,216,127,145,10,74,56,139,111,160,167,62,179,130,94,30,27,67,144,24,72,126,53,148,99,57,4,140,24,164,156,169,255,147,78,255,16,56,252,75,77,182,81,250,183,204,179,227,50,84,166,109,170,14,144,101,53,14,209,13,242,6,80,105,37,52,52,71,253,76,22,172,40,52,131,242,17,209,106,104,219,183,154,173,77,251,238,153,171,94,213,204,63,79,62,28,182,244,244,249,24,106,44,250,156,66,156,161,206,229,163,29,40,146,179,150,111,73,202,59,136,147,139,190,125,59,147,143,119,237,20,131,51,42,23,149,25,211,66,176,201,90,115,132,246,176,126,102,36,77,205,182,12,242,152,33,220,66,32,169,219,227,203,249,121,175,43,173,245,9,54,32,25,61,233,192,72,121,48,122,210,133,253,40,125,140,201,122,102,57,37,220,87,168,26,84,105,47,110,91,119,122,1,146,106,104,118,176,81,228,44,19,167,130,67,11,63,187,93,181,90,6,142,172,175,58,154,171,252,193,112,250,222,228,235,203,129,186,103,253,70,210,53,197,199,217,54,151,7,220,197,67,195,108,136,178,21,150,125,194,18,119,145,114,36,28,49,30,165,39,87,167,145,134,128,154,143,227,216,171,222,59,68,83,179,119,128,198,65,243,188,158,65,63,116,120,116,34,9,69,61,131,162,234,16,187,145,26,152,43,131,151,138,225,161,242,250,201,145,50,245,127,214,244,14,156,187,149,103,162,53,72,174,111,36,163,158,154,109,37,20,67,11,38,220,243,120,105,17,184,98,153,139,188,206,154,84,176,14,38,230,102,234,117,111,179,80,65,79,42,149,71,74,229,145,210,131,59,112,99,194,76,185,166,181,232,160,55,90,125,243,60,57,230,81,241,127,204,243,29,243,203,175,31,77,135,106,244,86,29,250,151,80,212,233,159,92,171,211,47,215,251,77,162,95,101,215,156,19,233,25,194,231,216,52,137,5,75,214,188,83,170,9,21,117,37,188,151,159,99,183,97,174,199,143,97,87,157,70,67,224,46,143,105,169,250,77,189,207,233,42,223,80,131,142,122,11,90,165,208,27,168,158,138,80,70,167,214,158,250,69,33,252,30,193,124,195,232,246,125,174,42,33,24,7,227,183,243,96,60,159,7,189,156,133,106,196,124,67,175,203,130,70,66,63,45,65,10,174,83,241,239,240,55,94,81,42,234,9,102,222,252,160,193,115,15,222,185,30,64,205,91,217,39,55,195,129,125,21,215,221,183,125,216,218,246,61,102,87,74,208,79,105,150,136,37,188,128,179,126,54,197,244,7,220,204,255,244,43,96,210,19,163,203,165,76,237,149,245,30,209,149,126,89,131,87,125,52,184,247,171,87,51,234,96,64,119,36,16,82,229,156,74,253,26,242,26,42,20,79,209,158,57,111,20,229,202,47,132,106,97,82,157,34,140,254,131,104,202,255,105,52,229,115,162,169,133,225,74,86,87,89,183,45,3,102,161,214,254,1,223,145,42,131,193,18,0,0 };

View File

@ -1,27 +1,5 @@
// Old PNG File
//const char HTML_INDEX[] PROGMEM = R"=====(
//<!DOCTYPE html><html> <head><meta charset=utf-8><title>Control</title><meta name=viewport content="width=device-width, initial-scale=1"><link rel="shortcut icon" href=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAPFBMVEUAAACA1VWR21qQ2liR3FqR3FqS3VuR3VqR3VuR3VqO21mS21uS3FqS3FqS21uJ2GKQ21qR3FuR3FoAAAB/3Gu7AAAAEnRSTlMABoA3kPBwz8i5Kzioxg4NVcU3uEJHAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAAN1wAADdcBQiibeAAAAAd0SU1FB+EFEhcEM+HpYwQAAABYSURBVBjThY/JDsAgCESt4lpX/v9jLQZJ6qF9t3khAyj1xXUKbQ4BVowDwqOYgExkkW4iY6lPaF06RqM8YItOuRbMaz6xjbsusDAW/drplBg47jP696cXE8bPA1eUDeK2AAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTA1LTE4VDIzOjA0OjUxKzAyOjAwxE59ewAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0wNS0xOFQyMzowNDo1MSswMjowMLUTxccAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAAAElFTkSuQmCC><link rel=stylesheet href=/css/normalize.css><link rel=stylesheet href=/css/style.css><script src=/js/zepto.min.js></script><script src=/js/slider.js></script><script src=/js/graph.js></script><script src=/js/controls.js></script><script src=/js/tabbedcontent.js></script></head> <body onload=javascript:start();> <div> <h4> <div id=mainHeader>Control</div> <span id=conStatus class=label>Offline</span> </h4> </div> <hr> <div class=container> <div id=row class="row u-full-width"></div> <ul id=tabsnav class="navigation navigation-tabs u-full-width"></ul> <div id=tabscontent class="tabscontent u-full-width"></div> </div> </body> </html>
//)=====";
// SVG Water Drop Icon
//const char HTML_INDEX[] PROGMEM = R"=====(
//<!DOCTYPE html><html> <head><meta charset=utf-8><title>Control</title><meta name=viewport content="width=device-width, initial-scale=1"><link rel="shortcut icon" href="data:image/svg+xml,%3Csvg data-name='Layer 1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath d='M504.08 256C504.08 119 393 7.92 256 7.92S7.92 119 7.92 256 119 504.08 256 504.08 504.08 393 504.08 256z' style='fill:%23009040'/%3E%3Cpath d='M289.15 157.48A306.25 306.25 0 0 1 256 84.69a306.22 306.22 0 0 1-33.14 72.8c-28.24 56.86-73.39 86.85-73.39 158.38 0 61.55 45 111.44 106.53 111.44 61.55 0 106.53-49.89 106.53-111.44.01-71.54-45.14-101.52-73.38-158.39z' style='fill:%2398c8f0'/%3E%3Cpath d='M289.15 157.48A306.25 306.25 0 0 1 256 84.69s-61.57 342.62 0 342.62c61.55 0 106.53-49.89 106.53-111.44.01-71.54-45.14-101.52-73.38-158.39z' style='fill:%23b0e0ff'/%3E%3Cpath d='M262.08 409.12c48.17 0 83.38-39 83.38-87.22.01-55.99-131.55 87.22-83.38 87.22z' style='fill:%23ffffff'/%3E%3C/svg%3E"><link rel=stylesheet href=/css/normalize.css><link rel=stylesheet href=/css/style.css><script src=/js/zepto.min.js></script><script src=/js/slider.js></script><script src=/js/graph.js></script><script src=/js/controls.js></script><script src=/js/tabbedcontent.js></script></head> <body onload=javascript:start();> <div> <h4> <div id=mainHeader>Control</div> <span id=conStatus class=label>Offline</span> </h4> </div> <hr> <div class=container> <div id=row class="row u-full-width"></div> <ul id=tabsnav class="navigation navigation-tabs u-full-width"></ul> <div id=tabscontent class="tabscontent u-full-width"></div> </div> </body> </html>
//)=====";
// Apple icon only
//const char HTML_INDEX[] PROGMEM = R"=====(
//<!DOCTYPE html><html> <head><meta charset=utf-8><title>Control</title><meta name=viewport content="width=device-width, initial-scale=1"><link rel="apple-touch-icon" href=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAAC0AQMAAAAHA5RxAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsIAAA7CARUoSoAAAAAGUExURYi/Sv///zEELvQAAAFGSURBVFjD7dUxbsUgEIRhLAqXPoKPskczRyNKkTJX4AiUFAhHr4g0kfZXslJKU36VwbOz6XZPT48//jqf4B/g7+Bv4JX8AjfwE/wA38Ez+AaeyC9wAz/BD/AdPINv4In8AjfwE/z44S19nx08g2/gifwCN/AT/ADfwTP4Bp7Ir3/y4Pfk4H2P4Hta8H8F85D/lDfK5+95tuC8BOdR59f8eV/m98M0v0+G+f3Tze+rZn6/VfP7sJjfn8ncvl3q0s9TXfp8qEv/d3XZF01d9ksV131UxHV/JXHZd0td9uNUl306wDt4A6/gBTz5vnTgxCf4AO/gDbyCF/Dk+wKf4AO8g7d/8h78nhm8L70PvmcN/q8ezAPlh/JG+cQ81+C89OA80vwu8Ju8gFfwBt7BB/gEX+A3eQGv4A28gw/wCb7Ab/L78cdf/gVoGRZWiw+tcgAAAABJRU5ErkJggg==><link rel=stylesheet href=/css/normalize.css><link rel=stylesheet href=/css/style.css><script src=/js/zepto.min.js></script><script src=/js/slider.js></script><script src=/js/graph.js></script><script src=/js/controls.js></script><script src=/js/tabbedcontent.js></script></head> <body onload=javascript:start();> <div> <h4> <div id=mainHeader>Control</div> <span id=conStatus class=label>Offline</span> </h4> </div> <hr> <div class=container> <div id=row class="row u-full-width"></div> <ul id=tabsnav class="navigation navigation-tabs u-full-width"></ul> <div id=tabscontent class="tabscontent u-full-width"></div> </div> </body> </html>
//)=====";
// SVG Logo Only
//const char HTML_INDEX[] PROGMEM = R"=====(
//<!DOCTYPE html><html> <head><meta charset=utf-8><title>Control</title><meta name=viewport content="width=device-width, initial-scale=1"><link rel="shortcut icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 180 180'%3E%3Cpath d='M74 46v28h55v27l40-40l-40-40v27zM104 100v28h-55v27l-40-40l40-40v27z' fill='%2388BF4A' /%3E%3C/svg%3E"><link rel=stylesheet href=/css/normalize.css><link rel=stylesheet href=/css/style.css><script src=/js/zepto.min.js></script><script src=/js/slider.js></script><script src=/js/graph.js></script><script src=/js/controls.js></script><script src=/js/tabbedcontent.js></script></head> <body onload=javascript:start();> <div> <h4> <div id=mainHeader>Control</div> <span id=conStatus class=label>Offline</span> </h4> </div> <hr> <div class=container> <div id=row class="row u-full-width"></div> <ul id=tabsnav class="navigation navigation-tabs u-full-width"></ul> <div id=tabscontent class="tabscontent u-full-width"></div> </div> </body> </html>
//)=====";
// SVG Logo & Apple icon
const char HTML_INDEX[] PROGMEM = R"=====( const char HTML_INDEX[] PROGMEM = R"=====(
<!DOCTYPE html><html><head><meta charset=utf-8><title>Control</title><meta name=viewport content="width=device-width, initial-scale=1"><link rel="shortcut icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 180 180'%3E%3Cpath d='M74 46v28h55v27l40-40l-40-40v27zM104 100v28h-55v27l-40-40l40-40v27z' fill='%2388BF4A' /%3E%3C/svg%3E"><link rel="apple-touch-icon" href=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAAC0AQMAAAAHA5RxAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsIAAA7CARUoSoAAAAAGUExURYi/Sv///zEELvQAAAFGSURBVFjD7dUxbsUgEIRhLAqXPoKPskczRyNKkTJX4AiUFAhHr4g0kfZXslJKU36VwbOz6XZPT48//jqf4B/g7+Bv4JX8AjfwE/wA38Ez+AaeyC9wAz/BD/AdPINv4In8AjfwE/z44S19nx08g2/gifwCN/AT/ADfwTP4Bp7Ir3/y4Pfk4H2P4Hta8H8F85D/lDfK5+95tuC8BOdR59f8eV/m98M0v0+G+f3Tze+rZn6/VfP7sJjfn8ncvl3q0s9TXfp8qEv/d3XZF01d9ksV131UxHV/JXHZd0td9uNUl306wDt4A6/gBTz5vnTgxCf4AO/gDbyCF/Dk+wKf4AO8g7d/8h78nhm8L70PvmcN/q8ezAPlh/JG+cQ81+C89OA80vwu8Ju8gFfwBt7BB/gEX+A3eQGv4A28gw/wCb7Ab/L78cdf/gVoGRZWiw+tcgAAAABJRU5ErkJggg==><link rel=stylesheet href=/css/normalize.css><link rel=stylesheet href=/css/style.css><script src=/js/zepto.min.js></script><script src=/js/slider.js></script><script src=/js/graph.js></script><script src=/js/controls.js></script><script src=/js/tabbedcontent.js></script></head><body onload=javascript:start();><div><h4><div id=mainHeader>Control</div> <span id=conStatus class=label>Offline</span></h4></div><hr><div class=container><div id=row class="row u-full-width"></div><ul id=tabsnav class="navigation navigation-tabs u-full-width"></ul><div id=tabscontent class="tabscontent u-full-width"></div></div></body></html> <!DOCTYPE html><html> <head><meta charset=utf-8><title>Control</title><meta name=viewport content="width=device-width, initial-scale=1"><link rel="shortcut icon" href=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAPFBMVEUAAACA1VWR21qQ2liR3FqR3FqS3VuR3VqR3VuR3VqO21mS21uS3FqS3FqS21uJ2GKQ21qR3FuR3FoAAAB/3Gu7AAAAEnRSTlMABoA3kPBwz8i5Kzioxg4NVcU3uEJHAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAAN1wAADdcBQiibeAAAAAd0SU1FB+EFEhcEM+HpYwQAAABYSURBVBjThY/JDsAgCESt4lpX/v9jLQZJ6qF9t3khAyj1xXUKbQ4BVowDwqOYgExkkW4iY6lPaF06RqM8YItOuRbMaz6xjbsusDAW/drplBg47jP696cXE8bPA1eUDeK2AAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTA1LTE4VDIzOjA0OjUxKzAyOjAwxE59ewAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0wNS0xOFQyMzowNDo1MSswMjowMLUTxccAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAAAElFTkSuQmCC><link rel=stylesheet href=/css/normalize.css><link rel=stylesheet href=/css/style.css><script src=/js/zepto.min.js></script><script src=/js/slider.js></script><script src=/js/graph.js></script><script src=/js/controls.js></script><script src=/js/tabbedcontent.js></script></head> <body onload=javascript:start();> <div> <h4> <div id=mainHeader>Control</div> <span id=conStatus class=label>Offline</span> </h4> </div> <hr> <div class=container> <div id=row class="row u-full-width"></div> <ul id=tabsnav class="navigation navigation-tabs u-full-width"></ul> <div id=tabscontent class="tabscontent u-full-width"></div> </div> </body> </html>
)====="; )=====";
const uint8_t HTML_INDEX_GZIP[916] PROGMEM = { 31,139,8,0,3,199,199,101,2,255,133,148,235,115,162,58,20,192,255,21,174,159,238,157,221,22,95,181,237,174,56,19,20,108,85,68,64,240,241,45,64,42,193,240,40,9,162,254,245,155,128,157,238,157,189,211,235,12,201,201,57,191,243,200,17,206,240,175,137,57,94,239,86,154,20,177,132,140,134,245,42,13,35,4,195,209,48,65,12,74,65,4,11,138,152,82,178,183,187,167,209,144,97,70,208,104,156,165,172,200,200,80,110,142,13,153,194,4,41,39,140,170,60,43,152,20,112,4,165,76,105,85,56,100,145,18,162,19,14,208,93,125,248,46,225,20,51,12,201,29,13,32,65,74,167,53,26,18,156,30,165,2,17,165,69,35,238,30,148,76,194,60,68,75,138,10,244,166,132,144,193,31,56,129,7,36,231,233,225,167,15,41,26,244,191,99,79,53,237,170,61,159,30,50,192,127,75,199,141,52,247,192,37,85,28,129,53,6,134,216,179,133,245,188,22,130,58,13,213,181,171,1,176,152,174,198,242,57,82,45,174,28,171,177,163,207,150,220,58,152,113,223,195,43,87,174,68,188,49,24,240,53,20,158,102,46,162,14,18,190,232,189,113,80,18,237,73,196,91,233,170,225,105,110,205,118,188,141,221,237,188,91,93,130,237,158,254,46,30,167,231,149,118,207,227,114,179,155,221,78,226,116,59,165,83,219,248,195,229,89,119,58,183,184,31,231,57,163,139,188,170,220,155,150,143,34,190,150,218,206,154,24,64,205,64,239,184,82,171,235,19,126,152,95,113,118,62,244,151,94,224,246,74,109,246,82,223,116,51,91,216,109,11,96,224,134,78,173,32,149,163,147,171,232,73,167,2,96,18,6,170,133,177,143,106,91,216,118,220,142,174,126,211,116,45,10,52,227,219,75,190,171,68,35,212,157,227,218,170,167,198,235,104,39,207,38,20,28,198,154,195,250,36,223,202,167,231,120,97,237,103,131,119,253,153,245,142,17,184,196,157,243,214,157,251,86,95,245,178,106,82,189,155,187,131,118,62,30,55,125,188,27,144,21,212,219,3,251,221,120,218,189,50,179,180,125,3,94,7,231,216,167,37,157,128,141,28,22,57,81,15,253,199,120,53,120,30,4,91,237,201,95,129,14,114,39,104,222,21,213,205,182,182,190,121,177,143,187,173,77,204,100,121,217,111,244,246,222,2,23,99,162,245,22,107,208,89,172,181,190,55,121,189,154,49,104,155,177,123,158,95,193,133,203,213,89,123,120,70,149,248,43,188,182,237,69,237,253,148,251,173,115,230,119,237,124,159,30,129,17,131,243,242,210,174,150,78,251,108,234,214,197,184,102,213,114,146,117,12,135,86,70,156,85,198,194,93,159,131,64,148,176,15,53,111,23,234,203,211,62,181,123,187,237,140,128,151,176,23,94,30,114,63,97,215,93,87,175,246,206,195,41,72,144,255,24,87,176,110,169,70,244,245,209,41,173,100,60,254,237,77,166,236,66,16,141,16,98,205,75,44,7,148,202,105,86,36,144,224,43,186,231,167,255,131,107,101,3,210,160,192,57,147,104,17,40,114,76,229,43,202,89,118,159,224,244,62,230,70,185,177,254,65,81,130,67,84,124,137,28,10,152,71,95,18,65,243,173,211,47,33,6,125,31,133,183,111,254,223,164,92,15,19,105,232,103,225,69,202,82,146,193,80,137,225,9,54,246,31,148,193,130,253,253,207,79,78,132,248,36,102,79,191,17,37,28,42,9,196,233,11,119,71,197,231,200,105,40,154,195,84,16,60,165,195,32,43,169,20,16,72,169,66,160,143,200,200,124,123,227,157,69,188,8,142,113,90,174,131,222,92,163,226,150,160,241,16,69,243,52,168,248,76,91,100,213,205,216,18,98,121,247,86,18,210,204,46,62,170,110,97,74,34,80,126,113,154,194,143,88,45,46,226,3,100,56,75,165,79,241,78,64,127,68,41,201,103,66,1,220,154,247,17,233,119,213,127,23,240,177,137,198,214,119,172,103,247,47,103,53,186,226,210,5,0,0 };
const uint8_t HTML_INDEX_GZIP[916] PROGMEM = { 31,139,8,0,210,8,51,99,2,255,133,148,235,115,162,58,20,192,255,21,174,159,238,157,221,22,95,181,237,174,56,19,20,108,85,68,64,240,241,45,64,42,193,240,40,9,162,254,245,155,128,157,238,157,189,211,235,12,201,201,57,191,243,200,17,206,240,175,137,57,94,239,86,154,20,177,132,140,134,245,42,13,35,4,195,209,48,65,12,74,65,4,11,138,152,82,178,183,187,167,209,144,97,70,208,104,156,165,172,200,200,80,110,142,13,153,194,4,41,39,140,170,60,43,152,20,112,4,165,76,105,85,56,100,145,18,162,19,14,208,93,125,248,46,225,20,51,12,201,29,13,32,65,74,167,53,26,18,156,30,165,2,17,165,69,35,238,30,148,76,194,60,68,75,138,10,244,166,132,144,193,31,56,129,7,36,231,233,225,167,15,41,26,244,191,99,79,53,237,170,61,159,30,50,192,127,75,199,141,52,247,192,37,85,28,129,53,6,134,216,179,133,245,188,22,130,58,13,213,181,171,1,176,152,174,198,242,57,82,45,174,28,171,177,163,207,150,220,58,152,113,223,195,43,87,174,68,188,49,24,240,53,20,158,102,46,162,14,18,190,232,189,113,80,18,237,73,196,91,233,170,225,105,110,205,118,188,141,221,237,188,91,93,130,237,158,254,46,30,167,231,149,118,207,227,114,179,155,221,78,226,116,59,165,83,219,248,195,229,89,119,58,183,184,31,231,57,163,139,188,170,220,155,150,143,34,190,150,218,206,154,24,64,205,64,239,184,82,171,235,19,126,152,95,113,118,62,244,151,94,224,246,74,109,246,82,223,116,51,91,216,109,11,96,224,134,78,173,32,149,163,147,171,232,73,167,2,96,18,6,170,133,177,143,106,91,216,118,220,142,174,126,211,116,45,10,52,227,219,75,190,171,68,35,212,157,227,218,170,167,198,235,104,39,207,38,20,28,198,154,195,250,36,223,202,167,231,120,97,237,103,131,119,253,153,245,142,17,184,196,157,243,214,157,251,86,95,245,178,106,82,189,155,187,131,118,62,30,55,125,188,27,144,21,212,219,3,251,221,120,218,189,50,179,180,125,3,94,7,231,216,167,37,157,128,141,28,22,57,81,15,253,199,120,53,120,30,4,91,237,201,95,129,14,114,39,104,222,21,213,205,182,182,190,121,177,143,187,173,77,204,100,121,217,111,244,246,222,2,23,99,162,245,22,107,208,89,172,181,190,55,121,189,154,49,104,155,177,123,158,95,193,133,203,213,89,123,120,70,149,248,43,188,182,237,69,237,253,148,251,173,115,230,119,237,124,159,30,129,17,131,243,242,210,174,150,78,251,108,234,214,197,184,102,213,114,146,117,12,135,86,70,156,85,198,194,93,159,131,64,148,176,15,53,111,23,234,203,211,62,181,123,187,237,140,128,151,176,23,94,30,114,63,97,215,93,87,175,246,206,195,41,72,144,255,24,87,176,110,169,70,244,245,209,41,173,100,60,254,237,77,166,236,66,16,141,16,98,205,75,44,7,148,202,105,86,36,144,224,43,186,231,167,255,131,107,101,3,210,160,192,57,147,104,17,40,114,76,229,43,202,89,118,159,224,244,62,230,70,185,177,254,65,81,130,67,84,124,137,28,10,152,71,95,18,65,243,173,211,47,33,6,125,31,133,183,111,254,223,164,92,15,19,105,232,103,225,69,202,82,146,193,80,137,225,9,54,246,31,148,193,130,253,253,207,79,78,132,248,36,102,79,191,17,37,28,42,9,196,233,11,119,71,197,231,200,105,40,154,195,84,16,60,165,195,32,43,169,20,16,72,169,66,160,143,200,200,124,123,227,157,69,188,8,142,113,90,174,131,222,92,163,226,150,160,241,16,69,243,52,168,248,76,91,100,213,205,216,18,98,121,247,86,18,210,204,46,62,170,110,97,74,34,80,126,113,154,194,143,88,45,46,226,3,100,56,75,165,79,241,78,64,127,68,41,201,103,66,1,220,154,247,17,233,119,213,127,23,240,177,137,198,214,119,172,103,247,47,103,53,186,226,210,5,0,0 };

View File

@ -1,5 +1,5 @@
const char CSS_NORMALIZE[] PROGMEM = R"=====( const char CSS_NORMALIZE[] PROGMEM = R"=====(
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:visible}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0} html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:visible}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}
)====="; )=====";
const uint8_t CSS_NORMALIZE_GZIP[859] PROGMEM = { 31,139,8,0,53,140,123,99,2,255,149,84,237,110,163,58,16,125,149,168,171,74,187,146,137,104,247,163,87,70,247,73,162,252,24,236,1,124,227,47,217,38,77,22,241,238,59,6,66,147,54,93,233,254,2,6,123,230,204,57,103,166,75,70,15,141,179,169,104,192,40,125,230,17,108,44,34,6,213,84,133,137,69,194,83,42,162,250,141,5,200,255,250,152,248,83,89,62,86,197,43,214,7,149,238,255,29,107,39,207,131,129,208,42,203,203,17,66,82,66,35,131,168,36,50,137,9,148,142,172,81,173,0,159,148,179,249,181,15,200,26,231,18,6,214,33,200,252,104,131,235,61,51,160,44,51,104,123,102,225,200,34,138,233,70,236,13,165,63,15,82,69,175,225,204,107,237,196,97,132,94,42,199,4,216,35,68,230,131,107,3,198,200,142,84,213,173,39,149,213,202,98,49,93,168,142,152,161,129,46,64,171,214,242,26,34,230,191,115,34,110,93,250,186,19,196,76,112,58,238,191,173,41,172,179,88,117,168,218,46,81,119,187,78,73,137,118,207,18,26,250,157,240,230,220,8,67,13,226,144,123,177,178,16,78,187,192,83,32,134,61,4,180,105,4,14,212,209,145,200,225,157,35,56,131,235,83,134,144,105,171,235,176,75,42,105,220,15,181,11,196,73,81,187,148,156,225,79,254,180,145,244,138,114,172,89,36,120,182,157,21,124,157,65,213,78,203,81,54,118,14,198,116,214,200,85,162,30,197,216,61,45,65,146,140,63,163,169,22,149,182,191,94,208,108,202,145,62,15,87,136,249,151,166,41,171,25,246,151,178,44,199,104,64,235,171,20,255,144,218,177,39,20,189,191,138,190,252,124,172,38,154,47,44,85,222,69,149,149,227,1,137,35,106,248,83,238,115,166,228,60,47,182,63,209,228,220,195,210,117,177,125,206,17,101,218,133,14,226,40,30,219,73,38,30,200,59,223,134,204,96,163,221,43,63,170,168,106,141,227,236,172,139,21,159,168,197,31,165,63,141,93,24,10,227,126,19,159,167,12,88,217,150,103,157,73,144,28,170,62,9,175,146,123,74,185,150,130,62,185,81,56,114,246,161,150,228,58,100,17,140,191,153,40,227,172,35,193,5,178,245,173,122,35,139,80,141,117,79,45,90,166,172,239,19,115,62,205,222,39,70,200,239,44,207,24,185,5,134,89,7,101,59,26,206,52,101,88,63,214,97,155,51,125,100,98,169,48,167,28,166,177,157,124,216,184,96,102,167,46,39,58,218,7,155,9,200,46,157,61,254,251,48,199,31,246,236,58,72,147,133,233,93,140,180,50,138,130,195,101,57,128,247,8,84,68,32,159,147,84,162,15,145,90,240,78,17,173,97,41,185,163,129,1,194,40,247,215,197,215,224,176,92,146,216,64,175,211,114,137,243,73,193,198,137,62,22,202,90,218,24,211,189,143,241,213,45,149,7,41,179,168,229,56,29,29,174,45,106,137,7,208,227,117,63,162,67,113,32,225,223,183,14,180,28,30,242,76,174,46,89,199,243,244,190,198,114,199,246,166,198,240,176,39,116,11,55,19,180,34,122,101,139,107,241,63,61,79,123,225,246,252,176,0,159,252,119,35,3,113,46,186,251,50,100,221,27,133,90,86,127,243,255,229,226,255,26,143,187,24,222,240,207,145,66,100,24,250,94,203,159,94,145,40,92,128,188,60,238,117,52,89,119,106,137,12,121,145,58,47,200,232,180,146,155,168,52,77,194,58,30,155,103,255,38,209,246,59,237,147,205,246,215,243,244,120,201,203,69,99,139,86,222,115,204,58,132,183,131,127,153,213,143,251,55,101,247,94,22,55,77,174,6,31,145,95,94,170,229,71,94,6,75,1,201,82,55,188,21,252,3,26,45,63,124,152,7,0,0 }; const uint8_t CSS_NORMALIZE_GZIP[859] PROGMEM = { 31,139,8,0,3,199,199,101,2,255,149,84,237,110,163,58,16,125,149,168,171,74,187,146,137,104,247,163,87,70,247,73,162,252,24,236,1,124,227,47,217,38,77,22,241,238,59,6,66,147,54,93,233,254,2,6,123,230,204,57,103,166,75,70,15,141,179,169,104,192,40,125,230,17,108,44,34,6,213,84,133,137,69,194,83,42,162,250,141,5,200,255,250,152,248,83,89,62,86,197,43,214,7,149,238,255,29,107,39,207,131,129,208,42,203,203,17,66,82,66,35,131,168,36,50,137,9,148,142,172,81,173,0,159,148,179,249,181,15,200,26,231,18,6,214,33,200,252,104,131,235,61,51,160,44,51,104,123,102,225,200,34,138,233,70,236,13,165,63,15,82,69,175,225,204,107,237,196,97,132,94,42,199,4,216,35,68,230,131,107,3,198,200,142,84,213,173,39,149,213,202,98,49,93,168,142,152,161,129,46,64,171,214,242,26,34,230,191,115,34,110,93,250,186,19,196,76,112,58,238,191,173,41,172,179,88,117,168,218,46,81,119,187,78,73,137,118,207,18,26,250,157,240,230,220,8,67,13,226,144,123,177,178,16,78,187,192,83,32,134,61,4,180,105,4,14,212,209,145,200,225,157,35,56,131,235,83,134,144,105,171,235,176,75,42,105,220,15,181,11,196,73,81,187,148,156,225,79,254,180,145,244,138,114,172,89,36,120,182,157,21,124,157,65,213,78,203,81,54,118,14,198,116,214,200,85,162,30,197,216,61,45,65,146,140,63,163,169,22,149,182,191,94,208,108,202,145,62,15,87,136,249,151,166,41,171,25,246,151,178,44,199,104,64,235,171,20,255,144,218,177,39,20,189,191,138,190,252,124,172,38,154,47,44,85,222,69,149,149,227,1,137,35,106,248,83,238,115,166,228,60,47,182,63,209,228,220,195,210,117,177,125,206,17,101,218,133,14,226,40,30,219,73,38,30,200,59,223,134,204,96,163,221,43,63,170,168,106,141,227,236,172,139,21,159,168,197,31,165,63,141,93,24,10,227,126,19,159,167,12,88,217,150,103,157,73,144,28,170,62,9,175,146,123,74,185,150,130,62,185,81,56,114,246,161,150,228,58,100,17,140,191,153,40,227,172,35,193,5,178,245,173,122,35,139,80,141,117,79,45,90,166,172,239,19,115,62,205,222,39,70,200,239,44,207,24,185,5,134,89,7,101,59,26,206,52,101,88,63,214,97,155,51,125,100,98,169,48,167,28,166,177,157,124,216,184,96,102,167,46,39,58,218,7,155,9,200,46,157,61,254,251,48,199,31,246,236,58,72,147,133,233,93,140,180,50,138,130,195,101,57,128,247,8,84,68,32,159,147,84,162,15,145,90,240,78,17,173,97,41,185,163,129,1,194,40,247,215,197,215,224,176,92,146,216,64,175,211,114,137,243,73,193,198,137,62,22,202,90,218,24,211,189,143,241,213,45,149,7,41,179,168,229,56,29,29,174,45,106,137,7,208,227,117,63,162,67,113,32,225,223,183,14,180,28,30,242,76,174,46,89,199,243,244,190,198,114,199,246,166,198,240,176,39,116,11,55,19,180,34,122,101,139,107,241,63,61,79,123,225,246,252,176,0,159,252,119,35,3,113,46,186,251,50,100,221,27,133,90,86,127,243,255,229,226,255,26,143,187,24,222,240,207,145,66,100,24,250,94,203,159,94,145,40,92,128,188,60,238,117,52,89,119,106,137,12,121,145,58,47,200,232,180,146,155,168,52,77,194,58,30,155,103,255,38,209,246,59,237,147,205,246,215,243,244,120,201,203,69,99,139,86,222,115,204,58,132,183,131,127,153,213,143,251,55,101,247,94,22,55,77,174,6,31,145,95,94,170,229,71,94,6,75,1,201,82,55,188,21,252,3,26,45,63,124,152,7,0,0 };

View File

@ -12,4 +12,4 @@ function sliderDiscrete_tmplt(){var tmplt='<div class="slider">'+
function slider_move(parents,newW,sliderW,send){var slider_new_val=parseInt(Math.round((newW/sliderW)*100));var slider_fill=parents.find(".slider-fill");var slider_handle=parents.find(".slider-handle");var range=parents.find('input[type="range"]');range.next().html(newW);slider_fill.css("width",slider_new_val+"%");slider_handle.css({left:slider_new_val+"%",transition:"none","-webkit-transition":"none","-moz-transition":"none",});range.val(slider_new_val);if(parents.find(".slider-handle span").text()!=slider_new_val){parents.find(".slider-handle span").text(slider_new_val);var number=parents.attr("id").substring(2);if(send)websock.send("slvalue:"+slider_new_val+":"+number);}} function slider_move(parents,newW,sliderW,send){var slider_new_val=parseInt(Math.round((newW/sliderW)*100));var slider_fill=parents.find(".slider-fill");var slider_handle=parents.find(".slider-handle");var range=parents.find('input[type="range"]');range.next().html(newW);slider_fill.css("width",slider_new_val+"%");slider_handle.css({left:slider_new_val+"%",transition:"none","-webkit-transition":"none","-moz-transition":"none",});range.val(slider_new_val);if(parents.find(".slider-handle span").text()!=slider_new_val){parents.find(".slider-handle span").text(slider_new_val);var number=parents.attr("id").substring(2);if(send)websock.send("slvalue:"+slider_new_val+":"+number);}}
)====="; )=====";
const uint8_t JS_SLIDER_GZIP[881] PROGMEM = { 31,139,8,0,210,8,51,99,2,255,237,86,77,143,218,48,16,189,243,43,88,107,187,196,93,240,210,61,18,204,165,85,165,30,122,106,165,86,90,173,144,73,156,141,69,112,162,216,129,182,44,255,189,227,143,132,36,192,106,219,83,15,61,37,246,60,143,223,204,60,123,156,84,50,210,34,151,195,114,189,137,151,37,147,79,252,75,38,98,94,6,138,103,60,210,121,137,247,91,86,14,97,148,140,149,181,44,119,34,214,105,61,200,147,68,113,61,142,170,82,106,63,247,65,168,168,228,154,143,173,59,63,25,26,15,244,250,232,54,108,123,163,198,74,236,111,208,88,156,107,103,114,255,1,38,25,79,116,216,221,199,34,122,115,132,179,40,13,18,31,93,32,198,91,188,183,28,129,130,78,133,194,161,29,17,86,20,92,198,65,119,241,82,111,138,12,54,195,161,141,128,58,104,34,0,56,18,178,168,244,131,254,89,112,138,172,21,61,142,106,198,109,32,34,110,14,53,225,36,34,203,168,251,239,66,38,198,114,196,165,76,198,25,63,143,116,182,35,54,99,43,126,193,169,53,1,210,84,207,18,93,110,89,70,11,86,42,254,73,234,192,78,17,152,50,97,182,24,146,72,169,0,217,74,160,113,179,238,22,189,233,19,116,64,83,142,75,56,203,192,179,82,5,147,8,19,205,127,248,173,13,26,135,7,28,186,242,202,0,109,242,74,241,56,223,201,161,206,171,40,85,154,149,224,186,31,250,184,169,41,199,123,145,4,156,172,42,173,115,73,41,189,199,123,168,30,148,96,152,176,76,241,240,48,48,177,67,196,92,106,85,215,157,248,49,36,202,72,126,210,84,201,170,188,45,73,15,108,84,217,2,120,101,214,136,174,56,13,46,74,121,180,118,199,169,65,189,160,31,34,128,207,44,22,138,173,50,30,3,25,8,172,237,130,82,93,86,252,36,188,58,36,22,199,239,51,102,202,33,212,132,65,118,182,220,7,180,201,183,252,99,69,219,57,115,57,121,226,223,41,39,246,251,252,12,181,76,205,62,241,87,147,120,174,30,166,143,206,212,142,89,242,93,147,24,48,77,58,169,48,132,251,184,57,109,167,243,230,230,234,20,129,166,8,227,189,159,54,92,3,159,172,113,31,218,189,123,108,54,194,195,193,242,171,138,94,132,117,90,128,90,224,84,83,194,121,239,148,1,145,86,166,72,201,205,222,103,82,232,54,168,125,208,189,149,168,193,206,92,98,199,86,169,237,9,139,168,138,153,33,229,172,112,189,184,209,33,188,14,226,60,170,54,64,3,27,197,31,185,189,254,24,252,215,127,47,188,19,117,122,85,255,11,250,28,252,129,60,207,74,237,111,132,116,24,212,155,13,207,119,53,123,3,216,127,58,154,199,98,59,140,140,240,41,242,74,88,140,110,7,103,230,93,147,90,204,239,192,114,9,226,175,232,197,25,147,235,70,139,185,233,3,139,233,252,206,126,157,179,198,37,114,127,40,244,101,182,28,79,227,233,22,2,42,240,205,39,31,190,144,35,255,94,57,214,167,211,246,62,51,157,146,50,175,64,138,129,89,122,231,151,226,183,239,166,83,220,17,185,237,215,189,91,163,219,176,91,96,223,180,207,195,155,174,221,244,226,215,28,10,247,248,32,210,180,76,76,82,189,201,44,227,203,221,186,27,243,165,150,189,55,135,116,118,138,5,209,50,169,132,73,244,12,201,92,66,171,69,147,29,95,173,133,158,28,77,232,104,219,228,191,206,25,14,53,113,243,182,232,110,99,207,245,75,41,26,182,159,9,248,138,246,150,239,95,189,182,191,175,73,188,172,54,43,120,163,213,62,152,214,37,220,246,112,219,16,85,173,148,46,133,124,10,238,45,69,43,35,8,93,229,209,154,152,1,188,95,50,240,83,241,25,186,237,103,14,166,156,103,211,144,126,3,128,124,107,46,79,11,0,0 }; const uint8_t JS_SLIDER_GZIP[881] PROGMEM = { 31,139,8,0,3,199,199,101,2,255,237,86,77,143,218,48,16,189,243,43,88,107,187,196,93,240,210,61,18,204,165,85,165,30,122,106,165,86,90,173,144,73,156,141,69,112,162,216,129,182,44,255,189,227,143,132,36,192,106,219,83,15,61,37,246,60,143,223,204,60,123,156,84,50,210,34,151,195,114,189,137,151,37,147,79,252,75,38,98,94,6,138,103,60,210,121,137,247,91,86,14,97,148,140,149,181,44,119,34,214,105,61,200,147,68,113,61,142,170,82,106,63,247,65,168,168,228,154,143,173,59,63,25,26,15,244,250,232,54,108,123,163,198,74,236,111,208,88,156,107,103,114,255,1,38,25,79,116,216,221,199,34,122,115,132,179,40,13,18,31,93,32,198,91,188,183,28,129,130,78,133,194,161,29,17,86,20,92,198,65,119,241,82,111,138,12,54,195,161,141,128,58,104,34,0,56,18,178,168,244,131,254,89,112,138,172,21,61,142,106,198,109,32,34,110,14,53,225,36,34,203,168,251,239,66,38,198,114,196,165,76,198,25,63,143,116,182,35,54,99,43,126,193,169,53,1,210,84,207,18,93,110,89,70,11,86,42,254,73,234,192,78,17,152,50,97,182,24,146,72,169,0,217,74,160,113,179,238,22,189,233,19,116,64,83,142,75,56,203,192,179,82,5,147,8,19,205,127,248,173,13,26,135,7,28,186,242,202,0,109,242,74,241,56,223,201,161,206,171,40,85,154,149,224,186,31,250,184,169,41,199,123,145,4,156,172,42,173,115,73,41,189,199,123,168,30,148,96,152,176,76,241,240,48,48,177,67,196,92,106,85,215,157,248,49,36,202,72,126,210,84,201,170,188,45,73,15,108,84,217,2,120,101,214,136,174,56,13,46,74,121,180,118,199,169,65,189,160,31,34,128,207,44,22,138,173,50,30,3,25,8,172,237,130,82,93,86,252,36,188,58,36,22,199,239,51,102,202,33,212,132,65,118,182,220,7,180,201,183,252,99,69,219,57,115,57,121,226,223,41,39,246,251,252,12,181,76,205,62,241,87,147,120,174,30,166,143,206,212,142,89,242,93,147,24,48,77,58,169,48,132,251,184,57,109,167,243,230,230,234,20,129,166,8,227,189,159,54,92,3,159,172,113,31,218,189,123,108,54,194,195,193,242,171,138,94,132,117,90,128,90,224,84,83,194,121,239,148,1,145,86,166,72,201,205,222,103,82,232,54,168,125,208,189,149,168,193,206,92,98,199,86,169,237,9,139,168,138,153,33,229,172,112,189,184,209,33,188,14,226,60,170,54,64,3,27,197,31,185,189,254,24,252,215,127,47,188,19,117,122,85,255,11,250,28,252,129,60,207,74,237,111,132,116,24,212,155,13,207,119,53,123,3,216,127,58,154,199,98,59,140,140,240,41,242,74,88,140,110,7,103,230,93,147,90,204,239,192,114,9,226,175,232,197,25,147,235,70,139,185,233,3,139,233,252,206,126,157,179,198,37,114,127,40,244,101,182,28,79,227,233,22,2,42,240,205,39,31,190,144,35,255,94,57,214,167,211,246,62,51,157,146,50,175,64,138,129,89,122,231,151,226,183,239,166,83,220,17,185,237,215,189,91,163,219,176,91,96,223,180,207,195,155,174,221,244,226,215,28,10,247,248,32,210,180,76,76,82,189,201,44,227,203,221,186,27,243,165,150,189,55,135,116,118,138,5,209,50,169,132,73,244,12,201,92,66,171,69,147,29,95,173,133,158,28,77,232,104,219,228,191,206,25,14,53,113,243,182,232,110,99,207,245,75,41,26,182,159,9,248,138,246,150,239,95,189,182,191,175,73,188,172,54,43,120,163,213,62,152,214,37,220,246,112,219,16,85,173,148,46,133,124,10,238,45,69,43,35,8,93,229,209,154,152,1,188,95,50,240,83,241,25,186,237,103,14,166,156,103,211,144,126,3,128,124,107,46,79,11,0,0 };

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long