Compare commits

...

10 Commits

Author SHA1 Message Date
Gunnar Skjold
de19de2129 Fixed incorrect change in previous commit 2023-02-01 19:00:50 +01:00
Gunnar Skjold
1719263de0 Fixed config migration issue from v2.1 2023-02-01 18:46:22 +01:00
Gunnar Skjold
e70b872c98 Removed v2.0 config loading 2023-02-01 18:46:06 +01:00
Gunnar Skjold
b7d28238ab Extended cache for js and css 2023-01-31 17:16:18 +01:00
Gunnar Skjold
6c9a8b0692 Label fix 2023-01-31 17:04:46 +01:00
Gunnar Skjold
9f3dba3aab Fixed reconnect after setup 2023-01-30 20:58:08 +01:00
Gunnar Skjold
e4e4ad4107 Fixed autodetect 2023-01-30 18:32:17 +01:00
Gunnar Skjold
e8fb9570bb Chasing a reboot bug... 2023-01-30 16:52:12 +01:00
Gunnar Skjold
bc42099962 Fixing some bug reports 2023-01-30 16:07:08 +01:00
Gunnar Skjold
be71cbe609 Some changes after bug reports 2023-01-30 12:02:23 +01:00
16 changed files with 213 additions and 248 deletions

View File

@@ -22,10 +22,6 @@
#define CONFIG_NTP_START 872
#define CONFIG_MQTT_START 1004
#define CONFIG_MQTT_START_86 224
#define CONFIG_METER_START_87 784
#define CONFIG_ENTSOE_START_90 286
#define CONFIG_WIFI_START_91 16
#define CONFIG_METER_START_93 224
@@ -37,18 +33,6 @@ struct SystemConfig {
char country[3];
}; // 7
struct WiFiConfig91 {
char ssid[32];
char psk[64];
char ip[15];
char gateway[15];
char subnet[15];
char dns1[15];
char dns2[15];
char hostname[32];
bool mdns;
}; // 204
struct WiFiConfig {
char ssid[32];
char psk[64];
@@ -65,18 +49,6 @@ struct WiFiConfig {
bool autoreboot;
}; // 213
struct MqttConfig86 {
char host[128];
uint16_t port;
char clientId[32];
char publishTopic[64];
char subscribeTopic[64];
char username[64];
char password[64];
uint8_t payloadFormat;
bool ssl;
}; // 420
struct MqttConfig {
char host[128];
uint16_t port;
@@ -110,7 +82,7 @@ struct MeterConfig {
uint32_t accumulatedMultiplier;
uint8_t source;
uint8_t parser;
}; // 52
}; // 61
struct MeterConfig100 {
uint32_t baud;
@@ -127,7 +99,7 @@ struct MeterConfig100 {
uint32_t accumulatedMultiplier;
uint8_t source;
uint8_t parser;
}; // 50
}; // 59
struct MeterConfig95 {
uint32_t baud;
@@ -146,16 +118,6 @@ struct MeterConfig95 {
uint8_t parser;
}; // 50
struct MeterConfig87 {
uint8_t type;
uint8_t distributionSystem;
uint8_t mainFuse;
uint8_t productionCapacity;
uint8_t encryptionKey[16];
uint8_t authenticationKey[16];
bool substituteMissing;
}; // 37
struct DebugConfig {
bool telnet;
bool serial;
@@ -331,9 +293,6 @@ private:
uint8_t tempSensorCount = 0;
TempSensorConfig** tempSensors = NULL;
bool relocateConfig90(); // 2.0.0
bool relocateConfig91(); // 2.0.2
bool relocateConfig92(); // 2.0.3
bool relocateConfig93(); // 2.1.0
bool relocateConfig94(); // 2.1.0
bool relocateConfig95(); // 2.1.4

View File

@@ -358,8 +358,9 @@ bool AmsConfiguration::pinUsed(uint8_t pin, GpioConfig& config) {
}
bool AmsConfiguration::getGpioConfig(GpioConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(EEPROM_SIZE);
uint8_t configVersion = EEPROM.read(EEPROM_CONFIG_ADDRESS);
if(configVersion == EEPROM_CHECK_SUM || configVersion == EEPROM_CLEARED_INDICATOR) {
EEPROM.get(CONFIG_GPIO_START, config);
EEPROM.end();
return true;
@@ -505,6 +506,7 @@ bool AmsConfiguration::getEntsoeConfig(EntsoeConfig& config) {
}
return true;
} else {
clearEntsoe(config);
return false;
}
}
@@ -560,6 +562,7 @@ bool AmsConfiguration::getEnergyAccountingConfig(EnergyAccountingConfig& config)
if(config.hours > 5) config.hours = 5;
return true;
} else {
clearEnergyAccountingConfig(config);
return false;
}
}
@@ -713,30 +716,6 @@ bool AmsConfiguration::hasConfig() {
}
} else {
switch(configVersion) {
case 90:
configVersion = -1; // Prevent loop
if(relocateConfig90()) {
configVersion = 91;
} else {
configVersion = 0;
return false;
}
case 91:
configVersion = -1; // Prevent loop
if(relocateConfig91()) {
configVersion = 92;
} else {
configVersion = 0;
return false;
}
case 92:
configVersion = -1; // Prevent loop
if(relocateConfig92()) {
configVersion = 93;
} else {
configVersion = 0;
return false;
}
case 93:
configVersion = -1; // Prevent loop
if(relocateConfig93()) {
@@ -829,61 +808,8 @@ void AmsConfiguration::saveTempSensors() {
}
}
bool AmsConfiguration::relocateConfig90() {
EntsoeConfig entsoe;
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_ENTSOE_START_90, entsoe);
EEPROM.put(CONFIG_ENTSOE_START, entsoe);
EEPROM.put(EEPROM_CONFIG_ADDRESS, 91);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
bool AmsConfiguration::relocateConfig91() {
WiFiConfig91 wifi91;
WiFiConfig wifi;
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_WIFI_START_91, wifi91);
strcpy(wifi.ssid, wifi91.ssid);
strcpy(wifi.psk, wifi91.psk);
strcpy(wifi.ip, wifi91.ip);
strcpy(wifi.gateway, wifi91.gateway);
strcpy(wifi.subnet, wifi91.subnet);
strcpy(wifi.dns1, wifi91.dns1);
strcpy(wifi.dns2, wifi91.dns2);
strcpy(wifi.hostname, wifi91.hostname);
wifi.mdns = wifi91.mdns;
EEPROM.put(CONFIG_WIFI_START, wifi);
EEPROM.put(EEPROM_CONFIG_ADDRESS, 92);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
bool AmsConfiguration::relocateConfig92() {
WiFiConfig wifi;
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_WIFI_START, wifi);
#if defined(ESP32)
wifi.power = 195;
#elif defined(ESP8266)
wifi.power = 205;
#endif
EEPROM.put(CONFIG_WIFI_START, wifi);
EnergyAccountingConfig eac;
clearEnergyAccountingConfig(eac);
EEPROM.put(CONFIG_ENERGYACCOUNTING_START, eac);
EEPROM.put(EEPROM_CONFIG_ADDRESS, 93);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
bool AmsConfiguration::relocateConfig93() {
MeterConfig meter;
MeterConfig95 meter;
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_METER_START_93, meter);
meter.wattageMultiplier = 0;
@@ -910,7 +836,7 @@ bool AmsConfiguration::relocateConfig94() {
}
bool AmsConfiguration::relocateConfig95() {
MeterConfig meter;
MeterConfig95 meter;
MeterConfig95 meter95;
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_METER_START, meter);
@@ -931,7 +857,7 @@ bool AmsConfiguration::relocateConfig96() {
SystemConfig sys;
EEPROM.get(CONFIG_SYSTEM_START, sys);
MeterConfig meter;
MeterConfig100 meter;
EEPROM.get(CONFIG_METER_START, meter);
meter.source = 1; // Serial
meter.parser = 0; // Auto

View File

@@ -89,8 +89,8 @@ private:
EnergyAccountingConfig *config = NULL;
Timezone *tz = NULL;
uint8_t currentHour = 0, currentDay = 0, currentThresholdIdx = 0;
double use, costHour, costDay;
double produce, incomeHour, incomeDay;
double use = 0, costHour = 0, costDay = 0;
double produce = 0, incomeHour = 0, incomeDay = 0;
EnergyAccountingData data = { 0, 0, 0, 0, 0, 0 };
void calcDayCost();

View File

@@ -185,7 +185,8 @@ double EnergyAccounting::getUseThisHour() {
double EnergyAccounting::getUseToday() {
float ret = 0.0;
time_t now = time(nullptr);
if(now < BUILD_EPOCH) return 0;
if(now < BUILD_EPOCH) return 0.0;
if(tz == NULL) return 0.0;
tmElements_t utc, local;
breakTime(tz->toLocal(now), local);
for(int i = 0; i < currentHour; i++) {
@@ -197,7 +198,7 @@ double EnergyAccounting::getUseToday() {
double EnergyAccounting::getUseThisMonth() {
time_t now = time(nullptr);
if(now < BUILD_EPOCH) return 0;
if(now < BUILD_EPOCH) return 0.0;
float ret = 0;
for(int i = 0; i < currentDay; i++) {
ret += ds->getDayImport(i) / 1000.0;
@@ -212,7 +213,7 @@ double EnergyAccounting::getProducedThisHour() {
double EnergyAccounting::getProducedToday() {
float ret = 0.0;
time_t now = time(nullptr);
if(now < BUILD_EPOCH) return 0;
if(now < BUILD_EPOCH) return 0.0;
tmElements_t utc;
for(int i = 0; i < currentHour; i++) {
breakTime(now - ((currentHour - i) * 3600), utc);
@@ -223,7 +224,7 @@ double EnergyAccounting::getProducedToday() {
double EnergyAccounting::getProducedThisMonth() {
time_t now = time(nullptr);
if(now < BUILD_EPOCH) return 0;
if(now < BUILD_EPOCH) return 0.0;
float ret = 0;
for(int i = 0; i < currentDay; i++) {
ret += ds->getDayExport(i) / 1000.0;
@@ -279,6 +280,8 @@ uint8_t EnergyAccounting::getCurrentThreshold() {
}
float EnergyAccounting::getMonthMax() {
if(config == NULL)
return 0.0;
uint8_t count = 0;
uint32_t maxHour = 0.0;
bool included[5] = { false, false, false, false, false };
@@ -308,6 +311,8 @@ float EnergyAccounting::getMonthMax() {
}
EnergyAccountingPeak EnergyAccounting::getPeak(uint8_t num) {
if(config == NULL)
return EnergyAccountingPeak({0,0});
if(num < 1 || num > 5) return EnergyAccountingPeak({0,0});
uint8_t count = 0;

View File

@@ -64,6 +64,10 @@
{#if sysinfo.upgrading}
<Mask active=true message="Device is upgrading, please wait"/>
{:else if sysinfo.booting}
<Mask active=true message="Device is booting, please wait"/>
{#if sysinfo.trying}
<Mask active=true message="Device is booting, please wait. Trying to reach it on {sysinfo.trying}"/>
{:else}
<Mask active=true message="Device is booting, please wait"/>
{/if}
{/if}
</div>

View File

@@ -1,54 +1,79 @@
<script>
export let data;
export let currency;
let hasExport = data && (data.om || data.e > 0);
export let hasExport;
</script>
<div class="mx-2 text-sm">
<strong>Real time calculation</strong>
{#if data && data.h !== undefined}
<div class="flex">
<div>Hour</div>
<div class="flex-auto text-right">{data.h.u ? data.h.u.toFixed(2) : '-'} kWh {#if currency && (hasExport)}/ {data.h.c ? data.h.c.toFixed(2) : '-'} {currency}{/if}</div>
</div>
<div class="flex">
<div>Day</div>
<div class="flex-auto text-right">{data.d.u ? data.d.u.toFixed(1) : '-'} kWh {#if currency && (hasExport)}/ {data.d.c ? data.d.c.toFixed(2) : '-'} {currency}{/if}</div>
</div>
<div class="flex">
<div>Month</div>
<div class="flex-auto text-right">{data.m.u ? data.m.u.toFixed(0) : '-'} kWh {#if currency && (hasExport)}/ {data.m.c ? data.m.c.toFixed(2) : '-'} {currency}{/if}</div>
</div>
<div class="mt-4">
{#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.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.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.i ? data.m.i.toFixed(2) : '-'} {currency}{/if}</div>
</div>
<br/><br/>
{#if data}
{#if hasExport && currency}
<strong>Import</strong>
<div class="grid grid-cols-3 mb-3">
<div>Hour</div>
<div class="text-right">{data.h.u ? data.h.u.toFixed(2) : '-'} kWh</div>
<div class="text-right">{data.h.c ? data.h.c.toFixed(2) : '-'} {currency}</div>
<div>Day</div>
<div class="text-right">{data.d.u ? data.d.u.toFixed(1) : '-'} kWh</div>
<div class="text-right">{data.d.c ? data.d.c.toFixed(1) : '-'} {currency}</div>
<div>Month</div>
<div class="text-right">{data.m.u ? data.m.u.toFixed(0) : '-'} kWh</div>
<div class="text-right">{data.m.c ? data.m.c.toFixed(0) : '-'} {currency}</div>
</div>
<strong>Export</strong>
<div class="grid grid-cols-3">
<div>Hour</div>
<div class="text-right">{data.h.p ? data.h.p.toFixed(2) : '-'} kWh</div>
<div class="text-right">{data.h.i ? data.h.i.toFixed(2) : '-'} {currency}</div>
<div>Day</div>
<div class="text-right">{data.d.p ? data.d.p.toFixed(1) : '-'} kWh</div>
<div class="text-right">{data.d.i ? data.d.i.toFixed(1) : '-'} {currency}</div>
<div>Month</div>
<div class="text-right">{data.m.p ? data.m.p.toFixed(0) : '-'} kWh</div>
<div class="text-right">{data.m.i ? data.m.i.toFixed(0) : '-'} {currency}</div>
</div>
{:else if hasExport}
<strong>Import</strong>
<div class="grid grid-cols-2 mb-3">
<div>Hour</div>
<div class="text-right">{data.h.u ? data.h.u.toFixed(2) : '-'} kWh</div>
<div>Day</div>
<div class="text-right">{data.d.u ? data.d.u.toFixed(1) : '-'} kWh</div>
<div>Month</div>
<div class="text-right">{data.m.u ? data.m.u.toFixed(0) : '-'} kWh</div>
</div>
<strong>Export</strong>
<div class="grid grid-cols-2">
<div>Hour</div>
<div class="text-right">{data.h.p ? data.h.p.toFixed(2) : '-'} kWh</div>
<div>Day</div>
<div class="text-right">{data.d.p ? data.d.p.toFixed(1) : '-'} kWh</div>
<div>Month</div>
<div class="text-right">{data.m.p ? data.m.p.toFixed(0) : '-'} kWh</div>
</div>
{:else}
<div class="flex">
<div>Hour</div>
<div class="flex-auto text-right">{data.h.c ? data.h.c.toFixed(2) : '-'} {currency}</div>
</div>
<div class="flex">
<div>Day</div>
<div class="flex-auto text-right">{data.d.c ? data.d.c.toFixed(2) : '-'} {currency}</div>
</div>
<div class="flex">
<div>Month</div>
<div class="flex-auto text-right">{data.m.c ? data.m.c.toFixed(2) : '-'} {currency}</div>
</div>
<strong>Consumption</strong>
<div class="grid grid-cols-2 mb-3">
<div>Hour</div>
<div class="text-right">{data.h.u ? data.h.u.toFixed(2) : '-'} kWh</div>
<div>Day</div>
<div class="text-right">{data.d.u ? data.d.u.toFixed(1) : '-'} kWh</div>
<div>Month</div>
<div class="text-right">{data.m.u ? data.m.u.toFixed(0) : '-'} kWh</div>
</div>
{#if currency}
<strong>Cost</strong>
<div class="grid grid-cols-2">
<div>Hour</div>
<div class="text-right">{data.h.c ? data.h.c.toFixed(2) : '-'} {currency}</div>
<div>Day</div>
<div class="text-right">{data.d.c ? data.d.c.toFixed(1) : '-'} {currency}</div>
<div>Month</div>
<div class="text-right">{data.m.c ? data.m.c.toFixed(0) : '-'} {currency}</div>
</div>
{/if}
{/if}
</div>
{/if}
</div>

View File

@@ -7,6 +7,7 @@
import HelpIcon from './HelpIcon.svelte';
import CountrySelectOptions from './CountrySelectOptions.svelte';
import { Link, navigate } from 'svelte-navigator';
import SubnetOptions from './SubnetOptions.svelte';
export let sysinfo = {}
@@ -67,8 +68,10 @@
getConfiguration();
let isFactoryReset = false;
let isFactoryResetComplete = false;
async function factoryReset() {
if(confirm("Are you sure you want to factory reset the device?")) {
isFactoryReset = true;
const data = new URLSearchParams();
data.append("perform", "true");
const response = await fetch('/reset', {
@@ -76,7 +79,8 @@
body: data
});
let res = (await response.json());
isFactoryReset = res.success;
isFactoryReset = false;
isFactoryResetComplete = res.success;
}
}
@@ -362,11 +366,9 @@
<option value="dhcp">DHCP</option>
<option value="static">Static</option>
</select>
<input name="ni" bind:value={configuration.n.i} type="text" class="in-m w-full" disabled={configuration.n.m == 'dhcp'}/>
<select name="ns" bind:value={configuration.n.s} class="in-l" disabled={configuration.n.m == 'dhcp'}>
<option value="255.255.255.0">/24</option>
<option value="255.255.0.0">/16</option>
<option value="255.0.0.0">/8</option>
<input name="ni" bind:value={configuration.n.i} type="text" class="in-m w-full" disabled={configuration.n.m == 'dhcp'} required={configuration.n.m == 'static'}/>
<select name="ns" bind:value={configuration.n.s} class="in-l" disabled={configuration.n.m == 'dhcp'} required={configuration.n.m == 'static'}>
<SubnetOptions/>
</select>
</div>
</div>
@@ -765,4 +767,5 @@
<Mask active={loading} message="Loading configuration"/>
<Mask active={saving} message="Saving configuration"/>
<Mask active={isFactoryReset} message="Device have been factory reset and switched to AP mode"/>
<Mask active={isFactoryReset} message="Performing factory reset"/>
<Mask active={isFactoryResetComplete} message="Device have been factory reset and switched to AP mode"/>

View File

@@ -1,6 +1,6 @@
<script>
import { pricesStore, dayPlotStore, monthPlotStore, temperaturesStore } from './DataStores.js';
import { metertype, uiVisibility } from './Helpers.js';
import { ampcol, exportcol, metertype, uiVisibility } from './Helpers.js';
import PowerGauge from './PowerGauge.svelte';
import VoltPlot from './VoltPlot.svelte';
import AmpPlot from './AmpPlot.svelte';
@@ -32,23 +32,23 @@
});
</script>
<div class="grid xl:grid-cols-6 lg:grid-cols-4 md:grid-cols-3 sm:grid-cols-2">
<div class="grid 2xl:grid-cols-6 xl:grid-cols-5 lg:grid-cols-4 md:grid-cols-3 sm:grid-cols-2">
{#if uiVisibility(sysinfo.ui.i, data.i)}
<div class="cnt">
<div class="grid grid-cols-2">
<div class="col-span-2">
<PowerGauge val={data.i ? data.i : 0} max={data.im} unit="W" label="Import" sub={data.p} subunit={prices.currency}/>
<div class="cnt">
<div class="grid grid-cols-2">
<div class="col-span-2">
<PowerGauge val={data.i ? data.i : 0} max={data.im ? data.im : 15000} unit="W" label="Import" sub={data.p} subunit={prices.currency} colorFn={ampcol}/>
</div>
<div>{data.mt ? metertype(data.mt) : '-'}</div>
<div class="text-right">{data.ic ? data.ic.toFixed(1) : '-'} kWh</div>
</div>
<div>{data.mt ? metertype(data.mt) : '-'}</div>
<div class="text-right">{data.ic ? data.ic.toFixed(1) : '-'} kWh</div>
</div>
</div>
{/if}
{#if uiVisibility(sysinfo.ui.e, data.om || data.e > 0)}
<div class="cnt">
<div class="grid grid-cols-2">
<div class="col-span-2">
<PowerGauge val={data.e ? data.e : 0} max={data.om ? data.om : 10000} unit="W" label="Export"/>
<PowerGauge val={data.e ? data.e : 0} max={data.om ? data.om * 1000 : 10000} unit="W" label="Export" colorFn={exportcol}/>
</div>
<div></div>
<div class="text-right">{data.ec ? data.ec.toFixed(1) : '-'} kWh</div>
@@ -56,48 +56,48 @@
</div>
{/if}
{#if uiVisibility(sysinfo.ui.v, data.u1 > 100 || data.u2 > 100 || data.u3 > 100)}
<div class="cnt">
<VoltPlot u1={data.u1} u2={data.u2} u3={data.u3} ds={data.ds}/>
</div>
<div class="cnt">
<VoltPlot u1={data.u1} u2={data.u2} u3={data.u3} ds={data.ds}/>
</div>
{/if}
{#if uiVisibility(sysinfo.ui.a, data.i1 > 0.01 || data.i2 > 0.01 || data.i3 > 0.01)}
<div class="cnt">
<AmpPlot u1={data.u1} u2={data.u2} u3={data.u3} i1={data.i1} i2={data.i2} i3={data.i3} max={data.mf ? data.mf : 32}/>
</div>
<div class="cnt">
<AmpPlot u1={data.u1} u2={data.u2} u3={data.u3} i1={data.i1} i2={data.i2} i3={data.i3} max={data.mf ? data.mf : 32}/>
</div>
{/if}
{#if uiVisibility(sysinfo.ui.r, data.ri > 0 || data.re > 0 || data.ric > 0 || data.rec > 0)}
<div class="cnt">
<ReactiveData importInstant={data.ri} exportInstant={data.re} importTotal={data.ric} exportTotal={data.rec}/>
</div>
<div class="cnt">
<ReactiveData importInstant={data.ri} exportInstant={data.re} importTotal={data.ric} exportTotal={data.rec}/>
</div>
{/if}
{#if uiVisibility(sysinfo.ui.c, data.ea)}
<div class="cnt">
<AccountingData data={data.ea} currency={prices.currency}/>
</div>
<div class="cnt">
<AccountingData data={data.ea} currency={prices.currency} hasExport={data.om > 0 || data.e > 0}/>
</div>
{/if}
{#if data && data.pr && (data.pr.startsWith("10YNO") || data.pr == '10Y1001A1001A48H')}
<div class="cnt h-64">
<TariffPeakChart />
</div>
{#if uiVisibility(sysinfo.ui.t, data.pr && (data.pr.startsWith("10YNO") || data.pr == '10Y1001A1001A48H'))}
<div class="cnt h-64">
<TariffPeakChart />
</div>
{/if}
{#if uiVisibility(sysinfo.ui.p, (typeof data.p == "number") && !Number.isNaN(data.p))}
<div class="cnt xl:col-span-6 lg:col-span-4 md:col-span-3 sm:col-span-2 h-64">
<div class="cnt 2xl:col-span-6 xl:col-span-5 lg:col-span-4 md:col-span-3 sm:col-span-2 h-64">
<PricePlot json={prices}/>
</div>
{/if}
{#if uiVisibility(sysinfo.ui.d, dayPlot)}
<div class="cnt xl:col-span-6 lg:col-span-4 md:col-span-3 sm:col-span-2 h-64">
<DayPlot json={dayPlot} />
</div>
<div class="cnt 2xl:col-span-6 xl:col-span-5 lg:col-span-4 md:col-span-3 sm:col-span-2 h-64">
<DayPlot json={dayPlot} />
</div>
{/if}
{#if uiVisibility(sysinfo.ui.m, monthPlot)}
<div class="cnt xl:col-span-6 lg:col-span-4 md:col-span-3 sm:col-span-2 h-64">
<MonthPlot json={monthPlot} />
</div>
<div class="cnt 2xl:col-span-6 xl:col-span-5 lg:col-span-4 md:col-span-3 sm:col-span-2 h-64">
<MonthPlot json={monthPlot} />
</div>
{/if}
{#if uiVisibility(sysinfo.ui.s, data.t && data.t != -127 && temperatures.c > 1)}
<div class="cnt xl:col-span-6 lg:col-span-4 md:col-span-3 sm:col-span-2 h-64">
<TemperaturePlot json={temperatures} />
</div>
<div class="cnt 2xl:col-span-6 xl:col-span-5 lg:col-span-4 md:col-span-3 sm:col-span-2 h-64">
<TemperaturePlot json={temperatures} />
</div>
{/if}
</div>

View File

@@ -25,7 +25,8 @@ let sysinfo = {
booting: false,
upgrading: false,
ui: {},
security: 0
security: 0,
trying: null
};
export const sysinfoStore = writable(sysinfo);
export async function getSysinfo() {

View File

@@ -15,6 +15,13 @@ export function ampcol(pct) {
else return '#32d900';
};
export function exportcol(pct) {
if(pct > 75) return '#32d900';
else if(pct > 50) return '#77d900';
else if(pct > 25) return '#94d900';
else return '#dcd800';
};
export function metertype(mt) {
switch(mt) {
case 1:

View File

@@ -1,6 +1,5 @@
<script>
import PowerGaugeSvg from './PowerGaugeSvg.svelte';
import { ampcol } from './Helpers.js';
export let val;
export let max;
@@ -8,10 +7,16 @@
export let label;
export let sub = "";
export let subunit = "";
export let colorFn;
let pct = 0;
$: {
pct = (Math.min(val,max)/max) * 100
}
</script>
<div class="pl-root">
<PowerGaugeSvg pct={val/max * 100} color={ampcol(val/max * 100)}/>
<PowerGaugeSvg pct={pct} color={colorFn(pct)}/>
<span class="pl-ov">
<span class="pl-lab">{label}</span>
<br/>

View File

@@ -1,6 +1,7 @@
<script>
import { sysinfoStore } from './DataStores.js';
import Mask from './Mask.svelte'
import SubnetOptions from './SubnetOptions.svelte';
export let sysinfo = {}
@@ -12,7 +13,15 @@
var url = "";
tries++;
var retry = function() {
setTimeout(scanForDevice, 1000);
};
if(sysinfo.net.ip && tries%3 == 0) {
if(sysinfo.net.ip == '0.0.0.0') {
retry();
return;
};
url = "http://" + sysinfo.net.ip;
} else if(sysinfo.hostname && tries%3 == 1) {
url = "http://" + sysinfo.hostname;
@@ -22,10 +31,10 @@
url = "";
}
if(console) console.log("Trying url " + url);
var retry = function() {
setTimeout(scanForDevice, 1000);
};
sysinfoStore.update(s => {
s.trying = url;
return s;
});
var xhr = new XMLHttpRequest();
xhr.timeout = 5000;
@@ -42,12 +51,10 @@
async function handleSubmit(e) {
loadingOrSaving = true;
const formData = new FormData(e.target);
let hostname = sysinfo.hostname;
const data = new URLSearchParams();
for (let field of formData) {
const [key, value] = field;
data.append(key, value)
if(key == 'sh') hostname = value;
}
const response = await fetch('/save', {
@@ -58,9 +65,15 @@
loadingOrSaving = false;
sysinfoStore.update(s => {
s.hostname = hostname;
s.hostname = formData.get('sh');
s.usrcfg = res.success;
s.booting = res.reboot;
if(staticIp) {
s.net.ip = formData.get('si');
s.net.mask = formData.get('su');
s.net.gw = formData.get('sg');
s.net.dns1 = formData.get('sd');
}
setTimeout(scanForDevice, 5000);
return s;
});
@@ -75,14 +88,14 @@
<strong class="text-sm">Setup</strong>
<div class="my-3">
SSID<br/>
<input name="ss" type="text" class="in-s"/>
<input name="ss" type="text" class="in-s" required/>
</div>
<div class="my-3">
PSK<br/>
<input name="sp" type="password" class="in-s" autocomplete="off"/>
</div>
<div>
Hostname:
Hostname
<input name="sh" bind:value={sysinfo.hostname} type="text" class="in-s" maxlength="32" pattern="[a-z0-9_-]+" placeholder="Optional, ex.: ams-reader" autocomplete="off"/>
</div>
<div class="my-3">
@@ -92,9 +105,7 @@
<div class="flex">
<input name="si" type="text" class="in-f w-full" required={staticIp}/>
<select name="su" class="in-l" required={staticIp}>
<option value="255.255.255.0">/24</option>
<option value="255.255.0.0">/16</option>
<option value="255.0.0.0">/8</option>
<SubnetOptions/>
</select>
</div>
{/if}

View File

@@ -0,0 +1,16 @@
<optgroup label="Most common is /24 (255.255.255.0)">
<option value="255.255.255.0">/24</option>
</optgroup>
<optgroup label="Smaller subnets">
<option value="255.255.255.128">/25</option>
<option value="255.255.255.192">/26</option>
<option value="255.255.255.224">/27</option>
<option value="255.255.255.240">/28</option>
<option value="255.255.255.248">/29</option>
</optgroup>
<optgroup label="Larger subnets">
<option value="255.255.254.0">/23</option>
<option value="255.255.252.0">/22</option>
<option value="255.255.0.0">/16</option>
</optgroup>

View File

@@ -6,6 +6,7 @@ static const char HEADER_LOCATION[] PROGMEM = "Location";
static const char CACHE_CONTROL_NO_CACHE[] PROGMEM = "no-cache, no-store, must-revalidate";
static const char CACHE_1HR[] PROGMEM = "public, max-age=3600";
static const char CACHE_1MO[] PROGMEM = "public, max-age=2592000";
static const char PRAGMA_NO_CACHE[] PROGMEM = "no-cache";
static const char EXPIRES_OFF[] PROGMEM = "-1";
static const char AUTHENTICATE_BASIC[] PROGMEM = "Basic realm=\"Secure Area\"";

View File

@@ -358,7 +358,6 @@ void AmsWebServer::dataJson() {
}
#endif
uint8_t hanStatus;
if(meterState->getLastError() != 0) {
hanStatus = 3;
@@ -407,7 +406,7 @@ void AmsWebServer::dataJson() {
snprintf_P(buf, BufferSize, DATA_JSON,
maxPwr == 0 ? meterState->isThreePhase() ? 20000 : 10000 : maxPwr,
meterConfig->productionCapacity,
meterConfig->mainFuse == 0 ? 32 : meterConfig->mainFuse,
meterConfig->mainFuse == 0 ? 40 : meterConfig->mainFuse,
meterState->getActiveImportPower(),
meterState->getActiveExportPower(),
meterState->getReactiveImportPower(),
@@ -454,7 +453,7 @@ void AmsWebServer::dataJson() {
ea->getCostThisMonth(),
ea->getProducedThisMonth(),
ea->getIncomeThisMonth(),
priceRegion.c_str(),
eapi == NULL ? "" : priceRegion.c_str(),
meterState->getLastError(),
eapi == NULL ? 0 : eapi->getLastError(),
(uint32_t) now,
@@ -734,7 +733,7 @@ void AmsWebServer::indexCss() {
if(!checkSecurity(2))
return;
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_1HR);
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_1MO);
server.setContentLength(INDEX_CSS_LEN);
server.send_P(200, MIME_CSS, INDEX_CSS);
}
@@ -745,7 +744,7 @@ void AmsWebServer::indexJs() {
if(!checkSecurity(2))
return;
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_1HR);
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_1MO);
server.setContentLength(INDEX_JS_LEN);
server.send_P(200, MIME_JS, INDEX_JS);
}
@@ -1047,16 +1046,18 @@ void AmsWebServer::handleSave() {
success = false;
}
#endif
config->setGpioConfig(*gpioConfig);
if(success) {
config->setGpioConfig(*gpioConfig);
SystemConfig sys;
config->getSystemConfig(sys);
sys.boardType = success ? boardType : 0xFF;
sys.vendorConfigured = success;
config->setSystemConfig(sys);
SystemConfig sys;
config->getSystemConfig(sys);
sys.boardType = success ? boardType : 0xFF;
sys.vendorConfigured = success;
config->setSystemConfig(sys);
}
}
if(server.hasArg(F("s")) && server.arg(F("s")) == F("true")) {
if(server.hasArg(F("s")) && server.arg(F("s")) == F("true") && server.hasArg(F("ss")) && !server.arg(F("ss")).isEmpty()) {
SystemConfig sys;
config->getSystemConfig(sys);

View File

@@ -122,6 +122,8 @@ DSMRParser *dsmrParser = NULL;
void setup() {
Serial.begin(115200);
config.hasConfig(); // Need to run this to make sure all configuration have been migrated before we load GPIO config
if(!config.getGpioConfig(gpioConfig)) {
config.clearGpio(gpioConfig);
}
@@ -598,8 +600,8 @@ void loop() {
meterState.setLastError(98);
}
try {
if(meterState.getLastError() > 0) {
if(now - meterAutodetectLastChange > 15000 && (meterConfig.baud == 0 || meterConfig.parity == 0)) {
if(meterState.getListType() == 0) {
if(now - meterAutodetectLastChange > 20000 && (meterConfig.baud == 0 || meterConfig.parity == 0)) {
meterAutodetect = true;
meterAutoIndex++; // Default is to try the first one in setup()
debugI("Meter serial autodetect, swapping to: %d, %d, %s", bauds[meterAutoIndex], parities[meterAutoIndex], inverts[meterAutoIndex] ? "true" : "false");
@@ -608,7 +610,6 @@ void loop() {
meterAutodetectLastChange = now;
}
} else if(meterAutodetect) {
meterAutoIndex--; // Last one worked, so lets use that
debugI("Meter serial autodetected, saving: %d, %d, %s", bauds[meterAutoIndex], parities[meterAutoIndex], inverts[meterAutoIndex] ? "true" : "false");
meterAutodetect = false;
meterConfig.baud = bauds[meterAutoIndex];