mirror of
https://github.com/s00500/ESPUI.git
synced 2024-11-22 04:00:55 +00:00
Merge pull request #199 from MartinMueller2003/master
Significant restructuring by MartinMueller
This commit is contained in:
commit
e3eea09e43
38
data/js/controls.js
vendored
38
data/js/controls.js
vendored
@ -115,6 +115,7 @@ function colorClass(colorId) {
|
|||||||
|
|
||||||
var websock;
|
var websock;
|
||||||
var websockConnected = false;
|
var websockConnected = false;
|
||||||
|
var WebSocketTimer = null;
|
||||||
|
|
||||||
function requestOrientationPermission() {
|
function requestOrientationPermission() {
|
||||||
/*
|
/*
|
||||||
@ -183,12 +184,14 @@ function restoreGraphData(id) {
|
|||||||
function restart() {
|
function restart() {
|
||||||
$(document).add("*").off();
|
$(document).add("*").off();
|
||||||
$("#row").html("");
|
$("#row").html("");
|
||||||
websock.close();
|
conStatusError();
|
||||||
start();
|
start();
|
||||||
}
|
}
|
||||||
|
|
||||||
function conStatusError() {
|
function conStatusError() {
|
||||||
|
if (true === websockConnected) {
|
||||||
websockConnected = false;
|
websockConnected = false;
|
||||||
|
websock.close();
|
||||||
$("#conStatus").removeClass("color-green");
|
$("#conStatus").removeClass("color-green");
|
||||||
$("#conStatus").addClass("color-red");
|
$("#conStatus").addClass("color-red");
|
||||||
$("#conStatus").html("Error / No Connection ↻");
|
$("#conStatus").html("Error / No Connection ↻");
|
||||||
@ -197,6 +200,7 @@ function conStatusError() {
|
|||||||
click: restart,
|
click: restart,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleVisibilityChange() {
|
function handleVisibilityChange() {
|
||||||
if (!websockConnected && !document.hidden) {
|
if (!websockConnected && !document.hidden) {
|
||||||
@ -217,6 +221,20 @@ function start() {
|
|||||||
} else {
|
} else {
|
||||||
websock = new WebSocket("ws://" + window.location.hostname + "/ws");
|
websock = new WebSocket("ws://" + window.location.hostname + "/ws");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// is the timer running?
|
||||||
|
if (null === WebSocketTimer) {
|
||||||
|
// timer runs forever
|
||||||
|
WebSocketTimer = setInterval(function () {
|
||||||
|
// console.info("Periodic Timer has expired");
|
||||||
|
// is the socket closed?
|
||||||
|
if (websock.readyState === 3) {
|
||||||
|
// console.info("Web Socket Is Closed");
|
||||||
|
restart();
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
|
} // end timer was not running
|
||||||
|
|
||||||
websock.onopen = function (evt) {
|
websock.onopen = function (evt) {
|
||||||
console.log("websock open");
|
console.log("websock open");
|
||||||
$("#conStatus").addClass("color-green");
|
$("#conStatus").addClass("color-green");
|
||||||
@ -230,13 +248,23 @@ function start() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
websock.onerror = function (evt) {
|
websock.onerror = function (evt) {
|
||||||
|
console.log("websock Error");
|
||||||
console.log(evt);
|
console.log(evt);
|
||||||
conStatusError();
|
|
||||||
|
restart();
|
||||||
};
|
};
|
||||||
|
|
||||||
var handleEvent = function (evt) {
|
var handleEvent = function (evt) {
|
||||||
console.log(evt);
|
console.log(evt);
|
||||||
|
try {
|
||||||
var data = JSON.parse(evt.data);
|
var data = JSON.parse(evt.data);
|
||||||
|
}
|
||||||
|
catch (Event) {
|
||||||
|
console.error(Event);
|
||||||
|
// start the update over again
|
||||||
|
websock.send("uiok:" + 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
var e = document.body;
|
var e = document.body;
|
||||||
var center = "";
|
var center = "";
|
||||||
|
|
||||||
@ -704,8 +732,10 @@ var rangeSlider = function (isDiscrete) {
|
|||||||
sliderchange($(this).attr("id").replace(/^\D+/g, ""));
|
sliderchange($(this).attr("id").replace(/^\D+/g, ""));
|
||||||
};
|
};
|
||||||
|
|
||||||
range.on({input: function() {
|
range.on({
|
||||||
$(this).next().html(this.value)}
|
input: function () {
|
||||||
|
$(this).next().html(this.value)
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
range.each(function () {
|
range.each(function () {
|
||||||
|
11
data/js/controls.min.js
vendored
11
data/js/controls.min.js
vendored
@ -1,13 +1,16 @@
|
|||||||
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 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"";}}
|
||||||
var websock;var websockConnected=false;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);return savedData[id];}
|
function restoreGraphData(id){var savedData=localStorage.getItem("espuigraphs",graphData);if(savedData!=null){savedData=JSON.parse(savedData);return savedData[id];}
|
||||||
return[];}
|
return[];}
|
||||||
function restart(){$(document).add("*").off();$("#row").html("");websock.close();start();}
|
function restart(){$(document).add("*").off();$("#row").html("");conStatusError();start();}
|
||||||
function conStatusError(){websockConnected=false;$("#conStatus").removeClass("color-green");$("#conStatus").addClass("color-red");$("#conStatus").html("Error / No Connection ↻");$("#conStatus").off();$("#conStatus").on({click:restart,});}
|
function conStatusError(){if(true===websockConnected){websockConnected=false;websock.close();$("#conStatus").removeClass("color-green");$("#conStatus").addClass("color-red");$("#conStatus").html("Error / No Connection ↻");$("#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(){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");}
|
||||||
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(evt);conStatusError();};var handleEvent=function(evt){console.log(evt);var data=JSON.parse(evt.data);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;}
|
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);}
|
||||||
|
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;}
|
||||||
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);},});}
|
||||||
|
@ -13,25 +13,31 @@ src_dir = ./src
|
|||||||
data_dir = ../../data
|
data_dir = ../../data
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
lib_extra_dirs = ../../
|
framework = arduino
|
||||||
board_build.filesystem = littlefs
|
board_build.filesystem = littlefs
|
||||||
|
lib_extra_dirs = ../../
|
||||||
|
lib_deps =
|
||||||
|
bblanchon/ArduinoJson @ ^6.18.5
|
||||||
|
https://github.com/esphome/ESPAsyncWebServer @ 3.0.0 ; Updated lib, seems to have recent patches.
|
||||||
|
|
||||||
|
lib_ignore =
|
||||||
|
ESP Async WebServer ; force the use of the esphome version
|
||||||
|
AsyncTCP ; force the use of the esphome version
|
||||||
|
LittleFS_esp32 ; force the use of the ESP32 built into the core version
|
||||||
|
|
||||||
; Additional scripts: Usage: see https://github.com/s00500/ESPUI/issues/144#issuecomment-1005135077
|
; Additional scripts: Usage: see https://github.com/s00500/ESPUI/issues/144#issuecomment-1005135077
|
||||||
;extra_scripts =
|
;extra_scripts =
|
||||||
; LittleFSBuilder.py
|
; LittleFSBuilder.py
|
||||||
|
|
||||||
[env:esp8266]
|
[env:esp8266]
|
||||||
platform = espressif8266
|
platform = espressif8266
|
||||||
framework = arduino
|
|
||||||
board = nodemcuv2
|
board = nodemcuv2
|
||||||
lib_deps =
|
|
||||||
bblanchon/ArduinoJson @ ^6.18.5
|
|
||||||
me-no-dev/ESP Async WebServer @ ^1.2.3
|
|
||||||
|
|
||||||
[env:esp32]
|
[env:esp32]
|
||||||
platform = espressif32
|
platform = espressif32
|
||||||
framework = arduino
|
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
|
monitor_filters = esp32_exception_decoder
|
||||||
|
board_build.flash_mode = dout
|
||||||
lib_deps =
|
lib_deps =
|
||||||
lorol/LittleFS_esp32@^1.0.6
|
${env.lib_deps}
|
||||||
bblanchon/ArduinoJson @ ^6.18.5
|
me-no-dev/AsyncTCP@1.1.1
|
||||||
me-no-dev/ESP Async WebServer @ ^1.2.3
|
|
||||||
|
788
src/ESPUI.cpp
788
src/ESPUI.cpp
File diff suppressed because it is too large
Load Diff
219
src/ESPUI.h
219
src/ESPUI.h
@ -1,18 +1,21 @@
|
|||||||
#ifndef ESPUI_h
|
#pragma once
|
||||||
#define ESPUI_h
|
|
||||||
|
|
||||||
|
// 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>
|
||||||
#include "ArduinoJson.h"
|
#include <ArduinoJson.h>
|
||||||
#include "stdlib_noniso.h"
|
#include <stdlib_noniso.h>
|
||||||
|
#include <LittleFS.h>
|
||||||
|
#include <map>
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
|
||||||
|
#include "ESPUIcontrol.h"
|
||||||
|
#include "ESPUIclient.h"
|
||||||
|
|
||||||
#if defined(ESP32)
|
#if defined(ESP32)
|
||||||
#include <AsyncTCP.h>
|
#include <AsyncTCP.h>
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
#include <LITTLEFS.h>
|
|
||||||
|
|
||||||
#include "WiFi.h"
|
#include "WiFi.h"
|
||||||
|
|
||||||
#else
|
#else
|
||||||
@ -21,9 +24,7 @@
|
|||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
#include <ESP8266mDNS.h>
|
#include <ESP8266mDNS.h>
|
||||||
#include <ESPAsyncTCP.h>
|
#include <ESPAsyncTCP.h>
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
#include <Hash.h>
|
#include <Hash.h>
|
||||||
#include <LittleFS.h>
|
|
||||||
|
|
||||||
#define FILE_WRITE "w"
|
#define FILE_WRITE "w"
|
||||||
|
|
||||||
@ -31,166 +32,18 @@
|
|||||||
|
|
||||||
// Message Types (and control types)
|
// Message Types (and control types)
|
||||||
|
|
||||||
enum ControlType : uint8_t
|
enum MessageTypes : uint8_t
|
||||||
{
|
{
|
||||||
// fixed controls
|
|
||||||
Title = 0,
|
|
||||||
|
|
||||||
// updatable controls
|
|
||||||
Pad,
|
|
||||||
PadWithCenter,
|
|
||||||
Button,
|
|
||||||
Label,
|
|
||||||
Switcher,
|
|
||||||
Slider,
|
|
||||||
Number,
|
|
||||||
Text,
|
|
||||||
Graph,
|
|
||||||
GraphPoint,
|
|
||||||
Tab,
|
|
||||||
Select,
|
|
||||||
Option,
|
|
||||||
Min,
|
|
||||||
Max,
|
|
||||||
Step,
|
|
||||||
Gauge,
|
|
||||||
Accel,
|
|
||||||
Separator,
|
|
||||||
Time,
|
|
||||||
|
|
||||||
UpdateOffset = 100,
|
|
||||||
UpdatePad = 101,
|
|
||||||
UpdatePadWithCenter,
|
|
||||||
ButtonButton,
|
|
||||||
UpdateLabel,
|
|
||||||
UpdateSwitcher,
|
|
||||||
UpdateSlider,
|
|
||||||
UpdateNumber,
|
|
||||||
UpdateText,
|
|
||||||
ClearGraph,
|
|
||||||
UpdateTab,
|
|
||||||
UpdateSelection,
|
|
||||||
UpdateOption,
|
|
||||||
UpdateMin,
|
|
||||||
UpdateMax,
|
|
||||||
UpdateStep,
|
|
||||||
UpdateGauge,
|
|
||||||
UpdateAccel,
|
|
||||||
UpdateSeparator,
|
|
||||||
UpdateTime,
|
|
||||||
|
|
||||||
InitialGui = 200,
|
InitialGui = 200,
|
||||||
Reload = 201,
|
Reload = 201,
|
||||||
ExtendGUI = 210
|
ExtendGUI = 210,
|
||||||
|
UpdateGui = 220,
|
||||||
|
ExtendedUpdateGui = 230,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define UI_INITIAL_GUI ControlType::InitialGui
|
#define UI_INITIAL_GUI MessageTypes::InitialGui
|
||||||
#define UI_RELOAD ControlType::Reload
|
#define UI_EXTEND_GUI MessageTypes::ExtendGUI
|
||||||
#define UI_EXTEND_GUI ControlType::ExtendGUI
|
#define UI_RELOAD MessageTypes::Reload
|
||||||
|
|
||||||
#define UI_TITLE ControlType::Title
|
|
||||||
#define UI_LABEL ControlType::Label
|
|
||||||
#define UI_BUTTON ControlType::Button
|
|
||||||
#define UI_SWITCHER ControlType::Switcher
|
|
||||||
#define UI_PAD ControlType::Pad
|
|
||||||
#define UI_CPAD ControlType::Cpad
|
|
||||||
#define UI_SLIDER ControlType::Slider
|
|
||||||
#define UI_NUMBER ControlType::Number
|
|
||||||
#define UI_TEXT_INPUT ControlType::Text
|
|
||||||
#define UI_GRAPH ControlType::Graph
|
|
||||||
#define UI_ADD_GRAPH_POINT ControlType::GraphPoint
|
|
||||||
|
|
||||||
#define UPDATE_LABEL ControlType::UpdateLabel
|
|
||||||
#define UPDATE_SWITCHER ControlType::UpdateSwitcher
|
|
||||||
#define UPDATE_SLIDER ControlType::UpdateSlider
|
|
||||||
#define UPDATE_NUMBER ControlType::UpdateNumber
|
|
||||||
#define UPDATE_TEXT_INPUT ControlType::UpdateText
|
|
||||||
#define CLEAR_GRAPH ControlType::ClearGraph
|
|
||||||
|
|
||||||
// Colors
|
|
||||||
enum ControlColor : uint8_t
|
|
||||||
{
|
|
||||||
Turquoise,
|
|
||||||
Emerald,
|
|
||||||
Peterriver,
|
|
||||||
Wetasphalt,
|
|
||||||
Sunflower,
|
|
||||||
Carrot,
|
|
||||||
Alizarin,
|
|
||||||
Dark,
|
|
||||||
None = 0xFF
|
|
||||||
};
|
|
||||||
#define COLOR_TURQUOISE ControlColor::Turquoise
|
|
||||||
#define COLOR_EMERALD ControlColor::Emerald
|
|
||||||
#define COLOR_PETERRIVER ControlColor::Peterriver
|
|
||||||
#define COLOR_WETASPHALT ControlColor::Wetasphalt
|
|
||||||
#define COLOR_SUNFLOWER ControlColor::Sunflower
|
|
||||||
#define COLOR_CARROT ControlColor::Carrot
|
|
||||||
#define COLOR_ALIZARIN ControlColor::Alizarin
|
|
||||||
#define COLOR_DARK ControlColor::Dark
|
|
||||||
#define COLOR_NONE ControlColor::None
|
|
||||||
|
|
||||||
class Control
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ControlType type;
|
|
||||||
uint16_t id; // just mirroring the id here for practical reasons
|
|
||||||
const char* label;
|
|
||||||
void (*callback)(Control*, int);
|
|
||||||
void (*extendedCallback)(Control*, int, void*);
|
|
||||||
void* user;
|
|
||||||
String value;
|
|
||||||
ControlColor color;
|
|
||||||
bool visible;
|
|
||||||
bool wide;
|
|
||||||
bool vertical;
|
|
||||||
bool enabled;
|
|
||||||
uint16_t parentControl;
|
|
||||||
String panelStyle;
|
|
||||||
String elementStyle;
|
|
||||||
String inputType;
|
|
||||||
Control* next;
|
|
||||||
|
|
||||||
static constexpr uint16_t noParent = 0xffff;
|
|
||||||
|
|
||||||
Control(ControlType type, const char* label, void (*callback)(Control*, int, void*), void* UserData,
|
|
||||||
const String& value, ControlColor color, bool visible, uint16_t parentControl)
|
|
||||||
: type(type),
|
|
||||||
label(label),
|
|
||||||
callback(nullptr),
|
|
||||||
extendedCallback(callback),
|
|
||||||
user(UserData),
|
|
||||||
value(value),
|
|
||||||
color(color),
|
|
||||||
visible(visible),
|
|
||||||
wide(false),
|
|
||||||
vertical(false),
|
|
||||||
enabled(true),
|
|
||||||
parentControl(parentControl),
|
|
||||||
next(nullptr)
|
|
||||||
{
|
|
||||||
id = idCounter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
Control(const Control& control)
|
|
||||||
: type(control.type),
|
|
||||||
id(control.id),
|
|
||||||
label(control.label),
|
|
||||||
callback(control.callback),
|
|
||||||
extendedCallback(control.extendedCallback),
|
|
||||||
user(control.user),
|
|
||||||
value(control.value),
|
|
||||||
color(control.color),
|
|
||||||
visible(control.visible),
|
|
||||||
parentControl(control.parentControl),
|
|
||||||
next(control.next)
|
|
||||||
{ }
|
|
||||||
void SendCallback(int type);
|
|
||||||
bool HasCallback() { return ((nullptr != callback) || (nullptr != extendedCallback)); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
static uint16_t idCounter;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Values
|
// Values
|
||||||
#define B_DOWN -1
|
#define B_DOWN -1
|
||||||
@ -232,10 +85,15 @@ public:
|
|||||||
jsonUpdateDocumentSize = 2000;
|
jsonUpdateDocumentSize = 2000;
|
||||||
jsonInitialDocumentSize = 8000;
|
jsonInitialDocumentSize = 8000;
|
||||||
sliderContinuous = false;
|
sliderContinuous = false;
|
||||||
|
#ifdef ESP32
|
||||||
|
ControlsSemaphore = xSemaphoreCreateMutex();
|
||||||
|
xSemaphoreGive(ControlsSemaphore);
|
||||||
|
#endif // def ESP32
|
||||||
}
|
}
|
||||||
unsigned int jsonUpdateDocumentSize;
|
unsigned int jsonUpdateDocumentSize;
|
||||||
unsigned int jsonInitialDocumentSize;
|
unsigned int jsonInitialDocumentSize;
|
||||||
bool sliderContinuous;
|
bool sliderContinuous;
|
||||||
|
void onWsEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len);
|
||||||
bool captivePortal = true;
|
bool captivePortal = true;
|
||||||
|
|
||||||
void setVerbosity(Verbosity verbosity);
|
void setVerbosity(Verbosity verbosity);
|
||||||
@ -257,7 +115,7 @@ public:
|
|||||||
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, void (*callback)(Control*, int));
|
||||||
uint16_t addControl(ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl, void (*callback)(Control*, int, void *), void* UserData);
|
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_reload_ui = false);
|
bool removeControl(uint16_t id, bool force_rebuild_ui = false);
|
||||||
|
|
||||||
// create Elements
|
// create Elements
|
||||||
// Create Event Button
|
// Create Event Button
|
||||||
@ -297,11 +155,15 @@ public:
|
|||||||
// Update Elements
|
// Update Elements
|
||||||
|
|
||||||
Control* getControl(uint16_t id);
|
Control* getControl(uint16_t id);
|
||||||
|
Control* getControlNoLock(uint16_t id);
|
||||||
|
|
||||||
// Update Elements
|
// Update Elements
|
||||||
void updateControlValue(uint16_t id, const String& value, int clientId = -1);
|
void updateControlValue(uint16_t id, const String& value, int clientId = -1);
|
||||||
void updateControlValue(Control* control, const String& value, int clientId = -1);
|
void updateControlValue(Control* control, const String& value, int clientId = -1);
|
||||||
|
|
||||||
|
void updateControlLabel(uint16_t control, const char * value, int clientId = -1);
|
||||||
|
void updateControlLabel(Control* control, const char * value, int clientId = -1);
|
||||||
|
|
||||||
void updateControl(uint16_t id, int clientId = -1);
|
void updateControl(uint16_t id, int clientId = -1);
|
||||||
void updateControl(Control* control, int clientId = -1);
|
void updateControl(Control* control, int clientId = -1);
|
||||||
|
|
||||||
@ -333,22 +195,37 @@ public:
|
|||||||
const char* ui_title = "ESPUI"; // Store UI Title and Header Name
|
const char* ui_title = "ESPUI"; // Store UI Title and Header Name
|
||||||
Control* controls = nullptr;
|
Control* controls = nullptr;
|
||||||
void jsonReload();
|
void jsonReload();
|
||||||
void jsonDom(uint16_t startidx, AsyncWebSocketClient* client = nullptr);
|
void jsonDom(uint16_t startidx, AsyncWebSocketClient* client = nullptr, bool Updating = false);
|
||||||
|
|
||||||
Verbosity verbosity;
|
Verbosity verbosity;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend class ESPUIclient;
|
||||||
|
friend class ESPUIcontrol;
|
||||||
|
|
||||||
|
#ifdef ESP32
|
||||||
|
SemaphoreHandle_t ControlsSemaphore = NULL;
|
||||||
|
#endif // def ESP32
|
||||||
|
|
||||||
|
void RemoveToBeDeletedControls();
|
||||||
|
|
||||||
AsyncWebServer* server;
|
AsyncWebServer* server;
|
||||||
AsyncWebSocket* ws;
|
AsyncWebSocket* ws;
|
||||||
|
|
||||||
private:
|
|
||||||
const char* basicAuthUsername = nullptr;
|
const char* basicAuthUsername = nullptr;
|
||||||
const char* basicAuthPassword = nullptr;
|
const char* basicAuthPassword = nullptr;
|
||||||
bool basicAuth = true;
|
bool basicAuth = true;
|
||||||
|
|
||||||
uint16_t controlCount = 0;
|
uint16_t controlCount = 0;
|
||||||
|
|
||||||
void prepareJSONChunk(AsyncWebSocketClient* client, uint16_t startindex, JsonArray* items);
|
#define ClientUpdateType_t ESPUIclient::ClientUpdateType_t
|
||||||
|
void NotifyClients(ClientUpdateType_t newState);
|
||||||
|
void NotifyClient(uint32_t WsClientId, ClientUpdateType_t newState);
|
||||||
|
void ClearControlUpdateFlags();
|
||||||
|
|
||||||
|
bool SendJsonDocToWebSocket(ArduinoJson::DynamicJsonDocument& document, uint16_t clientId);
|
||||||
|
|
||||||
|
std::map<uint32_t, ESPUIclient*> MapOfClients;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern ESPUIClass ESPUI;
|
extern ESPUIClass ESPUI;
|
||||||
#endif
|
|
||||||
|
491
src/ESPUIclient.cpp
Normal file
491
src/ESPUIclient.cpp
Normal file
@ -0,0 +1,491 @@
|
|||||||
|
#include "ESPUI.h"
|
||||||
|
#include "ESPUIclient.h"
|
||||||
|
#include "ESPUIcontrol.h"
|
||||||
|
|
||||||
|
ESPUIclient::ESPUIclient(AsyncWebSocketClient * _client):
|
||||||
|
client(_client)
|
||||||
|
{
|
||||||
|
fsm_EspuiClient_state_Idle_imp.SetParent(this);
|
||||||
|
fsm_EspuiClient_state_SendingUpdate_imp.SetParent(this);
|
||||||
|
fsm_EspuiClient_state_Rebuilding_imp.SetParent(this);
|
||||||
|
fsm_EspuiClient_state_Reloading_imp.SetParent(this);
|
||||||
|
|
||||||
|
fsm_EspuiClient_state_Idle_imp.Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
ESPUIclient::ESPUIclient(const ESPUIclient& source):
|
||||||
|
client(source.client)
|
||||||
|
{
|
||||||
|
fsm_EspuiClient_state_Idle_imp.SetParent(this);
|
||||||
|
fsm_EspuiClient_state_SendingUpdate_imp.SetParent(this);
|
||||||
|
fsm_EspuiClient_state_Rebuilding_imp.SetParent(this);
|
||||||
|
fsm_EspuiClient_state_Reloading_imp.SetParent(this);
|
||||||
|
|
||||||
|
fsm_EspuiClient_state_Idle_imp.Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
ESPUIclient::~ESPUIclient()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ESPUIclient::CanSend()
|
||||||
|
{
|
||||||
|
bool Response = false;
|
||||||
|
if (nullptr != client)
|
||||||
|
{
|
||||||
|
Response = client->canSend();
|
||||||
|
}
|
||||||
|
return Response;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESPUIclient::FillInHeader(DynamicJsonDocument& document)
|
||||||
|
{
|
||||||
|
document[F("type")] = UI_EXTEND_GUI;
|
||||||
|
document[F("sliderContinuous")] = ESPUI.sliderContinuous;
|
||||||
|
document[F("startindex")] = 0;
|
||||||
|
document[F("totalcontrols")] = ESPUI.controlCount;
|
||||||
|
JsonArray items = document.createNestedArray(F("controls"));
|
||||||
|
JsonObject titleItem = items.createNestedObject();
|
||||||
|
titleItem[F("type")] = (int)UI_TITLE;
|
||||||
|
titleItem[F("label")] = ESPUI.ui_title;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ESPUIclient::IsSyncronized()
|
||||||
|
{
|
||||||
|
return ((ClientUpdateType_t::Synchronized == ClientUpdateType) &&
|
||||||
|
(&fsm_EspuiClient_state_Idle_imp == pCurrentFsmState));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ESPUIclient::SendClientNotification(ClientUpdateType_t value)
|
||||||
|
{
|
||||||
|
bool Response = false;
|
||||||
|
|
||||||
|
do // once
|
||||||
|
{
|
||||||
|
if(!CanSend())
|
||||||
|
{
|
||||||
|
// Serial.println(F("ESPUIclient::NotifyClient"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicJsonDocument document(ESPUI.jsonUpdateDocumentSize);
|
||||||
|
FillInHeader(document);
|
||||||
|
if(ClientUpdateType_t::ReloadNeeded == value)
|
||||||
|
{
|
||||||
|
// Serial.println(F("ESPUIclient::SendClientNotification:set type to reload"));
|
||||||
|
document["type"] = int(UI_RELOAD);
|
||||||
|
}
|
||||||
|
// dont send any controls
|
||||||
|
|
||||||
|
Response = SendJsonDocToWebSocket(document);
|
||||||
|
// Serial.println(String("ESPUIclient::SendClientNotification:NotificationSent:Response: ") + String(Response));
|
||||||
|
|
||||||
|
} while (false);
|
||||||
|
return Response;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESPUIclient::NotifyClient(ClientUpdateType_t newState)
|
||||||
|
{
|
||||||
|
SetState(newState);
|
||||||
|
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
|
||||||
|
void ESPUIclient::onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t len)
|
||||||
|
{
|
||||||
|
// Serial.println(String("ESPUIclient::OnWsEvent: type: ") + String(type));
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case WS_EVT_PONG:
|
||||||
|
{
|
||||||
|
#if defined(DEBUG_ESPUI)
|
||||||
|
if (ESPUI.verbosity)
|
||||||
|
{
|
||||||
|
Serial.println(F("ESPUIclient::OnWsEvent:WS_EVT_PONG"));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case WS_EVT_ERROR:
|
||||||
|
{
|
||||||
|
#if defined(DEBUG_ESPUI)
|
||||||
|
if (ESPUI.verbosity)
|
||||||
|
{
|
||||||
|
Serial.println(F("ESPUIclient::OnWsEvent:WS_EVT_ERROR"));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case WS_EVT_CONNECT:
|
||||||
|
{
|
||||||
|
#if defined(DEBUG_ESPUI)
|
||||||
|
if (ESPUI.verbosity)
|
||||||
|
{
|
||||||
|
Serial.println(F("ESPUIclient::OnWsEvent:WS_EVT_CONNECT"));
|
||||||
|
Serial.println(client->id());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Serial.println("ESPUIclient:onWsEvent:WS_EVT_CONNECT: Call NotifyClient: RebuildNeeded");
|
||||||
|
NotifyClient(ClientUpdateType_t::RebuildNeeded);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case WS_EVT_DATA:
|
||||||
|
{
|
||||||
|
// Serial.println(F("ESPUIclient::OnWsEvent:WS_EVT_DATA"));
|
||||||
|
String msg = "";
|
||||||
|
msg.reserve(len + 1);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
msg += (char)data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
String cmd = msg.substring(0, msg.indexOf(":"));
|
||||||
|
String value = msg.substring(cmd.length() + 1, msg.lastIndexOf(':'));
|
||||||
|
uint16_t id = msg.substring(msg.lastIndexOf(':') + 1).toInt();
|
||||||
|
|
||||||
|
#if defined(DEBUG_ESPUI)
|
||||||
|
if (ESPUI.verbosity >= Verbosity::VerboseJSON)
|
||||||
|
{
|
||||||
|
Serial.println(String(F(" WS msg: ")) + msg);
|
||||||
|
Serial.println(String(F(" WS cmd: ")) + cmd);
|
||||||
|
Serial.println(String(F(" WS id: ")) + String(id));
|
||||||
|
Serial.println(String(F("WS value: ")) + String(value));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (cmd.equals(F("uiok")))
|
||||||
|
{
|
||||||
|
// Serial.println(F("ESPUIclient::OnWsEvent:WS_EVT_DATA:uiok:ProcessAck"));
|
||||||
|
pCurrentFsmState->ProcessAck(id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd.equals(F("uiuok")))
|
||||||
|
{
|
||||||
|
// Serial.println(F("WS_EVT_DATA: uiuok. Unlock new async notifications"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Control* control = ESPUI.getControl(id);
|
||||||
|
if (nullptr == control)
|
||||||
|
{
|
||||||
|
#if defined(DEBUG_ESPUI)
|
||||||
|
if (ESPUI.verbosity)
|
||||||
|
{
|
||||||
|
Serial.println(String(F("No control found for ID ")) + String(id));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
control->onWsEvent(cmd, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
// Serial.println(F("ESPUIclient::OnWsEvent:default"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} // end switch
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Prepare a chunk of elements as a single JSON string. If the allowed number of elements is greater than the total
|
||||||
|
number this will represent the entire UI. More likely, it will represent a small section of the UI to be sent. The
|
||||||
|
client will acknowledge receipt by requesting the next chunk.
|
||||||
|
*/
|
||||||
|
uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex,
|
||||||
|
DynamicJsonDocument & rootDoc,
|
||||||
|
bool InUpdateMode)
|
||||||
|
{
|
||||||
|
#ifdef ESP32
|
||||||
|
xSemaphoreTake(ESPUI.ControlsSemaphore, portMAX_DELAY);
|
||||||
|
#endif // def ESP32
|
||||||
|
|
||||||
|
// Serial.println(String("prepareJSONChunk: Start. InUpdateMode: ") + String(InUpdateMode));
|
||||||
|
int elementcount = 0;
|
||||||
|
|
||||||
|
do // once
|
||||||
|
{
|
||||||
|
// Follow the list until control points to the startindex'th node
|
||||||
|
Control* control = ESPUI.controls;
|
||||||
|
uint32_t currentIndex = 0;
|
||||||
|
JsonArray items = rootDoc[F("controls")];
|
||||||
|
|
||||||
|
while ((startindex > currentIndex) && (nullptr != control))
|
||||||
|
{
|
||||||
|
// only count active controls
|
||||||
|
if (!control->ToBeDeleted())
|
||||||
|
{
|
||||||
|
if(InUpdateMode)
|
||||||
|
{
|
||||||
|
// In update mode we only count the controls that have been updated.
|
||||||
|
if(control->IsUpdated())
|
||||||
|
{
|
||||||
|
++currentIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// not in update mode. Count all active controls
|
||||||
|
++currentIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
control = control->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// any controls left to be processed?
|
||||||
|
if(nullptr == control)
|
||||||
|
{
|
||||||
|
// Serial.println("prepareJSONChunk: No controls to process");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep track of the number of elements we have serialised into this
|
||||||
|
// message. Overflow is detected and handled later in this loop
|
||||||
|
// and needs an index to the last item added.
|
||||||
|
while (nullptr != control)
|
||||||
|
{
|
||||||
|
// skip deleted controls or controls that have not been updated
|
||||||
|
if (control->ToBeDeleted())
|
||||||
|
{
|
||||||
|
// Serial.println(String("prepareJSONChunk: Ignoring Deleted control: ") + String(control->id));
|
||||||
|
control = control->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(InUpdateMode)
|
||||||
|
{
|
||||||
|
if(control->IsUpdated())
|
||||||
|
{
|
||||||
|
// dont skip this control
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// control has not been updated. Skip it
|
||||||
|
control = control->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject item = items.createNestedObject();
|
||||||
|
elementcount++;
|
||||||
|
control->MarshalControl(item, InUpdateMode);
|
||||||
|
|
||||||
|
if (rootDoc.overflowed())
|
||||||
|
{
|
||||||
|
// String("prepareJSONChunk: too much data in the message. Remove the last entry");
|
||||||
|
if (1 == elementcount)
|
||||||
|
{
|
||||||
|
Serial.println(String(F("ERROR: prepareJSONChunk: Control ")) + String(control->id) + F(" is too large to be sent to the browser."));
|
||||||
|
rootDoc.clear();
|
||||||
|
item = items.createNestedObject();
|
||||||
|
control->MarshalErrorMessage(item);
|
||||||
|
elementcount = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Serial.println(String("prepareJSONChunk: Defering control: ") + String(control->id));
|
||||||
|
// Serial.println(String("prepareJSONChunk: elementcount: ") + String(elementcount));
|
||||||
|
|
||||||
|
items.remove(elementcount);
|
||||||
|
--elementcount;
|
||||||
|
}
|
||||||
|
// exit the loop
|
||||||
|
control = nullptr;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
control = control->next;
|
||||||
|
}
|
||||||
|
} // end while (control != nullptr)
|
||||||
|
|
||||||
|
} while (false);
|
||||||
|
|
||||||
|
#ifdef ESP32
|
||||||
|
xSemaphoreGive(ESPUI.ControlsSemaphore);
|
||||||
|
#endif // def ESP32
|
||||||
|
|
||||||
|
// Serial.println(String("prepareJSONChunk: elementcount: ") + String(elementcount));
|
||||||
|
return elementcount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Convert & Transfer Arduino elements to JSON elements. This function sends a chunk of
|
||||||
|
JSON describing the controls of the UI, starting from the control at index startidx.
|
||||||
|
If startidx is 0 then a UI_INITIAL_GUI message will be sent, else a UI_EXTEND_GUI.
|
||||||
|
Both message types contain a list of serialised UI elements. Only a portion of the UI
|
||||||
|
will be sent in order to avoid websocket buffer overflows. The client will acknowledge
|
||||||
|
receipt of a partial message by requesting the next chunk of UI.
|
||||||
|
|
||||||
|
The protocol is:
|
||||||
|
SERVER: SendControlsToClient(0):
|
||||||
|
"UI_INITIAL_GUI: n serialised UI elements"
|
||||||
|
CLIENT: controls.js:handleEvent()
|
||||||
|
"uiok:n"
|
||||||
|
SERVER: SendControlsToClient(n):
|
||||||
|
"UI_EXTEND_GUI: n serialised UI elements"
|
||||||
|
CLIENT: controls.js:handleEvent()
|
||||||
|
"uiok:2*n"
|
||||||
|
etc.
|
||||||
|
Returns true if all controls have been sent (aka: Done)
|
||||||
|
*/
|
||||||
|
bool ESPUIclient::SendControlsToClient(uint16_t startidx,
|
||||||
|
ClientUpdateType_t TransferMode)
|
||||||
|
{
|
||||||
|
bool Response = false;
|
||||||
|
// Serial.println(String("ESPUIclient:SendControlsToClient:startidx: ") + String(startidx));
|
||||||
|
do // once
|
||||||
|
{
|
||||||
|
if(!CanSend())
|
||||||
|
{
|
||||||
|
// Serial.println("ESPUIclient:SendControlsToClient: Cannot Send to clients.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startidx >= ESPUI.controlCount)
|
||||||
|
{
|
||||||
|
// Serial.println("ESPUIclient:SendControlsToClient: No more controls to send.");
|
||||||
|
Response = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicJsonDocument document(ESPUI.jsonInitialDocumentSize);
|
||||||
|
FillInHeader(document);
|
||||||
|
document[F("startindex")] = startidx;
|
||||||
|
document[F("totalcontrols")] = 65534; // ESPUI.controlCount;
|
||||||
|
|
||||||
|
if(0 == startidx)
|
||||||
|
{
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
// Serial.println(String("ESPUIclient:SendControlsToClient:type: ") + String((uint32_t)document["type"]));
|
||||||
|
|
||||||
|
// Serial.println("ESPUIclient:SendControlsToClient: Build Controls.");
|
||||||
|
if(prepareJSONChunk(startidx, document, ClientUpdateType_t::UpdateNeeded == TransferMode))
|
||||||
|
{
|
||||||
|
#if defined(DEBUG_ESPUI)
|
||||||
|
if (ESPUI.verbosity >= Verbosity::VerboseJSON)
|
||||||
|
{
|
||||||
|
Serial.println(F("ESPUIclient:SendControlsToClient: Sending elements --------->"));
|
||||||
|
String json;
|
||||||
|
serializeJson(document, json);
|
||||||
|
Serial.println(json);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Serial.println("ESPUIclient:SendControlsToClient: Send message.");
|
||||||
|
if(true == SendJsonDocToWebSocket(document))
|
||||||
|
{
|
||||||
|
// Serial.println("ESPUIclient:SendControlsToClient: Sent.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Serial.println("ESPUIclient:SendControlsToClient: Send failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Serial.println("ESPUIclient:SendControlsToClient: No elements to send.");
|
||||||
|
Response = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
// Serial.println(String("ESPUIclient:SendControlsToClient:Response: ") + String(Response));
|
||||||
|
return Response;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ESPUIclient::SendJsonDocToWebSocket(DynamicJsonDocument& document)
|
||||||
|
{
|
||||||
|
bool Response = true;
|
||||||
|
|
||||||
|
do // once
|
||||||
|
{
|
||||||
|
if (!CanSend())
|
||||||
|
{
|
||||||
|
#if defined(DEBUG_ESPUI)
|
||||||
|
if (ESPUI.verbosity >= Verbosity::VerboseJSON)
|
||||||
|
{
|
||||||
|
Serial.println(F("ESPUIclient::SendJsonDocToWebSocket: Cannot Send to client. Not sending websocket message"));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// Serial.println("ESPUIclient::SendJsonDocToWebSocket: Cannot Send to client. Not sending websocket message");
|
||||||
|
Response = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
String json;
|
||||||
|
json.reserve(document.size() / 2);
|
||||||
|
json.clear();
|
||||||
|
serializeJson(document, json);
|
||||||
|
|
||||||
|
#if defined(DEBUG_ESPUI)
|
||||||
|
if (ESPUI.verbosity >= Verbosity::VerboseJSON)
|
||||||
|
{
|
||||||
|
Serial.println(String(F("ESPUIclient::SendJsonDocToWebSocket: json: '")) + json + "'");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(DEBUG_ESPUI)
|
||||||
|
if (ESPUI.verbosity >= Verbosity::VerboseJSON)
|
||||||
|
{
|
||||||
|
Serial.println(F("ESPUIclient::SendJsonDocToWebSocket: client.text"));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// Serial.println(F("ESPUIclient::SendJsonDocToWebSocket: client.text"));
|
||||||
|
client->text(json);
|
||||||
|
|
||||||
|
} while (false);
|
||||||
|
|
||||||
|
return Response;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESPUIclient::SetState(ClientUpdateType_t value)
|
||||||
|
{
|
||||||
|
// only a higher priority state request can replace the current state request
|
||||||
|
if(uint32_t(ClientUpdateType) < uint32_t(value))
|
||||||
|
{
|
||||||
|
ClientUpdateType = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
62
src/ESPUIclient.h
Normal file
62
src/ESPUIclient.h
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include "ESPUIclientFsm.h"
|
||||||
|
#include "ESPUIcontrol.h"
|
||||||
|
|
||||||
|
class ESPUIclient
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum ClientUpdateType_t
|
||||||
|
{ // this is an orderd list. highest number is highest priority
|
||||||
|
Synchronized = 0,
|
||||||
|
UpdateNeeded = 1,
|
||||||
|
RebuildNeeded = 2,
|
||||||
|
ReloadNeeded = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// bool HasBeenNotified = false; // Set when a notification has been sent and we are waiting for a reply
|
||||||
|
// bool DelayedNotification = false; // set if a delayed notification is needed
|
||||||
|
|
||||||
|
ClientUpdateType_t ClientUpdateType = ClientUpdateType_t::RebuildNeeded;
|
||||||
|
|
||||||
|
AsyncWebSocketClient * client = nullptr;
|
||||||
|
|
||||||
|
friend class fsm_EspuiClient_state_Idle;
|
||||||
|
friend class fsm_EspuiClient_state_SendingUpdate;
|
||||||
|
friend class fsm_EspuiClient_state_Rebuilding;
|
||||||
|
friend class fsm_EspuiClient_state_WaitForAck;
|
||||||
|
friend class fsm_EspuiClient_state_Reloading;
|
||||||
|
friend class fsm_EspuiClient_state;
|
||||||
|
|
||||||
|
fsm_EspuiClient_state_Idle fsm_EspuiClient_state_Idle_imp;
|
||||||
|
fsm_EspuiClient_state_SendingUpdate fsm_EspuiClient_state_SendingUpdate_imp;
|
||||||
|
fsm_EspuiClient_state_Rebuilding fsm_EspuiClient_state_Rebuilding_imp;
|
||||||
|
fsm_EspuiClient_state_Reloading fsm_EspuiClient_state_Reloading_imp;
|
||||||
|
fsm_EspuiClient_state* pCurrentFsmState = &fsm_EspuiClient_state_Idle_imp;
|
||||||
|
|
||||||
|
time_t EspuiClientEndTime = 0;
|
||||||
|
|
||||||
|
// bool NeedsNotification() { return pCurrentFsmState != &fsm_EspuiClient_state_Idle_imp; }
|
||||||
|
|
||||||
|
bool CanSend();
|
||||||
|
void FillInHeader(ArduinoJson::DynamicJsonDocument& document);
|
||||||
|
uint32_t prepareJSONChunk(uint16_t startindex, DynamicJsonDocument& rootDoc, bool InUpdateMode);
|
||||||
|
bool SendControlsToClient(uint16_t startidx, ClientUpdateType_t TransferMode);
|
||||||
|
|
||||||
|
bool SendClientNotification(ClientUpdateType_t value);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ESPUIclient(AsyncWebSocketClient * _client);
|
||||||
|
ESPUIclient(const ESPUIclient & source);
|
||||||
|
virtual ~ESPUIclient();
|
||||||
|
void NotifyClient(ClientUpdateType_t value);
|
||||||
|
void onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t len);
|
||||||
|
bool IsSyncronized();
|
||||||
|
uint32_t id() { return client->id(); }
|
||||||
|
void SetState(ClientUpdateType_t value);
|
||||||
|
bool SendJsonDocToWebSocket(ArduinoJson::DynamicJsonDocument& document);
|
||||||
|
};
|
107
src/ESPUIclientFsm.cpp
Normal file
107
src/ESPUIclientFsm.cpp
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
#include "ESPUI.h"
|
||||||
|
#include "ESPUIclient.h"
|
||||||
|
|
||||||
|
//----------------------------------------------
|
||||||
|
// FSM definitions
|
||||||
|
//----------------------------------------------
|
||||||
|
void fsm_EspuiClient_state::Init()
|
||||||
|
{
|
||||||
|
// Serial.println(String("fsm_EspuiClient_state:Init: ") + GetStateName());
|
||||||
|
Parent->pCurrentFsmState = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------
|
||||||
|
//----------------------------------------------
|
||||||
|
//----------------------------------------------
|
||||||
|
bool fsm_EspuiClient_state_Idle::NotifyClient()
|
||||||
|
{
|
||||||
|
bool Response = false;
|
||||||
|
|
||||||
|
// Serial.println(F("fsm_EspuiClient_state_Idle: NotifyClient"));
|
||||||
|
ClientUpdateType_t TypeToProcess = Parent->ClientUpdateType;
|
||||||
|
// Clear the type so that we capture any changes in type that happen
|
||||||
|
// while we are processing the current request.
|
||||||
|
Parent->ClientUpdateType = ClientUpdateType_t::Synchronized;
|
||||||
|
|
||||||
|
// Start processing the current request.
|
||||||
|
switch (TypeToProcess)
|
||||||
|
{
|
||||||
|
case ClientUpdateType_t::Synchronized:
|
||||||
|
{
|
||||||
|
// Serial.println(F("fsm_EspuiClient_state_Idle: NotifyClient:State:Synchronized"));
|
||||||
|
// Parent->fsm_EspuiClient_state_Idle_imp.Init();
|
||||||
|
Response = true; // Parent->SendClientNotification(ClientUpdateType_t::UpdateNeeded);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ClientUpdateType_t::UpdateNeeded:
|
||||||
|
{
|
||||||
|
// Serial.println(F("fsm_EspuiClient_state_Idle: NotifyClient:State:UpdateNeeded"));
|
||||||
|
Parent->fsm_EspuiClient_state_SendingUpdate_imp.Init();
|
||||||
|
Response = Parent->SendClientNotification(ClientUpdateType_t::UpdateNeeded);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ClientUpdateType_t::RebuildNeeded:
|
||||||
|
{
|
||||||
|
// Serial.println(F("fsm_EspuiClient_state_Idle: NotifyClient:State:RebuildNeeded"));
|
||||||
|
Parent->fsm_EspuiClient_state_Rebuilding_imp.Init();
|
||||||
|
Response = Parent->SendClientNotification(ClientUpdateType_t::RebuildNeeded);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ClientUpdateType_t::ReloadNeeded:
|
||||||
|
{
|
||||||
|
// Serial.println(F("fsm_EspuiClient_state_Idle: NotifyClient:State:ReloadNeeded"));
|
||||||
|
Parent->fsm_EspuiClient_state_Reloading_imp.Init();
|
||||||
|
Response = Parent->SendClientNotification(ClientUpdateType_t::ReloadNeeded);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Response;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_EspuiClient_state_Idle::ProcessAck(uint16_t)
|
||||||
|
{
|
||||||
|
// 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"));
|
||||||
|
Parent->NotifyClient(ClientUpdateType_t::RebuildNeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------
|
||||||
|
//----------------------------------------------
|
||||||
|
//----------------------------------------------
|
||||||
|
bool fsm_EspuiClient_state_SendingUpdate::NotifyClient()
|
||||||
|
{
|
||||||
|
// Serial.println(F("fsm_EspuiClient_state_SendingUpdate:NotifyClient"));
|
||||||
|
return true; /* Ignore request */
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_EspuiClient_state_SendingUpdate::ProcessAck(uint16_t ControlIndex)
|
||||||
|
{
|
||||||
|
// Serial.println(F("fsm_EspuiClient_state_SendingUpdate: ProcessAck"));
|
||||||
|
if(Parent->SendControlsToClient(ControlIndex, ClientUpdateType_t::UpdateNeeded))
|
||||||
|
{
|
||||||
|
// 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.NotifyClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------
|
||||||
|
//----------------------------------------------
|
||||||
|
//----------------------------------------------
|
||||||
|
bool fsm_EspuiClient_state_Rebuilding::NotifyClient()
|
||||||
|
{
|
||||||
|
// Serial.println(F("fsm_EspuiClient_state_Rebuilding: NotifyClient"));
|
||||||
|
return true; /* Ignore request */
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_EspuiClient_state_Rebuilding::ProcessAck(uint16_t ControlIndex)
|
||||||
|
{
|
||||||
|
// Serial.println(F("fsm_EspuiClient_state_Rebuilding: ProcessAck"));
|
||||||
|
if(Parent->SendControlsToClient(ControlIndex, ClientUpdateType_t::RebuildNeeded))
|
||||||
|
{
|
||||||
|
// 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.NotifyClient();
|
||||||
|
}
|
||||||
|
}
|
79
src/ESPUIclientFsm.h
Normal file
79
src/ESPUIclientFsm.h
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
|
// forward declaration
|
||||||
|
class ESPUIclient;
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/*
|
||||||
|
* Generic fsm base class.
|
||||||
|
*/
|
||||||
|
/*****************************************************************************/
|
||||||
|
/*****************************************************************************/
|
||||||
|
class fsm_EspuiClient_state
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
fsm_EspuiClient_state() {};
|
||||||
|
virtual ~fsm_EspuiClient_state() {}
|
||||||
|
|
||||||
|
void Init();
|
||||||
|
virtual bool NotifyClient() = 0;
|
||||||
|
virtual void ProcessAck(uint16_t id) = 0;
|
||||||
|
virtual String GetStateName () = 0;
|
||||||
|
void SetParent(ESPUIclient * value) { Parent = value; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ESPUIclient * Parent = nullptr;
|
||||||
|
|
||||||
|
}; // fsm_EspuiClient_state
|
||||||
|
|
||||||
|
class fsm_EspuiClient_state_Idle : public fsm_EspuiClient_state
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
fsm_EspuiClient_state_Idle() {}
|
||||||
|
virtual ~fsm_EspuiClient_state_Idle() {}
|
||||||
|
|
||||||
|
virtual bool NotifyClient();
|
||||||
|
virtual void ProcessAck(uint16_t id);
|
||||||
|
String GetStateName() { return String(F("Idle")); }
|
||||||
|
|
||||||
|
}; // fsm_EspuiClient_state_Idle
|
||||||
|
|
||||||
|
class fsm_EspuiClient_state_SendingUpdate : public fsm_EspuiClient_state
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
fsm_EspuiClient_state_SendingUpdate() {}
|
||||||
|
virtual ~fsm_EspuiClient_state_SendingUpdate() {}
|
||||||
|
|
||||||
|
virtual bool NotifyClient();
|
||||||
|
virtual void ProcessAck(uint16_t id);
|
||||||
|
String GetStateName() { return String(F("Sending Update")); }
|
||||||
|
|
||||||
|
}; // fsm_EspuiClient_state_SendingUpdate
|
||||||
|
|
||||||
|
class fsm_EspuiClient_state_Rebuilding : public fsm_EspuiClient_state
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
fsm_EspuiClient_state_Rebuilding() {}
|
||||||
|
virtual ~fsm_EspuiClient_state_Rebuilding() {}
|
||||||
|
|
||||||
|
virtual bool NotifyClient();
|
||||||
|
virtual void ProcessAck(uint16_t id);
|
||||||
|
String GetStateName() { return String(F("Sending Rebuild")); }
|
||||||
|
|
||||||
|
}; // fsm_EspuiClient_state_Rebuilding
|
||||||
|
|
||||||
|
class fsm_EspuiClient_state_Reloading : public fsm_EspuiClient_state
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
fsm_EspuiClient_state_Reloading() {}
|
||||||
|
virtual ~fsm_EspuiClient_state_Reloading() {}
|
||||||
|
|
||||||
|
virtual bool NotifyClient() { return false; }
|
||||||
|
virtual void ProcessAck(uint16_t) {}
|
||||||
|
String GetStateName() { return String(F("Reloading")); }
|
||||||
|
|
||||||
|
}; // fsm_EspuiClient_state_Reloading
|
||||||
|
|
251
src/ESPUIcontrol.cpp
Normal file
251
src/ESPUIcontrol.cpp
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
#include "ESPUI.h"
|
||||||
|
|
||||||
|
static uint16_t idCounter = 0;
|
||||||
|
static const String ControlError = "*** ESPUI ERROR: Could not transfer control ***";
|
||||||
|
|
||||||
|
Control::Control(ControlType type, const char* label, void (*callback)(Control*, int, void*), void* UserData,
|
||||||
|
const String& value, ControlColor color, bool visible, uint16_t parentControl)
|
||||||
|
: type(type),
|
||||||
|
label(label),
|
||||||
|
callback(nullptr),
|
||||||
|
extendedCallback(callback),
|
||||||
|
user(UserData),
|
||||||
|
value(value),
|
||||||
|
color(color),
|
||||||
|
visible(visible),
|
||||||
|
wide(false),
|
||||||
|
vertical(false),
|
||||||
|
enabled(true),
|
||||||
|
parentControl(parentControl),
|
||||||
|
next(nullptr)
|
||||||
|
{
|
||||||
|
id = ++idCounter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Control::Control(const Control& Control)
|
||||||
|
: type(Control.type),
|
||||||
|
id(Control.id),
|
||||||
|
label(Control.label),
|
||||||
|
callback(Control.callback),
|
||||||
|
extendedCallback(Control.extendedCallback),
|
||||||
|
user(Control.user),
|
||||||
|
value(Control.value),
|
||||||
|
color(Control.color),
|
||||||
|
visible(Control.visible),
|
||||||
|
parentControl(Control.parentControl),
|
||||||
|
next(Control.next)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void Control::SendCallback(int type)
|
||||||
|
{
|
||||||
|
if(callback)
|
||||||
|
{
|
||||||
|
callback(this, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extendedCallback)
|
||||||
|
{
|
||||||
|
extendedCallback(this, type, user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Control::DeleteControl()
|
||||||
|
{
|
||||||
|
ControlSyncState = ControlSyncState_t::deleted;
|
||||||
|
extendedCallback = nullptr;
|
||||||
|
callback = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Control::MarshalControl(JsonObject & item, bool refresh)
|
||||||
|
{
|
||||||
|
item[F("id")] = id;
|
||||||
|
if(refresh)
|
||||||
|
{
|
||||||
|
item[F("type")] = uint32_t(type) + uint32_t(ControlType::UpdateOffset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item[F("type")] = uint32_t(type);
|
||||||
|
}
|
||||||
|
item[F("label")] = label;
|
||||||
|
item[F("value")] = value;
|
||||||
|
item[F("visible")] = visible;
|
||||||
|
item[F("color")] = (int)color;
|
||||||
|
item[F("enabled")] = enabled;
|
||||||
|
|
||||||
|
if (!panelStyle.isEmpty()) {item[F("panelStyle")] = panelStyle;}
|
||||||
|
if (!elementStyle.isEmpty()) {item[F("elementStyle")] = elementStyle;}
|
||||||
|
if (!inputType.isEmpty()) {item[F("inputType")] = inputType;}
|
||||||
|
if (wide == true) {item[F("wide")] = true;}
|
||||||
|
if (vertical == true) {item[F("vertical")] = true;}
|
||||||
|
if (parentControl != Control::noParent)
|
||||||
|
{
|
||||||
|
item[F("parentControl")] = String(parentControl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// special case for selects: to preselect an option, you have to add
|
||||||
|
// "selected" to <option>
|
||||||
|
if (ControlType::Option == type)
|
||||||
|
{
|
||||||
|
Control* ParentControl = ESPUI.getControlNoLock(parentControl);
|
||||||
|
if (nullptr == ParentControl)
|
||||||
|
{
|
||||||
|
item[F("selected")] = emptyString;
|
||||||
|
}
|
||||||
|
else if (ParentControl->value == value)
|
||||||
|
{
|
||||||
|
item[F("selected")] = F("selected");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item[F("selected")] = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Control::MarshalErrorMessage(JsonObject & item)
|
||||||
|
{
|
||||||
|
item[F("id")] = id;
|
||||||
|
item[F("type")] = uint32_t(ControlType::Label);
|
||||||
|
item[F("label")] = ControlError.c_str();
|
||||||
|
item[F("value")] = ControlError;
|
||||||
|
item[F("visible")] = true;
|
||||||
|
item[F("color")] = (int)ControlColor::Carrot;
|
||||||
|
item[F("enabled")] = true;
|
||||||
|
|
||||||
|
if (parentControl != Control::noParent)
|
||||||
|
{
|
||||||
|
item[F("parentControl")] = String(parentControl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Control::onWsEvent(String & cmd, String& data)
|
||||||
|
{
|
||||||
|
do // once
|
||||||
|
{
|
||||||
|
if (!HasCallback())
|
||||||
|
{
|
||||||
|
#if defined(DEBUG_ESPUI)
|
||||||
|
if (ESPUI.verbosity)
|
||||||
|
{
|
||||||
|
Serial.println(String(F("Control::onWsEvent:No callback found for ID ")) + String(id));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serial.println("Control::onWsEvent:Generating callback");
|
||||||
|
if (cmd.equals(F("bdown")))
|
||||||
|
{
|
||||||
|
SendCallback(B_DOWN);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd.equals(F("bup")))
|
||||||
|
{
|
||||||
|
SendCallback(B_UP);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd.equals(F("pfdown")))
|
||||||
|
{
|
||||||
|
SendCallback(P_FOR_DOWN);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd.equals(F("pfup")))
|
||||||
|
{
|
||||||
|
SendCallback(P_FOR_UP);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd.equals(F("pldown")))
|
||||||
|
{
|
||||||
|
SendCallback(P_LEFT_DOWN);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (cmd.equals(F("plup")))
|
||||||
|
{
|
||||||
|
SendCallback(P_LEFT_UP);
|
||||||
|
}
|
||||||
|
else if (cmd.equals(F("prdown")))
|
||||||
|
{
|
||||||
|
SendCallback(P_RIGHT_DOWN);
|
||||||
|
}
|
||||||
|
else if (cmd.equals(F("prup")))
|
||||||
|
{
|
||||||
|
SendCallback(P_RIGHT_UP);
|
||||||
|
}
|
||||||
|
else if (cmd.equals(F("pbdown")))
|
||||||
|
{
|
||||||
|
SendCallback(P_BACK_DOWN);
|
||||||
|
}
|
||||||
|
else if (cmd.equals(F("pbup")))
|
||||||
|
{
|
||||||
|
SendCallback(P_BACK_UP);
|
||||||
|
}
|
||||||
|
else if (cmd.equals(F("pcdown")))
|
||||||
|
{
|
||||||
|
SendCallback(P_CENTER_DOWN);
|
||||||
|
}
|
||||||
|
else if (cmd.equals(F("pcup")))
|
||||||
|
{
|
||||||
|
SendCallback(P_CENTER_UP);
|
||||||
|
}
|
||||||
|
else if (cmd.equals(F("sactive")))
|
||||||
|
{
|
||||||
|
value = "1";
|
||||||
|
SendCallback(S_ACTIVE);
|
||||||
|
}
|
||||||
|
else if (cmd.equals(F("sinactive")))
|
||||||
|
{
|
||||||
|
value = "0";
|
||||||
|
// updateControl(c, client->id());
|
||||||
|
SendCallback(S_INACTIVE);
|
||||||
|
}
|
||||||
|
else if (cmd.equals(F("slvalue")))
|
||||||
|
{
|
||||||
|
value = data;
|
||||||
|
// updateControl(c, client->id());
|
||||||
|
SendCallback(SL_VALUE);
|
||||||
|
}
|
||||||
|
else if (cmd.equals(F("nvalue")))
|
||||||
|
{
|
||||||
|
value = data;
|
||||||
|
// updateControl(c, client->id());
|
||||||
|
SendCallback(N_VALUE);
|
||||||
|
}
|
||||||
|
else if (cmd.equals(F("tvalue")))
|
||||||
|
{
|
||||||
|
value = data;
|
||||||
|
// updateControl(c, client->id());
|
||||||
|
SendCallback(T_VALUE);
|
||||||
|
}
|
||||||
|
else if (cmd.equals(F("tabvalue")))
|
||||||
|
{
|
||||||
|
SendCallback(0);
|
||||||
|
}
|
||||||
|
else if (cmd.equals(F("svalue")))
|
||||||
|
{
|
||||||
|
value = data;
|
||||||
|
// updateControl(c, client->id());
|
||||||
|
SendCallback(S_VALUE);
|
||||||
|
}
|
||||||
|
else if (cmd.equals(F("time")))
|
||||||
|
{
|
||||||
|
value = data;
|
||||||
|
// updateControl(c, client->id());
|
||||||
|
SendCallback(TM_VALUE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#if defined(DEBUG_ESPUI)
|
||||||
|
if (ESPUI.verbosity)
|
||||||
|
{
|
||||||
|
Serial.println(F("Control::onWsEvent:Malformed message from the websocket"));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
} while (false);
|
||||||
|
}
|
132
src/ESPUIcontrol.h
Normal file
132
src/ESPUIcontrol.h
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
|
enum ControlType : uint8_t
|
||||||
|
{
|
||||||
|
// fixed Controls
|
||||||
|
Title = 0,
|
||||||
|
|
||||||
|
// updatable Controls
|
||||||
|
Pad,
|
||||||
|
PadWithCenter,
|
||||||
|
Button,
|
||||||
|
Label,
|
||||||
|
Switcher,
|
||||||
|
Slider,
|
||||||
|
Number,
|
||||||
|
Text,
|
||||||
|
Graph,
|
||||||
|
GraphPoint,
|
||||||
|
Tab,
|
||||||
|
Select,
|
||||||
|
Option,
|
||||||
|
Min,
|
||||||
|
Max,
|
||||||
|
Step,
|
||||||
|
Gauge,
|
||||||
|
Accel,
|
||||||
|
Separator,
|
||||||
|
Time,
|
||||||
|
|
||||||
|
UpdateOffset = 100,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ControlColor : uint8_t
|
||||||
|
{
|
||||||
|
Turquoise,
|
||||||
|
Emerald,
|
||||||
|
Peterriver,
|
||||||
|
Wetasphalt,
|
||||||
|
Sunflower,
|
||||||
|
Carrot,
|
||||||
|
Alizarin,
|
||||||
|
Dark,
|
||||||
|
None = 0xFF
|
||||||
|
};
|
||||||
|
|
||||||
|
class Control
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ControlType type;
|
||||||
|
uint16_t id; // just mirroring the id here for practical reasons
|
||||||
|
const char* label;
|
||||||
|
void (*callback)(Control*, int);
|
||||||
|
void (*extendedCallback)(Control*, int, void*);
|
||||||
|
void* user;
|
||||||
|
String value;
|
||||||
|
ControlColor color;
|
||||||
|
bool visible;
|
||||||
|
bool wide;
|
||||||
|
bool vertical;
|
||||||
|
bool enabled;
|
||||||
|
uint16_t parentControl;
|
||||||
|
String panelStyle;
|
||||||
|
String elementStyle;
|
||||||
|
String inputType;
|
||||||
|
Control* next;
|
||||||
|
|
||||||
|
static constexpr uint16_t noParent = 0xffff;
|
||||||
|
|
||||||
|
Control(ControlType type,
|
||||||
|
const char* label,
|
||||||
|
void (*callback)(Control*, int, void*),
|
||||||
|
void* UserData,
|
||||||
|
const String& value,
|
||||||
|
ControlColor color,
|
||||||
|
bool visible,
|
||||||
|
uint16_t parentControl);
|
||||||
|
|
||||||
|
Control(const Control& Control);
|
||||||
|
|
||||||
|
void SendCallback(int type);
|
||||||
|
bool HasCallback() { return ((nullptr != callback) || (nullptr != extendedCallback)); }
|
||||||
|
void MarshalControl(ArduinoJson::JsonObject& item, bool refresh);
|
||||||
|
void MarshalErrorMessage(ArduinoJson::JsonObject& item);
|
||||||
|
bool ToBeDeleted() { return (ControlSyncState_t::deleted == ControlSyncState); }
|
||||||
|
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);
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum ControlSyncState_t
|
||||||
|
{
|
||||||
|
synchronized = 0,
|
||||||
|
updated,
|
||||||
|
deleted,
|
||||||
|
};
|
||||||
|
ControlSyncState_t ControlSyncState = ControlSyncState_t::synchronized;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define UI_TITLE ControlType::Title
|
||||||
|
#define UI_LABEL ControlType::Label
|
||||||
|
#define UI_BUTTON ControlType::Button
|
||||||
|
#define UI_SWITCHER ControlType::Switcher
|
||||||
|
#define UI_PAD ControlType::Pad
|
||||||
|
#define UI_CPAD ControlType::Cpad
|
||||||
|
#define UI_SLIDER ControlType::Slider
|
||||||
|
#define UI_NUMBER ControlType::Number
|
||||||
|
#define UI_TEXT_INPUT ControlType::Text
|
||||||
|
#define UI_GRAPH ControlType::Graph
|
||||||
|
#define UI_ADD_GRAPH_POINT ControlType::GraphPoint
|
||||||
|
|
||||||
|
#define UPDATE_LABEL ControlType::UpdateLabel
|
||||||
|
#define UPDATE_SWITCHER ControlType::UpdateSwitcher
|
||||||
|
#define UPDATE_SLIDER ControlType::UpdateSlider
|
||||||
|
#define UPDATE_NUMBER ControlType::UpdateNumber
|
||||||
|
#define UPDATE_TEXT_INPUT ControlType::UpdateText
|
||||||
|
#define CLEAR_GRAPH ControlType::ClearGraph
|
||||||
|
|
||||||
|
// Colors
|
||||||
|
#define COLOR_TURQUOISE ControlColor::Turquoise
|
||||||
|
#define COLOR_EMERALD ControlColor::Emerald
|
||||||
|
#define COLOR_PETERRIVER ControlColor::Peterriver
|
||||||
|
#define COLOR_WETASPHALT ControlColor::Wetasphalt
|
||||||
|
#define COLOR_SUNFLOWER ControlColor::Sunflower
|
||||||
|
#define COLOR_CARROT ControlColor::Carrot
|
||||||
|
#define COLOR_ALIZARIN ControlColor::Alizarin
|
||||||
|
#define COLOR_DARK ControlColor::Dark
|
||||||
|
#define COLOR_NONE ControlColor::None
|
File diff suppressed because one or more lines are too long
@ -16,4 +16,4 @@ function renderGraphSvg(dataArray,renderId){var figure=document.getElementById(r
|
|||||||
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[1245] PROGMEM = { 31,139,8,0,124,57,147,98,2,255,205,87,95,111,219,54,16,127,247,167,112,4,44,16,107,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,219,82,134,58,45,186,135,56,226,253,231,241,119,119,228,98,157,69,130,229,217,56,101,25,253,192,73,177,116,11,194,105,38,188,234,38,138,104,89,230,220,147,246,11,237,162,60,43,197,120,203,98,177,196,175,95,5,161,89,47,41,75,150,2,207,27,66,178,22,130,114,60,183,235,130,85,52,45,63,81,254,200,162,127,240,85,16,46,172,219,108,189,162,156,69,143,156,100,229,34,231,176,112,99,34,200,29,203,60,253,159,84,94,81,169,21,252,146,10,237,54,132,143,21,227,61,91,44,112,45,49,181,26,69,165,201,90,116,106,212,20,235,51,1,87,216,48,103,86,217,139,242,156,199,134,101,105,51,35,19,114,42,214,60,219,137,252,157,146,121,99,131,213,145,161,157,225,234,133,245,140,94,52,126,38,218,111,184,247,68,254,30,104,173,178,246,215,104,235,149,137,17,189,104,67,153,212,6,195,253,62,220,143,154,44,145,138,149,159,105,22,83,14,249,201,57,131,243,81,210,153,39,108,222,76,102,148,220,7,158,175,11,28,231,17,100,54,19,126,196,41,17,244,215,148,170,213,253,131,235,44,133,40,222,204,102,219,237,214,223,94,249,57,79,102,175,130,32,152,149,155,196,241,156,196,65,161,53,244,137,192,33,159,105,167,0,93,48,213,196,227,151,84,220,8,193,217,223,107,65,93,39,74,73,89,58,94,103,39,19,103,170,132,107,247,42,45,216,128,168,94,147,10,107,212,77,59,84,169,164,12,246,14,201,32,92,175,217,162,155,46,140,177,83,57,104,103,119,119,20,85,12,129,223,141,157,137,242,62,113,224,67,154,143,91,77,35,85,67,67,33,164,220,85,190,24,214,167,205,174,177,18,8,217,100,130,118,224,211,101,83,125,178,63,29,2,31,227,224,242,146,93,96,173,100,78,76,208,74,156,155,100,165,11,9,83,255,124,150,101,148,255,246,120,119,139,239,212,198,22,41,64,202,109,208,225,50,84,203,29,238,184,114,60,214,203,144,142,103,54,218,195,139,129,233,188,164,171,131,243,37,69,1,248,124,183,100,105,236,42,29,4,8,222,195,214,233,121,201,110,104,80,243,157,100,75,157,228,107,44,117,210,219,100,203,167,146,45,219,100,3,253,155,148,71,194,89,124,11,13,243,153,229,241,195,15,191,26,58,99,57,4,140,24,180,156,169,255,179,62,254,33,112,248,87,154,109,179,244,95,39,207,142,107,76,185,182,71,117,128,44,107,113,136,111,144,55,128,74,171,161,161,57,234,23,178,96,69,161,153,130,39,76,107,161,237,205,106,112,54,189,185,103,104,122,85,51,220,60,249,120,216,175,211,231,99,168,241,232,115,10,121,118,81,40,79,40,80,36,23,173,220,146,148,247,144,39,23,125,249,114,33,79,169,118,68,193,30,85,136,202,205,3,156,93,150,96,115,106,205,22,218,205,250,153,209,52,53,219,10,200,99,129,112,11,137,164,110,79,44,151,151,189,161,180,222,39,216,128,100,244,100,0,35,21,193,232,201,16,246,163,244,20,147,245,64,114,74,184,140,80,53,133,210,94,220,182,225,244,2,36,213,208,236,96,163,200,89,38,206,5,135,86,126,118,187,106,173,12,108,89,223,99,180,84,249,157,225,244,181,135,175,39,191,186,68,253,78,210,53,197,199,167,109,110,6,184,139,135,70,216,48,101,171,44,251,148,37,238,34,229,72,57,98,60,74,207,174,78,163,13,9,53,31,199,185,87,189,119,136,167,102,239,0,143,131,229,121,61,131,190,235,240,232,100,18,138,122,6,69,213,97,118,51,53,48,87,6,47,21,195,67,229,229,147,35,101,234,255,162,249,29,56,119,43,207,100,107,144,93,223,72,70,61,53,219,106,40,129,22,76,184,231,101,210,34,112,197,50,23,121,157,53,169,96,29,76,204,181,211,235,94,85,161,130,158,52,42,143,140,202,35,163,7,23,220,198,133,153,114,77,107,209,73,111,172,250,230,237,113,44,163,242,127,42,243,21,243,203,175,95,68,135,102,52,169,78,253,183,48,212,233,159,92,155,211,207,210,135,77,162,159,92,55,156,19,233,25,198,199,216,52,137,5,75,214,188,83,170,9,21,117,37,188,149,31,99,183,17,174,199,143,17,87,157,70,67,224,62,143,105,169,250,77,77,231,116,149,111,168,65,71,77,130,86,41,52,1,213,83,17,202,232,220,218,83,191,40,132,223,35,152,111,24,221,190,205,85,37,4,227,96,252,122,30,140,231,243,160,87,178,80,141,152,111,232,77,89,208,72,232,119,35,104,193,117,42,254,3,254,198,43,74,69,61,193,204,131,30,44,120,238,193,35,214,3,168,121,43,251,158,102,56,176,79,222,186,251,182,175,86,219,190,199,236,90,41,250,41,205,18,177,132,231,109,214,47,166,132,254,132,155,249,95,126,5,66,122,98,116,165,148,171,189,242,222,163,186,210,207,102,136,170,143,7,247,126,245,36,70,29,12,232,142,4,74,170,156,83,233,170,165,215,112,161,120,138,118,207,121,99,40,87,113,33,84,43,147,234,28,101,244,3,178,41,255,167,217,148,207,201,166,86,134,43,89,93,101,221,182,12,152,133,90,251,23,198,24,146,161,158,18,0,0 };
|
const uint8_t JS_GRAPH_GZIP[1245] PROGMEM = { 31,139,8,0,210,8,51,99,2,255,205,87,95,111,219,54,16,127,247,167,112,4,44,16,107,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,219,82,134,58,45,186,135,56,226,253,231,241,119,119,228,98,157,69,130,229,217,56,101,25,253,192,73,177,116,11,194,105,38,188,234,38,138,104,89,230,220,147,246,11,237,162,60,43,197,120,203,98,177,196,175,95,5,161,89,47,41,75,150,2,207,27,66,178,22,130,114,60,183,235,130,85,52,45,63,81,254,200,162,127,240,85,16,46,172,219,108,189,162,156,69,143,156,100,229,34,231,176,112,99,34,200,29,203,60,253,159,84,94,81,169,21,252,146,10,237,54,132,143,21,227,61,91,44,112,45,49,181,26,69,165,201,90,116,106,212,20,235,51,1,87,216,48,103,86,217,139,242,156,199,134,101,105,51,35,19,114,42,214,60,219,137,252,157,146,121,99,131,213,145,161,157,225,234,133,245,140,94,52,126,38,218,111,184,247,68,254,30,104,173,178,246,215,104,235,149,137,17,189,104,67,153,212,6,195,253,62,220,143,154,44,145,138,149,159,105,22,83,14,249,201,57,131,243,81,210,153,39,108,222,76,102,148,220,7,158,175,11,28,231,17,100,54,19,126,196,41,17,244,215,148,170,213,253,131,235,44,133,40,222,204,102,219,237,214,223,94,249,57,79,102,175,130,32,152,149,155,196,241,156,196,65,161,53,244,137,192,33,159,105,167,0,93,48,213,196,227,151,84,220,8,193,217,223,107,65,93,39,74,73,89,58,94,103,39,19,103,170,132,107,247,42,45,216,128,168,94,147,10,107,212,77,59,84,169,164,12,246,14,201,32,92,175,217,162,155,46,140,177,83,57,104,103,119,119,20,85,12,129,223,141,157,137,242,62,113,224,67,154,143,91,77,35,85,67,67,33,164,220,85,190,24,214,167,205,174,177,18,8,217,100,130,118,224,211,101,83,125,178,63,29,2,31,227,224,242,146,93,96,173,100,78,76,208,74,156,155,100,165,11,9,83,255,124,150,101,148,255,246,120,119,139,239,212,198,22,41,64,202,109,208,225,50,84,203,29,238,184,114,60,214,203,144,142,103,54,218,195,139,129,233,188,164,171,131,243,37,69,1,248,124,183,100,105,236,42,29,4,8,222,195,214,233,121,201,110,104,80,243,157,100,75,157,228,107,44,117,210,219,100,203,167,146,45,219,100,3,253,155,148,71,194,89,124,11,13,243,153,229,241,195,15,191,26,58,99,57,4,140,24,180,156,169,255,179,62,254,33,112,248,87,154,109,179,244,95,39,207,142,107,76,185,182,71,117,128,44,107,113,136,111,144,55,128,74,171,161,161,57,234,23,178,96,69,161,153,130,39,76,107,161,237,205,106,112,54,189,185,103,104,122,85,51,220,60,249,120,216,175,211,231,99,168,241,232,115,10,121,118,81,40,79,40,80,36,23,173,220,146,148,247,144,39,23,125,249,114,33,79,169,118,68,193,30,85,136,202,205,3,156,93,150,96,115,106,205,22,218,205,250,153,209,52,53,219,10,200,99,129,112,11,137,164,110,79,44,151,151,189,161,180,222,39,216,128,100,244,100,0,35,21,193,232,201,16,246,163,244,20,147,245,64,114,74,184,140,80,53,133,210,94,220,182,225,244,2,36,213,208,236,96,163,200,89,38,206,5,135,86,126,118,187,106,173,12,108,89,223,99,180,84,249,157,225,244,181,135,175,39,191,186,68,253,78,210,53,197,199,167,109,110,6,184,139,135,70,216,48,101,171,44,251,148,37,238,34,229,72,57,98,60,74,207,174,78,163,13,9,53,31,199,185,87,189,119,136,167,102,239,0,143,131,229,121,61,131,190,235,240,232,100,18,138,122,6,69,213,97,118,51,53,48,87,6,47,21,195,67,229,229,147,35,101,234,255,162,249,29,56,119,43,207,100,107,144,93,223,72,70,61,53,219,106,40,129,22,76,184,231,101,210,34,112,197,50,23,121,157,53,169,96,29,76,204,181,211,235,94,85,161,130,158,52,42,143,140,202,35,163,7,23,220,198,133,153,114,77,107,209,73,111,172,250,230,237,113,44,163,242,127,42,243,21,243,203,175,95,68,135,102,52,169,78,253,183,48,212,233,159,92,155,211,207,210,135,77,162,159,92,55,156,19,233,25,198,199,216,52,137,5,75,214,188,83,170,9,21,117,37,188,149,31,99,183,17,174,199,143,17,87,157,70,67,224,62,143,105,169,250,77,77,231,116,149,111,168,65,71,77,130,86,41,52,1,213,83,17,202,232,220,218,83,191,40,132,223,35,152,111,24,221,190,205,85,37,4,227,96,252,122,30,140,231,243,160,87,178,80,141,152,111,232,77,89,208,72,232,119,35,104,193,117,42,254,3,254,198,43,74,69,61,193,204,131,30,44,120,238,193,35,214,3,168,121,43,251,158,102,56,176,79,222,186,251,182,175,86,219,190,199,236,90,41,250,41,205,18,177,132,231,109,214,47,166,132,254,132,155,249,95,126,5,66,122,98,116,165,148,171,189,242,222,163,186,210,207,102,136,170,143,7,247,126,245,36,70,29,12,232,142,4,74,170,156,83,233,170,165,215,112,161,120,138,118,207,121,99,40,87,113,33,84,43,147,234,28,101,244,3,178,41,255,167,217,148,207,201,166,86,134,43,89,93,101,221,182,12,152,133,90,251,23,198,24,146,161,158,18,0,0 };
|
||||||
|
@ -2,4 +2,4 @@ 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>
|
<!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,124,57,147,98,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 };
|
||||||
|
@ -2,4 +2,4 @@ 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:hidden}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:hidden}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[861] PROGMEM = { 31,139,8,0,124,57,147,98,2,255,149,84,237,142,155,58,16,125,149,104,171,74,183,146,137,216,237,199,94,25,221,39,137,242,99,176,7,112,227,47,217,38,155,20,241,238,119,12,132,36,219,108,165,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,133,5,200,159,125,76,252,185,44,63,87,197,27,214,7,149,30,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,103,39,136,153,224,116,220,127,89,83,88,103,177,234,80,181,93,162,238,118,157,146,18,237,158,37,52,244,59,225,221,185,17,134,26,196,33,247,98,101,33,156,118,129,167,64,12,123,8,104,211,8,28,168,163,35,145,195,59,71,112,6,215,167,12,33,211,86,215,97,151,84,210,184,31,106,23,136,147,162,118,41,57,195,159,253,105,35,233,21,229,88,179,72,240,108,59,43,248,54,131,170,157,150,163,108,236,28,140,233,172,145,171,68,61,138,177,123,94,130,36,25,127,65,83,45,42,109,127,188,162,217,148,35,125,30,110,16,243,79,77,83,86,51,236,79,101,89,142,209,128,214,55,41,254,37,181,99,79,40,122,127,19,125,253,254,185,154,104,190,176,84,121,23,85,86,142,7,36,142,168,225,15,185,207,153,146,243,188,216,126,71,147,115,15,75,215,197,246,37,71,148,105,23,58,136,163,120,108,39,153,120,32,239,124,25,50,131,141,118,111,124,214,100,156,141,117,113,226,51,117,248,173,244,167,177,11,67,97,220,47,162,243,148,241,42,219,242,44,51,233,145,67,213,7,225,85,113,79,41,215,74,208,39,55,10,71,198,62,212,146,76,135,44,130,241,119,3,101,156,117,164,183,64,182,190,85,87,174,8,213,88,247,212,161,101,202,250,62,49,231,211,108,125,34,132,236,206,242,136,145,89,96,152,101,80,182,163,217,76,83,134,245,99,157,181,57,211,21,222,81,69,85,107,188,84,152,83,14,211,212,78,54,108,92,48,179,81,151,19,29,173,131,205,4,100,151,206,30,255,123,154,227,79,123,118,27,164,193,194,244,46,70,82,25,69,193,225,178,27,192,123,4,42,34,144,207,73,42,209,135,72,45,120,167,136,214,176,148,220,209,188,0,97,148,251,219,226,107,112,88,46,73,108,160,215,105,185,196,249,164,96,227,68,31,11,101,45,45,140,233,222,239,241,213,44,149,7,41,179,168,229,56,29,29,110,29,106,137,7,208,227,109,63,162,67,113,32,225,223,183,14,180,27,158,242,72,174,46,89,167,243,244,190,198,114,199,246,166,198,240,180,39,116,11,55,19,180,34,122,101,139,91,241,63,60,79,107,225,254,252,176,0,159,252,119,39,3,113,46,186,199,50,100,221,27,133,90,86,127,242,255,229,226,95,141,199,67,12,87,252,115,164,16,25,134,126,212,242,135,87,36,10,23,32,239,142,71,29,77,214,157,90,34,67,94,164,206,251,49,58,173,228,38,42,77,147,176,142,199,230,197,95,37,218,126,165,117,178,217,254,120,153,30,175,121,183,104,108,209,202,71,142,89,135,240,126,240,47,179,250,251,250,77,217,189,151,189,77,147,171,193,71,228,151,151,106,249,145,151,193,82,64,178,212,13,215,130,255,3,4,241,118,208,151,7,0,0 };
|
const uint8_t CSS_NORMALIZE_GZIP[861] PROGMEM = { 31,139,8,0,210,8,51,99,2,255,149,84,237,142,155,58,16,125,149,104,171,74,183,146,137,216,237,199,94,25,221,39,137,242,99,176,7,112,227,47,217,38,155,20,241,238,119,12,132,36,219,108,165,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,133,5,200,159,125,76,252,185,44,63,87,197,27,214,7,149,30,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,103,39,136,153,224,116,220,127,89,83,88,103,177,234,80,181,93,162,238,118,157,146,18,237,158,37,52,244,59,225,221,185,17,134,26,196,33,247,98,101,33,156,118,129,167,64,12,123,8,104,211,8,28,168,163,35,145,195,59,71,112,6,215,167,12,33,211,86,215,97,151,84,210,184,31,106,23,136,147,162,118,41,57,195,159,253,105,35,233,21,229,88,179,72,240,108,59,43,248,54,131,170,157,150,163,108,236,28,140,233,172,145,171,68,61,138,177,123,94,130,36,25,127,65,83,45,42,109,127,188,162,217,148,35,125,30,110,16,243,79,77,83,86,51,236,79,101,89,142,209,128,214,55,41,254,37,181,99,79,40,122,127,19,125,253,254,185,154,104,190,176,84,121,23,85,86,142,7,36,142,168,225,15,185,207,153,146,243,188,216,126,71,147,115,15,75,215,197,246,37,71,148,105,23,58,136,163,120,108,39,153,120,32,239,124,25,50,131,141,118,111,124,214,100,156,141,117,113,226,51,117,248,173,244,167,177,11,67,97,220,47,162,243,148,241,42,219,242,44,51,233,145,67,213,7,225,85,113,79,41,215,74,208,39,55,10,71,198,62,212,146,76,135,44,130,241,119,3,101,156,117,164,183,64,182,190,85,87,174,8,213,88,247,212,161,101,202,250,62,49,231,211,108,125,34,132,236,206,242,136,145,89,96,152,101,80,182,163,217,76,83,134,245,99,157,181,57,211,21,222,81,69,85,107,188,84,152,83,14,211,212,78,54,108,92,48,179,81,151,19,29,173,131,205,4,100,151,206,30,255,123,154,227,79,123,118,27,164,193,194,244,46,70,82,25,69,193,225,178,27,192,123,4,42,34,144,207,73,42,209,135,72,45,120,167,136,214,176,148,220,209,188,0,97,148,251,219,226,107,112,88,46,73,108,160,215,105,185,196,249,164,96,227,68,31,11,101,45,45,140,233,222,239,241,213,44,149,7,41,179,168,229,56,29,29,110,29,106,137,7,208,227,109,63,162,67,113,32,225,223,183,14,180,27,158,242,72,174,46,89,167,243,244,190,198,114,199,246,166,198,240,180,39,116,11,55,19,180,34,122,101,139,91,241,63,60,79,107,225,254,252,176,0,159,252,119,39,3,113,46,186,199,50,100,221,27,133,90,86,127,242,255,229,226,95,141,199,67,12,87,252,115,164,16,25,134,126,212,242,135,87,36,10,23,32,239,142,71,29,77,214,157,90,34,67,94,164,206,251,49,58,173,228,38,42,77,147,176,142,199,230,197,95,37,218,126,165,117,178,217,254,120,153,30,175,121,183,104,108,209,202,71,142,89,135,240,126,240,47,179,250,251,250,77,217,189,151,189,77,147,171,193,71,228,151,151,106,249,145,151,193,82,64,178,212,13,215,130,255,3,4,241,118,208,151,7,0,0 };
|
||||||
|
@ -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,124,57,147,98,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,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 };
|
||||||
|
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
Loading…
Reference in New Issue
Block a user