More v2.2

This commit is contained in:
Gunnar Skjold
2022-12-02 19:03:16 +01:00
parent 000cfd8697
commit 148fb14c93
31 changed files with 645 additions and 312 deletions

View File

@@ -70,6 +70,9 @@ public:
bool isThreePhase();
bool isTwoPhase();
int8_t getLastError();
void setLastError(int8_t);
protected:
unsigned long lastUpdateMillis = 0;
unsigned long lastList2 = 0;
@@ -84,6 +87,8 @@ protected:
float powerFactor = 0, l1PowerFactor = 0, l2PowerFactor = 0, l3PowerFactor = 0;
double activeImportCounter = 0, reactiveImportCounter = 0, activeExportCounter = 0, reactiveExportCounter = 0;
bool threePhase = false, twoPhase = false, counterEstimated = false;
int8_t lastError = 0x00;
};
#endif

View File

@@ -214,3 +214,11 @@ bool AmsData::isThreePhase() {
bool AmsData::isTwoPhase() {
return this->twoPhase;
}
int8_t AmsData::getLastError() {
return lastError;
}
void AmsData::setLastError(int8_t lastError) {
this->lastError = lastError;
}

View File

@@ -40,17 +40,20 @@
"h" : {
"u" : %.2f,
"c" : %.2f,
"p" : %.2f
"p" : %.2f,
"i" : %.2f
},
"d" : {
"u" : %.2f,
"c" : %.2f,
"p" : %.2f
"p" : %.2f,
"i" : %.2f
},
"m" : {
"u" : %.2f,
"c" : %.2f,
"p" : %.2f
"p" : %.2f,
"i" : %.2f
}
},
"c" : %u

View File

@@ -776,12 +776,15 @@ void AmsWebServer::dataJson() {
ea->getUseThisHour(),
ea->getCostThisHour(),
ea->getProducedThisHour(),
ea->getIncomeThisHour(),
ea->getUseToday(),
ea->getCostToday(),
ea->getProducedToday(),
ea->getIncomeToday(),
ea->getUseThisMonth(),
ea->getCostThisMonth(),
ea->getProducedThisMonth(),
ea->getIncomeThisMonth(),
(uint32_t) time(nullptr)
);

View File

@@ -12,6 +12,18 @@ struct EnergyAccountingPeak {
};
struct EnergyAccountingData {
uint8_t version;
uint8_t month;
uint16_t costYesterday;
uint16_t costThisMonth;
uint16_t costLastMonth;
uint16_t incomeYesterday;
uint16_t incomeThisMonth;
uint16_t incomeLastMonth;
EnergyAccountingPeak peaks[5];
};
struct EnergyAccountingData4 {
uint8_t version;
uint8_t month;
uint16_t costYesterday;
@@ -55,6 +67,12 @@ public:
double getCostThisMonth();
uint16_t getCostLastMonth();
double getIncomeThisHour();
double getIncomeToday();
double getIncomeYesterday();
double getIncomeThisMonth();
uint16_t getIncomeLastMonth();
float getMonthMax();
uint8_t getCurrentThreshold();
EnergyAccountingPeak getPeak(uint8_t);
@@ -72,7 +90,7 @@ private:
Timezone *tz = NULL;
uint8_t currentHour = 0, currentDay = 0, currentThresholdIdx = 0;
double use, costHour, costDay;
double produce;
double produce, incomeHour, incomeDay;
EnergyAccountingData data = { 0, 0, 0, 0, 0, 0 };
void calcDayCost();

View File

@@ -45,8 +45,9 @@ bool EnergyAccounting::update(AmsData* amsData) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EnergyAccounting) Initializing data at %lld\n", (int64_t) now);
if(!load()) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EnergyAccounting) Unable to load existing data\n");
data = { 4, local.Month,
0, 0, 0,
data = { 5, local.Month,
0, 0, 0, // Cost
0, 0, 0, // Income
0, 0, // Peak 1
0, 0, // Peak 2
0, 0, // Peak 3
@@ -58,6 +59,7 @@ bool EnergyAccounting::update(AmsData* amsData) {
debugger->printf("(EnergyAccounting) Peak hour from day %d: %d\n", data.peaks[i].day, data.peaks[i].value*10);
}
debugger->printf("(EnergyAccounting) Loaded cost yesterday: %.2f, this month: %d, last month: %d\n", data.costYesterday / 10.0, data.costThisMonth, data.costLastMonth);
debugger->printf("(EnergyAccounting) Loaded income yesterday: %.2f, this month: %d, last month: %d\n", data.incomeYesterday / 10.0, data.incomeThisMonth, data.incomeLastMonth);
}
init = true;
}
@@ -70,11 +72,14 @@ bool EnergyAccounting::update(AmsData* amsData) {
if(local.Hour != currentHour && (amsData->getListType() >= 3 || local.Minute == 1)) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EnergyAccounting) New local hour %d\n", local.Hour);
tmElements_t oneHrAgo;
tmElements_t oneHrAgo, oneHrAgoLocal;
breakTime(now-3600, oneHrAgo);
uint16_t val = ds->getHourImport(oneHrAgo.Hour) / 10;
ret |= updateMax(val, local.Day);
breakTime(tz->toLocal(now-3600), oneHrAgoLocal);
ret |= updateMax(val, oneHrAgoLocal.Day);
currentHour = local.Hour; // Need to be defined here so that day cost is correctly calculated
if(local.Hour > 0) {
calcDayCost();
}
@@ -82,13 +87,18 @@ bool EnergyAccounting::update(AmsData* amsData) {
use = 0;
produce = 0;
costHour = 0;
currentHour = local.Hour;
incomeHour = 0;
if(local.Day != currentDay) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EnergyAccounting) New day %d\n", local.Day);
data.costYesterday = costDay * 10;
data.costThisMonth += costDay;
costDay = 0;
data.incomeYesterday = incomeDay * 10;
data.incomeThisMonth += incomeDay;
incomeDay = 0;
currentDay = local.Day;
ret = true;
}
@@ -97,6 +107,8 @@ bool EnergyAccounting::update(AmsData* amsData) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EnergyAccounting) New month %d\n", local.Month);
data.costLastMonth = data.costThisMonth;
data.costThisMonth = 0;
data.incomeLastMonth = data.incomeThisMonth;
data.incomeThisMonth = 0;
for(uint8_t i = 0; i < 5; i++) {
data.peaks[i] = { 0, 0 };
}
@@ -124,6 +136,13 @@ bool EnergyAccounting::update(AmsData* amsData) {
if(kwhe > 0) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) Adding %.4f kWh export\n", kwhe);
produce += kwhe;
if(eapi != NULL && eapi->getValueForHour(0) != ENTSOE_NO_VALUE) {
float price = eapi->getValueForHour(0);
float income = price * kwhe;
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) and %.4f %s\n", income / 100.0, eapi->getCurrency());
incomeHour += income;
incomeDay += income;
}
}
if(config != NULL) {
@@ -141,13 +160,20 @@ void EnergyAccounting::calcDayCost() {
breakTime(tz->toLocal(now), local);
if(eapi != NULL && eapi->getValueForHour(0) != ENTSOE_NO_VALUE) {
if(initPrice) costDay = 0;
if(initPrice) {
costDay = 0;
incomeDay = 0;
}
for(int i = 0; i < currentHour; i++) {
float price = eapi->getValueForHour(i - currentHour);
float price = eapi->getValueForHour(i - local.Hour);
if(price == ENTSOE_NO_VALUE) break;
breakTime(now - ((currentHour - i) * 3600), utc);
breakTime(now - ((local.Hour - i) * 3600), utc);
int16_t wh = ds->getHourImport(utc.Hour);
costDay += price * (wh / 1000.0);
wh = ds->getHourExport(utc.Hour);
incomeDay += price * (wh / 1000.0);
}
initPrice = true;
}
@@ -161,9 +187,10 @@ double EnergyAccounting::getUseToday() {
float ret = 0.0;
time_t now = time(nullptr);
if(now < BUILD_EPOCH) return 0;
tmElements_t utc;
tmElements_t utc, local;
breakTime(tz->toLocal(now), local);
for(int i = 0; i < currentHour; i++) {
breakTime(now - ((currentHour - i) * 3600), utc);
breakTime(now - ((local.Hour - i) * 3600), utc);
ret += ds->getHourImport(utc.Hour) / 1000.0;
}
return ret + getUseThisHour();
@@ -226,6 +253,26 @@ uint16_t EnergyAccounting::getCostLastMonth() {
return data.costLastMonth;
}
double EnergyAccounting::getIncomeThisHour() {
return incomeHour;
}
double EnergyAccounting::getIncomeToday() {
return incomeDay;
}
double EnergyAccounting::getIncomeYesterday() {
return data.incomeYesterday / 10.0;
}
double EnergyAccounting::getIncomeThisMonth() {
return data.incomeThisMonth + getIncomeToday();
}
uint16_t EnergyAccounting::getIncomeLastMonth() {
return data.incomeLastMonth;
}
uint8_t EnergyAccounting::getCurrentThreshold() {
if(config == NULL)
return 0;
@@ -309,14 +356,27 @@ bool EnergyAccounting::load() {
file.readBytes(buf, file.size());
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EnergyAccounting) Data version %d\n", buf[0]);
if(buf[0] == 4) {
if(buf[0] == 5) {
EnergyAccountingData* data = (EnergyAccountingData*) buf;
memcpy(&this->data, data, sizeof(this->data));
ret = true;
} else if(buf[0] == 4) {
EnergyAccountingData4* data = (EnergyAccountingData4*) buf;
this->data = { 5, data->month,
(uint16_t) (data->costYesterday / 10), (uint16_t) (data->costThisMonth / 100), (uint16_t) (data->costLastMonth / 100),
0,0,0, // Income from production
data->peaks[0].day, data->peaks[0].value,
data->peaks[1].day, data->peaks[1].value,
data->peaks[2].day, data->peaks[2].value,
data->peaks[3].day, data->peaks[3].value,
data->peaks[4].day, data->peaks[4].value
};
ret = true;
} else if(buf[0] == 3) {
EnergyAccountingData* data = (EnergyAccountingData*) buf;
this->data = { 4, data->month,
this->data = { 5, data->month,
(uint16_t) (data->costYesterday / 10), (uint16_t) (data->costThisMonth / 100), (uint16_t) (data->costLastMonth / 100),
0,0,0, // Income from production
data->peaks[0].day, data->peaks[0].value,
data->peaks[1].day, data->peaks[1].value,
data->peaks[2].day, data->peaks[2].value,
@@ -325,8 +385,9 @@ bool EnergyAccounting::load() {
};
ret = true;
} else {
data = { 4, 0,
0, 0, 0,
data = { 5, 0,
0, 0, 0, // Cost
0,0,0, // Income from production
0, 0, // Peak 1
0, 0, // Peak 2
0, 0, // Peak 3

View File

@@ -31,7 +31,7 @@
<div class="container mx-auto m-3">
<Router>
<Header data={data} sysinfo={sysinfo}/>
<Header data={data}/>
<Route path="/">
<Dashboard data={data}/>
</Route>

View File

@@ -25,15 +25,15 @@
{#if hasExport}
<div class="flex">
<div>Hour</div>
<div class="flex-auto text-right">{data.h.p ? data.h.p.toFixed(2) : '-'} kWh {#if currency}/ {data.h.pc ? data.h.pc.toFixed(2) : '-'} {currency}{/if}</div>
<div class="flex-auto text-right">{data.h.p ? data.h.p.toFixed(2) : '-'} kWh {#if currency}/ {data.h.i ? data.h.i.toFixed(2) : '-'} {currency}{/if}</div>
</div>
<div class="flex">
<div>Day</div>
<div class="flex-auto text-right">{data.d.p ? data.d.p.toFixed(1) : '-'} kWh {#if currency}/ {data.d.pc ? data.d.pc.toFixed(2) : '-'} {currency}{/if}</div>
<div class="flex-auto text-right">{data.d.p ? data.d.p.toFixed(1) : '-'} kWh {#if currency}/ {data.d.i ? data.d.i.toFixed(2) : '-'} {currency}{/if}</div>
</div>
<div class="flex">
<div>Month</div>
<div class="flex-auto text-right">{data.m.p ? data.m.p.toFixed(0) : '-'} kWh {#if currency}/ {data.m.pc ? data.m.pc.toFixed(2) : '-'} {currency}{/if}</div>
<div class="flex-auto text-right">{data.m.p ? data.m.p.toFixed(0) : '-'} kWh {#if currency}/ {data.m.i ? data.m.i.toFixed(2) : '-'} {currency}{/if}</div>
</div>
{:else}
<div class="flex">

View File

@@ -36,7 +36,7 @@
<div class="cnt">
<form on:submit|preventDefault={handleSubmit}>
<div>
Below are some stuff we need to know
Various permissions we need to do stuff:
</div>
<hr/>
<div class="my-3">

View File

@@ -1,14 +1,17 @@
<script>
import Mask from "./Mask.svelte";
export let action;
export let title;
let uploading = false;
</script>
<div class="grid xl:grid-cols-4 lg:grid-cols-2 md:grid-cols-2">
<div class="cnt">
<strong>Upload {title}</strong>
<p class="mb-4">Select a suitable file and click upload</p>
<form action="{action}" enctype="multipart/form-data" method="post">
<form action="{action}" enctype="multipart/form-data" method="post" on:submit={() => uploading=true}>
<input name="file" type="file">
<div class="w-full text-right mt-4">
<button type="submit" class="btn-pri">Upload</button>
@@ -16,3 +19,4 @@
</form>
</div>
</div>
<Mask active={uploading} message="Uploading file, please wait"/>

View File

@@ -2,7 +2,7 @@
import { Link } from "svelte-navigator";
import { sysinfoStore, getGitHubReleases, gitHubReleaseStore } from './DataStores.js';
import { upgrade, getNextVersion } from './UpgradeHelper';
import { boardtype } from './Helpers.js';
import { boardtype, hanError, mqttError } from './Helpers.js';
import GitHubLogo from './../assets/github.svg';
import Uptime from "./Uptime.svelte";
import Badge from './Badge.svelte';
@@ -13,7 +13,7 @@
import DownloadIcon from "./DownloadIcon.svelte";
export let data = {}
export let sysinfo = {}
let sysinfo = {}
let nextVersion = {};
@@ -28,11 +28,16 @@
}
}
}
sysinfoStore.subscribe(update => {
sysinfo = update;
if(update.fwconsent === 1) {
getGitHubReleases();
}
});
gitHubReleaseStore.subscribe(releases => {
nextVersion = getNextVersion(sysinfo.version, releases);
});
getGitHubReleases();
</script>
<nav class="bg-violet-600 p-1 rounded-md mx-2">
@@ -47,13 +52,19 @@
{/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">
<div class="flex-auto flex-wrap my-auto justify-center p-2">
<Badge title="ESP" text={sysinfo.booting ? 'Booting' : data.v > 2.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 flex-wrap">
{#if data.he < 0}
<div class="bd-red">{ 'HAN error: ' + hanError(data.he) }</div>
{/if}
{#if data.me < 0}
<div class="bd-red">{ 'MQTT error: ' + mqttError(data.me) }</div>
{/if}
<div class="flex-auto p-2 flex flex-row-reverse flex-wrap">
<div class="flex-none">
<a class="float-right" href='https://github.com/gskjold/AmsToMqttBridge' target='_blank' rel="noreferrer" aria-label="GitHub"><img class="gh-logo" src={GitHubLogo} alt="GitHub repo"/></a>
</div>

View File

@@ -91,3 +91,33 @@ export function boardtype(c, b) {
return "Generic ESP8266";
}
}
export function hanError(err) {
switch(err) {
case -1: return "Parse error";
case -2: return "Incomplete data received";
case -3: return "Payload boundry flag missing";
case -4: return "Header checksum error";
case -5: return "Footer checksum error";
case -9: return "Unknown data received, check meter config";
case -41: return "Frame length not equal";
case -51: return "Authentication failed";
case -52: return "Decryption failed";
case -53: return "Encryption key invalid";
}
if(err < 0) return "Unspecified error "+err;
return "";
}
export function mqttError(err) {
switch(err) {
case -3: return "Connection failed";
case -4: return "Network timeout";
case -10: return "Connection denied";
case -11: return "Failed to subscribe";
case -13: return "Connection lost";
}
if(err < 0) return "Unspecified error "+err;
return "";
}

View File

@@ -4,6 +4,7 @@
import { upgrade, getNextVersion } from './UpgradeHelper';
import DownloadIcon from './DownloadIcon.svelte';
import { Link } from 'svelte-navigator';
import Mask from './Mask.svelte';
export let sysinfo;
@@ -45,6 +46,8 @@
}
let fileinput;
let files = [];
let uploading = false;
getSysinfo();
</script>
@@ -125,15 +128,16 @@
</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}
<form action="/firmware" enctype="multipart/form-data" method="post" on:submit={() => uploading=true}>
<input style="display:none" name="file" type="file" accept=".bin" bind:this={fileinput} bind:files={files}>
{#if 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">Select firmware file for upgrade</button>
{:else if fileinput}
{fileinput.files[0].name}
{:else}
{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>
<Mask active={uploading} message="Uploading firmware, please wait"/>

View File

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

View File

@@ -83,7 +83,6 @@ private:
void monthplotJson();
void energyPriceJson();
void temperatureJson();
void wifiScanJson();
void tariffJson();
void configurationJson();

View File

@@ -0,0 +1,5 @@
"d": {
"s": %s,
"t": %s,
"l": %d
},

View File

@@ -0,0 +1,7 @@
"g": {
"t": "%s",
"h": "%s",
"s": %d,
"u": "%s",
"p": "%s"
},

View File

@@ -0,0 +1,28 @@
"i": {
"h": %s,
"a": %s,
"l": {
"p": %s,
"i": %s
},
"r": {
"r": %s,
"g": %s,
"b": %s,
"i": %s
},
"t": {
"d": %s,
"a": %s
},
"v": {
"p": %s,
"o": %.2f,
"m": %.3f,
"d": {
"v": %d,
"g": %d
},
"b": %.1f
}
}

View File

@@ -0,0 +1,20 @@
"m": {
"b": %d,
"p": %d,
"i": %s,
"d": %d,
"f": %d,
"r": %d,
"e": {
"e": %s,
"k": "%s",
"a": "%s"
},
"m": {
"e": %s,
"w": %.3f,
"v": %.3f,
"a": %.3f,
"c": %.3f
}
},

View File

@@ -0,0 +1,15 @@
"q": {
"h": "%s",
"p": %d,
"u": "%s",
"a": "%s",
"c": "%s",
"b": "%s",
"m": %d,
"s": {
"e": %s,
"c": %s,
"r": %s,
"k": %s
}
},

View File

@@ -0,0 +1,11 @@
"n": {
"m": "%s",
"i": "%s",
"s": "%s",
"g": "%s",
"d1": "%s",
"d2": "%s",
"d": %s,
"n1": "%s",
"h": %s
},

View File

@@ -0,0 +1,7 @@
"p": {
"e": %s,
"t": "%s",
"r": "%s",
"c": "%s",
"m": %.3f
},

View File

@@ -0,0 +1,15 @@
"t": {
"t": [
%d,
%d,
%d,
%d,
%d,
%d,
%d,
%d,
%d,
%d
],
"h": %d
},

View File

@@ -0,0 +1,6 @@
"w": {
"s": "%s",
"p": "%s",
"w": %.1f,
"z": %d
},

View File

@@ -40,19 +40,23 @@
"h" : {
"u" : %.2f,
"c" : %.2f,
"p" : %.2f
"p" : %.2f,
"i" : %.2f
},
"d" : {
"u" : %.2f,
"c" : %.2f,
"p" : %.2f
"p" : %.2f,
"i" : %.2f
},
"m" : {
"u" : %.2f,
"c" : %.2f,
"p" : %.2f
"p" : %.2f,
"i" : %.2f
}
},
"pr" : "%s",
"he" : %d,
"c" : %lu
}

View File

@@ -0,0 +1,4 @@
{
"d": %d,
"v": %.2f
}

View File

@@ -0,0 +1,25 @@
{
"version": "%s",
"chip": "%s",
"chipId": "%s",
"mac": "%s",
"board": %d,
"vndcfg": %s,
"usrcfg": %s,
"fwconsent": %d,
"hostname": "%s",
"booting": %s,
"upgrading": %s,
"net": {
"ip": "%s",
"mask": "%s",
"gw": "%s",
"dns1": "%s",
"dns2": "%s"
},
"meter": {
"mfg": %d,
"model": "%s",
"id": "%s"
}
}

View File

@@ -0,0 +1,17 @@
{
"t": [
%d,
%d,
%d,
%d,
%d,
%d,
%d,
%d,
%d,
%d
],
"p": [ %s ],
"c": %d,
"m": %.2f
}

View File

@@ -3,8 +3,6 @@
#include "base64.h"
#include "hexutils.h"
#include <ArduinoJson.h>
#include "html/index_html.h"
#include "html/index_css.h"
#include "html/index_js.h"
@@ -15,6 +13,18 @@
#include "html/energyprice_json.h"
#include "html/tempsensor_json.h"
#include "html/response_json.h"
#include "html/sysinfo_json.h"
#include "html/tariff_json.h"
#include "html/peak_json.h"
#include "html/conf_general_json.h"
#include "html/conf_meter_json.h"
#include "html/conf_wifi_json.h"
#include "html/conf_net_json.h"
#include "html/conf_mqtt_json.h"
#include "html/conf_price_json.h"
#include "html/conf_thresholds_json.h"
#include "html/conf_debug_json.h"
#include "html/conf_gpio_json.h"
#include "version.h"
@@ -60,8 +70,6 @@ void AmsWebServer::setup(AmsConfiguration* config, GpioConfig* gpioConfig, Meter
server.on(F("/temperature.json"), HTTP_GET, std::bind(&AmsWebServer::temperatureJson, this));
server.on(F("/tariff.json"), HTTP_GET, std::bind(&AmsWebServer::tariffJson, this));
server.on(F("/wifiscan.json"), HTTP_GET, std::bind(&AmsWebServer::wifiScanJson, this));
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));
@@ -169,17 +177,8 @@ void AmsWebServer::faviconIco() {
void AmsWebServer::sysinfoJson() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /sysinfo.json over http...\n");
DynamicJsonDocument doc(1024);
doc[F("version")] = VERSION;
#if defined(CONFIG_IDF_TARGET_ESP32S2)
doc[F("chip")] = "esp32s2";
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
doc[F("chip")] = "esp32c3";
#elif defined(ESP32)
doc[F("chip")] = "esp32";
#elif defined(ESP8266)
doc[F("chip")] = "esp8266";
#endif
SystemConfig sys;
config->getSystemConfig(sys);
uint32_t chipId;
#if defined(ESP32)
@@ -188,39 +187,54 @@ void AmsWebServer::sysinfoJson() {
chipId = ESP.getChipId();
#endif
String chipIdStr = String(chipId, HEX);
doc[F("chipId")] = chipIdStr;
doc[F("mac")] = WiFi.macAddress();
SystemConfig sys;
config->getSystemConfig(sys);
doc[F("board")] = sys.boardType;
doc[F("vndcfg")] = sys.vendorConfigured;
doc[F("usrcfg")] = sys.userConfigured;
doc[F("fwconsent")] = sys.dataCollectionConsent;
doc[F("country")] = sys.country;
String hostname;
if(sys.userConfigured) {
WiFiConfig wifiConfig;
config->getWiFiConfig(wifiConfig);
doc[F("hostname")] = wifiConfig.hostname;
hostname = String(wifiConfig.hostname);
} else {
doc[F("hostname")] = "ams-"+chipIdStr;
hostname = "ams-"+chipIdStr;
}
doc[F("booting")] = performRestart;
doc[F("upgrading")] = rebootForUpgrade;
IPAddress dns1 = WiFi.dnsIP(0);
IPAddress dns2 = WiFi.dnsIP(1);
doc[F("net")][F("ip")] = WiFi.localIP().toString();
doc[F("net")][F("mask")] = WiFi.subnetMask().toString();
doc[F("net")][F("gw")] = WiFi.gatewayIP().toString();
doc[F("net")][F("dns1")] = WiFi.dnsIP(0).toString();
doc[F("net")][F("dns2")] = WiFi.dnsIP(1).toString();
snprintf_P(buf, BufferSize, SYSINFO_JSON,
VERSION,
#if defined(CONFIG_IDF_TARGET_ESP32S2)
"esp32s2",
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
"esp32c3",
#elif defined(ESP32)
"esp32",
#elif defined(ESP8266)
"esp8266",
#endif
chipIdStr.c_str(),
WiFi.macAddress().c_str(),
sys.boardType,
sys.vendorConfigured ? "true" : "false",
sys.userConfigured ? "true" : "false",
sys.dataCollectionConsent,
hostname.c_str(),
performRestart ? "true" : "false",
rebootForUpgrade ? "true" : "false",
WiFi.localIP().toString().c_str(),
WiFi.subnetMask().toString().c_str(),
WiFi.gatewayIP().toString().c_str(),
dns1.isSet() ? dns1.toString().c_str() : "",
dns2.isSet() ? dns2.toString().c_str() : "",
meterState->getMeterType(),
meterState->getMeterModel().c_str(),
meterState->getMeterId().c_str()
);
doc[F("meter")][F("mfg")] = meterState->getMeterType();
doc[F("meter")][F("model")] = meterState->getMeterModel();
doc[F("meter")][F("id")] = meterState->getMeterId();
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE);
server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE);
server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF);
serializeJson(doc, buf, BufferSize);
server.setContentLength(strlen(buf));
server.send(200, MIME_JSON, buf);
server.handleClient();
@@ -276,7 +290,9 @@ void AmsWebServer::dataJson() {
uint8_t hanStatus;
if(meterConfig->baud == 0 || meterState->getLastUpdateMillis() == 0) {
if(meterState->getLastError() < 0) {
hanStatus = 3;
} else if((meterConfig->baud == 0 || meterState->getLastUpdateMillis() == 0) && now < 15000) {
hanStatus = 0;
} else if(now - meterState->getLastUpdateMillis() < 15000) {
hanStatus = 1;
@@ -313,7 +329,7 @@ void AmsWebServer::dataJson() {
String peaks = "";
for(uint8_t i = 1; i <= ea->getConfig()->hours; i++) {
if(!peaks.isEmpty()) peaks += ",";
peaks += String(ea->getPeak(i).value);
peaks += String(ea->getPeak(i).value / 100.0);
}
snprintf_P(buf, BufferSize, DATA_JSON,
@@ -357,13 +373,17 @@ void AmsWebServer::dataJson() {
ea->getUseThisHour(),
ea->getCostThisHour(),
ea->getProducedThisHour(),
ea->getIncomeThisHour(),
ea->getUseToday(),
ea->getCostToday(),
ea->getProducedToday(),
ea->getIncomeToday(),
ea->getUseThisMonth(),
ea->getCostThisMonth(),
ea->getProducedThisMonth(),
ea->getIncomeThisMonth(),
eapi == NULL ? "" : eapi->getArea(),
meterState->getLastError(),
(uint32_t) time(nullptr)
);
@@ -665,28 +685,13 @@ void AmsWebServer::indexJs() {
void AmsWebServer::configurationJson() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /configuration.json over http...\n");
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE);
server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE);
server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF);
if(!checkSecurity(1))
return;
DynamicJsonDocument doc(2048);
doc[F("version")] = VERSION;
NtpConfig ntpConfig;
config->getNtpConfig(ntpConfig);
WiFiConfig wifiConfig;
config->getWiFiConfig(wifiConfig);
WebConfig webConfig;
config->getWebConfig(webConfig);
doc[F("g")][F("t")] = ntpConfig.timezone;
doc[F("g")][F("h")] = wifiConfig.hostname;
doc[F("g")][F("s")] = webConfig.security;
doc[F("g")][F("u")] = webConfig.username;
doc[F("g")][F("p")] = strlen(webConfig.password) > 0 ? "***" : "";
bool encen = false;
for(uint8_t i = 0; i < 16; i++) {
@@ -695,166 +700,143 @@ void AmsWebServer::configurationJson() {
}
}
config->getMeterConfig(*meterConfig);
doc[F("m")][F("b")] = meterConfig->baud;
doc[F("m")][F("p")] = meterConfig->parity;
doc[F("m")][F("i")] = meterConfig->invert;
doc[F("m")][F("d")] = meterConfig->distributionSystem;
doc[F("m")][F("f")] = meterConfig->mainFuse;
doc[F("m")][F("r")] = meterConfig->productionCapacity;
doc[F("m")][F("e")][F("e")] = encen;
doc[F("m")][F("e")][F("k")] = toHex(meterConfig->encryptionKey, 16);
doc[F("m")][F("e")][F("a")] = toHex(meterConfig->authenticationKey, 16);
doc[F("m")][F("m")][F("e")] = meterConfig->wattageMultiplier > 1 || meterConfig->voltageMultiplier > 1 || meterConfig->amperageMultiplier > 1 || meterConfig->accumulatedMultiplier > 1;
doc[F("m")][F("m")][F("w")] = meterConfig->wattageMultiplier / 1000.0;
doc[F("m")][F("m")][F("v")] = meterConfig->voltageMultiplier / 1000.0;
doc[F("m")][F("m")][F("a")] = meterConfig->amperageMultiplier / 1000.0;
doc[F("m")][F("m")][F("c")] = meterConfig->accumulatedMultiplier / 1000.0;
EnergyAccountingConfig eac;
config->getEnergyAccountingConfig(eac);
doc[F("t")][F("t")][0] = eac.thresholds[0];
doc[F("t")][F("t")][1] = eac.thresholds[1];
doc[F("t")][F("t")][2] = eac.thresholds[2];
doc[F("t")][F("t")][3] = eac.thresholds[3];
doc[F("t")][F("t")][4] = eac.thresholds[4];
doc[F("t")][F("t")][5] = eac.thresholds[5];
doc[F("t")][F("t")][6] = eac.thresholds[6];
doc[F("t")][F("t")][7] = eac.thresholds[7];
doc[F("t")][F("t")][8] = eac.thresholds[8];
doc[F("t")][F("t")][9] = eac.thresholds[9];
doc[F("t")][F("h")] = eac.hours;
doc[F("w")][F("s")] = wifiConfig.ssid;
doc[F("w")][F("p")] = strlen(wifiConfig.psk) > 0 ? "***" : "";
doc[F("w")][F("w")] = wifiConfig.power / 10.0;
doc[F("w")][F("z")] = wifiConfig.sleep;
doc[F("n")][F("m")] = strlen(wifiConfig.ip) > 0 ? "static" : "dhcp";
doc[F("n")][F("i")] = wifiConfig.ip;
doc[F("n")][F("s")] = wifiConfig.subnet;
doc[F("n")][F("g")] = wifiConfig.gateway;
doc[F("n")][F("d1")] = wifiConfig.dns1;
doc[F("n")][F("d2")] = wifiConfig.dns2;
doc[F("n")][F("d")] = wifiConfig.mdns;
doc[F("n")][F("n1")] = ntpConfig.server;
doc[F("n")][F("h")] = ntpConfig.dhcp;
EnergyAccountingConfig* eac = ea->getConfig();
MqttConfig mqttConfig;
config->getMqttConfig(mqttConfig);
doc[F("q")][F("h")] = mqttConfig.host;
doc[F("q")][F("p")] = mqttConfig.port;
doc[F("q")][F("u")] = mqttConfig.username;
doc[F("q")][F("a")] = strlen(mqttConfig.password) > 0 ? "***" : "";
doc[F("q")][F("c")] = mqttConfig.clientId;
doc[F("q")][F("b")] = mqttConfig.publishTopic;
doc[F("q")][F("m")] = mqttConfig.payloadFormat;
doc[F("q")][F("s")][F("e")] = mqttConfig.ssl;
if(LittleFS.begin()) {
doc[F("q")][F("s")][F("c")] = LittleFS.exists(FILE_MQTT_CA);
doc[F("q")][F("s")][F("r")] = LittleFS.exists(FILE_MQTT_CERT);
doc[F("q")][F("s")][F("k")] = LittleFS.exists(FILE_MQTT_KEY);
LittleFS.end();
} else {
doc[F("q")][F("s")][F("c")] = false;
doc[F("q")][F("s")][F("r")] = false;
doc[F("q")][F("s")][F("k")] = false;
}
EntsoeConfig entsoe;
config->getEntsoeConfig(entsoe);
doc[F("p")][F("e")] = strlen(entsoe.token) > 0;
doc[F("p")][F("t")] = entsoe.token;
doc[F("p")][F("r")] = entsoe.area;
doc[F("p")][F("c")] = entsoe.currency;
doc[F("p")][F("m")] = entsoe.multiplier / 1000.0;
DebugConfig debugConfig;
config->getDebugConfig(debugConfig);
doc[F("d")][F("s")] = debugConfig.serial;
doc[F("d")][F("t")] = debugConfig.telnet;
doc[F("d")][F("l")] = debugConfig.level;
GpioConfig gpioConfig;
config->getGpioConfig(gpioConfig);
if(gpioConfig.hanPin == 0xff)
doc[F("i")][F("h")] = nullptr;
else
doc[F("i")][F("h")] = gpioConfig.hanPin;
if(gpioConfig.apPin == 0xff)
doc[F("i")][F("a")] = nullptr;
else
doc[F("i")][F("a")] = gpioConfig.apPin;
if(gpioConfig.ledPin == 0xff)
doc[F("i")][F("l")][F("p")] = nullptr;
else
doc[F("i")][F("l")][F("p")] = gpioConfig.ledPin;
doc[F("i")][F("l")][F("i")] = gpioConfig.ledInverted;
if(gpioConfig.ledPinRed == 0xff)
doc[F("i")][F("r")][F("r")] = nullptr;
else
doc[F("i")][F("r")][F("r")] = gpioConfig.ledPinRed;
bool qsc = false;
bool qsr = false;
bool qsk = false;
if(gpioConfig.ledPinGreen == 0xff)
doc[F("i")][F("r")][F("g")] = nullptr;
else
doc[F("i")][F("r")][F("g")] = gpioConfig.ledPinGreen;
if(LittleFS.begin()) {
qsc = LittleFS.exists(FILE_MQTT_CA);
qsr = LittleFS.exists(FILE_MQTT_CERT);
qsk = LittleFS.exists(FILE_MQTT_KEY);
LittleFS.end();
}
if(gpioConfig.ledPinBlue == 0xff)
doc[F("i")][F("r")][F("b")] = nullptr;
else
doc[F("i")][F("r")][F("b")] = gpioConfig.ledPinBlue;
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE);
server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE);
server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF);
doc[F("i")][F("r")][F("i")] = gpioConfig.ledRgbInverted;
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
server.send_P(200, MIME_JSON, PSTR("{\"version\":\""));
server.sendContent_P(VERSION);
server.sendContent_P(PSTR("\","));
snprintf_P(buf, BufferSize, CONF_GENERAL_JSON,
ntpConfig.timezone,
wifiConfig.hostname,
webConfig.security,
webConfig.username,
strlen(webConfig.password) > 0 ? "***" : ""
);
server.sendContent(buf);
snprintf_P(buf, BufferSize, CONF_METER_JSON,
meterConfig->baud,
meterConfig->parity,
meterConfig->invert ? "true" : "false",
meterConfig->distributionSystem,
meterConfig->mainFuse,
meterConfig->productionCapacity,
encen ? "true" : "false",
toHex(meterConfig->encryptionKey, 16).c_str(),
toHex(meterConfig->authenticationKey, 16).c_str(),
meterConfig->wattageMultiplier > 1 || meterConfig->voltageMultiplier > 1 || meterConfig->amperageMultiplier > 1 || meterConfig->accumulatedMultiplier > 1 ? "true" : "false",
meterConfig->wattageMultiplier / 1000.0,
meterConfig->voltageMultiplier / 1000.0,
meterConfig->amperageMultiplier / 1000.0,
meterConfig->accumulatedMultiplier / 1000.0
);
server.sendContent(buf);
if(gpioConfig.tempSensorPin == 0xff)
doc[F("i")][F("t")][F("d")] = nullptr;
else
doc[F("i")][F("t")][F("d")] = gpioConfig.tempSensorPin;
if(gpioConfig.tempAnalogSensorPin == 0xff)
doc[F("i")][F("t")][F("a")] = nullptr;
else
doc[F("i")][F("t")][F("a")] = gpioConfig.tempAnalogSensorPin;
if(gpioConfig.vccPin == 0xff)
doc[F("i")][F("v")][F("p")] = nullptr;
else
doc[F("i")][F("v")][F("p")] = gpioConfig.vccPin;
if(gpioConfig.vccOffset == 0)
doc[F("i")][F("v")][F("o")] = nullptr;
else
doc[F("i")][F("v")][F("o")] = gpioConfig.vccOffset / 100.0;
if(gpioConfig.vccMultiplier == 0)
doc[F("i")][F("v")][F("m")] = nullptr;
else
doc[F("i")][F("v")][F("m")] = gpioConfig.vccMultiplier / 1000.0;
if(gpioConfig.vccResistorVcc == 0)
doc[F("i")][F("v")][F("d")][F("v")] = nullptr;
else
doc[F("i")][F("v")][F("d")][F("v")] = gpioConfig.vccResistorVcc;
if(gpioConfig.vccResistorGnd == 0)
doc[F("i")][F("v")][F("d")][F("g")] = nullptr;
else
doc[F("i")][F("v")][F("d")][F("g")] = gpioConfig.vccResistorGnd;
if(gpioConfig.vccBootLimit == 0)
doc[F("i")][F("v")][F("b")] = nullptr;
else
doc[F("i")][F("v")][F("b")] = gpioConfig.vccBootLimit / 10.0;
serializeJson(doc, buf, BufferSize);
server.send(200, MIME_JSON, buf);
snprintf_P(buf, BufferSize, CONF_THRESHOLDS_JSON,
eac->thresholds[0],
eac->thresholds[1],
eac->thresholds[2],
eac->thresholds[3],
eac->thresholds[4],
eac->thresholds[5],
eac->thresholds[6],
eac->thresholds[7],
eac->thresholds[8],
eac->thresholds[9],
eac->hours
);
server.sendContent(buf);
snprintf_P(buf, BufferSize, CONF_WIFI_JSON,
wifiConfig.ssid,
strlen(wifiConfig.psk) > 0 ? "***" : "",
wifiConfig.power / 10.0,
wifiConfig.sleep
);
server.sendContent(buf);
snprintf_P(buf, BufferSize, CONF_NET_JSON,
strlen(wifiConfig.ip) > 0 ? "static" : "dhcp",
wifiConfig.ip,
wifiConfig.subnet,
wifiConfig.gateway,
wifiConfig.dns1,
wifiConfig.dns2,
wifiConfig.mdns ? "true" : "false",
ntpConfig.server,
ntpConfig.dhcp ? "true" : "false"
);
server.sendContent(buf);
snprintf_P(buf, BufferSize, CONF_MQTT_JSON,
mqttConfig.host,
mqttConfig.port,
mqttConfig.username,
strlen(mqttConfig.password) > 0 ? "***" : "",
mqttConfig.clientId,
mqttConfig.publishTopic,
mqttConfig.payloadFormat,
mqttConfig.ssl ? "true" : "false",
qsc ? "true" : "false",
qsr ? "true" : "false",
qsk ? "true" : "false"
);
server.sendContent(buf);
snprintf_P(buf, BufferSize, CONF_PRICE_JSON,
strlen(entsoe.token) > 0 ? "true" : "false",
entsoe.token,
entsoe.area,
entsoe.currency,
entsoe.multiplier / 1000.0
);
server.sendContent(buf);
snprintf_P(buf, BufferSize, CONF_DEBUG_JSON,
debugConfig.serial ? "true" : "false",
debugConfig.telnet ? "true" : "false",
debugConfig.level
);
server.sendContent(buf);
snprintf_P(buf, BufferSize, CONF_GPIO_JSON,
gpioConfig->hanPin == 0xff ? "null" : String(gpioConfig->hanPin, 10).c_str(),
gpioConfig->apPin == 0xff ? "null" : String(gpioConfig->apPin, 10).c_str(),
gpioConfig->hanPin == 0xff ? "null" : String(gpioConfig->hanPin, 10).c_str(),
gpioConfig->ledInverted ? "true" : "false",
gpioConfig->ledPinRed == 0xff ? "null" : String(gpioConfig->ledPinRed, 10).c_str(),
gpioConfig->ledPinGreen == 0xff ? "null" : String(gpioConfig->ledPinGreen, 10).c_str(),
gpioConfig->ledPinBlue == 0xff ? "null" : String(gpioConfig->ledPinBlue, 10).c_str(),
gpioConfig->ledRgbInverted ? "true" : "false",
gpioConfig->tempSensorPin == 0xff ? "null" : String(gpioConfig->tempSensorPin, 10).c_str(),
gpioConfig->tempAnalogSensorPin == 0xff ? "null" : String(gpioConfig->tempAnalogSensorPin, 10).c_str(),
gpioConfig->vccPin == 0xff ? "null" : String(gpioConfig->vccPin, 10).c_str(),
gpioConfig->vccOffset / 100.0,
gpioConfig->vccMultiplier / 1000.0,
gpioConfig->vccResistorVcc,
gpioConfig->vccResistorGnd,
gpioConfig->vccBootLimit / 10.0
);
server.sendContent(buf);
server.sendContent("}");
}
void AmsWebServer::handleSave() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Handling save method from http"));
if(!checkSecurity(1))
@@ -1300,23 +1282,10 @@ void AmsWebServer::handleSave() {
}
}
void AmsWebServer::wifiScanJson() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /wifiscan.json over http...\n");
DynamicJsonDocument doc(512);
serializeJson(doc, buf, BufferSize);
server.send(200, MIME_JSON, buf);
}
void AmsWebServer::reboot() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /reboot over http...\n");
DynamicJsonDocument doc(128);
doc[F("reboot")] = true;
serializeJson(doc, buf, BufferSize);
server.send(200, MIME_JSON, buf);
server.send(200, MIME_JSON, "{\"reboot\":true}");
server.handleClient();
delay(250);
@@ -1601,32 +1570,43 @@ void AmsWebServer::mqttKeyUpload() {
void AmsWebServer::tariffJson() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /tariff.json over http...\n");
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE);
server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE);
server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF);
if(!checkSecurity(1))
return;
EnergyAccountingConfig* eac = ea->getConfig();
EnergyAccountingData data = ea->getData();
DynamicJsonDocument doc(512);
JsonArray thresholds = doc.createNestedArray(F("t"));
for(uint8_t x = 0;x < 10; x++) {
thresholds.add(eac->thresholds[x]);
}
JsonArray peaks = doc.createNestedArray(F("p"));
String peaks;
for(uint8_t x = 0;x < min((uint8_t) 5, eac->hours); x++) {
JsonObject p = peaks.createNestedObject();
EnergyAccountingPeak peak = ea->getPeak(x+1);
p["d"] = peak.day;
p["v"] = peak.value / 100.0;
int len = snprintf_P(buf, BufferSize, PEAK_JSON,
peak.day,
peak.value / 100.0
);
buf[len] = '\0';
if(!peaks.isEmpty()) peaks += ",";
peaks += String(buf);
}
doc["c"] = ea->getCurrentThreshold();
doc["m"] = ea->getMonthMax();
serializeJson(doc, buf, BufferSize);
snprintf_P(buf, BufferSize, TARIFF_JSON,
eac->thresholds[0],
eac->thresholds[1],
eac->thresholds[2],
eac->thresholds[3],
eac->thresholds[4],
eac->thresholds[5],
eac->thresholds[6],
eac->thresholds[7],
eac->thresholds[8],
eac->thresholds[9],
peaks.c_str(),
ea->getCurrentThreshold(),
ea->getMonthMax()
);
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE);
server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE);
server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF);
server.setContentLength(strlen(buf));
server.send(200, MIME_JSON, buf);
}

View File

@@ -2,7 +2,7 @@
extra_configs = platformio-user.ini
[common]
lib_deps = EEPROM, LittleFS, DNSServer, 256dpi/MQTT@2.5.0, OneWireNg@0.10.0, DallasTemperature@3.9.1, EspSoftwareSerial@6.14.1, https://github.com/gskjold/RemoteDebug.git, Time@1.6.1, Timezone@1.2.4, AmsConfiguration, AmsData, AmsDataStorage, HwTools, Uptime, EntsoePriceApi, EnergyAccounting, AmsMqttHandler, RawMqttHandler, JsonMqttHandler, DomoticzMqttHandler, HomeAssistantMqttHandler, ArduinoJson, SvelteUi
lib_deps = EEPROM, LittleFS, DNSServer, 256dpi/MQTT@2.5.0, OneWireNg@0.10.0, DallasTemperature@3.9.1, EspSoftwareSerial@6.14.1, https://github.com/gskjold/RemoteDebug.git, Time@1.6.1, Timezone@1.2.4, AmsConfiguration, AmsData, AmsDataStorage, HwTools, Uptime, EntsoePriceApi, EnergyAccounting, AmsMqttHandler, RawMqttHandler, JsonMqttHandler, DomoticzMqttHandler, HomeAssistantMqttHandler, SvelteUi
lib_ignore = OneWire
extra_scripts =
pre:scripts/addversion.py

View File

@@ -826,6 +826,7 @@ bool readHanPort() {
if(pos == DATA_PARSE_INCOMPLETE) {
return false;
} else if(pos == DATA_PARSE_UNKNOWN_DATA) {
meterState.setLastError(pos);
debugV("Unknown data payload:");
len = len + hanSerial->readBytes(hanBuffer+len, BUF_SIZE_HAN-len);
debugPrint(hanBuffer, 0, len);
@@ -837,6 +838,7 @@ bool readHanPort() {
len = 0;
return false;
} else if(pos < 0) {
meterState.setLastError(pos);
printHanReadError(pos);
len += hanSerial->readBytes(hanBuffer+len, BUF_SIZE_HAN-len);
if(mqttEnabled && mqtt != NULL && mqttHandler == NULL) {
@@ -852,6 +854,7 @@ bool readHanPort() {
for(int i = pos+ctx.length; i<BUF_SIZE_HAN; i++) {
hanBuffer[i] = 0x00;
}
meterState.setLastError(DATA_PARSE_OK);
AmsData data;
char* payload = ((char *) (hanBuffer)) + pos;
@@ -1706,8 +1709,9 @@ void configFileParse() {
sDs = true;
} else if(strncmp_P(buf, PSTR("energyaccounting "), 17) == 0) {
uint8_t i = 0;
EnergyAccountingData ead = { 4, 0,
0, 0, 0,
EnergyAccountingData ead = { 0, 0,
0, 0, 0, // Cost
0, 0, 0, // Income
0, 0, // Peak 1
0, 0, // Peak 2
0, 0, // Peak 3
@@ -1716,33 +1720,73 @@ void configFileParse() {
};
char * pch = strtok (buf+17," ");
while (pch != NULL) {
if(i == 0) {
// Ignore version
} else if(i == 1) {
long val = String(pch).toInt();
ead.month = val;
} else if(i == 2) {
double val = String(pch).toDouble();
if(val > 0.0) {
ead.peaks[0] = { 1, (uint16_t) (val*100) };
}
} else if(i == 3) {
double val = String(pch).toDouble();
ead.costYesterday = val * 10;
} else if(i == 4) {
double val = String(pch).toDouble();
ead.costThisMonth = val;
} else if(i == 5) {
double val = String(pch).toDouble();
ead.costLastMonth = val;
} else if(i >= 6 && i < 18) {
uint8_t hour = i-6;
if(hour%2 == 0) {
if(ead.version < 5) {
if(i == 0) {
long val = String(pch).toInt();
ead.peaks[hour/2].day = val;
} else {
ead.version = val;
} else if(i == 1) {
long val = String(pch).toInt();
ead.month = val;
} else if(i == 2) {
double val = String(pch).toDouble();
ead.peaks[hour/2].value = val * 100;
if(val > 0.0) {
ead.peaks[0] = { 1, (uint16_t) (val*100) };
}
} else if(i == 3) {
double val = String(pch).toDouble();
ead.costYesterday = val * 10;
} else if(i == 4) {
double val = String(pch).toDouble();
ead.costThisMonth = val;
} else if(i == 5) {
double val = String(pch).toDouble();
ead.costLastMonth = val;
} else if(i >= 6 && i < 18) {
uint8_t hour = i-6;
if(hour%2 == 0) {
long val = String(pch).toInt();
ead.peaks[hour/2].day = val;
} else {
double val = String(pch).toDouble();
ead.peaks[hour/2].value = val * 100;
}
}
} else {
if(i == 1) {
long val = String(pch).toInt();
ead.month = val;
} else if(i == 2) {
double val = String(pch).toDouble();
if(val > 0.0) {
ead.peaks[0] = { 1, (uint16_t) (val*100) };
}
} else if(i == 3) {
double val = String(pch).toDouble();
ead.costYesterday = val * 10;
} else if(i == 4) {
double val = String(pch).toDouble();
ead.costThisMonth = val;
} else if(i == 5) {
double val = String(pch).toDouble();
ead.costLastMonth = val;
} else if(i == 6) {
double val = String(pch).toDouble();
ead.incomeYesterday= val * 10;
} else if(i == 7) {
double val = String(pch).toDouble();
ead.incomeThisMonth = val;
} else if(i == 8) {
double val = String(pch).toDouble();
ead.incomeLastMonth = val;
} else if(i >= 9 && i < 21) {
uint8_t hour = i-9;
if(hour%2 == 0) {
long val = String(pch).toInt();
ead.peaks[hour/2].day = val;
} else {
double val = String(pch).toDouble();
ead.peaks[hour/2].value = val * 100;
}
}
}
pch = strtok (NULL, " ");