mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2026-10-31 13:52:15 +01:00
feat: remove the GitHub update
The release check, release list, download, cancel and /recoverFilesystem go, with the GitHub button and the Auto Check for Updates setting in the web UI (docs/adr/0007-no-github-update.md). The HTTPS session it needed failed for want of a 35-40 KB block (#76), it checked no certificate, and the web UI's buttons had been getting 404. Updates are uploads only (/updateFirmware, /updateApplication). The old socket still sends fwStatus with the installed versions, which the web UI and Home Assistant read. checkForUpdate stays in NVS and the backup file so their formats don't change. The release image shrinks by 126.5 KB: 142 KB of the OTA slot is free. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
539a49eb77
commit
83c946dd5f
14 changed files with 25 additions and 1009 deletions
|
|
@ -160,10 +160,6 @@
|
||||||
<input id="cbSsdpBroadcast" name="ssdpBroadcast" type="checkbox" data-bind="general.ssdpBroadcast" style="display:inline-block;" />
|
<input id="cbSsdpBroadcast" name="ssdpBroadcast" type="checkbox" data-bind="general.ssdpBroadcast" style="display:inline-block;" />
|
||||||
<label for="cbSsdpBroadcast" style="display:inline-block;cursor:pointer;">Broadcast uPnP over SSDP</label>
|
<label for="cbSsdpBroadcast" style="display:inline-block;cursor:pointer;">Broadcast uPnP over SSDP</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-group" style="margin-top:-12px;">
|
|
||||||
<input id="cbCheckForUpdate" type="checkbox" data-bind="general.checkForUpdate" style="display:inline-block;" />
|
|
||||||
<label for="cbCheckForUpdate" style="display:inline-block;cursor:pointer;">Auto Check for Updates</label>
|
|
||||||
</div>
|
|
||||||
<div class="button-container">
|
<div class="button-container">
|
||||||
<button id="btnSaveGeneral" type="button" onclick="general.setGeneral();">
|
<button id="btnSaveGeneral" type="button" onclick="general.setGeneral();">
|
||||||
Save
|
Save
|
||||||
|
|
@ -251,9 +247,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-container">
|
<div class="button-container">
|
||||||
<button id="btnUpdateGithub" type="button" onclick="firmware.updateGithub();">
|
|
||||||
<i class="icss-github-o" style="font-size:1.7em;margin-top:-14px;margin-bottom:-10px;margin-right:7px;"></i><span>Github Update</span>
|
|
||||||
</button>
|
|
||||||
<button id="btnUpdateFirmware" type="button" onclick="firmware.updateFirmware();">
|
<button id="btnUpdateFirmware" type="button" onclick="firmware.updateFirmware();">
|
||||||
Update Firmware
|
Update Firmware
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -579,9 +579,6 @@ async function initSockets() {
|
||||||
case 'memStatus':
|
case 'memStatus':
|
||||||
firmware.procMemoryStatus(msg);
|
firmware.procMemoryStatus(msg);
|
||||||
break;
|
break;
|
||||||
case 'updateProgress':
|
|
||||||
firmware.procUpdateProgress(msg);
|
|
||||||
break;
|
|
||||||
case 'fwStatus':
|
case 'fwStatus':
|
||||||
firmware.procFwStatus(msg);
|
firmware.procFwStatus(msg);
|
||||||
break;
|
break;
|
||||||
|
|
@ -4569,268 +4566,11 @@ class Firmware {
|
||||||
}
|
}
|
||||||
|
|
||||||
procFwStatus(rel) {
|
procFwStatus(rel) {
|
||||||
console.log(rel);
|
|
||||||
let div = document.getElementById('divFirmwareUpdate');
|
let div = document.getElementById('divFirmwareUpdate');
|
||||||
if (rel.available && rel.status === 0 && rel.checkForUpdate !== false) {
|
|
||||||
div.style.color = 'black';
|
|
||||||
div.innerHTML = `<span>Firmware ${rel.fwVersion.name} Installed<span><span style="color:red"> ${rel.latest.name} Available</span>`;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
switch (rel.status) {
|
|
||||||
case 2: // Awaiting update.
|
|
||||||
div.style.color = 'red';
|
|
||||||
div.innerHTML = `Preparing firmware update`;
|
|
||||||
break;
|
|
||||||
case 3: // Updating -- this will be set by the update progress.
|
|
||||||
break;
|
|
||||||
case 4: // Complete
|
|
||||||
if (rel.error !== 0) {
|
|
||||||
div.style.color = 'red';
|
|
||||||
let e = errors.find(x => x.code === rel.error) || { code: rel.error, desc: 'Unspecified error' };
|
|
||||||
let inst = document.getElementById('divGitInstall');
|
|
||||||
if (inst) {
|
|
||||||
inst.remove();
|
|
||||||
ui.errorMessage(e.desc);
|
|
||||||
}
|
|
||||||
div.innerHTML = e.desc;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
div.innerHTML = `Firmware update complete`;
|
|
||||||
// Throw up a wait message this will be cleared on the reload.
|
|
||||||
ui.waitMessage(document.getElementById('divContainer'));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
div.style.color = 'red';
|
|
||||||
div.innerHTML = `Cancelling firmware update`;
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
div.style.color = 'red';
|
|
||||||
div.innerHTML = `Firmware update cancelled`;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
div.style.color = 'black';
|
div.style.color = 'black';
|
||||||
div.innerHTML = `Firmware ${rel.fwVersion.name} Installed`;
|
div.innerHTML = `Firmware ${rel.fwVersion.name} Installed`;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
div.style.display = '';
|
div.style.display = '';
|
||||||
}
|
}
|
||||||
procUpdateProgress(prog) {
|
|
||||||
let pct = Math.round((prog.loaded / prog.total) * 100);
|
|
||||||
let file = prog.part === 100 ? 'Application' : 'Firmware';
|
|
||||||
let div = document.getElementById('divFirmwareUpdate');
|
|
||||||
if (div) {
|
|
||||||
div.style.color = 'red';
|
|
||||||
div.innerHTML = `Updating ${file} to ${prog.ver} ${pct}%`;
|
|
||||||
}
|
|
||||||
general.reloadApp = true;
|
|
||||||
let git = document.getElementById('divGitInstall');
|
|
||||||
if (git) {
|
|
||||||
// Update the status on the client that started the install.
|
|
||||||
if (pct >= 100 && prog.part === 100) git.remove();
|
|
||||||
else {
|
|
||||||
if (prog.part === 100) {
|
|
||||||
document.getElementById('btnCancelUpdate').style.display = 'none';
|
|
||||||
}
|
|
||||||
let p = prog.part === 100 ? document.getElementById('progApplicationDownload') : document.getElementById('progFirmwareDownload');
|
|
||||||
if (p) {
|
|
||||||
p.style.setProperty('--progress', `${pct}%`);
|
|
||||||
p.setAttribute('data-progress', `${pct}%`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
async installGitRelease(div) {
|
|
||||||
if (!this.isMobile()) {
|
|
||||||
console.log('Starting backup');
|
|
||||||
try {
|
|
||||||
await firmware.backup();
|
|
||||||
console.log('Backup Complete');
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
ui.serviceError(el, err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let obj = ui.fromElement(div);
|
|
||||||
console.log(obj);
|
|
||||||
putJSONSync(`/downloadFirmware?ver=${obj.version}`, {}, (err, ver) => {
|
|
||||||
if (err) ui.serviceError(err);
|
|
||||||
else {
|
|
||||||
general.reloadApp = true;
|
|
||||||
// Change the display and allow the percentage to be shown when the socket emits the progress.
|
|
||||||
let html = `<div>Installing ${ver.name}</div><div style="font-size:.7em;margin-top:4px;">Please wait as the files are downloaded and installed. Once the application update process starts you may no longer cancel the update as this will corrupt the downloaded files.</div>`;
|
|
||||||
html += `<div class="progress-bar" id="progFirmwareDownload" style="--progress:0%;margin-top:10px;text-align:center;"></div>`;
|
|
||||||
html += `<label for="progFirmwareDownload" style="font-size:10pt;">Firmware Install Progress</label>`;
|
|
||||||
html += `<div class="progress-bar" id="progApplicationDownload" style="--progress:0%;margin-top:10px;text-align:center;"></div>`;
|
|
||||||
html += `<label for="progFirmwareDownload" style="font-size:10pt;">Application Install Progress</label>`;
|
|
||||||
html += `<hr></hr><div class="button-container" style="text-align:center;">`;
|
|
||||||
html += `<button id="btnCancelUpdate" type="button" style="width:40%;padding-left:20px;padding-right:20px;display:inline-block;" onclick="firmware.cancelInstallGit(document.getElementById('divGitInstall'));">Cancel</button>`;
|
|
||||||
html += `</div>`;
|
|
||||||
div.innerHTML = html;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
cancelInstallGit(div) {
|
|
||||||
putJSONSync(`/cancelFirmware`, {}, (err, ver) => {
|
|
||||||
if (err) ui.serviceError(err);
|
|
||||||
else console.log(ver);
|
|
||||||
div.remove();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
updateGithub() {
|
|
||||||
getJSONSync('/getReleases', (err, rel) => {
|
|
||||||
if (err) ui.serviceError(err);
|
|
||||||
else {
|
|
||||||
console.log(rel);
|
|
||||||
let div = document.createElement('div');
|
|
||||||
let chip = document.getElementById('divContainer').getAttribute('data-chipmodel');
|
|
||||||
div.setAttribute('id', 'divGitInstall')
|
|
||||||
div.setAttribute('class', 'inst-overlay');
|
|
||||||
div.style.width = '100%';
|
|
||||||
div.style.alignContent = 'center';
|
|
||||||
// Sort the releases so that the pre-releases are at the bottom.
|
|
||||||
rel.releases.sort((a, b) => a.preRelease === b.preRelease && b.draft === a.draft ? 0 : a.preRelease ? 1 : -1);
|
|
||||||
|
|
||||||
let html = `<div>Select a version from the repository to install using the dropdown below. Then press the update button to install that version.</div><div style="font-size:.7em;margin-top:4px;">Select Main to install the most recent alpha version from the repository.</div>`;
|
|
||||||
html += `<div id="divPrereleaseWarning" style="display:none;width:100%;color:red;text-align:center;font-weight:bold;"><span style="margin-top:7px;width:100%;padding:3px;display:inline-block;border-radius:5px;background:white;">WARNING<span><hr style="margin:0px" /><div style="font-size:.7em;padding-left:1em;padding-right:1em;color:black;font-weight:normal;">You have selected a pre-released beta version that has not been fully tested or published for general use.</div></div>`;
|
|
||||||
html += `<div class="field-group" style="text-align:center;">`;
|
|
||||||
html += `<select id="selVersion" data-bind="version" style="width:70%;font-size:2em;color:white;text-align-last:center;" onchange="firmware.gitReleaseSelected(document.getElementById('divGitInstall'));">`
|
|
||||||
for (let i = 0; i < rel.releases.length; i++) {
|
|
||||||
if (rel.releases[i].hwVersions.length === 0 || rel.releases[i].hwVersions.indexOf(chip) >= 0)
|
|
||||||
html += `<option style="text-align:left;font-size:.5em;color:black;" data-prerelease="${rel.releases[i].preRelease}" value="${rel.releases[i].version.name}">${rel.releases[i].name}${rel.releases[i].preRelease ? ' - Pre' : ''}</option>`
|
|
||||||
}
|
|
||||||
html += `</select><label for="selVersion">Select a version</label></div>`;
|
|
||||||
html += `<div class="button-container" id="divReleaseNotes" style="text-align:center;margin-top:-20px;display:none;"><button type="button" onclick="firmware.showReleaseNotes(document.getElementById('selVersion').value);" style="display:inline-block;width:auto;padding-left:20px;padding-right:20px;">Release Notes</button></div>`;
|
|
||||||
if (this.isMobile()) {
|
|
||||||
html += `<div style="width:100%;color:red;text-align:center;font-weight:bold;"><span style="margin-top:7px;width:100%;background:yellow;padding:3px;display:inline-block;border-radius:5px;background:white;">WARNING<span></div>`;
|
|
||||||
html += '<hr/><div style="font-size:14px;margin-bottom:10px;">This browser does not support automatic backups. It is highly recommended that you back up your configuration using the backup button before proceeding.</div>';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
html += '<hr/><div style="font-size:14px;margin-bottom:10px;">A backup file for your configuration will be downloaded to your browser. If the firmware update process fails please restore this file using the restore button after going through the onboarding process.</div>'
|
|
||||||
}
|
|
||||||
html += `<hr></hr><div class="button-container" style="text-align:center;">`;
|
|
||||||
if (this.isMobile()) {
|
|
||||||
html += `<button id="btnBackupCfg" type="button" style="display:inline-block;width:calc(80% + 7px);padding-left:20px;padding-right:20px;margin-right:4px;" onclick="firmware.backup();">Backup</button>`;
|
|
||||||
}
|
|
||||||
html += `<button id="btnUpdate" type="button" style="width:40%;padding-left:20px;padding-right:20px;display:inline-block;margin-right:7px;" onclick="firmware.installGitRelease(document.getElementById('divGitInstall'));">Update</button>`;
|
|
||||||
html += `<button id="btnClose" type="button" style="width:40%;padding-left:20px;padding-right:20px;display:inline-block;" onclick="document.getElementById('divGitInstall').remove();">Cancel</button>`;
|
|
||||||
|
|
||||||
html += `</div></div>`;
|
|
||||||
|
|
||||||
div.innerHTML = html;
|
|
||||||
document.getElementById('divContainer').appendChild(div);
|
|
||||||
this.gitReleaseSelected(div);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
gitReleaseSelected(div) {
|
|
||||||
let obj = ui.fromElement(div);
|
|
||||||
let divNotes = div.querySelector('#divReleaseNotes');
|
|
||||||
let divPre = div.querySelector('#divPrereleaseWarning');
|
|
||||||
|
|
||||||
let sel = div.querySelector('#selVersion');
|
|
||||||
if (sel && sel.selectedIndex !== -1 && makeBool(sel.options[sel.selectedIndex].getAttribute('data-prerelease'))) {
|
|
||||||
if (divPre) divPre.style.display = '';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
if (divPre) divPre.style.display = 'none';
|
|
||||||
|
|
||||||
if (divNotes) {
|
|
||||||
if (!obj.version || obj.version === 'main' || obj.version === '') divNotes.style.display = 'none';
|
|
||||||
else divNotes.style.display = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async getReleaseInfo(tag) {
|
|
||||||
let overlay = ui.waitMessage(document.getElementById('divContainer'));
|
|
||||||
try {
|
|
||||||
let ret = {};
|
|
||||||
ret.resp = await fetch(`https://api.github.com/repos/cjkas/espsomfy-rts/releases/tags/${tag}`);
|
|
||||||
if (ret.resp.ok)
|
|
||||||
ret.info = await ret.resp.json();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
return { err: err };
|
|
||||||
}
|
|
||||||
finally { overlay.remove(); }
|
|
||||||
}
|
|
||||||
async showReleaseNotes(tagName) {
|
|
||||||
console.log(tagName);
|
|
||||||
let r = await this.getReleaseInfo(tagName);
|
|
||||||
console.log(r);
|
|
||||||
let fnToItem = (txt, tag) => { }
|
|
||||||
if (r.resp.ok) {
|
|
||||||
// Convert this to html.
|
|
||||||
let lines = r.info.body.split('\r\n');
|
|
||||||
let ctx = { html: '', llvl: 0, lines: r.info.body.split('\r\n'), ndx: 0 };
|
|
||||||
ctx.toHead = function (txt) {
|
|
||||||
let num = txt.indexOf(' ');
|
|
||||||
return `<h${num}>${txt.substring(num).trim()}</h${num}>`;
|
|
||||||
};
|
|
||||||
ctx.toUL = function () {
|
|
||||||
let txt = this.lines[this.ndx++];
|
|
||||||
let tok = this.token(txt);
|
|
||||||
this.html += `<ul>${this.toLI(tok.txt)}`;
|
|
||||||
while (this.ndx < this.lines.length) {
|
|
||||||
txt = this.lines[this.ndx];
|
|
||||||
let t = this.token(txt);
|
|
||||||
if (t.ch === '*') {
|
|
||||||
if (t.indent !== tok.indent) this.toUL();
|
|
||||||
else {
|
|
||||||
this.html += this.toLI(t.txt);
|
|
||||||
this.ndx++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else break;
|
|
||||||
}
|
|
||||||
this.html += '</ul>';
|
|
||||||
};
|
|
||||||
ctx.toLI = function (txt) { return `<li>${txt.trim()}</li>`; }
|
|
||||||
ctx.token = function (txt) {
|
|
||||||
let tok = { ch: '', indent: 0, txt:'' }
|
|
||||||
for (let i = 0; i < txt.length; i++) {
|
|
||||||
if (txt[i] === ' ') tok.indent++;
|
|
||||||
else {
|
|
||||||
tok.ch = txt[i];
|
|
||||||
let tmp = txt.substring(tok.indent);
|
|
||||||
tok.txt = tmp.substring(tmp.indexOf(' '));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tok;
|
|
||||||
};
|
|
||||||
ctx.next = function () {
|
|
||||||
if (this.ndx >= this.lines.length) return false;
|
|
||||||
let tok = this.token(this.lines[this.ndx]);
|
|
||||||
switch (tok.ch) {
|
|
||||||
case '#':
|
|
||||||
this.html += this.toHead(this.lines[this.ndx]);
|
|
||||||
this.ndx++;
|
|
||||||
break;
|
|
||||||
case '*':
|
|
||||||
this.toUL();
|
|
||||||
break;
|
|
||||||
case '':
|
|
||||||
this.ndx++;
|
|
||||||
this.html += `<br/><div>${tok.txt}</div>`;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this.ndx++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
while (ctx.next());
|
|
||||||
console.log(ctx);
|
|
||||||
ui.infoMessage(ctx.html);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateFirmware() {
|
updateFirmware() {
|
||||||
let div = this.createFileUploader('/updateFirmware');
|
let div = this.createFileUploader('/updateFirmware');
|
||||||
let inst = div.querySelector('div[id=divInstText]');
|
let inst = div.querySelector('div[id=divInstText]');
|
||||||
|
|
|
||||||
|
|
@ -239,7 +239,6 @@ bool ConfigSettings::toJSON(JsonObject &obj) {
|
||||||
obj["hostname"] = this->hostname;
|
obj["hostname"] = this->hostname;
|
||||||
obj["connType"] = static_cast<uint8_t>(this->connType);
|
obj["connType"] = static_cast<uint8_t>(this->connType);
|
||||||
obj["chipModel"] = this->chipModel;
|
obj["chipModel"] = this->chipModel;
|
||||||
obj["checkForUpdate"] = this->checkForUpdate;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool ConfigSettings::requiresAuth() { return this->Security.type != security_types::None; }
|
bool ConfigSettings::requiresAuth() { return this->Security.type != security_types::None; }
|
||||||
|
|
@ -247,7 +246,6 @@ bool ConfigSettings::fromJSON(JsonObject &obj) {
|
||||||
if(!obj["ssdpBroadcast"].isNull()) this->ssdpBroadcast = obj["ssdpBroadcast"];
|
if(!obj["ssdpBroadcast"].isNull()) this->ssdpBroadcast = obj["ssdpBroadcast"];
|
||||||
if(!obj["hostname"].isNull()) this->parseValueString(obj, "hostname", this->hostname, sizeof(this->hostname));
|
if(!obj["hostname"].isNull()) this->parseValueString(obj, "hostname", this->hostname, sizeof(this->hostname));
|
||||||
if(!obj["connType"].isNull()) this->connType = static_cast<conn_types_t>(obj["connType"].as<uint8_t>());
|
if(!obj["connType"].isNull()) this->connType = static_cast<conn_types_t>(obj["connType"].as<uint8_t>());
|
||||||
if(!obj["checkForUpdate"].isNull()) this->checkForUpdate = obj["checkForUpdate"];
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void ConfigSettings::print() {
|
void ConfigSettings::print() {
|
||||||
|
|
|
||||||
|
|
@ -183,6 +183,8 @@ class ConfigSettings: BaseSettings {
|
||||||
appver_t fwVersion;
|
appver_t fwVersion;
|
||||||
appver_t appVersion;
|
appver_t appVersion;
|
||||||
bool ssdpBroadcast = true;
|
bool ssdpBroadcast = true;
|
||||||
|
// Nothing reads it since the GitHub update went (docs/adr/0007-no-github-update.md).
|
||||||
|
// NVS and the backup file still carry it so their formats stay the same.
|
||||||
bool checkForUpdate = true;
|
bool checkForUpdate = true;
|
||||||
uint8_t status;
|
uint8_t status;
|
||||||
IPSettings IP;
|
IPSettings IP;
|
||||||
|
|
|
||||||
520
src/GitOTA.cpp
520
src/GitOTA.cpp
|
|
@ -1,520 +0,0 @@
|
||||||
#include <WiFi.h>
|
|
||||||
#include <WiFiClientSecure.h>
|
|
||||||
#include <Update.h>
|
|
||||||
#include <HTTPClient.h>
|
|
||||||
#include "TaskWdt.h"
|
|
||||||
#include "esp_log.h"
|
|
||||||
#include "ConfigSettings.h"
|
|
||||||
#include "GitOTA.h"
|
|
||||||
#include "Utils.h"
|
|
||||||
#include "outputs/SocketEmitter.h"
|
|
||||||
#include "Somfy.h"
|
|
||||||
#include "entryPoints/Web.h"
|
|
||||||
#include "WResp.h"
|
|
||||||
#include "ESPNetwork.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
extern ConfigSettings settings;
|
|
||||||
extern SocketEmitter sockEmit;
|
|
||||||
extern SomfyShadeController somfy;
|
|
||||||
extern rebootDelay_t rebootDelay;
|
|
||||||
extern Web webServer;
|
|
||||||
extern ESPNetwork net;
|
|
||||||
|
|
||||||
static const char *TAG = "OTA";
|
|
||||||
|
|
||||||
#define MAX_BUFF_SIZE 4096
|
|
||||||
void GitRelease::setReleaseProperty(const char *key, const char *val) {
|
|
||||||
if(strcmp(key, "id") == 0) this->id = atol(val);
|
|
||||||
else if(strcmp(key, "draft") == 0) this->draft = toBoolean(val, false);
|
|
||||||
else if(strcmp(key, "prerelease") == 0) this->preRelease = toBoolean(val, false);
|
|
||||||
else if(strcmp(key, "name") == 0) strlcpy(this->name, val, sizeof(this->name));
|
|
||||||
else if(strcmp(key, "tag_name") == 0) {
|
|
||||||
this->version.parse(val);
|
|
||||||
}
|
|
||||||
else if(strcmp(key, "published_at") == 0) {
|
|
||||||
//Serial.printf("Key:[%s] Value:[%s]\n", key, val);
|
|
||||||
this->releaseDate = Timestamp::parseUTCTime(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void GitRelease::setAssetProperty(const char *key, const char *val) {
|
|
||||||
if(strcmp(key, "name") == 0) {
|
|
||||||
//Serial.println(val);
|
|
||||||
char fsFile[64];
|
|
||||||
releaseAssetName(fsFile, sizeof(fsFile), "littlefs", settings.chipModel);
|
|
||||||
if(strcmp(val, fsFile) == 0) this->hasFS = true;
|
|
||||||
else if(strstr(val, "ino.esp32.bin")) {
|
|
||||||
if(strlen(this->hwVersions)) strcat(this->hwVersions, ",");
|
|
||||||
strcat(this->hwVersions, "32");
|
|
||||||
}
|
|
||||||
else if(strstr(val, "ino.esp32s3.bin")) {
|
|
||||||
if(strlen(this->hwVersions)) strcat(this->hwVersions, ",");
|
|
||||||
strcat(this->hwVersions, "s3");
|
|
||||||
}
|
|
||||||
else if(strstr(val, "ino.esp32s2.bin")) {
|
|
||||||
if(strlen(this->hwVersions)) strcat(this->hwVersions, ",");
|
|
||||||
strcat(this->hwVersions, "s2");
|
|
||||||
}
|
|
||||||
else if(strstr(val, "ino.esp32c3.bin")) {
|
|
||||||
if(strlen(this->hwVersions)) strcat(this->hwVersions, ",");
|
|
||||||
strcat(this->hwVersions, "c3");
|
|
||||||
}
|
|
||||||
else if(strstr(val, "ino.esp32c2.bin")) {
|
|
||||||
if(strlen(this->hwVersions)) strcat(this->hwVersions, ",");
|
|
||||||
strcat(this->hwVersions, "c2");
|
|
||||||
}
|
|
||||||
else if(strstr(val, "ino.esp32c6.bin")) {
|
|
||||||
if(strlen(this->hwVersions)) strcat(this->hwVersions, ",");
|
|
||||||
strcat(this->hwVersions, "c6");
|
|
||||||
}
|
|
||||||
else if(strstr(val, "ino.esp32h2.bin")) {
|
|
||||||
if(strlen(this->hwVersions)) strcat(this->hwVersions, ",");
|
|
||||||
strcat(this->hwVersions, "h2");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#define ERR_CLIENT_OFFSET -50
|
|
||||||
|
|
||||||
int16_t GitRepo::getReleases(uint8_t num) {
|
|
||||||
WiFiClientSecure sclient;
|
|
||||||
sclient.setInsecure();
|
|
||||||
sclient.setHandshakeTimeout(3);
|
|
||||||
uint8_t ndx = 0;
|
|
||||||
uint8_t count = min((uint8_t)GIT_MAX_RELEASES, num);
|
|
||||||
char url[128];
|
|
||||||
memset(this->releases, 0x00, sizeof(GitRelease) * GIT_MAX_RELEASES);
|
|
||||||
sprintf(url, "https://api.github.com/repos/cjkas/espsomfy-rts/releases?per_page=%d&page=1", count);
|
|
||||||
GitRelease *main = &this->releases[GIT_MAX_RELEASES];
|
|
||||||
main->releaseDate = Timestamp::now();
|
|
||||||
main->id = 1;
|
|
||||||
main->main = true;
|
|
||||||
strcpy(main->version.name, "main");
|
|
||||||
strcpy(main->name, "Main");
|
|
||||||
strcpy(main->hwVersions, "32,s3");
|
|
||||||
HTTPClient https;
|
|
||||||
https.setReuse(false);
|
|
||||||
if(https.begin(sclient, url)) {
|
|
||||||
resetTaskWdt();
|
|
||||||
int httpCode = https.GET();
|
|
||||||
ESP_LOGD(TAG, "[HTTPS] GET... code: %d", httpCode);
|
|
||||||
if(httpCode > 0) {
|
|
||||||
int len = https.getSize();
|
|
||||||
ESP_LOGD(TAG, "[HTTPS] GET... code: %d - %d", httpCode, len);
|
|
||||||
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
|
|
||||||
WiFiClient *stream = https.getStreamPtr();
|
|
||||||
uint8_t buff[128] = {0};
|
|
||||||
char jsonElem[32] = "";
|
|
||||||
char jsonValue[128] = "";
|
|
||||||
int arrTok = 0;
|
|
||||||
int objTok = 0;
|
|
||||||
bool inQuote = false;
|
|
||||||
bool inElem = false;
|
|
||||||
bool inValue = false;
|
|
||||||
bool awaitValue = false;
|
|
||||||
bool inAss = false;
|
|
||||||
while(https.connected() && (len > 0 || len == -1) && ndx < count) {
|
|
||||||
size_t size = stream->available();
|
|
||||||
if(size) {
|
|
||||||
resetTaskWdt();
|
|
||||||
int c = stream->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size));
|
|
||||||
//Serial.write(buff, c);
|
|
||||||
if(len > 0) len -= c;
|
|
||||||
// Now we should have some data.
|
|
||||||
for(uint8_t i = 0; i < c; i++) {
|
|
||||||
// Read the buffer a byte at a time until we have a key value pair.
|
|
||||||
char ch = static_cast<char>(buff[i]);
|
|
||||||
if(ch == '[') {
|
|
||||||
arrTok++;
|
|
||||||
if(arrTok == 2 && strcmp(jsonElem, "assets") == 0) {
|
|
||||||
inElem = inValue = awaitValue = false;
|
|
||||||
inAss = true;
|
|
||||||
//Serial.printf("%s: %d\n", jsonElem, arrTok);
|
|
||||||
}
|
|
||||||
else if(arrTok < 2) inAss = false;
|
|
||||||
}
|
|
||||||
else if(ch == ']') {
|
|
||||||
arrTok--;
|
|
||||||
if(arrTok < 2) inAss = false;
|
|
||||||
}
|
|
||||||
else if(ch == '{') {
|
|
||||||
objTok++;
|
|
||||||
if(objTok != 1 && !inAss) inElem = inValue = awaitValue = false;
|
|
||||||
}
|
|
||||||
else if(ch == '}') {
|
|
||||||
objTok--;
|
|
||||||
if(objTok == 0) ndx++;
|
|
||||||
}
|
|
||||||
else if(objTok == 1 || inAss) {
|
|
||||||
// We only want data from the root object.
|
|
||||||
//if(inAss) Serial.print(ch);
|
|
||||||
if(ch == '\"') {
|
|
||||||
inQuote = !inQuote;
|
|
||||||
if(inElem) {
|
|
||||||
inElem = false;
|
|
||||||
awaitValue = true;
|
|
||||||
}
|
|
||||||
else if(inValue) {
|
|
||||||
inValue = false;
|
|
||||||
inElem = false;
|
|
||||||
awaitValue = false;
|
|
||||||
if(inAss)
|
|
||||||
this->releases[ndx].setAssetProperty(jsonElem, jsonValue);
|
|
||||||
else
|
|
||||||
this->releases[ndx].setReleaseProperty(jsonElem, jsonValue);
|
|
||||||
memset(jsonElem, 0x00, sizeof(jsonElem));
|
|
||||||
memset(jsonValue, 0x00, sizeof(jsonValue));
|
|
||||||
}
|
|
||||||
else if(awaitValue) inValue = true;
|
|
||||||
else {
|
|
||||||
inElem = true;
|
|
||||||
awaitValue = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(awaitValue) {
|
|
||||||
if(ch != ' ' && ch != ':') {
|
|
||||||
strncat(jsonValue, &ch, 1);
|
|
||||||
awaitValue = false;
|
|
||||||
inValue = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if((!inQuote && ch == ',') || ch == '\r' || ch == '\n') {
|
|
||||||
inElem = inValue = awaitValue = false;
|
|
||||||
if(strlen(jsonElem) > 0) {
|
|
||||||
if(inAss)
|
|
||||||
this->releases[ndx].setAssetProperty(jsonElem, jsonValue);
|
|
||||||
else
|
|
||||||
this->releases[ndx].setReleaseProperty(jsonElem, jsonValue);
|
|
||||||
}
|
|
||||||
memset(jsonElem, 0x00, sizeof(jsonElem));
|
|
||||||
memset(jsonValue, 0x00, sizeof(jsonValue));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(inElem) {
|
|
||||||
if(strlen(jsonElem) < sizeof(jsonElem) - 1) strncat(jsonElem, &ch, 1);
|
|
||||||
}
|
|
||||||
else if(inValue) {
|
|
||||||
if(strlen(jsonValue) < sizeof(jsonValue) - 1) strncat(jsonValue, &ch, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delay(1);
|
|
||||||
}
|
|
||||||
//else break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
https.end();
|
|
||||||
sclient.stop();
|
|
||||||
return httpCode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
https.end();
|
|
||||||
sclient.stop();
|
|
||||||
}
|
|
||||||
settings.printAvailHeap();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#define UPDATE_ERR_OFFSET 20
|
|
||||||
#define ERR_DOWNLOAD_HTTP -40
|
|
||||||
#define ERR_DOWNLOAD_BUFFER -41
|
|
||||||
#define ERR_DOWNLOAD_CONNECTION -42
|
|
||||||
|
|
||||||
void GitUpdater::loop() {
|
|
||||||
if(!net.connected()) return;
|
|
||||||
if(this->status == GIT_STATUS_READY) {
|
|
||||||
if(settings.checkForUpdate &&
|
|
||||||
(millis() > net.connectTime + 60000) && // Wait a minute before checking after connection.
|
|
||||||
(this->lastCheck + 86400000 < millis() || this->lastCheck == 0) && !rebootDelay.reboot) { // 1 day
|
|
||||||
this->checkForUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(this->status == GIT_AWAITING_UPDATE) {
|
|
||||||
ESP_LOGI(TAG, "Starting update process.........");
|
|
||||||
this->status = GIT_UPDATING;
|
|
||||||
this->beginUpdate(this->targetRelease);
|
|
||||||
this->status = GIT_STATUS_READY;
|
|
||||||
this->emitUpdateCheck();
|
|
||||||
}
|
|
||||||
else if(this->status == GIT_UPDATE_CANCELLING) {
|
|
||||||
ESP_LOGI(TAG, "Cancelling update process..........");
|
|
||||||
if(!this->lockFS) {
|
|
||||||
this->status = GIT_UPDATE_CANCELLED;
|
|
||||||
this->cancelled = true;
|
|
||||||
this->emitUpdateCheck();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void GitUpdater::checkForUpdate() {
|
|
||||||
if(this->status != 0) return; // If we are already checking.
|
|
||||||
ESP_LOGI(TAG, "Check github for updates...");
|
|
||||||
|
|
||||||
this->status = GIT_STATUS_CHECK;
|
|
||||||
settings.printAvailHeap();
|
|
||||||
this->lastCheck = millis();
|
|
||||||
if(this->checkInternet() == 0) {
|
|
||||||
GitRepo repo;
|
|
||||||
this->updateAvailable = false;
|
|
||||||
this->error = repo.getReleases(2);
|
|
||||||
if(this->error == 0) { // Get 2 releases so we can filter our pre-releases
|
|
||||||
this->setCurrentRelease(repo);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this->emitUpdateCheck();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this->status = GIT_STATUS_READY;
|
|
||||||
}
|
|
||||||
void GitUpdater::setCurrentRelease(GitRepo &repo) {
|
|
||||||
this->updateAvailable = false;
|
|
||||||
for(uint8_t i = 0; i < 2; i++) {
|
|
||||||
if(repo.releases[i].draft || repo.releases[i].preRelease || repo.releases[i].id == 0) continue;
|
|
||||||
// Compare the versions.
|
|
||||||
this->latest.copy(repo.releases[i].version);
|
|
||||||
if(repo.releases[i].version.compare(settings.fwVersion) > 0) {
|
|
||||||
// We have a new release.
|
|
||||||
this->updateAvailable = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this->emitUpdateCheck();
|
|
||||||
}
|
|
||||||
void GitUpdater::emitUpdateCheck(uint8_t num) {
|
|
||||||
JsonSockEvent *json = sockEmit.beginEmit("fwStatus");
|
|
||||||
json->beginObject();
|
|
||||||
json->addElem("available", this->updateAvailable);
|
|
||||||
json->addElem("status", this->status);
|
|
||||||
json->addElem("error", (int32_t)this->error);
|
|
||||||
json->addElem("cancelled", this->cancelled);
|
|
||||||
json->addElem("checkForUpdate", settings.checkForUpdate);
|
|
||||||
json->addElem("inetAvailable", this->inetAvailable);
|
|
||||||
json->beginObject("fwVersion");
|
|
||||||
settings.fwVersion.toJSON(json);
|
|
||||||
json->endObject();
|
|
||||||
json->beginObject("appVersion");
|
|
||||||
settings.appVersion.toJSON(json);
|
|
||||||
json->endObject();
|
|
||||||
json->beginObject("latest");
|
|
||||||
this->latest.toJSON(json);
|
|
||||||
json->endObject();
|
|
||||||
json->endObject();
|
|
||||||
sockEmit.endEmit(num);
|
|
||||||
}
|
|
||||||
int GitUpdater::checkInternet() {
|
|
||||||
int err = 500;
|
|
||||||
uint32_t t = millis();
|
|
||||||
WiFiClientSecure sclient;
|
|
||||||
sclient.setInsecure();
|
|
||||||
sclient.setHandshakeTimeout(3);
|
|
||||||
resetTaskWdt();
|
|
||||||
HTTPClient https;
|
|
||||||
https.setReuse(false);
|
|
||||||
if(https.begin(sclient, "https://github.com/cjkas/ESPSomfy-RTS")) {
|
|
||||||
https.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
|
|
||||||
https.setTimeout(3000);
|
|
||||||
resetTaskWdt();
|
|
||||||
int httpCode = https.sendRequest("HEAD");
|
|
||||||
resetTaskWdt();
|
|
||||||
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY || httpCode == HTTP_CODE_FOUND) {
|
|
||||||
err = 0;
|
|
||||||
ESP_LOGI(TAG, "Internet is Available: %ldms", millis() - t);
|
|
||||||
this->inetAvailable = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
err = httpCode;
|
|
||||||
ESP_LOGE(TAG, "Internet is Unavailable: %d: %ldms", err, millis() - t);
|
|
||||||
this->inetAvailable = false;
|
|
||||||
}
|
|
||||||
https.end();
|
|
||||||
sclient.stop();
|
|
||||||
}
|
|
||||||
resetTaskWdt();
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
void GitUpdater::emitDownloadProgress(size_t total, size_t loaded, const char *evt) { this->emitDownloadProgress(255, total, loaded, evt); }
|
|
||||||
void GitUpdater::emitDownloadProgress(uint8_t num, size_t total, size_t loaded, const char *evt) {
|
|
||||||
JsonSockEvent *json = sockEmit.beginEmit(evt);
|
|
||||||
json->beginObject();
|
|
||||||
json->addElem("ver", this->targetRelease);
|
|
||||||
json->addElem("part", (int32_t)this->partition);
|
|
||||||
json->addElem("file", this->currentFile);
|
|
||||||
json->addElem("total", (uint32_t)total);
|
|
||||||
json->addElem("loaded", (uint32_t)loaded);
|
|
||||||
json->addElem("error", (uint32_t)this->error);
|
|
||||||
json->endObject();
|
|
||||||
sockEmit.endEmit(num);
|
|
||||||
/*
|
|
||||||
char buf[420];
|
|
||||||
snprintf(buf, sizeof(buf), "{\"ver\":\"%s\",\"part\":%d,\"file\":\"%s\",\"total\":%d,\"loaded\":%d, \"error\":%d}", this->targetRelease, this->partition, this->currentFile, total, loaded, this->error);
|
|
||||||
if(num >= 255) sockEmit.sendToClients(evt, buf);
|
|
||||||
else sockEmit.sendToClient(num, evt, buf);
|
|
||||||
*/
|
|
||||||
sockEmit.loop();
|
|
||||||
webServer.loop();
|
|
||||||
}
|
|
||||||
bool GitUpdater::beginUpdate(const char *version) {
|
|
||||||
ESP_LOGI(TAG, "Begin update called...");
|
|
||||||
if(strcmp(version, "Main") == 0) strcpy(this->baseUrl, "https://raw.githubusercontent.com/cjkas/ESPSomfy-RTS/master/");
|
|
||||||
else sprintf(this->baseUrl, "https://github.com/cjkas/ESPSomfy-RTS/releases/download/%s/", version);
|
|
||||||
|
|
||||||
strcpy(this->targetRelease, version);
|
|
||||||
this->emitUpdateCheck();
|
|
||||||
releaseAssetName(this->currentFile, sizeof(this->currentFile), "ino", settings.chipModel);
|
|
||||||
this->partition = U_FLASH;
|
|
||||||
this->lockFS = this->cancelled = false;
|
|
||||||
this->error = 0;
|
|
||||||
this->error = this->downloadFile();
|
|
||||||
if(this->error == 0 && !this->cancelled) {
|
|
||||||
somfy.commit();
|
|
||||||
releaseAssetName(this->currentFile, sizeof(this->currentFile), "littlefs", settings.chipModel);
|
|
||||||
this->partition = U_SPIFFS;
|
|
||||||
this->lockFS = true;
|
|
||||||
this->error = this->downloadFile();
|
|
||||||
this->lockFS = false;
|
|
||||||
if(this->error == 0) {
|
|
||||||
settings.fwVersion.parse(version);
|
|
||||||
delay(100);
|
|
||||||
ESP_LOGI(TAG, "Committing Configuration...");
|
|
||||||
somfy.commit();
|
|
||||||
}
|
|
||||||
rebootDelay.reboot = true;
|
|
||||||
rebootDelay.rebootTime = millis() + 500;
|
|
||||||
}
|
|
||||||
this->status = GIT_UPDATE_COMPLETE;
|
|
||||||
this->emitUpdateCheck();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool GitUpdater::recoverFilesystem() {
|
|
||||||
sprintf(this->baseUrl, "https://github.com/cjkas/ESPSomfy-RTS/releases/download/%s/", settings.fwVersion.name);
|
|
||||||
releaseAssetName(this->currentFile, sizeof(this->currentFile), "littlefs", settings.chipModel);
|
|
||||||
this->status = GIT_UPDATING;
|
|
||||||
this->partition = U_SPIFFS;
|
|
||||||
this->lockFS = true;
|
|
||||||
this->error = this->downloadFile();
|
|
||||||
this->lockFS = false;
|
|
||||||
if(this->error == 0) {
|
|
||||||
delay(100);
|
|
||||||
ESP_LOGI(TAG, "Committing Configuration...");
|
|
||||||
somfy.commit();
|
|
||||||
}
|
|
||||||
this->status = GIT_UPDATE_COMPLETE;
|
|
||||||
rebootDelay.reboot = true;
|
|
||||||
rebootDelay.rebootTime = millis() + 500;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool GitUpdater::endUpdate() { return true; }
|
|
||||||
int8_t GitUpdater::downloadFile() {
|
|
||||||
ESP_LOGI(TAG, "Begin update %s", this->currentFile);
|
|
||||||
WiFiClientSecure sclient;
|
|
||||||
sclient.setInsecure();
|
|
||||||
HTTPClient https;
|
|
||||||
char url[196];
|
|
||||||
sprintf(url, "%s%s", this->baseUrl, this->currentFile);
|
|
||||||
ESP_LOGD(TAG, "%s", url);
|
|
||||||
resetTaskWdt();
|
|
||||||
if(https.begin(sclient, url)) {
|
|
||||||
https.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
|
|
||||||
ESP_LOGD(TAG, "[HTTPS] GET...");
|
|
||||||
int httpCode = https.GET();
|
|
||||||
if(httpCode > 0) {
|
|
||||||
size_t len = https.getSize();
|
|
||||||
size_t total = 0;
|
|
||||||
uint8_t pct = 0;
|
|
||||||
ESP_LOGD(TAG, "[HTTPS] GET... code: %d - %d", httpCode, len);
|
|
||||||
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY || httpCode == HTTP_CODE_FOUND) {
|
|
||||||
WiFiClient *stream = https.getStreamPtr();
|
|
||||||
if(!Update.begin(len, this->partition)) {
|
|
||||||
ESP_LOGE(TAG, "Update Error detected!!!!!");
|
|
||||||
https.end();
|
|
||||||
return -(Update.getError() + UPDATE_ERR_OFFSET);
|
|
||||||
}
|
|
||||||
uint8_t *buff = (uint8_t *)malloc(MAX_BUFF_SIZE);
|
|
||||||
if(buff) {
|
|
||||||
this->emitDownloadProgress(len, total);
|
|
||||||
int timeouts = 0;
|
|
||||||
while(https.connected() && (len > 0 || len == -1) && total < len) {
|
|
||||||
size_t size = stream->available();
|
|
||||||
resetTaskWdt();
|
|
||||||
if(size) {
|
|
||||||
timeouts = 0;
|
|
||||||
if(this->cancelled && !this->lockFS) {
|
|
||||||
Update.abort();
|
|
||||||
free(buff);
|
|
||||||
https.end();
|
|
||||||
return -(Update.getError() + UPDATE_ERR_OFFSET);
|
|
||||||
}
|
|
||||||
int c = stream->readBytes(buff, ((size > MAX_BUFF_SIZE) ? MAX_BUFF_SIZE : size));
|
|
||||||
total += c;
|
|
||||||
//Serial.println(total);
|
|
||||||
if (Update.write(buff, c) != c) {
|
|
||||||
ESP_LOGE(TAG, "Upload of %s aborted invalid size %d", url, c);
|
|
||||||
free(buff);
|
|
||||||
https.end();
|
|
||||||
sclient.stop();
|
|
||||||
return -(Update.getError() + UPDATE_ERR_OFFSET);
|
|
||||||
}
|
|
||||||
// Calculate the percentage.
|
|
||||||
uint8_t p = (uint8_t)floor(((float)total / (float)len) * 100.0f);
|
|
||||||
if(p != pct) {
|
|
||||||
pct = p;
|
|
||||||
ESP_LOGD(TAG, "LEN:%d TOTAL:%d %d%%", len, total, pct);
|
|
||||||
this->emitDownloadProgress(len, total);
|
|
||||||
}
|
|
||||||
delay(1);
|
|
||||||
if(total >= len) {
|
|
||||||
if(!Update.end(true)) {
|
|
||||||
ESP_LOGE(TAG, "Error downloading update...");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ESP_LOGI(TAG, "Update.end Called...");
|
|
||||||
}
|
|
||||||
https.end();
|
|
||||||
sclient.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
timeouts++;
|
|
||||||
if(timeouts >= 500) {
|
|
||||||
Update.abort();
|
|
||||||
https.end();
|
|
||||||
free(buff);
|
|
||||||
ESP_LOGE(TAG, "Stream timeout!!!");
|
|
||||||
return -43;
|
|
||||||
}
|
|
||||||
sockEmit.loop();
|
|
||||||
webServer.loop();
|
|
||||||
delay(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(buff);
|
|
||||||
if(len > total) {
|
|
||||||
Update.abort();
|
|
||||||
somfy.commit();
|
|
||||||
ESP_LOGE(TAG, "Error downloading file!!!");
|
|
||||||
return -42;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ESP_LOGI(TAG, "Update %s complete", this->currentFile);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// TODO: memory allocation error.
|
|
||||||
ESP_LOGE(TAG, "Unable to allocate memory for update!!!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ESP_LOGE(TAG, "Invalid HTTP Code... %d", httpCode);
|
|
||||||
return httpCode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ESP_LOGE(TAG, "Invalid HTTP Code: %d", httpCode);
|
|
||||||
}
|
|
||||||
https.end();
|
|
||||||
sclient.stop();
|
|
||||||
ESP_LOGI(TAG, "End update %s", this->currentFile);
|
|
||||||
}
|
|
||||||
resetTaskWdt();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
67
src/GitOTA.h
67
src/GitOTA.h
|
|
@ -1,67 +0,0 @@
|
||||||
#ifndef GITOTA_H
|
|
||||||
#define GITOTA_H
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <ArduinoJson.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include "ConfigSettings.h"
|
|
||||||
#include "WResp.h"
|
|
||||||
|
|
||||||
#define GIT_MAX_RELEASES 5
|
|
||||||
#define GIT_STATUS_READY 0
|
|
||||||
#define GIT_STATUS_CHECK 1
|
|
||||||
#define GIT_AWAITING_UPDATE 2
|
|
||||||
#define GIT_UPDATING 3
|
|
||||||
#define GIT_UPDATE_COMPLETE 4
|
|
||||||
#define GIT_UPDATE_CANCELLING 5
|
|
||||||
#define GIT_UPDATE_CANCELLED 6
|
|
||||||
|
|
||||||
class GitRelease {
|
|
||||||
public:
|
|
||||||
uint64_t id = 0;
|
|
||||||
bool draft = false;
|
|
||||||
bool preRelease = false;
|
|
||||||
bool main = false;
|
|
||||||
bool hasFS = false;
|
|
||||||
char hwVersions[128] = "";
|
|
||||||
time_t releaseDate;
|
|
||||||
char name[32] = "";
|
|
||||||
appver_t version;
|
|
||||||
void setReleaseProperty(const char *key, const char *val);
|
|
||||||
void setAssetProperty(const char *key, const char *val);
|
|
||||||
|
|
||||||
};
|
|
||||||
class GitRepo {
|
|
||||||
public:
|
|
||||||
int16_t getReleases(uint8_t num = GIT_MAX_RELEASES);
|
|
||||||
GitRelease releases[GIT_MAX_RELEASES + 1];
|
|
||||||
|
|
||||||
};
|
|
||||||
class GitUpdater {
|
|
||||||
public:
|
|
||||||
bool lockFS = false;
|
|
||||||
bool canCancel = true;
|
|
||||||
uint8_t status = 0;
|
|
||||||
uint32_t lastCheck = 0;
|
|
||||||
bool updateAvailable = false;
|
|
||||||
bool inetAvailable = false;
|
|
||||||
appver_t latest;
|
|
||||||
bool cancelled = false;
|
|
||||||
int16_t error = 0;
|
|
||||||
char targetRelease[32];
|
|
||||||
char currentFile[64] = "";
|
|
||||||
char baseUrl[128] = "";
|
|
||||||
int partition = 0;
|
|
||||||
void checkForUpdate();
|
|
||||||
bool beginUpdate(const char *release);
|
|
||||||
bool endUpdate();
|
|
||||||
int8_t downloadFile();
|
|
||||||
void setCurrentRelease(GitRepo &repo);
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
bool recoverFilesystem();
|
|
||||||
int checkInternet();
|
|
||||||
void emitUpdateCheck(uint8_t num=255);
|
|
||||||
void emitDownloadProgress(size_t total, size_t loaded, const char *evt = "updateProgress");
|
|
||||||
void emitDownloadProgress(uint8_t num, size_t total, size_t loaded, const char *evt = "updateProgress");
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
@ -4,14 +4,12 @@
|
||||||
#include "ConfigSettings.h"
|
#include "ConfigSettings.h"
|
||||||
#include "Somfy.h"
|
#include "Somfy.h"
|
||||||
#include "ConfigFile.h"
|
#include "ConfigFile.h"
|
||||||
#include "GitOTA.h"
|
|
||||||
#include "ModelEditor.h"
|
#include "ModelEditor.h"
|
||||||
|
|
||||||
static const char *TAG = "Somfy";
|
static const char *TAG = "Somfy";
|
||||||
|
|
||||||
extern SomfyShadeController somfy;
|
extern SomfyShadeController somfy;
|
||||||
extern ConfigSettings settings;
|
extern ConfigSettings settings;
|
||||||
extern GitUpdater git;
|
|
||||||
|
|
||||||
void SomfyShadeController::end() { this->transceiver.disableReceive(); }
|
void SomfyShadeController::end() { this->transceiver.disableReceive(); }
|
||||||
SomfyShadeController::SomfyShadeController() {
|
SomfyShadeController::SomfyShadeController() {
|
||||||
|
|
@ -77,7 +75,6 @@ bool SomfyShadeController::begin() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void SomfyShadeController::commit() {
|
void SomfyShadeController::commit() {
|
||||||
if(git.lockFS) return;
|
|
||||||
resetTaskWdt(); // Make sure we don't reset inadvertently.
|
resetTaskWdt(); // Make sure we don't reset inadvertently.
|
||||||
ShadeConfigFile file;
|
ShadeConfigFile file;
|
||||||
file.begin();
|
file.begin();
|
||||||
|
|
@ -87,7 +84,6 @@ void SomfyShadeController::commit() {
|
||||||
this->lastCommit = millis();
|
this->lastCommit = millis();
|
||||||
}
|
}
|
||||||
void SomfyShadeController::writeBackup() {
|
void SomfyShadeController::writeBackup() {
|
||||||
if(git.lockFS) return;
|
|
||||||
resetTaskWdt(); // Make sure we don't reset inadvertently.
|
resetTaskWdt(); // Make sure we don't reset inadvertently.
|
||||||
this->backupData = "";
|
this->backupData = "";
|
||||||
this->backupData.reserve(16384);
|
this->backupData.reserve(16384);
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@
|
||||||
#include "ShadeRequests.h"
|
#include "ShadeRequests.h"
|
||||||
#include "SomfyShadeOps.h"
|
#include "SomfyShadeOps.h"
|
||||||
#include "ModelSnapshot.h"
|
#include "ModelSnapshot.h"
|
||||||
#include "GitOTA.h"
|
|
||||||
#include "esp_core_dump.h"
|
#include "esp_core_dump.h"
|
||||||
#include "esp_system.h"
|
#include "esp_system.h"
|
||||||
#include "esp_app_desc.h"
|
#include "esp_app_desc.h"
|
||||||
|
|
@ -42,7 +41,6 @@ ESPNetwork net;
|
||||||
rebootDelay_t rebootDelay;
|
rebootDelay_t rebootDelay;
|
||||||
SomfyShadeController somfy;
|
SomfyShadeController somfy;
|
||||||
MqttConnection mqtt;
|
MqttConnection mqtt;
|
||||||
GitUpdater git;
|
|
||||||
SomfyShadeOps shadeOps;
|
SomfyShadeOps shadeOps;
|
||||||
ShadeRequests shadeRequests(shadeOps);
|
ShadeRequests shadeRequests(shadeOps);
|
||||||
LoopQueue loopQueue;
|
LoopQueue loopQueue;
|
||||||
|
|
@ -133,7 +131,6 @@ void setup() {
|
||||||
delay(1000);
|
delay(1000);
|
||||||
net.setup();
|
net.setup();
|
||||||
somfy.begin();
|
somfy.begin();
|
||||||
//git.checkForUpdate();
|
|
||||||
// The core starts the watchdog with a 5s timeout before setup(), so
|
// The core starts the watchdog with a 5s timeout before setup(), so
|
||||||
// esp_task_wdt_init() would fail and keep that.
|
// esp_task_wdt_init() would fail and keep that.
|
||||||
const esp_task_wdt_config_t wdt_config = { .timeout_ms = 15000, .idle_core_mask = 1, .trigger_panic = true };
|
const esp_task_wdt_config_t wdt_config = { .timeout_ms = 15000, .idle_core_mask = 1, .trigger_panic = true };
|
||||||
|
|
@ -173,11 +170,6 @@ void loop() {
|
||||||
logIfSlow(TAG, "Loop changes", timing.lap(), LOOP_SLOW_MS);
|
logIfSlow(TAG, "Loop changes", timing.lap(), LOOP_SLOW_MS);
|
||||||
esp_task_wdt_reset();
|
esp_task_wdt_reset();
|
||||||
if(net.connected() || net.softAPOpened) {
|
if(net.connected() || net.softAPOpened) {
|
||||||
if(!rebootDelay.reboot && net.connected() && !net.softAPOpened) {
|
|
||||||
git.loop();
|
|
||||||
logIfSlow(TAG, "Loop git", timing.lap(), LOOP_SLOW_MS);
|
|
||||||
esp_task_wdt_reset();
|
|
||||||
}
|
|
||||||
webServer.loop();
|
webServer.loop();
|
||||||
logIfSlow(TAG, "Loop web server", timing.lap(), LOOP_SLOW_MS);
|
logIfSlow(TAG, "Loop web server", timing.lap(), LOOP_SLOW_MS);
|
||||||
esp_task_wdt_reset();
|
esp_task_wdt_reset();
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ void registerApiRoutes(AsyncWebServer &server) {
|
||||||
server.on("/room", HTTP_GET, [](AsyncWebServerRequest *r) { webServer.handleRoom(r); });
|
server.on("/room", HTTP_GET, [](AsyncWebServerRequest *r) { webServer.handleRoom(r); });
|
||||||
server.on("/shade", HTTP_GET, [](AsyncWebServerRequest *r) { webServer.handleShade(r); });
|
server.on("/shade", HTTP_GET, [](AsyncWebServerRequest *r) { webServer.handleShade(r); });
|
||||||
server.on("/group", HTTP_GET, [](AsyncWebServerRequest *r) { webServer.handleGroup(r); });
|
server.on("/group", HTTP_GET, [](AsyncWebServerRequest *r) { webServer.handleGroup(r); });
|
||||||
server.on("/downloadFirmware", WebRequestMethodComposite(HTTP_GET) | HTTP_POST, [](AsyncWebServerRequest *r) { webServer.handleDownloadFirmware(r); });
|
|
||||||
server.on("/backup", WebRequestMethodComposite(HTTP_GET) | HTTP_POST, [](AsyncWebServerRequest *r) { webServer.handleBackup(r); });
|
server.on("/backup", WebRequestMethodComposite(HTTP_GET) | HTTP_POST, [](AsyncWebServerRequest *r) { webServer.handleBackup(r); });
|
||||||
server.on("/reboot", WebRequestMethodComposite(HTTP_POST) | HTTP_PUT, [](AsyncWebServerRequest *r) { webServer.handleReboot(r); });
|
server.on("/reboot", WebRequestMethodComposite(HTTP_POST) | HTTP_PUT, [](AsyncWebServerRequest *r) { webServer.handleReboot(r); });
|
||||||
// JSON body endpoints
|
// JSON body endpoints
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
#include "Web.h"
|
#include "Web.h"
|
||||||
#include "WebRoutes.h"
|
#include "WebRoutes.h"
|
||||||
#include "MqttConnection.h"
|
#include "MqttConnection.h"
|
||||||
#include "GitOTA.h"
|
|
||||||
#include "ESPNetwork.h"
|
#include "ESPNetwork.h"
|
||||||
#include "Timing.h"
|
#include "Timing.h"
|
||||||
#include <AsyncTCP.h>
|
#include <AsyncTCP.h>
|
||||||
|
|
@ -32,7 +31,6 @@ extern LoopStats loopStats;
|
||||||
extern SomfyShadeController somfy;
|
extern SomfyShadeController somfy;
|
||||||
extern Web webServer;
|
extern Web webServer;
|
||||||
extern MqttConnection mqtt;
|
extern MqttConnection mqtt;
|
||||||
extern GitUpdater git;
|
|
||||||
extern ESPNetwork net;
|
extern ESPNetwork net;
|
||||||
extern LoopQueue loopQueue;
|
extern LoopQueue loopQueue;
|
||||||
extern SnapshotSlots snapshotSlots;
|
extern SnapshotSlots snapshotSlots;
|
||||||
|
|
@ -203,38 +201,13 @@ void serializeAppVersion(JsonFormatter &json, appver_t &ver) {
|
||||||
json.addElem("build", ver.build);
|
json.addElem("build", ver.build);
|
||||||
json.addElem("suffix", ver.suffix);
|
json.addElem("suffix", ver.suffix);
|
||||||
}
|
}
|
||||||
void serializeGitVersion(JsonFormatter &json) {
|
void serializeVersion(JsonFormatter &json) {
|
||||||
json.addElem("available", git.updateAvailable);
|
|
||||||
json.addElem("status", git.status);
|
|
||||||
json.addElem("error", (int32_t)git.error);
|
|
||||||
json.addElem("cancelled", git.cancelled);
|
|
||||||
json.addElem("checkForUpdate", settings.checkForUpdate);
|
|
||||||
json.addElem("inetAvailable", git.inetAvailable);
|
|
||||||
json.beginObject("fwVersion");
|
json.beginObject("fwVersion");
|
||||||
serializeAppVersion(json, settings.fwVersion);
|
serializeAppVersion(json, settings.fwVersion);
|
||||||
json.endObject();
|
json.endObject();
|
||||||
json.beginObject("appVersion");
|
json.beginObject("appVersion");
|
||||||
serializeAppVersion(json, settings.appVersion);
|
serializeAppVersion(json, settings.appVersion);
|
||||||
json.endObject();
|
json.endObject();
|
||||||
json.beginObject("latest");
|
|
||||||
serializeAppVersion(json, git.latest);
|
|
||||||
json.endObject();
|
|
||||||
}
|
|
||||||
void serializeGitRelease(GitRelease *rel, JsonFormatter &json) {
|
|
||||||
Timestamp ts;
|
|
||||||
char buff[20];
|
|
||||||
sprintf(buff, "%llu", rel->id);
|
|
||||||
json.addElem("id", buff);
|
|
||||||
json.addElem("name", rel->name);
|
|
||||||
json.addElem("date", ts.getISOTime(rel->releaseDate));
|
|
||||||
json.addElem("draft", rel->draft);
|
|
||||||
json.addElem("preRelease", rel->preRelease);
|
|
||||||
json.addElem("main", rel->main);
|
|
||||||
json.addElem("hasFS", rel->hasFS);
|
|
||||||
json.addElem("hwVersions", rel->hwVersions);
|
|
||||||
json.beginObject("version");
|
|
||||||
serializeAppVersion(json, rel->version);
|
|
||||||
json.endObject();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The parts of /controller that aren't in the shade model.
|
// The parts of /controller that aren't in the shade model.
|
||||||
|
|
@ -251,7 +224,7 @@ static void controllerHeader(JsonFormatter &json, const model_snapshot_t &snap)
|
||||||
json.endObject();
|
json.endObject();
|
||||||
json.endObject();
|
json.endObject();
|
||||||
json.beginObject("version");
|
json.beginObject("version");
|
||||||
serializeGitVersion(json);
|
serializeVersion(json);
|
||||||
json.endObject();
|
json.endObject();
|
||||||
}
|
}
|
||||||
// The parts of /discovery that aren't in the shade model.
|
// The parts of /discovery that aren't in the shade model.
|
||||||
|
|
@ -262,14 +235,12 @@ static void discoveryHeader(JsonFormatter &json, const model_snapshot_t &snap) {
|
||||||
else if(net.connType == conn_types_t::wifi) connType = "Wifi";
|
else if(net.connType == conn_types_t::wifi) connType = "Wifi";
|
||||||
json.addElem("serverId", settings.serverId);
|
json.addElem("serverId", settings.serverId);
|
||||||
json.addElem("version", settings.fwVersion.name);
|
json.addElem("version", settings.fwVersion.name);
|
||||||
json.addElem("latest", git.latest.name);
|
|
||||||
json.addElem("model", "ESPSomfyRTS");
|
json.addElem("model", "ESPSomfyRTS");
|
||||||
json.addElem("hostname", settings.hostname);
|
json.addElem("hostname", settings.hostname);
|
||||||
json.addElem("authType", static_cast<uint8_t>(settings.Security.type));
|
json.addElem("authType", static_cast<uint8_t>(settings.Security.type));
|
||||||
json.addElem("permissions", settings.Security.permissions);
|
json.addElem("permissions", settings.Security.permissions);
|
||||||
json.addElem("chipModel", settings.chipModel);
|
json.addElem("chipModel", settings.chipModel);
|
||||||
json.addElem("connType", connType);
|
json.addElem("connType", connType);
|
||||||
json.addElem("checkForUpdate", settings.checkForUpdate);
|
|
||||||
json.beginObject("memory");
|
json.beginObject("memory");
|
||||||
json.addElem("max", ESP.getMaxAllocHeap());
|
json.addElem("max", ESP.getMaxAllocHeap());
|
||||||
json.addElem("free", ESP.getFreeHeap());
|
json.addElem("free", ESP.getFreeHeap());
|
||||||
|
|
@ -697,40 +668,6 @@ void Web::handleSetSensor(AsyncWebServerRequest *request, JsonVariant &json) {
|
||||||
}
|
}
|
||||||
else request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"shadeId was not provided\"}"));
|
else request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"shadeId was not provided\"}"));
|
||||||
}
|
}
|
||||||
void Web::handleDownloadFirmware(AsyncWebServerRequest *request) {
|
|
||||||
if(request->method() == HTTP_OPTIONS) { request->send(200); return; }
|
|
||||||
if(!this->isAuthenticated(request)) return;
|
|
||||||
GitRepo repo;
|
|
||||||
GitRelease *rel = nullptr;
|
|
||||||
int8_t err = repo.getReleases();
|
|
||||||
ESP_LOGI(TAG, "Async downloadFirmware called...");
|
|
||||||
if(err == 0) {
|
|
||||||
if(asyncHasParam(request, "ver")) {
|
|
||||||
String ver = asyncParam(request, "ver");
|
|
||||||
if(ver == "latest") rel = &repo.releases[0];
|
|
||||||
else if(ver == "main") rel = &repo.releases[GIT_MAX_RELEASES];
|
|
||||||
else {
|
|
||||||
for(uint8_t i = 0; i < GIT_MAX_RELEASES; i++) {
|
|
||||||
if(repo.releases[i].id == 0) continue;
|
|
||||||
if(strcmp(repo.releases[i].name, ver.c_str()) == 0) { rel = &repo.releases[i]; break; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(rel) {
|
|
||||||
AsyncJsonResp resp;
|
|
||||||
resp.beginResponse(request, g_async_content, sizeof(g_async_content));
|
|
||||||
resp.beginObject();
|
|
||||||
serializeGitRelease(rel, resp);
|
|
||||||
resp.endObject();
|
|
||||||
resp.endResponse();
|
|
||||||
strcpy(git.targetRelease, rel->name);
|
|
||||||
git.status = GIT_AWAITING_UPDATE;
|
|
||||||
}
|
|
||||||
else request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Release not found in repo.\"}"));
|
|
||||||
}
|
|
||||||
else request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Release version not supplied.\"}"));
|
|
||||||
}
|
|
||||||
else request->send(err, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Error communicating with Github.\"}"));
|
|
||||||
}
|
|
||||||
void Web::handleBackup(AsyncWebServerRequest *request) {
|
void Web::handleBackup(AsyncWebServerRequest *request) {
|
||||||
if(!this->isAuthenticated(request)) return;
|
if(!this->isAuthenticated(request)) return;
|
||||||
bool attach = false;
|
bool attach = false;
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ class Web {
|
||||||
void handleRepeatCommand(AsyncWebServerRequest *request, JsonVariant &json);
|
void handleRepeatCommand(AsyncWebServerRequest *request, JsonVariant &json);
|
||||||
void handleSetPositions(AsyncWebServerRequest *request, JsonVariant &json);
|
void handleSetPositions(AsyncWebServerRequest *request, JsonVariant &json);
|
||||||
void handleSetSensor(AsyncWebServerRequest *request, JsonVariant &json);
|
void handleSetSensor(AsyncWebServerRequest *request, JsonVariant &json);
|
||||||
void handleDownloadFirmware(AsyncWebServerRequest *request);
|
|
||||||
void handleBackup(AsyncWebServerRequest *request);
|
void handleBackup(AsyncWebServerRequest *request);
|
||||||
void handleReboot(AsyncWebServerRequest *request);
|
void handleReboot(AsyncWebServerRequest *request);
|
||||||
void handleNotFound(AsyncWebServerRequest *request);
|
void handleNotFound(AsyncWebServerRequest *request);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
#include "Somfy.h"
|
#include "Somfy.h"
|
||||||
#include "LoopQueue.h"
|
#include "LoopQueue.h"
|
||||||
#include "ModelSnapshot.h"
|
#include "ModelSnapshot.h"
|
||||||
#include "GitOTA.h"
|
|
||||||
|
|
||||||
// What WebUiRoutes, ApiRoutes and the handlers both of them call (Web.cpp)
|
// What WebUiRoutes, ApiRoutes and the handlers both of them call (Web.cpp)
|
||||||
// share. Internal to the web servers.
|
// share. Internal to the web servers.
|
||||||
|
|
@ -40,6 +39,6 @@ void serializeShade(SomfyShade *shade, JsonFormatter &json);
|
||||||
void serializeShade(SomfyShade *shade, JsonFormatter &json, bool paired, uint16_t lastRollingCode);
|
void serializeShade(SomfyShade *shade, JsonFormatter &json, bool paired, uint16_t lastRollingCode);
|
||||||
void serializeTransceiverConfig(JsonFormatter &json);
|
void serializeTransceiverConfig(JsonFormatter &json);
|
||||||
void serializeAppVersion(JsonFormatter &json, appver_t &ver);
|
void serializeAppVersion(JsonFormatter &json, appver_t &ver);
|
||||||
void serializeGitVersion(JsonFormatter &json);
|
// The installed firmware and web application versions.
|
||||||
void serializeGitRelease(GitRelease *rel, JsonFormatter &json);
|
void serializeVersion(JsonFormatter &json);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
#include "Web.h"
|
#include "Web.h"
|
||||||
#include "WebRoutes.h"
|
#include "WebRoutes.h"
|
||||||
#include "MqttConnection.h"
|
#include "MqttConnection.h"
|
||||||
#include "GitOTA.h"
|
|
||||||
#include "ESPNetwork.h"
|
#include "ESPNetwork.h"
|
||||||
#include <AsyncTCP.h>
|
#include <AsyncTCP.h>
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
|
@ -31,7 +30,6 @@ extern rebootDelay_t rebootDelay;
|
||||||
extern SomfyShadeController somfy;
|
extern SomfyShadeController somfy;
|
||||||
extern Web webServer;
|
extern Web webServer;
|
||||||
extern MqttConnection mqtt;
|
extern MqttConnection mqtt;
|
||||||
extern GitUpdater git;
|
|
||||||
extern ESPNetwork net;
|
extern ESPNetwork net;
|
||||||
extern LoopQueue loopQueue;
|
extern LoopQueue loopQueue;
|
||||||
|
|
||||||
|
|
@ -189,50 +187,6 @@ void registerWebUiRoutes(AsyncWebServer &server) {
|
||||||
request->send(LittleFS, "/shades.tmp", _encoding_text);
|
request->send(LittleFS, "/shades.tmp", _encoding_text);
|
||||||
});
|
});
|
||||||
|
|
||||||
server.on("/getReleases", HTTP_GET, [](AsyncWebServerRequest *request) {
|
|
||||||
GitRepo repo;
|
|
||||||
repo.getReleases();
|
|
||||||
git.setCurrentRelease(repo);
|
|
||||||
AsyncJsonResp resp;
|
|
||||||
resp.beginResponse(request, g_async_content, sizeof(g_async_content));
|
|
||||||
resp.beginObject();
|
|
||||||
// Inline GitRepo::toJSON
|
|
||||||
resp.beginObject("fwVersion");
|
|
||||||
serializeAppVersion(resp, settings.fwVersion);
|
|
||||||
resp.endObject();
|
|
||||||
resp.beginObject("appVersion");
|
|
||||||
serializeAppVersion(resp, settings.appVersion);
|
|
||||||
resp.endObject();
|
|
||||||
resp.beginArray("releases");
|
|
||||||
for(uint8_t i = 0; i < GIT_MAX_RELEASES + 1; i++) {
|
|
||||||
if(repo.releases[i].id == 0) continue;
|
|
||||||
resp.beginObject();
|
|
||||||
serializeGitRelease(&repo.releases[i], resp);
|
|
||||||
resp.endObject();
|
|
||||||
}
|
|
||||||
resp.endArray();
|
|
||||||
resp.endObject();
|
|
||||||
resp.endResponse();
|
|
||||||
});
|
|
||||||
|
|
||||||
server.on("/downloadFirmware", HTTP_GET, [](AsyncWebServerRequest *request) { webServer.handleDownloadFirmware(request); });
|
|
||||||
|
|
||||||
server.on("/cancelFirmware", HTTP_GET, [](AsyncWebServerRequest *request) {
|
|
||||||
if(!git.lockFS) {
|
|
||||||
git.status = GIT_UPDATE_CANCELLING;
|
|
||||||
AsyncJsonResp resp;
|
|
||||||
resp.beginResponse(request, g_async_content, sizeof(g_async_content));
|
|
||||||
resp.beginObject();
|
|
||||||
serializeGitVersion(resp);
|
|
||||||
resp.endObject();
|
|
||||||
resp.endResponse();
|
|
||||||
git.cancelled = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Cannot cancel during filesystem update.\"}"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.on("/backup", HTTP_GET, [](AsyncWebServerRequest *request) { webServer.handleBackup(request); });
|
server.on("/backup", HTTP_GET, [](AsyncWebServerRequest *request) { webServer.handleBackup(request); });
|
||||||
|
|
||||||
server.on("/restore", HTTP_POST,
|
server.on("/restore", HTTP_POST,
|
||||||
|
|
@ -550,10 +504,6 @@ void registerWebUiRoutes(AsyncWebServer &server) {
|
||||||
// updateShadeConfig - file upload
|
// updateShadeConfig - file upload
|
||||||
server.on("/updateShadeConfig", HTTP_POST,
|
server.on("/updateShadeConfig", HTTP_POST,
|
||||||
[](AsyncWebServerRequest *request) {
|
[](AsyncWebServerRequest *request) {
|
||||||
if(git.lockFS) {
|
|
||||||
request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Filesystem update in progress\"}"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
request->send(200, _encoding_json, "{\"status\":\"ERROR\",\"desc\":\"Updating Shade Config: \"}");
|
request->send(200, _encoding_json, "{\"status\":\"ERROR\",\"desc\":\"Updating Shade Config: \"}");
|
||||||
},
|
},
|
||||||
[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
|
[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
|
||||||
|
|
@ -759,11 +709,9 @@ void registerWebUiRoutes(AsyncWebServer &server) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
JsonObject obj = json.as<JsonObject>();
|
JsonObject obj = json.as<JsonObject>();
|
||||||
if(!obj["hostname"].isNull() || !obj["ssdpBroadcast"].isNull() || !obj["checkForUpdate"].isNull()) {
|
if(!obj["hostname"].isNull() || !obj["ssdpBroadcast"].isNull()) {
|
||||||
bool checkForUpdate = settings.checkForUpdate;
|
|
||||||
settings.fromJSON(obj);
|
settings.fromJSON(obj);
|
||||||
settings.save();
|
settings.save();
|
||||||
if(settings.checkForUpdate != checkForUpdate) git.emitUpdateCheck();
|
|
||||||
if(!obj["hostname"].isNull()) net.updateHostname();
|
if(!obj["hostname"].isNull()) net.updateHostname();
|
||||||
}
|
}
|
||||||
if(!obj["ntpServer"].isNull() || !obj["posixZone"].isNull()) {
|
if(!obj["ntpServer"].isNull() || !obj["posixZone"].isNull()) {
|
||||||
|
|
@ -872,7 +820,6 @@ void registerWebUiRoutes(AsyncWebServer &server) {
|
||||||
resp.addElem("hostname", settings.hostname);
|
resp.addElem("hostname", settings.hostname);
|
||||||
resp.addElem("connType", static_cast<uint8_t>(settings.connType));
|
resp.addElem("connType", static_cast<uint8_t>(settings.connType));
|
||||||
resp.addElem("chipModel", settings.chipModel);
|
resp.addElem("chipModel", settings.chipModel);
|
||||||
resp.addElem("checkForUpdate", settings.checkForUpdate);
|
|
||||||
resp.addElem("ntpServer", settings.NTP.ntpServer);
|
resp.addElem("ntpServer", settings.NTP.ntpServer);
|
||||||
resp.addElem("posixZone", settings.NTP.posixZone);
|
resp.addElem("posixZone", settings.NTP.posixZone);
|
||||||
resp.endObject();
|
resp.endObject();
|
||||||
|
|
@ -952,17 +899,6 @@ void registerWebUiRoutes(AsyncWebServer &server) {
|
||||||
resp.endResponse();
|
resp.endResponse();
|
||||||
});
|
});
|
||||||
|
|
||||||
server.on("/recoverFilesystem", WebRequestMethodComposite(HTTP_GET) | HTTP_POST, [](AsyncWebServerRequest *request) {
|
|
||||||
if(git.status == GIT_UPDATING)
|
|
||||||
request->send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Filesystem is updating. Please wait!!!\"}");
|
|
||||||
else if(git.status != GIT_STATUS_READY)
|
|
||||||
request->send(200, "application/json", "{\"status\":\"ERROR\",\"desc\":\"Cannot recover file system at this time.\"}");
|
|
||||||
else {
|
|
||||||
git.recoverFilesystem();
|
|
||||||
request->send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Recovering filesystem from github please wait!!!\"}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.onNotFound([](AsyncWebServerRequest *r) {
|
server.onNotFound([](AsyncWebServerRequest *r) {
|
||||||
if(r->method() == HTTP_OPTIONS) { r->send(200); return; }
|
if(r->method() == HTTP_OPTIONS) { r->send(200); return; }
|
||||||
webServer.handleNotFound(r);
|
webServer.handleNotFound(r);
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
#include "ConfigSettings.h"
|
#include "ConfigSettings.h"
|
||||||
#include "Somfy.h"
|
#include "Somfy.h"
|
||||||
#include "ESPNetwork.h"
|
#include "ESPNetwork.h"
|
||||||
#include "GitOTA.h"
|
|
||||||
#include "Timing.h"
|
#include "Timing.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
|
|
@ -16,7 +15,6 @@ extern ConfigSettings settings;
|
||||||
extern ESPNetwork net;
|
extern ESPNetwork net;
|
||||||
extern SomfyShadeController somfy;
|
extern SomfyShadeController somfy;
|
||||||
extern SocketEmitter sockEmit;
|
extern SocketEmitter sockEmit;
|
||||||
extern GitUpdater git;
|
|
||||||
|
|
||||||
AsyncWebServer wsServer(8080);
|
AsyncWebServer wsServer(8080);
|
||||||
AsyncWebSocket ws("/");
|
AsyncWebSocket ws("/");
|
||||||
|
|
@ -147,6 +145,20 @@ uint8_t SocketEmitter::activeClients(uint8_t room) {
|
||||||
if(room < SOCK_MAX_ROOMS) return this->rooms[room].activeClients();
|
if(room < SOCK_MAX_ROOMS) return this->rooms[room].activeClients();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
// The installed versions, in the fwStatus event the web UI and Home Assistant
|
||||||
|
// read them from.
|
||||||
|
static void emitFirmwareVersion(uint8_t num) {
|
||||||
|
JsonSockEvent *json = sockEmit.beginEmit("fwStatus");
|
||||||
|
json->beginObject();
|
||||||
|
json->beginObject("fwVersion");
|
||||||
|
settings.fwVersion.toJSON(json);
|
||||||
|
json->endObject();
|
||||||
|
json->beginObject("appVersion");
|
||||||
|
settings.appVersion.toJSON(json);
|
||||||
|
json->endObject();
|
||||||
|
json->endObject();
|
||||||
|
sockEmit.endEmit(num);
|
||||||
|
}
|
||||||
void SocketEmitter::initClients() {
|
void SocketEmitter::initClients() {
|
||||||
for(uint8_t i = 0; i < sizeof(this->newClients); i++) {
|
for(uint8_t i = 0; i < sizeof(this->newClients); i++) {
|
||||||
uint8_t slot = this->newClients[i];
|
uint8_t slot = this->newClients[i];
|
||||||
|
|
@ -162,8 +174,8 @@ void SocketEmitter::initClients() {
|
||||||
somfy.emitState(slot);
|
somfy.emitState(slot);
|
||||||
logElapsed(TAG, "Socket init somfy state", step.lap());
|
logElapsed(TAG, "Socket init somfy state", step.lap());
|
||||||
if(!ws.hasClient(asyncId)) { this->newClients[i] = 255; continue; }
|
if(!ws.hasClient(asyncId)) { this->newClients[i] = 255; continue; }
|
||||||
git.emitUpdateCheck(slot);
|
emitFirmwareVersion(slot);
|
||||||
logElapsed(TAG, "Socket init update check", step.lap());
|
logElapsed(TAG, "Socket init firmware version", step.lap());
|
||||||
if(!ws.hasClient(asyncId)) { this->newClients[i] = 255; continue; }
|
if(!ws.hasClient(asyncId)) { this->newClients[i] = 255; continue; }
|
||||||
net.emitSockets(slot);
|
net.emitSockets(slot);
|
||||||
logElapsed(TAG, "Socket init network", step.lap());
|
logElapsed(TAG, "Socket init network", step.lap());
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue