Compare commits

...

11 Commits

Author SHA1 Message Date
Gunnar Skjold
9d902c63de Fixed NPE 2023-05-22 12:51:26 +02:00
Gunnar Skjold
a57405a7a5 Fixed NaN in clock 2023-05-22 10:26:12 +02:00
Gunnar Skjold
550d32ee33 Fixed issue with new device 2023-05-22 09:55:15 +02:00
Gunnar Skjold
9f7e174c7b Updated code that fixes broken DNS on IPv6 networks 2023-05-21 13:50:05 +02:00
Gunnar Skjold
104617afd2 Use device timezone in gui 2023-05-21 12:34:00 +02:00
Gunnar Skjold
959664f61d Even more changes for negative prices 2023-05-21 12:10:00 +02:00
Gunnar Skjold
5834b07393 Adjustments to avoid dns resolving in a closed network 2023-05-20 22:03:26 +02:00
Gunnar Skjold
8dbcf2424a Hack to fix DNS on networks with IPv6 2023-05-20 21:59:18 +02:00
Gunnar Skjold
74345046b1 Adjustments for negative prices 2023-05-20 21:58:57 +02:00
Gunnar Skjold
516c80e38c Fixed negative and 0 price 2023-05-16 22:04:51 +02:00
Gunnar Skjold
17bbac8670 Updated exchange rate api call 2023-05-05 19:34:48 +02:00
16 changed files with 126 additions and 66 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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)) {

File diff suppressed because one or more lines are too long

View File

@@ -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>

View File

@@ -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}

View File

@@ -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)}

View File

@@ -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();

View File

@@ -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),

View File

@@ -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">

View File

@@ -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++) {

View File

@@ -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,

View File

@@ -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"
}
}
})

View File

@@ -50,5 +50,6 @@
"c" : %.2f,
"p" : %.2f,
"i" : %.2f
}
},
"clock_offset": %d
}

View File

@@ -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);

View File

@@ -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);