Update to 1.1.0

* Reduced socket traffic on load.
* Added code to check max socket connections and reconnect when a slot becomes available.
* Improve load times for Shade Manager
This commit is contained in:
Robert Strouse 2023-02-05 10:20:53 -08:00
parent 0ab2444104
commit dcd90a4c29
15 changed files with 103 additions and 63 deletions

View file

@ -58,9 +58,8 @@ void ConfigSettings::print() {
this->NTP.print(); this->NTP.print();
this->WIFI.print(); this->WIFI.print();
} }
void ConfigSettings::emitSockets() { void ConfigSettings::emitSockets() {}
void ConfigSettings::emitSockets(uint8_t num) {}
}
bool MQTTSettings::begin() { bool MQTTSettings::begin() {
this->load(); this->load();
return true; return true;

View file

@ -2,7 +2,7 @@
#ifndef configsettings_h #ifndef configsettings_h
#define configsettings_h #define configsettings_h
#define FW_VERSION "v1.0.9" #define FW_VERSION "v1.1.0"
enum DeviceStatus { enum DeviceStatus {
DS_OK = 0, DS_OK = 0,
DS_ERROR = 1, DS_ERROR = 1,
@ -78,6 +78,7 @@ class ConfigSettings: BaseSettings {
bool load(); bool load();
void print(); void print();
void emitSockets(); void emitSockets();
void emitSockets(uint8_t num);
bool toJSON(DynamicJsonDocument &doc); bool toJSON(DynamicJsonDocument &doc);
}; };

View file

@ -58,7 +58,17 @@ void Network::emitSockets() {
} }
else else
sockEmit.sendToClients("wifiStrength", "{\"ssid\":\"\", \"strength\":-100,\"channel\":-1}"); sockEmit.sendToClients("wifiStrength", "{\"ssid\":\"\", \"strength\":-100,\"channel\":-1}");
}
void Network::emitSockets(uint8_t num) {
if(WiFi.status() == WL_CONNECTED) {
char buf[128];
snprintf(buf, sizeof(buf), "{\"ssid\":\"%s\",\"strength\":%d,\"channel\":%d}", WiFi.SSID(), WiFi.RSSI(), WiFi.channel());
sockEmit.sendToClient(num, "wifiStrength", buf);
this->lastRSSI = WiFi.RSSI();
this->lastChannel = WiFi.channel();
}
else
sockEmit.sendToClient(num, "wifiStrength", "{\"ssid\":\"\", \"strength\":-100,\"channel\":-1}");
} }
void Network::setConnected() { void Network::setConnected() {
WiFi.hostname(settings.WIFI.hostname); WiFi.hostname(settings.WIFI.hostname);

View file

@ -25,6 +25,7 @@ class Network {
void loop(); void loop();
void end(); void end();
void emitSockets(); void emitSockets();
void emitSockets(uint8_t num);
uint32_t getChipId(); uint32_t getChipId();
}; };
#endif #endif

View file

@ -4,9 +4,11 @@
#include "Sockets.h" #include "Sockets.h"
#include "ConfigSettings.h" #include "ConfigSettings.h"
#include "Somfy.h" #include "Somfy.h"
#include "Network.h"
extern ConfigSettings settings; extern ConfigSettings settings;
extern Network net;
extern SomfyShadeController somfy; extern SomfyShadeController somfy;
WebSocketsServer sockServer = WebSocketsServer(8080); WebSocketsServer sockServer = WebSocketsServer(8080);
@ -28,6 +30,7 @@ void SocketEmitter::startup() {
} }
void SocketEmitter::begin() { void SocketEmitter::begin() {
sockServer.begin(); sockServer.begin();
sockServer.enableHeartbeat(20000, 10000, 3);
sockServer.onEvent(this->wsEvent); sockServer.onEvent(this->wsEvent);
} }
void SocketEmitter::loop() { void SocketEmitter::loop() {
@ -57,8 +60,17 @@ void SocketEmitter::end() { sockServer.close(); }
void SocketEmitter::disconnect() { sockServer.disconnect(); } void SocketEmitter::disconnect() { sockServer.disconnect(); }
void SocketEmitter::wsEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) { void SocketEmitter::wsEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) {
switch(type) { switch(type) {
case WStype_ERROR:
if(length > 0)
Serial.printf("Socket Error: %s\n", payload);
else
Serial.println("Socket Error: \n");
break;
case WStype_DISCONNECTED: case WStype_DISCONNECTED:
Serial.printf("Socket [%u] Disconnected!\n", num); if(length > 0)
Serial.printf("Socket [%u] Disconnected!\n [%s]", num, payload);
else
Serial.printf("Socket [%u] Disconnected!\n", num);
break; break;
case WStype_CONNECTED: case WStype_CONNECTED:
{ {
@ -66,8 +78,9 @@ void SocketEmitter::wsEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t
Serial.printf("Socket [%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); Serial.printf("Socket [%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
// Send all the current shade settings to the client. // Send all the current shade settings to the client.
sockServer.sendTXT(num, "Connected"); sockServer.sendTXT(num, "Connected");
settings.emitSockets(); settings.emitSockets(num);
somfy.emitState(num); somfy.emitState(num);
net.emitSockets(num);
} }
break; break;
case WStype_TEXT: case WStype_TEXT:
@ -86,8 +99,11 @@ void SocketEmitter::wsEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t
// send message to client // send message to client
// sockServer.sendBIN(num, payload, length); // sockServer.sendBIN(num, payload, length);
break; break;
case WStype_PONG:
//Serial.printf("Pong from %u\n", num);
break;
case WStype_PING: case WStype_PING:
Serial.printf("Ping from %u\n", num); //Serial.printf("Ping from %u\n", num);
break; break;
} }

Binary file not shown.

Binary file not shown.

View file

@ -6,39 +6,8 @@
<link rel="stylesheet" href="main.css" type="text/css" /> <link rel="stylesheet" href="main.css" type="text/css" />
<link rel="stylesheet" href="icons.css" type="text/css" /> <link rel="stylesheet" href="icons.css" type="text/css" />
<script> <script>
//{"ssid":"Casa Del Mar Resort and Spa 2.4","strength":-53,"channel":2}
/******* SPRING TYPE BLUE ********/
/******** E07-M1101D-SMA *********/
// 1 Black: GND
// 2 Red: VCC 3v3
// 3 Purple: GDO0 - TX GPIO12
// 4 Blue: CSN GPIO5
// 5 Brown: SCK GPIO18
// 6 Green: MOSI GPIO23
// 7 Orange: MISO GPIO19
// 8 Yellow: GD02 - RX GPIO13
/********** D-SUN GREEN *********/
// 1 GND
// 2 VCC
// 3 MOSI
// 4 SCLK
// 5 MISO
// 6 GDO2-RX
// 7 GDO0-TX
// 8 CSN
/********** KYNAR colors ********/
// Black: GND
// Red: VCC
// White: GDO0-TX
// Blue: CSN
// White: SCK
// Yellow: MOSI
// Green: MISO
// Blue: GDO2-RX
class General { class General {
init() { async init() {
this.setAppVersion(); this.setAppVersion();
this.setTimeZones(); this.setTimeZones();
this.loadGeneral(); this.loadGeneral();
@ -158,7 +127,6 @@
break; break;
} }
} }
//if (dd.selectedIndex < 0) dd.selectedIndex = 0;
document.getElementById('spanFwVersion').innerText = settings.fwVersion; document.getElementById('spanFwVersion').innerText = settings.fwVersion;
document.getElementsByName('hostname')[0].value = settings.hostname; document.getElementsByName('hostname')[0].value = settings.hostname;
document.getElementsByName('ntptimeserver')[0].value = settings.ntpServer; document.getElementsByName('ntptimeserver')[0].value = settings.ntpServer;
@ -167,7 +135,7 @@
}); });
}; };
setAppVersion() { document.getElementById('spanAppVersion').innerText = 'v1.0.9'; }; setAppVersion() { document.getElementById('spanAppVersion').innerText = 'v1.1.0'; };
setTimeZones() { setTimeZones() {
let dd = document.getElementById('selTimeZone'); let dd = document.getElementById('selTimeZone');
dd.length = 0; dd.length = 0;
@ -247,14 +215,17 @@
class Wifi { class Wifi {
init() { init() {
document.getElementById("divNetworkStrength").innerHTML = this.displaySignal(-100); document.getElementById("divNetworkStrength").innerHTML = this.displaySignal(-100);
this.loadAPs();
//this.loadAPs();
}; };
async loadAPs() { async loadAPs() {
if (document.getElementById('btnScanAPs').classList.contains('disabled')) return; if (document.getElementById('btnScanAPs').classList.contains('disabled')) return;
document.getElementById('divAps').innerHTML = '<div style="display:flex;justify-content:center;align-items:center;"><div class="lds-roller"><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div></div>'; document.getElementById('divAps').innerHTML = '<div style="display:flex;justify-content:center;align-items:center;"><div class="lds-roller"><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div></div>';
document.getElementById('btnScanAPs').classList.add('disabled'); document.getElementById('btnScanAPs').classList.add('disabled');
document.getElementById('btnConnectWiFi').classList.add('disabled');
getJSON('/scanaps', (err, aps) => { getJSON('/scanaps', (err, aps) => {
document.getElementById('btnScanAPs').classList.remove('disabled'); document.getElementById('btnScanAPs').classList.remove('disabled');
document.getElementById('btnConnectWiFi').classList.remove('disabled');
console.log(aps); console.log(aps);
if (err) { if (err) {
this.displayAPs({ connected: { name: '', passphrase: '' }, accessPoints: [] }); this.displayAPs({ connected: { name: '', passphrase: '' }, accessPoints: [] });
@ -284,7 +255,9 @@
let ap = nets[i]; let ap = nets[i];
div += `<div class="wifiSignal" onclick="wifi.selectSSID(this);" data-channel="${ap.channel}" data-encryption="${ap.encryption}" data-strength="${ap.strength}" data-mac="${ap.macAddress}"><span class="ssid">${ap.name}</span><span class="strength">${this.displaySignal(ap.strength)}</span></div>`; div += `<div class="wifiSignal" onclick="wifi.selectSSID(this);" data-channel="${ap.channel}" data-encryption="${ap.encryption}" data-strength="${ap.strength}" data-mac="${ap.macAddress}"><span class="ssid">${ap.name}</span><span class="strength">${this.displaySignal(ap.strength)}</span></div>`;
} }
document.getElementById('divAps').innerHTML = div; let divAps = document.getElementById('divAps');
divAps.setAttribute('data-lastloaded', new Date().getTime());
divAps.innerHTML = div;
document.getElementsByName('ssid')[0].value = aps.connected.name; document.getElementsByName('ssid')[0].value = aps.connected.name;
document.getElementsByName('passphrase')[0].value = aps.connected.passphrase; document.getElementsByName('passphrase')[0].value = aps.connected.passphrase;
this.procWifiStrength(aps.connected); this.procWifiStrength(aps.connected);
@ -341,7 +314,7 @@
}; };
var wifi = new Wifi(); var wifi = new Wifi();
class Somfy { class Somfy {
init() { async init() {
this.loadPins('inout', document.getElementById('selTransSCKPin')); this.loadPins('inout', document.getElementById('selTransSCKPin'));
this.loadPins('inout', document.getElementById('selTransCSNPin')); this.loadPins('inout', document.getElementById('selTransCSNPin'));
this.loadPins('inout', document.getElementById('selTransMOSIPin')); this.loadPins('inout', document.getElementById('selTransMOSIPin'));
@ -926,9 +899,7 @@
}; };
var somfy = new Somfy(); var somfy = new Somfy();
class MQTT { class MQTT {
init() { async init() { this.loadMQTT(); }
this.loadMQTT();
}
async loadMQTT() { async loadMQTT() {
let overlay = waitMessage(document.getElementById('fsMQTTSettings')); let overlay = waitMessage(document.getElementById('fsMQTTSettings'));
getJSON('/mqttsettings', (err, settings) => { getJSON('/mqttsettings', (err, settings) => {
@ -983,7 +954,7 @@
}; };
var mqtt = new MQTT(); var mqtt = new MQTT();
class Firmware { class Firmware {
init() { } async init() { }
createFileUploader(service) { createFileUploader(service) {
let div = document.createElement('div'); let div = document.createElement('div');
div.setAttribute('id', 'divUploadFile'); div.setAttribute('id', 'divUploadFile');
@ -1197,6 +1168,15 @@
el.appendChild(div); el.appendChild(div);
return div; return div;
} }
function socketError(el, msg) {
let div = document.createElement('div');
div.innerHTML = '<div id="divSocketAttempts" style="position:absolute;width:100%;left:0px;padding-right:24px;text-align:right;top:0px;font-size:18px;"><span>Attempts: </span><span id="spanSocketAttempts"></span></div><div class="innerError"><div>Could not connect to server</div><hr></hr><div style="font-size:.7em">' + msg + '</div></div>';
div.classList.add('errorMessage');
div.classList.add('socket-error');
el.appendChild(div);
return div;
}
function promptMessage(el, msg, onYes) { function promptMessage(el, msg, onYes) {
let div = document.createElement('div'); let div = document.createElement('div');
div.innerHTML = '<div class="innerError">' + msg + '</div><button id="btnYes" type="button">Yes</button><button type="button" onclick="clearErrors();">No</button></div>'; div.innerHTML = '<div class="innerError">' + msg + '</div><button id="btnYes" type="button">Yes</button><button type="button" onclick="clearErrors();">No</button></div>';
@ -1240,6 +1220,7 @@
var sockIsOpen = false; var sockIsOpen = false;
var connecting = false; var connecting = false;
var connects = 0; var connects = 0;
var connectFailed = 0;
async function initSockets() { async function initSockets() {
if (connecting) return; if (connecting) return;
console.log('Connecting to socket...'); console.log('Connecting to socket...');
@ -1300,18 +1281,30 @@
sockIsOpen = true; sockIsOpen = true;
connecting = false; connecting = false;
connects++; connects++;
connectFailed = 0;
let wms = document.getElementsByClassName('socket-wait'); let wms = document.getElementsByClassName('socket-wait');
for (let i = 0; i < wms.length; i++) { for (let i = 0; i < wms.length; i++) {
wms[i].remove(); wms[i].remove();
} }
let errs = document.getElementsByClassName('socket-error');
for (let i = 0; i < errs.length; i++)
errs[i].remove();
(async () => {
await general.init();
await somfy.init();
await mqtt.init();
await wifi.init();
})();
}; };
socket.onclose = (evt) => { socket.onclose = (evt) => {
if(document.getElementsByClassName('socket-wait') === 0) if (document.getElementsByClassName('socket-wait') === 0)
waitMessage(document.getElementById('divContainer')).classList.add('socket-wait'); waitMessage(document.getElementById('divContainer')).classList.add('socket-wait');
if (evt.wasClean) { if (evt.wasClean) {
console.log({ msg: 'close-clean', evt: evt }); console.log({ msg: 'close-clean', evt: evt });
connectFailed = 0;
tConnect = setTimeout(async () => { await reopenSocket(); }, 10000); tConnect = setTimeout(async () => { await reopenSocket(); }, 10000);
console.log('Reconnecting socket in 10 seconds'); console.log('Reconnecting socket in 10 seconds');
} }
else { else {
console.log({ msg: 'close-died', reason: evt.reason, evt: evt, sock: socket }); console.log({ msg: 'close-died', reason: evt.reason, evt: evt, sock: socket });
@ -1320,8 +1313,23 @@
tConnect = setTimeout(async () => { await reopenSocket(); }, 3000); tConnect = setTimeout(async () => { await reopenSocket(); }, 3000);
} }
else { else {
console.log('Connecting socket in .5 seconds'); if (connecting) {
tConnect = setTimeout(async () => { await reopenSocket(); }, 500); connectFailed++;
let timeout = Math.min(connectFailed * 500, 10000);
console.log(`Initial socket did not connect try again (server was busy and timed out ${connectFailed} times)`);
tConnect = setTimeout(async () => { await reopenSocket(); }, timeout);
if (connectFailed === 5) {
socketError(document.getElementById('divContainer'), 'Too many clients connected. A maximum of 5 clients may be connected at any one time. Close some connections to the ESP Somfy RTS device to proceed.');
}
let spanAttempts = document.getElementById('spanSocketAttempts');
if (spanAttempts) spanAttempts.innerHTML = connectFailed.fmt("#,##0");
}
else {
console.log('Connecting socket in .5 seconds');
tConnect = setTimeout(async () => { await reopenSocket(); }, 500);
}
} }
} }
@ -1345,7 +1353,7 @@
</script> </script>
</head> </head>
<body> <body>
<div id="divContainer" class="container" style="user-select:none;"> <div id="divContainer" class="container" style="user-select:none;position:relative;">
<h1 style="text-align: center;"><span>ESPSomfy RTS</span><span class="button-outline" onclick="general.toggleConfig();" style="float:right;font-size:1.25rem;display:inline-block;vertical-align:middle;width:38px;height:38px;position:relative;padding-top:4px;"><span style="vertical-align:middle;clear:both;text-align:center;display:inline-block;"><i id="icoConfig" class="icss-gear" style=""></i></span></span></h1> <h1 style="text-align: center;"><span>ESPSomfy RTS</span><span class="button-outline" onclick="general.toggleConfig();" style="float:right;font-size:1.25rem;display:inline-block;vertical-align:middle;width:38px;height:38px;position:relative;padding-top:4px;"><span style="vertical-align:middle;clear:both;text-align:center;display:inline-block;"><i id="icoConfig" class="icss-gear" style=""></i></span></span></h1>
<div id="divConfigPnl" style="display:none;"> <div id="divConfigPnl" style="display:none;">
<div style="margin-top:-10px;text-align:center;font-size:12px;"> <div style="margin-top:-10px;text-align:center;font-size:12px;">
@ -1394,7 +1402,7 @@
<fieldset id="fsWiFiSettings" style="display:none;"> <fieldset id="fsWiFiSettings" style="display:none;">
<legend>WiFi Settings</legend> <legend>WiFi Settings</legend>
<form method="post" action="/scan"> <form method="post" action="/scan">
<div id="divAps"></div> <div id="divAps" data-lastloaded="0"></div>
<div class="button-container"><button id="btnScanAPs" type="button" onclick="wifi.loadAPs();">Scan</button></div> <div class="button-container"><button id="btnScanAPs" type="button" onclick="wifi.loadAPs();">Scan</button></div>
</form> </form>
<form method="post" action="/wifi" style="margin-top:8px;"> <form method="post" action="/wifi" style="margin-top:8px;">
@ -1635,7 +1643,6 @@
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
waitMessage(document.getElementById('divShadeControls')); waitMessage(document.getElementById('divShadeControls'));
general.init(); somfy.init(); mqtt.init(); wifi.init();
let tabs = document.querySelectorAll('div.tab-container > span'); let tabs = document.querySelectorAll('div.tab-container > span');
tabs.forEach((tab) => { tabs.forEach((tab) => {
tab.addEventListener('click', (evt) => { tab.addEventListener('click', (evt) => {
@ -1651,13 +1658,20 @@
} }
}); });
tab.classList.add('selected'); tab.classList.add('selected');
if (tab.getAttribute('data-grpid') === 'fsWiFiSettings') {
// Only load the networks if we need them.
if(new Date().getTime() - parseInt(document.getElementById('divAps').getAttribute('data-lastloaded'), 10) > 60000)
wifi.loadAPs();
}
let grp = document.getElementById(tab.getAttribute('data-grpid')); let grp = document.getElementById(tab.getAttribute('data-grpid'));
if (typeof grp !== 'undefined' && grp) grp.style.display = ''; if (typeof grp !== 'undefined' && grp) grp.style.display = '';
} }
}); });
}); });
(async () => { await initSockets(); })(); (async () => {
await initSockets();
})();
//initSockets(); //initSockets();
</script> </script>
</body> </body>

View file

@ -19,7 +19,6 @@ body {
margin-top: 77px; margin-top: 77px;
box-sizing: border-box; box-sizing: border-box;
} }
.container { .container {
margin: 0 auto; margin: 0 auto;
max-width: 450px; max-width: 450px;
@ -198,6 +197,12 @@ div.errorMessage {
div.errorMessage > div { div.errorMessage > div {
padding:10px; padding:10px;
} }
div.socket-error {
opacity: 1;
font-size: 20px;
min-height: 277px;
z-index: 20001;
}
div.instructions { div.instructions {
position: absolute; position: absolute;
left: 0px; left: 0px;
@ -269,22 +274,18 @@ div.wifiSignal {
padding-left: 10px; padding-left: 10px;
padding-right: 10px; padding-right: 10px;
} }
div.wifiSignal:hover { div.wifiSignal:hover {
background: #00bcd4; background: #00bcd4;
color: white; color: white;
} }
div.wifiSignal > span { div.wifiSignal > span {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
} }
span.ssid { span.ssid {
width: calc(100% - 50px); width: calc(100% - 50px);
margin-top: 10px; margin-top: 10px;
} }
#divAps { #divAps {
max-height: 200px; max-height: 200px;
overflow-y: auto; overflow-y: auto;
@ -292,7 +293,6 @@ span.ssid {
height: 150px; height: 150px;
padding-right: 7px; padding-right: 7px;
} }
div.signal { div.signal {
margin: 0px; margin: 0px;
color: lawngreen; color: lawngreen;
@ -300,7 +300,6 @@ div.signal {
width: 32px; width: 32px;
float: right; float: right;
} }
span.strength { span.strength {
position: relative; position: relative;
margin-top: 2px; margin-top: 2px;

BIN
icons/dark_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
icons/dark_icon@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
icons/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
icons/icon@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
icons/mono_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
icons/mono_icon@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB