mirror of
https://github.com/UtilitechAS/amsreader-firmware.git
synced 2026-03-12 13:35:29 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d902c63de | ||
|
|
a57405a7a5 | ||
|
|
550d32ee33 | ||
|
|
9f7e174c7b | ||
|
|
104617afd2 | ||
|
|
959664f61d | ||
|
|
5834b07393 | ||
|
|
8dbcf2424a | ||
|
|
74345046b1 | ||
|
|
516c80e38c | ||
|
|
17bbac8670 |
@@ -577,6 +577,11 @@ bool AmsDataStorage::isHappy() {
|
||||
}
|
||||
|
||||
bool AmsDataStorage::isDayHappy() {
|
||||
if(tz == NULL) {
|
||||
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(AmsDataStorage) Timezone is missing\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
time_t now = time(nullptr);
|
||||
if(now < FirmwareVersion::BuildEpoch) return false;
|
||||
|
||||
|
||||
@@ -179,6 +179,7 @@ bool EnergyAccounting::update(AmsData* amsData) {
|
||||
void EnergyAccounting::calcDayCost() {
|
||||
time_t now = time(nullptr);
|
||||
tmElements_t local, utc;
|
||||
if(tz == NULL) return;
|
||||
breakTime(tz->toLocal(now), local);
|
||||
|
||||
if(getPriceForHour(0) != ENTSOE_NO_VALUE) {
|
||||
@@ -205,10 +206,10 @@ float EnergyAccounting::getUseThisHour() {
|
||||
}
|
||||
|
||||
float EnergyAccounting::getUseToday() {
|
||||
if(tz == NULL) return 0.0;
|
||||
float ret = 0.0;
|
||||
time_t now = time(nullptr);
|
||||
if(now < FirmwareVersion::BuildEpoch) return 0.0;
|
||||
if(tz == NULL) return 0.0;
|
||||
tmElements_t utc, local;
|
||||
breakTime(tz->toLocal(now), local);
|
||||
for(uint8_t i = 0; i < currentHour; i++) {
|
||||
@@ -237,6 +238,7 @@ float EnergyAccounting::getProducedThisHour() {
|
||||
}
|
||||
|
||||
float EnergyAccounting::getProducedToday() {
|
||||
if(tz == NULL) return 0.0;
|
||||
float ret = 0.0;
|
||||
time_t now = time(nullptr);
|
||||
if(now < FirmwareVersion::BuildEpoch) return 0.0;
|
||||
|
||||
@@ -272,14 +272,14 @@ float EntsoeApi::getCurrencyMultiplier(const char* from, const char* to, time_t
|
||||
ESP.wdtFeed();
|
||||
#endif
|
||||
|
||||
snprintf_P(buf, BufferSize, PSTR("https://data.norges-bank.no/api/data/EXR/M.%s.NOK.SP?lastNObservations=1"), from);
|
||||
snprintf_P(buf, BufferSize, PSTR("https://data.norges-bank.no/api/data/EXR/B.%s.NOK.SP?lastNObservations=1"), from);
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("(EntsoeApi) Retrieving %s to NOK conversion\n"), from);
|
||||
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("(EntsoeApi) url: %s\n"), buf);
|
||||
if(retrieve(buf, &p)) {
|
||||
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("(EntsoeApi) got exchange rate %.4f\n"), p.getValue());
|
||||
currencyMultiplier = p.getValue();
|
||||
if(strncmp(to, "NOK", 3) != 0) {
|
||||
snprintf_P(buf, BufferSize, PSTR("https://data.norges-bank.no/api/data/EXR/M.%s.NOK.SP?lastNObservations=1"), to);
|
||||
snprintf_P(buf, BufferSize, PSTR("https://data.norges-bank.no/api/data/EXR/B.%s.NOK.SP?lastNObservations=1"), to);
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("(EntsoeApi) Retrieving %s to NOK conversion\n"), to);
|
||||
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("(EntsoeApi) url: %s\n"), buf);
|
||||
if(retrieve(buf, &p)) {
|
||||
|
||||
22
lib/SvelteUi/app/dist/index.js
vendored
22
lib/SvelteUi/app/dist/index.js
vendored
File diff suppressed because one or more lines are too long
@@ -8,12 +8,14 @@
|
||||
let yScale;
|
||||
let heightAvailable;
|
||||
let labelOffset;
|
||||
let labelOffset2;
|
||||
|
||||
$: {
|
||||
heightAvailable = height-(config.title ? 20 : 0);
|
||||
let innerWidth = width - (config.padding.left + config.padding.right);
|
||||
barWidth = innerWidth / config.points.length;
|
||||
labelOffset = barWidth < 25 ? 28 : 17;
|
||||
labelOffset2 = barWidth < 25 ? 10 : 100;
|
||||
|
||||
let yPerUnit = (heightAvailable-config.padding.top-config.padding.bottom)/(config.y.max-config.y.min);
|
||||
|
||||
@@ -104,13 +106,14 @@
|
||||
/>
|
||||
{#if barWidth > 15}
|
||||
<text
|
||||
y="{yScale(-point.value2) < yScale(0)+12 ? yScale(-point.value2) + 12 : yScale(-point.value2)-10}"
|
||||
y="{yScale(-point.value2) < yScale(0) + labelOffset2 ? yScale(0) - labelOffset+2 : yScale(-point.value2)-labelOffset}"
|
||||
x="{xScale(i) + barWidth/2}"
|
||||
width="{barWidth - 4}"
|
||||
dominant-baseline="middle"
|
||||
text-anchor="{barWidth < 25 ? 'left' : 'middle'}"
|
||||
fill="{yScale(-point.value2) < yScale(0)+12 ? point.color : 'white'}"
|
||||
transform="rotate({barWidth < 25 ? 90 : 0}, {xScale(i) + (barWidth/2)}, {yScale(point.value2 - config.y.min) > yScale(0)-12 ? yScale(point.value2 - config.y.min) - 12 : yScale(point.value2 - config.y.min)+9})"
|
||||
text-anchor="{'middle'}"
|
||||
fill="{yScale(-point.value2) < yScale(0) + labelOffset2 ? point.color : 'white'}"
|
||||
transform="rotate({barWidth < 25 ? 90 : 0}, {xScale(i) + (barWidth/2)}, {yScale(-point.value2) < yScale(0) + labelOffset2 ? yScale(0) - labelOffset+2 : yScale(-point.value2)-labelOffset})"
|
||||
|
||||
>{point.label2}</text>
|
||||
{#if point.title2}
|
||||
<title>{point.title2}</title>
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
<script>
|
||||
import { zeropad, monthnames } from './Helpers.js';
|
||||
import { zeropad, monthnames, addHours } from './Helpers.js';
|
||||
|
||||
export let timestamp;
|
||||
export let fullTimeColor;
|
||||
export let offset;
|
||||
|
||||
let showFull;
|
||||
$:{
|
||||
showFull = Math.abs(new Date().getTime()-timestamp.getTime()) < 300000;
|
||||
if(!isNaN(offset))
|
||||
addHours(timestamp, offset);
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if Math.abs(new Date().getTime()-timestamp.getTime()) < 300000 }
|
||||
{`${zeropad(timestamp.getDate())}. ${monthnames[timestamp.getMonth()]} ${zeropad(timestamp.getHours())}:${zeropad(timestamp.getMinutes())}`}
|
||||
{#if showFull }
|
||||
{`${zeropad(timestamp.getDate())}. ${monthnames[timestamp.getMonth()]} ${zeropad(timestamp.getUTCHours())}:${zeropad(timestamp.getMinutes())}`}
|
||||
{:else}
|
||||
<span class="{fullTimeColor}">{`${zeropad(timestamp.getDate())}.${zeropad(timestamp.getMonth()+1)}.${timestamp.getFullYear()} ${zeropad(timestamp.getHours())}:${zeropad(timestamp.getMinutes())}`}</span>
|
||||
<span class="{fullTimeColor}">{`${zeropad(timestamp.getDate())}.${zeropad(timestamp.getMonth()+1)}.${timestamp.getFullYear()} ${zeropad(timestamp.getUTCHours())}:${zeropad(timestamp.getMinutes())}`}</span>
|
||||
{/if}
|
||||
|
||||
@@ -82,17 +82,17 @@
|
||||
{/if}
|
||||
{#if uiVisibility(sysinfo.ui.p, data.pe && !Number.isNaN(data.p))}
|
||||
<div class="cnt gwf">
|
||||
<PricePlot json={prices}/>
|
||||
<PricePlot json={prices} sysinfo={sysinfo}/>
|
||||
</div>
|
||||
{/if}
|
||||
{#if uiVisibility(sysinfo.ui.d, dayPlot)}
|
||||
<div class="cnt gwf">
|
||||
<DayPlot json={dayPlot} />
|
||||
<DayPlot json={dayPlot} sysinfo={sysinfo}/>
|
||||
</div>
|
||||
{/if}
|
||||
{#if uiVisibility(sysinfo.ui.m, monthPlot)}
|
||||
<div class="cnt gwf">
|
||||
<MonthPlot json={monthPlot} />
|
||||
<MonthPlot json={monthPlot} sysinfo={sysinfo}/>
|
||||
</div>
|
||||
{/if}
|
||||
{#if uiVisibility(sysinfo.ui.s, data.t && data.t != -127 && temperatures.c > 1)}
|
||||
|
||||
@@ -59,7 +59,7 @@ export const dataStore = readable(data, (set) => {
|
||||
}
|
||||
if(lastPrice != data.p && data.pe) {
|
||||
lastPrice = data.p;
|
||||
setTimeout(getPrices, 4000);
|
||||
setTimeout(getPrices, 1000);
|
||||
}
|
||||
if(sysinfo.upgrading) {
|
||||
window.location.reload();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import BarChart from './BarChart.svelte';
|
||||
|
||||
export let json;
|
||||
export let sysinfo;
|
||||
|
||||
let config = {};
|
||||
let max = 0;
|
||||
@@ -15,6 +16,7 @@
|
||||
let points = [];
|
||||
let cur = addHours(new Date(), -24);
|
||||
let currentHour = new Date().getUTCHours();
|
||||
addHours(cur, sysinfo.clock_offset);
|
||||
for(i = currentHour; i<24; i++) {
|
||||
let imp = json["i"+zeropad(i)];
|
||||
let exp = json["e"+zeropad(i)];
|
||||
@@ -22,7 +24,7 @@
|
||||
if(exp === undefined) exp = 0;
|
||||
|
||||
xTicks.push({
|
||||
label: zeropad(cur.getHours())
|
||||
label: zeropad(cur.getUTCHours())
|
||||
});
|
||||
points.push({
|
||||
label: imp.toFixed(1),
|
||||
@@ -44,7 +46,7 @@
|
||||
if(exp === undefined) exp = 0;
|
||||
|
||||
xTicks.push({
|
||||
label: zeropad(cur.getHours())
|
||||
label: zeropad(cur.getUTCHours())
|
||||
});
|
||||
points.push({
|
||||
label: imp.toFixed(1),
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
<a class="float-right" href='https://github.com/UtilitechAS/amsreader-firmware' target='_blank' rel="noreferrer" aria-label="GitHub"><img class="gh-logo" src={GitHubLogo} alt="GitHub repo"/></a>
|
||||
</div>
|
||||
<div class="flex-none my-auto px-2">
|
||||
<Clock timestamp={ data.c ? new Date(data.c * 1000) : new Date(0) } fullTimeColor="text-red-500" />
|
||||
<Clock timestamp={ data.c ? new Date(data.c * 1000) : new Date(0) } offset={sysinfo.clock_offset} fullTimeColor="text-red-500" />
|
||||
</div>
|
||||
{#if sysinfo.vndcfg && sysinfo.usrcfg}
|
||||
<div class="flex-none px-1 mt-1" title="Configuration">
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<script>
|
||||
import { zeropad } from './Helpers.js';
|
||||
import { zeropad, addHours } from './Helpers.js';
|
||||
import BarChart from './BarChart.svelte';
|
||||
|
||||
export let json;
|
||||
export let sysinfo;
|
||||
|
||||
let config = {};
|
||||
let max = 0;
|
||||
@@ -15,6 +16,8 @@
|
||||
let points = [];
|
||||
let cur = new Date();
|
||||
let lm = new Date();
|
||||
addHours(cur, sysinfo.clock_offset);
|
||||
addHours(lm, sysinfo.clock_offset);
|
||||
lm.setDate(0);
|
||||
|
||||
for(i = cur.getDate(); i<=lm.getDate(); i++) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import BarChart from './BarChart.svelte';
|
||||
|
||||
export let json;
|
||||
export let sysinfo;
|
||||
|
||||
let config = {};
|
||||
let max = 0;
|
||||
@@ -18,16 +19,17 @@
|
||||
let xTicks = [];
|
||||
let points = [];
|
||||
let cur = new Date();
|
||||
addHours(cur, sysinfo.clock_offset);
|
||||
for(i = hour; i<24; i++) {
|
||||
val = json[zeropad(h++)];
|
||||
if(val == null) break;
|
||||
xTicks.push({
|
||||
label: zeropad(cur.getHours())
|
||||
label: zeropad(cur.getUTCHours())
|
||||
});
|
||||
points.push({
|
||||
label: val > 0 ? val.toFixed(d) : '',
|
||||
title: val > 0 ? val.toFixed(2) + ' ' + json.currency : '',
|
||||
value: val > 0 ? Math.abs(val*100) : 0,
|
||||
label: val >= 0 ? val.toFixed(d) : '',
|
||||
title: val >= 0 ? val.toFixed(2) + ' ' + json.currency : '',
|
||||
value: val >= 0 ? Math.abs(val*100) : 0,
|
||||
label2: val < 0 ? val.toFixed(d) : '',
|
||||
title2: val < 0 ? val.toFixed(2) + ' ' + json.currency : '',
|
||||
value2: val < 0 ? Math.abs(val*100) : 0,
|
||||
@@ -41,11 +43,11 @@
|
||||
val = json[zeropad(h++)];
|
||||
if(val == null) break;
|
||||
xTicks.push({
|
||||
label: zeropad(cur.getHours())
|
||||
label: zeropad(cur.getUTCHours())
|
||||
});
|
||||
points.push({
|
||||
label: val > 0 ? val.toFixed(d) : '',
|
||||
value: val > 0 ? Math.abs(val*100) : 0,
|
||||
label: val >= 0 ? val.toFixed(d) : '',
|
||||
value: val >= 0 ? Math.abs(val*100) : 0,
|
||||
label2: val < 0 ? val.toFixed(d) : '',
|
||||
value2: val < 0 ? Math.abs(val*100) : 0,
|
||||
color: '#7c3aed'
|
||||
@@ -55,12 +57,13 @@
|
||||
addHours(cur, 1);
|
||||
};
|
||||
|
||||
max = Math.ceil(max);
|
||||
min = Math.floor(min);
|
||||
let range = Math.max(max, Math.abs(min));
|
||||
|
||||
if(min < 0) {
|
||||
let yTickDistDown = min/4;
|
||||
for(i = 1; i < 5; i++) {
|
||||
min = Math.min((range/4)*-1, min);
|
||||
let yTicksNum = Math.ceil((Math.abs(min)/range) * 4);
|
||||
let yTickDistDown = min/yTicksNum;
|
||||
for(i = 1; i < yTicksNum+1; i++) {
|
||||
let val = (yTickDistDown*i);
|
||||
yTicks.push({
|
||||
value: val,
|
||||
@@ -69,8 +72,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
let yTickDistUp = max/4;
|
||||
for(i = 0; i < 5; i++) {
|
||||
max = Math.max((range/4), max);
|
||||
let xTicksNum = Math.ceil((max/range) * 4);
|
||||
let yTickDistUp = max/xTicksNum;
|
||||
for(i = 0; i < xTicksNum+1; i++) {
|
||||
let val = (yTickDistUp*i);
|
||||
yTicks.push({
|
||||
value: val,
|
||||
|
||||
@@ -17,18 +17,18 @@ export default defineConfig({
|
||||
plugins: [svelte()],
|
||||
server: {
|
||||
proxy: {
|
||||
"/data.json": "http://192.168.233.235",
|
||||
"/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.235",
|
||||
"/configuration.json": "http://192.168.233.235",
|
||||
"/tariff.json": "http://192.168.233.235",
|
||||
"/save": "http://192.168.233.235",
|
||||
"/reboot": "http://192.168.233.235",
|
||||
"/configfile": "http://192.168.233.235",
|
||||
"/upgrade": "http://192.168.233.235"
|
||||
"/data.json": "http://192.168.28.100",
|
||||
"/energyprice.json": "http://192.168.28.100",
|
||||
"/dayplot.json": "http://192.168.28.100",
|
||||
"/monthplot.json": "http://192.168.28.100",
|
||||
"/temperature.json": "http://192.168.28.100",
|
||||
"/sysinfo.json": "http://192.168.28.100",
|
||||
"/configuration.json": "http://192.168.28.100",
|
||||
"/tariff.json": "http://192.168.28.100",
|
||||
"/save": "http://192.168.28.100",
|
||||
"/reboot": "http://192.168.28.100",
|
||||
"/configfile": "http://192.168.28.100",
|
||||
"/upgrade": "http://192.168.28.100"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -50,5 +50,6 @@
|
||||
"c" : %.2f,
|
||||
"p" : %.2f,
|
||||
"i" : %.2f
|
||||
}
|
||||
},
|
||||
"clock_offset": %d
|
||||
}
|
||||
@@ -261,6 +261,8 @@ void AmsWebServer::sysinfoJson() {
|
||||
if(!meterId.isEmpty())
|
||||
meterId.replace(F("\\"), F("\\\\"));
|
||||
|
||||
time_t now = time(nullptr);
|
||||
|
||||
int size = snprintf_P(buf, BufferSize, SYSINFO_JSON,
|
||||
FirmwareVersion::VersionString,
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
@@ -326,7 +328,8 @@ void AmsWebServer::sysinfoJson() {
|
||||
ea->getUseLastMonth(),
|
||||
ea->getCostLastMonth(),
|
||||
ea->getProducedLastMonth(),
|
||||
ea->getIncomeLastMonth()
|
||||
ea->getIncomeLastMonth(),
|
||||
tz == NULL ? 0 : (tz->toLocal(now)-now)/3600
|
||||
);
|
||||
|
||||
stripNonAscii((uint8_t*) buf, size+1);
|
||||
|
||||
@@ -31,6 +31,7 @@ ADC_MODE(ADC_VCC);
|
||||
|
||||
#if defined(ESP32)
|
||||
#include <esp_task_wdt.h>
|
||||
#include <lwip/dns.h>
|
||||
#endif
|
||||
#define WDT_TIMEOUT 60
|
||||
|
||||
@@ -158,6 +159,30 @@ void printHanReadError(int pos);
|
||||
void debugPrint(byte *buffer, int start, int length);
|
||||
|
||||
|
||||
#if defined(ESP32)
|
||||
uint8_t dnsState = 0;
|
||||
ip_addr_t dns0;
|
||||
void WiFiEvent(WiFiEvent_t event) {
|
||||
switch(event) {
|
||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
||||
const ip_addr_t* dns = dns_getserver(0);
|
||||
memcpy(&dns0, dns, sizeof(dns0));
|
||||
|
||||
IPAddress res;
|
||||
int ret = WiFi.hostByName("hub.amsleser.no", res);
|
||||
if(ret == 0) {
|
||||
dnsState = 2;
|
||||
debugI_P(PSTR("No DNS, probably a closed network"));
|
||||
} else {
|
||||
debugI_P(PSTR("DNS is present and working, monitoring"));
|
||||
dnsState = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
@@ -362,15 +387,7 @@ void setup() {
|
||||
if(config.hasConfig()) {
|
||||
if(Debug.isActive(RemoteDebug::INFO)) config.print(&Debug);
|
||||
WiFi_connect();
|
||||
|
||||
NtpConfig ntp;
|
||||
if(config.getNtpConfig(ntp)) {
|
||||
tz = resolveTimezone(ntp.timezone);
|
||||
ws.setTimezone(tz);
|
||||
ds.setTimezone(tz);
|
||||
ea.setTimezone(tz);
|
||||
}
|
||||
|
||||
handleNtpChange();
|
||||
ds.load();
|
||||
} else {
|
||||
if(Debug.isActive(RemoteDebug::INFO)) {
|
||||
@@ -632,6 +649,16 @@ void handleSystem(unsigned long now) {
|
||||
if(end - start > 1000) {
|
||||
debugW_P(PSTR("Used %dms to send system update to MQTT"), millis()-start);
|
||||
}
|
||||
|
||||
#if defined(ESP32)
|
||||
if(dnsState == 1) {
|
||||
const ip_addr_t* dns = dns_getserver(0);
|
||||
if(memcmp(&dns0, dns, sizeof(dns0)) != 0) {
|
||||
dns_setserver(0, &dns0);
|
||||
debugI_P(PSTR("Had to reset DNS server"));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1328,6 +1355,7 @@ void WiFi_connect() {
|
||||
if(strlen(wifi.hostname) > 0) {
|
||||
WiFi.setHostname(wifi.hostname);
|
||||
}
|
||||
WiFi.onEvent(WiFiEvent);
|
||||
#endif
|
||||
WiFi.mode(WIFI_STA);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user