More v2.2

This commit is contained in:
Gunnar Skjold 2022-11-20 20:47:29 +01:00
parent ab7128c53a
commit 8e54f23367
15 changed files with 514 additions and 75 deletions

View File

@ -34,9 +34,19 @@
<StatusPage sysinfo={sysinfo} data={data}/>
</Route>
</Router>
{#if sysinfo.vndcfg === false}
{#if sysinfo.upgrading}
<Mask active=true message="Device is upgrading, please wait"/>
{:else if sysinfo.vndcfg === false}
{#if sysinfo.booting}
<Mask active=true message="Device is booting, please wait"/>
{:else}
<VendorModal sysinfo={sysinfo}/>
{/if}
{:else if sysinfo.usrcfg === false}
{#if sysinfo.booting}
<Mask active=true message="Device is booting, please wait"/>
{:else}
<SetupModal sysinfo={sysinfo}/>
{/if}
{/if}
</div>

View File

@ -4,7 +4,7 @@
export let chip;
</script>
<option value=""></option>
<option value={-1}></option>
{#if chip == 'esp8266'}
<optgroup label="amsleser.no">
<option value={7}>{boardtype(chip, 7)}</option>

View File

@ -518,6 +518,7 @@
</div>
{/if}
{#if configuration.i.v.p > 0 || sysinfo.chip == 'esp8266'}
<input type="hidden" name="iv" value="true"/>
<div class="my-1 flex flex-wrap">
<div>
Vcc offset<br/>

View File

@ -7,7 +7,8 @@ let sysinfo = {
vndcfg: null,
usrcfg: null,
fwconsent: null,
booting: false
booting: true,
upgrading: false
};
export const sysinfoStore = writable(sysinfo);
@ -17,20 +18,38 @@ export async function getSysinfo() {
sysinfoStore.set(sysinfo);
};
let lastTemp = -127;
let data = {};
export const dataStore = readable(data, (set) => {
async function getData(){
const response = await fetch("/data.json");
data = (await response.json())
set(data);
if(sysinfo.booting) {
getSysinfo();
}
let timeout;
async function getData() {
fetch("/data.json")
.then((res) => res.json())
.then((data) => {
set(data);
if(lastTemp != data.t) {
lastTemp = data.t;
getTemperatures();
}
if(sysinfo.upgrading) {
window.location.reload();
} else if(sysinfo.booting) {
getSysinfo();
}
timeout = setTimeout(getData, 5000);
})
.catch((err) => {
data.em = 3;
data.hm = 0;
data.wm = 0;
data.mm = 0;
set(data);
timeout = setTimeout(getData, 15000);
});
}
const interval = setInterval(getData, 5000);
getData();
return function stop() {
clearInterval(interval);
clearTimeout(timeout);
}
});
@ -40,13 +59,12 @@ export const pricesStore = readable(prices, (set) => {
const response = await fetch("/energyprice.json");
prices = (await response.json())
set(prices);
let date = new Date();
setTimeout(getPrices, (61-date.getMinutes())*60000)
}
const date = new Date();
const timeout = setTimeout(getPrices, (61-date.getMinutes())*60000)
getPrices();
return function stop() {
clearTimeout(timeout);
}
return function stop() {}
});
let dayPlot = {};
@ -55,13 +73,12 @@ export const dayPlotStore = readable(dayPlot, (set) => {
const response = await fetch("/dayplot.json");
dayPlot = (await response.json())
set(dayPlot);
let date = new Date();
setTimeout(getDayPlot, (61-date.getMinutes())*60000)
}
const date = new Date();
const timeout = setTimeout(getDayPlot, (61-date.getMinutes())*60000)
getDayPlot();
return function stop() {
clearTimeout(timeout);
}
return function stop() {}
});
let monthPlot = {};
@ -70,25 +87,31 @@ export const monthPlotStore = readable(monthPlot, (set) => {
const response = await fetch("/monthplot.json");
monthPlot = (await response.json())
set(monthPlot);
let date = new Date();
setTimeout(getmonthPlot, (24-date.getHours())*3600000)
}
const date = new Date();
const timeout = setTimeout(getmonthPlot, (24-date.getHours())*3600000)
getmonthPlot();
return function stop() {
clearTimeout(timeout);
}
return function stop() {}
});
let temperatures = {};
export const temperaturesStore = readable(temperatures, (set) => {
async function getTemperatures(){
const response = await fetch("/temperature.json");
temperatures = (await response.json())
set(temperatures);
}
const interval = setInterval(getTemperatures, 60000);
export async function getTemperatures() {
const response = await fetch("/temperature.json");
temperatures = (await response.json())
temperaturesStore.set(temperatures);
}
export const temperaturesStore = writable(temperatures, (set) => {
getTemperatures();
return function stop() {
clearTimeout(interval);
}
return function stop() {}
});
let releases = [];
export const gitHubReleaseStore = writable(releases);
export async function getGitHubReleases() {
const response = await fetch("https://api.github.com/repos/gskjold/AmsToMqttBridge/releases");
releases = (await response.json())
gitHubReleaseStore.set(releases);
};

View File

@ -0,0 +1,6 @@
<script></script>
<!-- Heroicons -->
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 8.25H7.5a2.25 2.25 0 00-2.25 2.25v9a2.25 2.25 0 002.25 2.25h9a2.25 2.25 0 002.25-2.25v-9a2.25 2.25 0 00-2.25-2.25H15M9 12l3 3m0 0l3-3m-3 3V2.25" />
</svg>

View File

@ -1,6 +1,8 @@
<script>
import { Link } from "svelte-navigator";
import { sysinfoStore } from './DataStores.js';
import { sysinfoStore, getGitHubReleases, gitHubReleaseStore } from './DataStores.js';
import { upgrade, getNextVersion } from './UpgradeHelper';
import { boardtype } from './Helpers.js';
import GitHubLogo from './../assets/github.svg';
import Uptime from "./Uptime.svelte";
import Badge from './Badge.svelte';
@ -9,10 +11,13 @@
import InfoIcon from "./InfoIcon.svelte";
import HelpIcon from "./HelpIcon.svelte";
import ReloadIcon from "./ReloadIcon.svelte";
import DownloadIcon from "./DownloadIcon.svelte";
export let data = {}
export let sysinfo = {}
let nextVersion = {};
async function reboot() {
const response = await fetch('/reboot', {
method: 'POST'
@ -29,6 +34,23 @@
reboot();
}
}
function askUpgrade() {
if(confirm('Do you want to upgrade this device to ' + nextVersion.tag_name + '?')) {
if((sysinfo.board != 2 && sysinfo.board != 4 && sysinfo.board != 7) || confirm('WARNING: ' + boardtype(sysinfo.chip, sysinfo.board) + ' must be connected to an external power supply during firmware upgrade. Failure to do so may cause power-down during upload resulting in non-functioning unit.')) {
sysinfoStore.update(s => {
s.upgrading = true;
return s;
});
upgrade(nextVersion);
}
}
}
gitHubReleaseStore.subscribe(releases => {
nextVersion = getNextVersion(sysinfo.version, releases);
});
getGitHubReleases();
</script>
<nav class="bg-violet-600 p-1 rounded-md mx-2">
@ -38,14 +60,16 @@
</div>
<div class="flex-none my-auto p-2 flex space-x-4">
<div class="flex-none my-auto"><Uptime epoch={data.u}/></div>
<div class="flex-none my-auto">{ data.t ? data.t.toFixed(1) : '-' }&deg;C</div>
{#if data.t > -50}
<div class="flex-none my-auto">{ data.t > -50 ? data.t.toFixed(1) : '-' }&deg;C</div>
{/if}
<div class="flex-none my-auto">Free mem: {data.m ? (data.m/1000).toFixed(1) : '-'}kb</div>
</div>
<div class="flex-auto my-auto justify-center p-2">
<Badge title="ESP" text={sysinfo.booting ? 'Booting' : data.v > 0 ? data.v.toFixed(2)+"V" : "ESP"} color={sysinfo.booting ? 'yellow' : data.em === 1 ? 'green' : data.em === 2 ? 'yellow' : data.em === 3 ? 'red' : 'gray'}/>
<Badge title="HAN" text="HAN" color={sysinfo.booting ? 'gray' : data.hm === 1 ? 'green' : data.hm === 2 ? 'yellow' : data.em === 3 ? 'red' : 'gray'}/>
<Badge title="WiFi" text={data.r ? data.r.toFixed(0)+"dBm" : "WiFi"} color={sysinfo.booting ? 'gray' : data.wm === 1 ? 'green' : data.wm === 2 ? 'yellow' : data.em === 3 ? 'red' : 'gray'}/>
<Badge title="MQTT" text="MQTT" color={sysinfo.booting ? 'gray' : data.mm === 1 ? 'green' : data.mm === 2 ? 'yellow' : data.em === 3 ? 'red' : 'gray'}/>
<Badge title="ESP" text={sysinfo.booting ? 'Booting' : data.v > 1.0 ? data.v.toFixed(2)+"V" : "ESP"} color={sysinfo.booting ? 'yellow' : data.em === 1 ? 'green' : data.em === 2 ? 'yellow' : data.em === 3 ? 'red' : 'gray'}/>
<Badge title="HAN" text="HAN" color={sysinfo.booting ? 'gray' : data.hm === 1 ? 'green' : data.hm === 2 ? 'yellow' : data.hm === 3 ? 'red' : 'gray'}/>
<Badge title="WiFi" text={data.r ? data.r.toFixed(0)+"dBm" : "WiFi"} color={sysinfo.booting ? 'gray' : data.wm === 1 ? 'green' : data.wm === 2 ? 'yellow' : data.wm === 3 ? 'red' : 'gray'}/>
<Badge title="MQTT" text="MQTT" color={sysinfo.booting ? 'gray' : data.mm === 1 ? 'green' : data.mm === 2 ? 'yellow' : data.mm === 3 ? 'red' : 'gray'}/>
</div>
<div class="flex-auto p-2 flex flex-row-reverse">
<div class="flex-none">
@ -57,15 +81,20 @@
<div class="flex-none px-1 mt-1" title="Configuration">
<Link to="/configuration"><GearIcon/></Link>
</div>
<div class="flex-none px-1 mt-1" title="Configuration">
<div class="flex-none px-1 mt-1" title="Reboot">
<button on:click={askReload} class={sysinfo.booting ? 'text-yellow-300' : ''}><ReloadIcon/></button>
</div>
<div class="flex-none px-1 mt-1" title="Device information">
<Link to="/status"><InfoIcon/></Link>
</div>
<div class="flex-none px-1 mt-1" title="Device information">
<div class="flex-none px-1 mt-1" title="Documentation">
<a href="https://github.com/gskjold/AmsToMqttBridge/wiki" target='_blank' rel="noreferrer"><HelpIcon/></a>
</div>
{#if sysinfo.fwconsent && nextVersion}
<div class="flex-none px-4 mt-1 text-yellow-500" title="New version: {nextVersion.tag_name}">
<button on:click={askUpgrade} class="flex"><DownloadIcon/> <span class="mt-1">{nextVersion.tag_name}</span></button>
</div>
{/if}
</div>
</div>
</nav>

View File

@ -31,7 +31,7 @@
}
.plot-value {
font-size: 1.7rem;
cursor: pointer;
//cursor: pointer;
}
.plot-unit {
font-size: 1.0rem;

View File

@ -5,9 +5,40 @@
export let sysinfo = {}
let staticIp = false;
let ntpDhcp = true;
let loadingOrSaving = false;
let tries = 0;
function scanForDevice() {
var url = "";
tries++;
if(sysinfo.net.ip && tries%3 == 0) {
url = "http://" + sysinfo.net.ip;
} else if(sysinfo.hostname && tries%3 == 1) {
url = "http://" + sysinfo.hostname;
} else if(sysinfo.hostname && tries%3 == 2) {
url = "http://" + sysinfo.hostname + ".local";
} else {
url = "";
}
if(console) console.log("Trying url " + url);
var retry = function() {
setTimeout(scanForDevice, 1000);
};
var xhr = new XMLHttpRequest();
xhr.timeout = 5000;
xhr.addEventListener('abort', retry);
xhr.addEventListener('error', retry);
xhr.addEventListener('timeout', retry);
xhr.addEventListener('load', function(e) {
window.location.href = url ? url : "/";
});
xhr.open("GET", url + "/is-alive", true);
xhr.send();
};
async function handleSubmit(e) {
loadingOrSaving = true;
const formData = new FormData(e.target)
@ -27,6 +58,7 @@
sysinfoStore.update(s => {
s.usrcfg = res.success;
s.booting = res.reboot;
setTimeout(scanForDevice, 5000);
return s;
});
}
@ -49,7 +81,7 @@
</div>
<div>
Hostname:
<input name="sh" type="text" class="h-10 rounded-md shadow-sm border-gray-300 w-full" maxlength="32" pattern="[a-z0-9_-]+" placeholder="Optional, ex.: ams-reader" value="ams-{sysinfo.chipId}"/>
<input name="sh" bind:value={sysinfo.hostname} type="text" class="h-10 rounded-md shadow-sm border-gray-300 w-full" maxlength="32" pattern="[a-z0-9_-]+" placeholder="Optional, ex.: ams-reader"/>
</div>
<div class="my-3">
<label><input type="checkbox" name="sm" value="static" class="rounded mb-1" bind:checked={staticIp} /> Static IP</label>

View File

@ -1,36 +1,58 @@
<script>
import { metertype, boardtype } from './Helpers.js';
import { getSysinfo } from './DataStores.js';
import { getSysinfo, gitHubReleaseStore, sysinfoStore } from './DataStores.js';
import { upgrade, getNextVersion } from './UpgradeHelper';
import DownloadIcon from './DownloadIcon.svelte';
import UploadIcon from './UploadIcon.svelte';
export let sysinfo = {}
export let data = {}
let nextVersion = {};
gitHubReleaseStore.subscribe(releases => {
nextVersion = getNextVersion(sysinfo.version, releases);
if(!nextVersion) {
nextVersion = releases[0];
}
});
function askUpgrade() {
if(confirm('Do you want to upgrade this device to ' + nextVersion.tag_name + '?')) {
if((sysinfo.board != 2 && sysinfo.board != 4 && sysinfo.board != 7) || confirm('WARNING: ' + boardtype(sysinfo.chip, sysinfo.board) + ' must be connected to an external power supply during firmware upgrade. Failure to do so may cause power-down during upload resulting in non-functioning unit.')) {
sysinfoStore.update(s => {
s.upgrading = true;
return s;
});
upgrade(nextVersion);
}
}
}
let fileinput;
getSysinfo();
</script>
<div class="grid xl:grid-cols-4 lg:grid-cols-2 md:grid-cols-2">
<div class="bg-white m-2 p-2 rounded-md shadow-lg pb-4 text-gray-700">
<strong class="text-sm">Device information</strong>
<div class="my-3">
<div class="my-2">
Chip: {sysinfo.chip}
</div>
<div class="my-3">
<div class="my-2">
Device: {boardtype(sysinfo.chip, sysinfo.board)}
</div>
<div class="my-3">
Firmware version: {sysinfo.version}
</div>
</div>
{#if sysinfo.meter}
<div class="bg-white m-2 p-2 rounded-md shadow-lg pb-4 text-gray-700">
<strong class="text-sm">Meter</strong>
<div class="my-3">
<div class="my-2">
Manufacturer: {metertype(sysinfo.meter.mfg)}
</div>
<div class="my-3">
<div class="my-2">
Model: {sysinfo.meter.model}
</div>
<div class="my-3">
<div class="my-2">
ID: {sysinfo.meter.id}
</div>
</div>
@ -38,18 +60,49 @@
{#if sysinfo.net}
<div class="bg-white m-2 p-2 rounded-md shadow-lg pb-4 text-gray-700">
<strong class="text-sm">Network</strong>
<div class="my-3">
<div class="my-2">
IP: {sysinfo.net.ip}
</div>
<div class="my-3">
<div class="my-2">
Mask: {sysinfo.net.mask}
</div>
<div class="my-3">
<div class="my-2">
Gateway: {sysinfo.net.gw}
</div>
<div class="my-3">
<div class="my-2">
DNS: {sysinfo.net.dns1} {#if sysinfo.net.dns2 != '0.0.0.0'}/ {sysinfo.net.dns2}{/if}
</div>
</div>
{/if}
<div class="bg-white m-2 p-2 rounded-md shadow-lg pb-4 text-gray-700">
<strong class="text-sm">Firmware</strong>
<div class="my-2">
Installed version: {sysinfo.version}
</div>
<div class="my-2 flex">
Latest version:
{#if sysinfo.fwconsent && nextVersion && nextVersion.tag_name}
<a href={nextVersion.html_url} class="ml-2 text-blue-600 hover:text-blue-800" target='_blank' rel="noreferrer">{nextVersion.tag_name}</a>
<div class="flex-none ml-2 text-green-500" title="Install this version">
<button on:click={askUpgrade}><DownloadIcon/></button>
</div>
{/if}
</div>
{#if sysinfo.board == 2 || sysinfo.board == 4 || sysinfo.board == 7 }
<div class="my-auto bg-red-500 text-red-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">
{boardtype(sysinfo.chip, sysinfo.board)} must be connected to an external power supply during firmware upgrade. Failure to do so may cause power-down during upload resulting in non-functioning unit.
</div>
{/if}
<div class="my-2 flex">
<form action="/firmware" enctype="multipart/form-data" method="post">
<input style="display:none" name="file" type="file" accept=".bin" bind:this={fileinput}>
{#if fileinput && fileinput.files.length == 0}
<button type="button" on:click={()=>{fileinput.click();}} class="text-xs py-1 px-2 rounded bg-blue-500 text-white float-right mr-3">Choose firmware file</button>
{:else if fileinput}
{fileinput.files[0].name}
<button type="submit" class="ml-2 text-xs py-1 px-2 rounded bg-blue-500 text-white float-right mr-3">Upload</button>
{/if}
</form>
</div>
</div>
</div>

View File

@ -0,0 +1,65 @@
export async function upgrade(version) {
const data = new URLSearchParams()
data.append('version', version.tag_name);
const response = await fetch('/upgrade', {
method: 'POST',
body: data
});
let res = (await response.json())
}
export function getNextVersion(currentVersion, releases) {
if(/^v\d{1,2}\.\d{1,2}\.\d{1,2}$/.test(currentVersion)) {
var v = currentVersion.substring(1).split('.');
var v_major = parseInt(v[0]);
var v_minor = parseInt(v[1]);
var v_patch = parseInt(v[2]);
releases.reverse();
var next_patch;
var next_minor;
var next_major;
for(var i = 0; i < releases.length; i++) {
var release = releases[i];
var ver2 = release.tag_name;
var v2 = ver2.substring(1).split('.');
var v2_major = parseInt(v2[0]);
var v2_minor = parseInt(v2[1]);
var v2_patch = parseInt(v2[2]);
if(v2_major == v_major) {
if(v2_minor == v_minor) {
if(v2_patch > v_patch) {
next_patch = release;
}
} else if(v2_minor == v_minor+1) {
next_minor = release;
}
} else if(v2_major == v_major+1) {
if(next_major) {
var mv = next_major.tag_name.substring(1).split('.');
var mv_major = parseInt(mv[0]);
var mv_minor = parseInt(mv[1]);
var mv_patch = parseInt(mv[2]);
if(v2_minor == mv_minor) {
next_major = release;
}
} else {
next_major = release;
}
}
};
if(next_minor) {
return next_minor;
} else if(next_major) {
return next_major;
} else if(next_patch) {
return next_patch;
}
return false;
} else {
return releases[0];
}
}

View File

@ -0,0 +1,6 @@
<script></script>
<!-- Heroicons -->
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 8.25H7.5a2.25 2.25 0 00-2.25 2.25v9a2.25 2.25 0 002.25 2.25h9a2.25 2.25 0 002.25-2.25v-9a2.25 2.25 0 00-2.25-2.25H15m0-3l-3-3m0 0l-3 3m3-3V15" />
</svg>

View File

@ -39,14 +39,14 @@
<strong class="text-sm">Vendor configuration</strong>
<div class="my-3">
Board type<br/>
<select name="b" class="h-10 rounded-md shadow-sm border-gray-300 p-0 w-full" bind:value={sysinfo.board}>
<select name="vb" bind:value={sysinfo.board} class="h-10 rounded-md shadow-sm border-gray-300 p-0 w-full">
<BoardTypeSelectOptions chip={sysinfo.chip}/>
</select>
</div>
{#if sysinfo.board && sysinfo.board > 20}
<div class="my-3">
HAN GPIO<br/>
<select name="h" class="h-10 rounded-md shadow-sm border-gray-300">
<select name="vh" class="h-10 rounded-md shadow-sm border-gray-300">
<UartSelectOptions chip={sysinfo.chip}/>
</select>
</div>

View File

@ -17,15 +17,17 @@ export default defineConfig({
plugins: [svelte()],
server: {
proxy: {
"/data.json": "http://192.168.233.244",
"/data.json": "http://192.168.233.229",
"/energyprice.json": "http://192.168.233.235",
"/dayplot.json": "http://192.168.233.235",
"/monthplot.json": "http://192.168.233.235",
"/temperature.json": "http://192.168.233.235",
"/sysinfo.json": "http://192.168.233.244",
"/configuration.json": "http://192.168.233.244",
"/save": "http://192.168.233.244",
"/reboot": "http://192.168.233.244"
"/sysinfo.json": "http://192.168.233.229",
"/configuration.json": "http://192.168.233.229",
"/save": "http://192.168.233.229",
"/reboot": "http://192.168.233.229",
"/firmware": "http://192.168.233.229",
"/upgrade": "http://192.168.233.229"
}
}
})

View File

@ -87,6 +87,12 @@ private:
void configurationJson();
void handleSave();
void reboot();
void upgrade();
void firmwarePost();
void firmwareUpload();
void isAliveCheck();
HTTPUpload& uploadFile(const char* path);
void notFound();
};

View File

@ -17,6 +17,10 @@
#include "version.h"
#if defined(ESP32)
#include <esp_task_wdt.h>
#endif
AmsWebServer::AmsWebServer(uint8_t* buf, RemoteDebug* Debug, HwTools* hw) {
this->debugger = Debug;
@ -51,6 +55,9 @@ void AmsWebServer::setup(AmsConfiguration* config, GpioConfig* gpioConfig, Meter
server.on(F("/configuration.json"), HTTP_GET, std::bind(&AmsWebServer::configurationJson, this));
server.on(F("/save"), HTTP_POST, std::bind(&AmsWebServer::handleSave, this));
server.on(F("/reboot"), HTTP_POST, std::bind(&AmsWebServer::reboot, this));
server.on(F("/upgrade"), HTTP_POST, std::bind(&AmsWebServer::upgrade, this));
server.on(F("/firmware"), HTTP_POST, std::bind(&AmsWebServer::firmwarePost, this), std::bind(&AmsWebServer::firmwareUpload, this));
server.on(F("/is-alive"), HTTP_GET, std::bind(&AmsWebServer::isAliveCheck, this));
server.onNotFound(std::bind(&AmsWebServer::notFound, this));
@ -154,7 +161,8 @@ void AmsWebServer::sysinfoJson() {
#else
chipId = ESP.getChipId();
#endif
doc["chipId"] = String(chipId, HEX);
String chipIdStr = String(chipId, HEX);;
doc["chipId"] = chipIdStr;
SystemConfig sys;
config->getSystemConfig(sys);
@ -164,6 +172,17 @@ void AmsWebServer::sysinfoJson() {
doc["fwconsent"] = sys.dataCollectionConsent;
doc["country"] = sys.country;
if(sys.userConfigured) {
WiFiConfig wifiConfig;
config->getWiFiConfig(wifiConfig);
doc["hostname"] = wifiConfig.hostname;
} else {
doc["hostname"] = "ams-"+chipIdStr;
}
doc["booting"] = performRestart;
doc["upgrading"] = rebootForUpgrade;
doc["net"]["ip"] = WiFi.localIP().toString();
doc["net"]["mask"] = WiFi.subnetMask().toString();
doc["net"]["gw"] = WiFi.gatewayIP().toString();
@ -176,6 +195,23 @@ void AmsWebServer::sysinfoJson() {
serializeJson(doc, buf, BufferSize);
server.send(200, MIME_JSON, buf);
server.handleClient();
delay(250);
if(performRestart || rebootForUpgrade) {
if(ds != NULL) {
ds->save();
}
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf(PSTR("Rebooting"));
delay(1000);
#if defined(ESP8266)
ESP.reset();
#elif defined(ESP32)
ESP.restart();
#endif
performRestart = false;
}
}
void AmsWebServer::dataJson() {
@ -802,8 +838,9 @@ void AmsWebServer::handleSave() {
bool success = true;
if(server.hasArg(F("v")) && server.arg(F("v")) == F("true")) {
int boardType = server.arg(F("b")).toInt();
int hanPin = server.arg(F("h")).toInt();
int boardType = server.arg(F("vb")).toInt();
int hanPin = server.arg(F("vh")).toInt();
config->clear();
#if defined(CONFIG_IDF_TARGET_ESP32S2)
switch(boardType) {
@ -854,7 +891,14 @@ void AmsWebServer::handleSave() {
case 2: // spenceme
config->clearGpio(*gpioConfig);
gpioConfig->vccBootLimit = 33;
gpioConfig->hanPin = 3;
gpioConfig->apPin = 0;
gpioConfig->ledPin = 2;
gpioConfig->ledInverted = true;
gpioConfig->tempSensorPin = 5;
break;
case 0: // roarfred
config->clearGpio(*gpioConfig);
gpioConfig->hanPin = 3;
gpioConfig->apPin = 0;
gpioConfig->ledPin = 2;
@ -900,6 +944,7 @@ void AmsWebServer::handleSave() {
success = false;
}
#endif
config->setGpioConfig(*gpioConfig);
SystemConfig sys;
config->getSystemConfig(sys);
@ -1119,11 +1164,16 @@ void AmsWebServer::handleSave() {
gpioConfig->tempSensorPin = server.hasArg(F("itd")) && !server.arg(F("itd")).isEmpty() ?server.arg(F("itd")).toInt() : 0xFF;
gpioConfig->tempAnalogSensorPin = server.hasArg(F("ita")) && !server.arg(F("ita")).isEmpty() ?server.arg(F("ita")).toInt() : 0xFF;
gpioConfig->vccPin = server.hasArg(F("ivp")) && !server.arg(F("ivp")).isEmpty() ? server.arg(F("ivp")).toInt() : 0xFF;
gpioConfig->vccResistorGnd = server.hasArg(F("ivdg")) && !server.arg(F("ivdg")).isEmpty() ? server.arg(F("ivdg")).toInt() : 0;
gpioConfig->vccResistorVcc = server.hasArg(F("ivdv")) && !server.arg(F("ivdv")).isEmpty() ? server.arg(F("ivdv")).toInt() : 0;
config->setGpioConfig(*gpioConfig);
}
if(server.hasArg(F("iv")) && server.arg(F("iv")) == F("true")) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Received Vcc config"));
gpioConfig->vccOffset = server.hasArg(F("ivo")) && !server.arg(F("ivo")).isEmpty() ? server.arg(F("ivo")).toFloat() * 100 : 0;
gpioConfig->vccMultiplier = server.hasArg(F("ivm")) && !server.arg(F("ivm")).isEmpty() ? server.arg(F("ivm")).toFloat() * 1000 : 1000;
gpioConfig->vccBootLimit = server.hasArg(F("ivb")) && !server.arg(F("ivb")).isEmpty() ? server.arg(F("ivb")).toFloat() * 10 : 0;
gpioConfig->vccResistorGnd = server.hasArg(F("ivdg")) && !server.arg(F("ivdg")).isEmpty() ? server.arg(F("ivdg")).toInt() : 0;
gpioConfig->vccResistorVcc = server.hasArg(F("ivdv")) && !server.arg(F("ivdv")).isEmpty() ? server.arg(F("ivdv")).toInt() : 0;
config->setGpioConfig(*gpioConfig);
}
@ -1250,4 +1300,160 @@ void AmsWebServer::reboot() {
ESP.restart();
#endif
performRestart = false;
}
}
void AmsWebServer::upgrade() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /upgrade over http...\n");
DynamicJsonDocument doc(128);
doc["reboot"] = true;
serializeJson(doc, buf, BufferSize);
server.send(200, MIME_JSON, buf);
server.handleClient();
delay(250);
String customFirmwareUrl = "";
if(server.hasArg(F("url"))) {
customFirmwareUrl = server.arg(F("url"));
}
String url = customFirmwareUrl.isEmpty() || !customFirmwareUrl.startsWith(F("http")) ? F("http://ams2mqtt.rewiredinvent.no/hub/firmware/update") : customFirmwareUrl;
if(server.hasArg(F("version"))) {
url += "/" + server.arg(F("version"));
}
WiFiClient client;
#if defined(ESP8266)
String chipType = F("esp8266");
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
String chipType = F("esp32s2");
#elif defined(ESP32)
#if defined(CONFIG_FREERTOS_UNICORE)
String chipType = F("esp32solo");
#else
String chipType = F("esp32");
#endif
#endif
#if defined(ESP8266)
ESPhttpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
t_httpUpdate_return ret = ESPhttpUpdate.update(client, url, VERSION);
#elif defined(ESP32)
HTTPUpdate httpUpdate;
httpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
HTTPUpdateResult ret = httpUpdate.update(client, url, String(VERSION) + "-" + chipType);
#endif
switch(ret) {
case HTTP_UPDATE_FAILED:
debugger->printf(PSTR("Update failed"));
break;
case HTTP_UPDATE_NO_UPDATES:
debugger->printf(PSTR("No Update"));
break;
case HTTP_UPDATE_OK:
debugger->printf(PSTR("Update OK"));
break;
}
}
void AmsWebServer::firmwarePost() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Handling firmware post..."));
if(!checkSecurity(1))
return;
server.send(200);
}
void AmsWebServer::firmwareUpload() {
if(!checkSecurity(1))
return;
HTTPUpload& upload = server.upload();
if(upload.status == UPLOAD_FILE_START) {
String filename = upload.filename;
if(!filename.endsWith(".bin")) {
server.send(500, MIME_PLAIN, "500: couldn't create file");
} else {
#if defined(ESP32)
esp_task_wdt_delete(NULL);
esp_task_wdt_deinit();
#elif defined(ESP8266)
ESP.wdtDisable();
#endif
}
}
uploadFile(FILE_FIRMWARE);
if(upload.status == UPLOAD_FILE_END) {
rebootForUpgrade = true;
server.sendHeader("Location","/");
server.send(302);
}
}
HTTPUpload& AmsWebServer::uploadFile(const char* path) {
HTTPUpload& upload = server.upload();
if(upload.status == UPLOAD_FILE_START){
if(uploading) {
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf(PSTR("Upload already in progress"));
server.send_P(500, MIME_HTML, PSTR("<html><body><h1>Upload already in progress!</h1></body></html>"));
} else if (!LittleFS.begin()) {
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf(PSTR("An Error has occurred while mounting LittleFS"));
server.send_P(500, MIME_HTML, PSTR("<html><body><h1>Unable to mount LittleFS!</h1></body></html>"));
} else {
uploading = true;
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf_P(PSTR("handleFileUpload file: %s\n"), path);
}
if(LittleFS.exists(path)) {
LittleFS.remove(path);
}
file = LittleFS.open(path, "w");
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf_P(PSTR("handleFileUpload Open file and write: %u\n"), upload.currentSize);
}
size_t written = file.write(upload.buf, upload.currentSize);
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf_P(PSTR("handleFileUpload Written: %u\n"), written);
}
}
} else if(upload.status == UPLOAD_FILE_WRITE) {
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf_P(PSTR("handleFileUpload Writing: %u\n"), upload.currentSize);
}
if(file) {
size_t written = file.write(upload.buf, upload.currentSize);
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf_P(PSTR("handleFileUpload Written: %u\n"), written);
}
delay(1);
if(written != upload.currentSize) {
file.flush();
file.close();
LittleFS.remove(path);
LittleFS.end();
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf(PSTR("An Error has occurred while writing file"));
server.send_P(500, MIME_JSON, PSTR("{ \"success\": false, \"message\": \"Unable to upload\" }"));
}
}
} else if(upload.status == UPLOAD_FILE_END) {
if(file) {
file.flush();
file.close();
// LittleFS.end();
} else {
server.send_P(500, MIME_JSON, PSTR("{ \"success\": false, \"message\": \"Unable to upload\" }"));
}
}
return upload;
}
void AmsWebServer::isAliveCheck() {
server.sendHeader(F("Access-Control-Allow-Origin"), F("*"));
server.send(200);
}