mirror of
https://github.com/UtilitechAS/amsreader-firmware.git
synced 2026-04-04 13:47:09 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de19de2129 | ||
|
|
1719263de0 | ||
|
|
e70b872c98 | ||
|
|
b7d28238ab | ||
|
|
6c9a8b0692 | ||
|
|
9f3dba3aab | ||
|
|
e4e4ad4107 | ||
|
|
e8fb9570bb | ||
|
|
bc42099962 | ||
|
|
be71cbe609 | ||
|
|
0d6df03c94 | ||
|
|
d777040c0a |
@@ -22,10 +22,6 @@
|
|||||||
#define CONFIG_NTP_START 872
|
#define CONFIG_NTP_START 872
|
||||||
#define CONFIG_MQTT_START 1004
|
#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
|
#define CONFIG_METER_START_93 224
|
||||||
|
|
||||||
|
|
||||||
@@ -37,18 +33,6 @@ struct SystemConfig {
|
|||||||
char country[3];
|
char country[3];
|
||||||
}; // 7
|
}; // 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 {
|
struct WiFiConfig {
|
||||||
char ssid[32];
|
char ssid[32];
|
||||||
char psk[64];
|
char psk[64];
|
||||||
@@ -65,18 +49,6 @@ struct WiFiConfig {
|
|||||||
bool autoreboot;
|
bool autoreboot;
|
||||||
}; // 213
|
}; // 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 {
|
struct MqttConfig {
|
||||||
char host[128];
|
char host[128];
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
@@ -110,7 +82,7 @@ struct MeterConfig {
|
|||||||
uint32_t accumulatedMultiplier;
|
uint32_t accumulatedMultiplier;
|
||||||
uint8_t source;
|
uint8_t source;
|
||||||
uint8_t parser;
|
uint8_t parser;
|
||||||
}; // 52
|
}; // 61
|
||||||
|
|
||||||
struct MeterConfig100 {
|
struct MeterConfig100 {
|
||||||
uint32_t baud;
|
uint32_t baud;
|
||||||
@@ -127,7 +99,7 @@ struct MeterConfig100 {
|
|||||||
uint32_t accumulatedMultiplier;
|
uint32_t accumulatedMultiplier;
|
||||||
uint8_t source;
|
uint8_t source;
|
||||||
uint8_t parser;
|
uint8_t parser;
|
||||||
}; // 50
|
}; // 59
|
||||||
|
|
||||||
struct MeterConfig95 {
|
struct MeterConfig95 {
|
||||||
uint32_t baud;
|
uint32_t baud;
|
||||||
@@ -146,16 +118,6 @@ struct MeterConfig95 {
|
|||||||
uint8_t parser;
|
uint8_t parser;
|
||||||
}; // 50
|
}; // 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 {
|
struct DebugConfig {
|
||||||
bool telnet;
|
bool telnet;
|
||||||
bool serial;
|
bool serial;
|
||||||
@@ -331,9 +293,6 @@ private:
|
|||||||
uint8_t tempSensorCount = 0;
|
uint8_t tempSensorCount = 0;
|
||||||
TempSensorConfig** tempSensors = NULL;
|
TempSensorConfig** tempSensors = NULL;
|
||||||
|
|
||||||
bool relocateConfig90(); // 2.0.0
|
|
||||||
bool relocateConfig91(); // 2.0.2
|
|
||||||
bool relocateConfig92(); // 2.0.3
|
|
||||||
bool relocateConfig93(); // 2.1.0
|
bool relocateConfig93(); // 2.1.0
|
||||||
bool relocateConfig94(); // 2.1.0
|
bool relocateConfig94(); // 2.1.0
|
||||||
bool relocateConfig95(); // 2.1.4
|
bool relocateConfig95(); // 2.1.4
|
||||||
|
|||||||
@@ -358,8 +358,9 @@ bool AmsConfiguration::pinUsed(uint8_t pin, GpioConfig& config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool AmsConfiguration::getGpioConfig(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.get(CONFIG_GPIO_START, config);
|
||||||
EEPROM.end();
|
EEPROM.end();
|
||||||
return true;
|
return true;
|
||||||
@@ -505,6 +506,7 @@ bool AmsConfiguration::getEntsoeConfig(EntsoeConfig& config) {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
clearEntsoe(config);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -560,6 +562,7 @@ bool AmsConfiguration::getEnergyAccountingConfig(EnergyAccountingConfig& config)
|
|||||||
if(config.hours > 5) config.hours = 5;
|
if(config.hours > 5) config.hours = 5;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
clearEnergyAccountingConfig(config);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -713,30 +716,6 @@ bool AmsConfiguration::hasConfig() {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch(configVersion) {
|
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:
|
case 93:
|
||||||
configVersion = -1; // Prevent loop
|
configVersion = -1; // Prevent loop
|
||||||
if(relocateConfig93()) {
|
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() {
|
bool AmsConfiguration::relocateConfig93() {
|
||||||
MeterConfig meter;
|
MeterConfig95 meter;
|
||||||
EEPROM.begin(EEPROM_SIZE);
|
EEPROM.begin(EEPROM_SIZE);
|
||||||
EEPROM.get(CONFIG_METER_START_93, meter);
|
EEPROM.get(CONFIG_METER_START_93, meter);
|
||||||
meter.wattageMultiplier = 0;
|
meter.wattageMultiplier = 0;
|
||||||
@@ -910,7 +836,7 @@ bool AmsConfiguration::relocateConfig94() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool AmsConfiguration::relocateConfig95() {
|
bool AmsConfiguration::relocateConfig95() {
|
||||||
MeterConfig meter;
|
MeterConfig95 meter;
|
||||||
MeterConfig95 meter95;
|
MeterConfig95 meter95;
|
||||||
EEPROM.begin(EEPROM_SIZE);
|
EEPROM.begin(EEPROM_SIZE);
|
||||||
EEPROM.get(CONFIG_METER_START, meter);
|
EEPROM.get(CONFIG_METER_START, meter);
|
||||||
@@ -931,7 +857,7 @@ bool AmsConfiguration::relocateConfig96() {
|
|||||||
SystemConfig sys;
|
SystemConfig sys;
|
||||||
EEPROM.get(CONFIG_SYSTEM_START, sys);
|
EEPROM.get(CONFIG_SYSTEM_START, sys);
|
||||||
|
|
||||||
MeterConfig meter;
|
MeterConfig100 meter;
|
||||||
EEPROM.get(CONFIG_METER_START, meter);
|
EEPROM.get(CONFIG_METER_START, meter);
|
||||||
meter.source = 1; // Serial
|
meter.source = 1; // Serial
|
||||||
meter.parser = 0; // Auto
|
meter.parser = 0; // Auto
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ protected:
|
|||||||
unsigned long lastList2 = 0;
|
unsigned long lastList2 = 0;
|
||||||
uint8_t listType = 0, meterType = AmsTypeUnknown;
|
uint8_t listType = 0, meterType = AmsTypeUnknown;
|
||||||
time_t packageTimestamp = 0;
|
time_t packageTimestamp = 0;
|
||||||
String listId, meterId, meterModel;
|
String listId = "", meterId = "", meterModel = "";
|
||||||
time_t meterTimestamp = 0;
|
time_t meterTimestamp = 0;
|
||||||
uint16_t activeImportPower = 0, reactiveImportPower = 0, activeExportPower = 0, reactiveExportPower = 0;
|
uint16_t activeImportPower = 0, reactiveImportPower = 0, activeExportPower = 0, reactiveExportPower = 0;
|
||||||
float l1voltage = 0, l2voltage = 0, l3voltage = 0, l1current = 0, l2current = 0, l3current = 0;
|
float l1voltage = 0, l2voltage = 0, l3voltage = 0, l1current = 0, l2current = 0, l3current = 0;
|
||||||
|
|||||||
@@ -89,8 +89,8 @@ private:
|
|||||||
EnergyAccountingConfig *config = NULL;
|
EnergyAccountingConfig *config = NULL;
|
||||||
Timezone *tz = NULL;
|
Timezone *tz = NULL;
|
||||||
uint8_t currentHour = 0, currentDay = 0, currentThresholdIdx = 0;
|
uint8_t currentHour = 0, currentDay = 0, currentThresholdIdx = 0;
|
||||||
double use, costHour, costDay;
|
double use = 0, costHour = 0, costDay = 0;
|
||||||
double produce, incomeHour, incomeDay;
|
double produce = 0, incomeHour = 0, incomeDay = 0;
|
||||||
EnergyAccountingData data = { 0, 0, 0, 0, 0, 0 };
|
EnergyAccountingData data = { 0, 0, 0, 0, 0, 0 };
|
||||||
|
|
||||||
void calcDayCost();
|
void calcDayCost();
|
||||||
|
|||||||
@@ -185,7 +185,8 @@ double EnergyAccounting::getUseThisHour() {
|
|||||||
double EnergyAccounting::getUseToday() {
|
double EnergyAccounting::getUseToday() {
|
||||||
float ret = 0.0;
|
float ret = 0.0;
|
||||||
time_t now = time(nullptr);
|
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;
|
tmElements_t utc, local;
|
||||||
breakTime(tz->toLocal(now), local);
|
breakTime(tz->toLocal(now), local);
|
||||||
for(int i = 0; i < currentHour; i++) {
|
for(int i = 0; i < currentHour; i++) {
|
||||||
@@ -197,7 +198,7 @@ double EnergyAccounting::getUseToday() {
|
|||||||
|
|
||||||
double EnergyAccounting::getUseThisMonth() {
|
double EnergyAccounting::getUseThisMonth() {
|
||||||
time_t now = time(nullptr);
|
time_t now = time(nullptr);
|
||||||
if(now < BUILD_EPOCH) return 0;
|
if(now < BUILD_EPOCH) return 0.0;
|
||||||
float ret = 0;
|
float ret = 0;
|
||||||
for(int i = 0; i < currentDay; i++) {
|
for(int i = 0; i < currentDay; i++) {
|
||||||
ret += ds->getDayImport(i) / 1000.0;
|
ret += ds->getDayImport(i) / 1000.0;
|
||||||
@@ -212,7 +213,7 @@ double EnergyAccounting::getProducedThisHour() {
|
|||||||
double EnergyAccounting::getProducedToday() {
|
double EnergyAccounting::getProducedToday() {
|
||||||
float ret = 0.0;
|
float ret = 0.0;
|
||||||
time_t now = time(nullptr);
|
time_t now = time(nullptr);
|
||||||
if(now < BUILD_EPOCH) return 0;
|
if(now < BUILD_EPOCH) return 0.0;
|
||||||
tmElements_t utc;
|
tmElements_t utc;
|
||||||
for(int i = 0; i < currentHour; i++) {
|
for(int i = 0; i < currentHour; i++) {
|
||||||
breakTime(now - ((currentHour - i) * 3600), utc);
|
breakTime(now - ((currentHour - i) * 3600), utc);
|
||||||
@@ -223,7 +224,7 @@ double EnergyAccounting::getProducedToday() {
|
|||||||
|
|
||||||
double EnergyAccounting::getProducedThisMonth() {
|
double EnergyAccounting::getProducedThisMonth() {
|
||||||
time_t now = time(nullptr);
|
time_t now = time(nullptr);
|
||||||
if(now < BUILD_EPOCH) return 0;
|
if(now < BUILD_EPOCH) return 0.0;
|
||||||
float ret = 0;
|
float ret = 0;
|
||||||
for(int i = 0; i < currentDay; i++) {
|
for(int i = 0; i < currentDay; i++) {
|
||||||
ret += ds->getDayExport(i) / 1000.0;
|
ret += ds->getDayExport(i) / 1000.0;
|
||||||
@@ -279,6 +280,8 @@ uint8_t EnergyAccounting::getCurrentThreshold() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
float EnergyAccounting::getMonthMax() {
|
float EnergyAccounting::getMonthMax() {
|
||||||
|
if(config == NULL)
|
||||||
|
return 0.0;
|
||||||
uint8_t count = 0;
|
uint8_t count = 0;
|
||||||
uint32_t maxHour = 0.0;
|
uint32_t maxHour = 0.0;
|
||||||
bool included[5] = { false, false, false, false, false };
|
bool included[5] = { false, false, false, false, false };
|
||||||
@@ -308,6 +311,8 @@ float EnergyAccounting::getMonthMax() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
EnergyAccountingPeak EnergyAccounting::getPeak(uint8_t num) {
|
EnergyAccountingPeak EnergyAccounting::getPeak(uint8_t num) {
|
||||||
|
if(config == NULL)
|
||||||
|
return EnergyAccountingPeak({0,0});
|
||||||
if(num < 1 || num > 5) return EnergyAccountingPeak({0,0});
|
if(num < 1 || num > 5) return EnergyAccountingPeak({0,0});
|
||||||
|
|
||||||
uint8_t count = 0;
|
uint8_t count = 0;
|
||||||
@@ -362,7 +367,9 @@ bool EnergyAccounting::load() {
|
|||||||
} else if(buf[0] == 4) {
|
} else if(buf[0] == 4) {
|
||||||
EnergyAccountingData4* data = (EnergyAccountingData4*) buf;
|
EnergyAccountingData4* data = (EnergyAccountingData4*) buf;
|
||||||
this->data = { 5, data->month,
|
this->data = { 5, data->month,
|
||||||
(uint16_t) (data->costYesterday / 10), (uint16_t) (data->costThisMonth / 100), (uint16_t) (data->costLastMonth / 100),
|
data->costYesterday,
|
||||||
|
data->costThisMonth,
|
||||||
|
data->costLastMonth,
|
||||||
0,0,0, // Income from production
|
0,0,0, // Income from production
|
||||||
data->peaks[0].day, data->peaks[0].value,
|
data->peaks[0].day, data->peaks[0].value,
|
||||||
data->peaks[1].day, data->peaks[1].value,
|
data->peaks[1].day, data->peaks[1].value,
|
||||||
|
|||||||
@@ -64,6 +64,10 @@
|
|||||||
{#if sysinfo.upgrading}
|
{#if sysinfo.upgrading}
|
||||||
<Mask active=true message="Device is upgrading, please wait"/>
|
<Mask active=true message="Device is upgrading, please wait"/>
|
||||||
{:else if sysinfo.booting}
|
{: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}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,54 +1,79 @@
|
|||||||
<script>
|
<script>
|
||||||
export let data;
|
export let data;
|
||||||
export let currency;
|
export let currency;
|
||||||
|
export let hasExport;
|
||||||
let hasExport = data && (data.om || data.e > 0);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="mx-2 text-sm">
|
<div class="mx-2 text-sm">
|
||||||
<strong>Real time calculation</strong>
|
<strong>Real time calculation</strong>
|
||||||
|
<br/><br/>
|
||||||
{#if data && data.h !== undefined}
|
|
||||||
<div class="flex">
|
{#if data}
|
||||||
<div>Hour</div>
|
{#if hasExport && currency}
|
||||||
<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>
|
<strong>Import</strong>
|
||||||
</div>
|
<div class="grid grid-cols-3 mb-3">
|
||||||
<div class="flex">
|
<div>Hour</div>
|
||||||
<div>Day</div>
|
<div class="text-right">{data.h.u ? data.h.u.toFixed(2) : '-'} kWh</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 class="text-right">{data.h.c ? data.h.c.toFixed(2) : '-'} {currency}</div>
|
||||||
</div>
|
<div>Day</div>
|
||||||
<div class="flex">
|
<div class="text-right">{data.d.u ? data.d.u.toFixed(1) : '-'} kWh</div>
|
||||||
<div>Month</div>
|
<div class="text-right">{data.d.c ? data.d.c.toFixed(1) : '-'} {currency}</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>Month</div>
|
||||||
</div>
|
<div class="text-right">{data.m.u ? data.m.u.toFixed(0) : '-'} kWh</div>
|
||||||
<div class="mt-4">
|
<div class="text-right">{data.m.c ? data.m.c.toFixed(0) : '-'} {currency}</div>
|
||||||
{#if hasExport}
|
</div>
|
||||||
<div class="flex">
|
<strong>Export</strong>
|
||||||
<div>Hour</div>
|
<div class="grid grid-cols-3">
|
||||||
<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>Hour</div>
|
||||||
</div>
|
<div class="text-right">{data.h.p ? data.h.p.toFixed(2) : '-'} kWh</div>
|
||||||
<div class="flex">
|
<div class="text-right">{data.h.i ? data.h.i.toFixed(2) : '-'} {currency}</div>
|
||||||
<div>Day</div>
|
<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 class="text-right">{data.d.p ? data.d.p.toFixed(1) : '-'} kWh</div>
|
||||||
</div>
|
<div class="text-right">{data.d.i ? data.d.i.toFixed(1) : '-'} {currency}</div>
|
||||||
<div class="flex">
|
<div>Month</div>
|
||||||
<div>Month</div>
|
<div class="text-right">{data.m.p ? data.m.p.toFixed(0) : '-'} kWh</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 class="text-right">{data.m.i ? data.m.i.toFixed(0) : '-'} {currency}</div>
|
||||||
</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}
|
{:else}
|
||||||
<div class="flex">
|
<strong>Consumption</strong>
|
||||||
<div>Hour</div>
|
<div class="grid grid-cols-2 mb-3">
|
||||||
<div class="flex-auto text-right">{data.h.c ? data.h.c.toFixed(2) : '-'} {currency}</div>
|
<div>Hour</div>
|
||||||
</div>
|
<div class="text-right">{data.h.u ? data.h.u.toFixed(2) : '-'} kWh</div>
|
||||||
<div class="flex">
|
<div>Day</div>
|
||||||
<div>Day</div>
|
<div class="text-right">{data.d.u ? data.d.u.toFixed(1) : '-'} kWh</div>
|
||||||
<div class="flex-auto text-right">{data.d.c ? data.d.c.toFixed(2) : '-'} {currency}</div>
|
<div>Month</div>
|
||||||
</div>
|
<div class="text-right">{data.m.u ? data.m.u.toFixed(0) : '-'} kWh</div>
|
||||||
<div class="flex">
|
</div>
|
||||||
<div>Month</div>
|
{#if currency}
|
||||||
<div class="flex-auto text-right">{data.m.c ? data.m.c.toFixed(2) : '-'} {currency}</div>
|
<strong>Cost</strong>
|
||||||
</div>
|
<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}
|
{/if}
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
import HelpIcon from './HelpIcon.svelte';
|
import HelpIcon from './HelpIcon.svelte';
|
||||||
import CountrySelectOptions from './CountrySelectOptions.svelte';
|
import CountrySelectOptions from './CountrySelectOptions.svelte';
|
||||||
import { Link, navigate } from 'svelte-navigator';
|
import { Link, navigate } from 'svelte-navigator';
|
||||||
|
import SubnetOptions from './SubnetOptions.svelte';
|
||||||
|
|
||||||
|
|
||||||
export let sysinfo = {}
|
export let sysinfo = {}
|
||||||
@@ -67,8 +68,10 @@
|
|||||||
getConfiguration();
|
getConfiguration();
|
||||||
|
|
||||||
let isFactoryReset = false;
|
let isFactoryReset = false;
|
||||||
|
let isFactoryResetComplete = false;
|
||||||
async function factoryReset() {
|
async function factoryReset() {
|
||||||
if(confirm("Are you sure you want to factory reset the device?")) {
|
if(confirm("Are you sure you want to factory reset the device?")) {
|
||||||
|
isFactoryReset = true;
|
||||||
const data = new URLSearchParams();
|
const data = new URLSearchParams();
|
||||||
data.append("perform", "true");
|
data.append("perform", "true");
|
||||||
const response = await fetch('/reset', {
|
const response = await fetch('/reset', {
|
||||||
@@ -76,7 +79,8 @@
|
|||||||
body: data
|
body: data
|
||||||
});
|
});
|
||||||
let res = (await response.json());
|
let res = (await response.json());
|
||||||
isFactoryReset = res.success;
|
isFactoryReset = false;
|
||||||
|
isFactoryResetComplete = res.success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,11 +366,9 @@
|
|||||||
<option value="dhcp">DHCP</option>
|
<option value="dhcp">DHCP</option>
|
||||||
<option value="static">Static</option>
|
<option value="static">Static</option>
|
||||||
</select>
|
</select>
|
||||||
<input name="ni" bind:value={configuration.n.i} type="text" class="in-m w-full" disabled={configuration.n.m == 'dhcp'}/>
|
<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'}>
|
<select name="ns" bind:value={configuration.n.s} class="in-l" disabled={configuration.n.m == 'dhcp'} required={configuration.n.m == 'static'}>
|
||||||
<option value="255.255.255.0">/24</option>
|
<SubnetOptions/>
|
||||||
<option value="255.255.0.0">/16</option>
|
|
||||||
<option value="255.0.0.0">/8</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -765,4 +767,5 @@
|
|||||||
|
|
||||||
<Mask active={loading} message="Loading configuration"/>
|
<Mask active={loading} message="Loading configuration"/>
|
||||||
<Mask active={saving} message="Saving 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"/>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import { pricesStore, dayPlotStore, monthPlotStore, temperaturesStore } from './DataStores.js';
|
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 PowerGauge from './PowerGauge.svelte';
|
||||||
import VoltPlot from './VoltPlot.svelte';
|
import VoltPlot from './VoltPlot.svelte';
|
||||||
import AmpPlot from './AmpPlot.svelte';
|
import AmpPlot from './AmpPlot.svelte';
|
||||||
@@ -32,23 +32,23 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</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)}
|
{#if uiVisibility(sysinfo.ui.i, data.i)}
|
||||||
<div class="cnt">
|
<div class="cnt">
|
||||||
<div class="grid grid-cols-2">
|
<div class="grid grid-cols-2">
|
||||||
<div class="col-span-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}/>
|
<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>
|
||||||
<div>{data.mt ? metertype(data.mt) : '-'}</div>
|
|
||||||
<div class="text-right">{data.ic ? data.ic.toFixed(1) : '-'} kWh</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
{#if uiVisibility(sysinfo.ui.e, data.om || data.e > 0)}
|
{#if uiVisibility(sysinfo.ui.e, data.om || data.e > 0)}
|
||||||
<div class="cnt">
|
<div class="cnt">
|
||||||
<div class="grid grid-cols-2">
|
<div class="grid grid-cols-2">
|
||||||
<div class="col-span-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>
|
<div></div>
|
||||||
<div class="text-right">{data.ec ? data.ec.toFixed(1) : '-'} kWh</div>
|
<div class="text-right">{data.ec ? data.ec.toFixed(1) : '-'} kWh</div>
|
||||||
@@ -56,48 +56,48 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if uiVisibility(sysinfo.ui.v, data.u1 > 100 || data.u2 > 100 || data.u3 > 100)}
|
{#if uiVisibility(sysinfo.ui.v, data.u1 > 100 || data.u2 > 100 || data.u3 > 100)}
|
||||||
<div class="cnt">
|
<div class="cnt">
|
||||||
<VoltPlot u1={data.u1} u2={data.u2} u3={data.u3} ds={data.ds}/>
|
<VoltPlot u1={data.u1} u2={data.u2} u3={data.u3} ds={data.ds}/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if uiVisibility(sysinfo.ui.a, data.i1 > 0.01 || data.i2 > 0.01 || data.i3 > 0.01)}
|
{#if uiVisibility(sysinfo.ui.a, data.i1 > 0.01 || data.i2 > 0.01 || data.i3 > 0.01)}
|
||||||
<div class="cnt">
|
<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}/>
|
<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>
|
||||||
{/if}
|
{/if}
|
||||||
{#if uiVisibility(sysinfo.ui.r, data.ri > 0 || data.re > 0 || data.ric > 0 || data.rec > 0)}
|
{#if uiVisibility(sysinfo.ui.r, data.ri > 0 || data.re > 0 || data.ric > 0 || data.rec > 0)}
|
||||||
<div class="cnt">
|
<div class="cnt">
|
||||||
<ReactiveData importInstant={data.ri} exportInstant={data.re} importTotal={data.ric} exportTotal={data.rec}/>
|
<ReactiveData importInstant={data.ri} exportInstant={data.re} importTotal={data.ric} exportTotal={data.rec}/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if uiVisibility(sysinfo.ui.c, data.ea)}
|
{#if uiVisibility(sysinfo.ui.c, data.ea)}
|
||||||
<div class="cnt">
|
<div class="cnt">
|
||||||
<AccountingData data={data.ea} currency={prices.currency}/>
|
<AccountingData data={data.ea} currency={prices.currency} hasExport={data.om > 0 || data.e > 0}/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if data && data.pr && (data.pr.startsWith("10YNO") || data.pr == '10Y1001A1001A48H')}
|
{#if uiVisibility(sysinfo.ui.t, data.pr && (data.pr.startsWith("10YNO") || data.pr == '10Y1001A1001A48H'))}
|
||||||
<div class="cnt h-64">
|
<div class="cnt h-64">
|
||||||
<TariffPeakChart />
|
<TariffPeakChart />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if uiVisibility(sysinfo.ui.p, (typeof data.p == "number") && !Number.isNaN(data.p))}
|
{#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}/>
|
<PricePlot json={prices}/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if uiVisibility(sysinfo.ui.d, dayPlot)}
|
{#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">
|
<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} />
|
<DayPlot json={dayPlot} />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if uiVisibility(sysinfo.ui.m, monthPlot)}
|
{#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">
|
<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} />
|
<MonthPlot json={monthPlot} />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if uiVisibility(sysinfo.ui.s, data.t && data.t != -127 && temperatures.c > 1)}
|
{#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">
|
<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} />
|
<TemperaturePlot json={temperatures} />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@@ -25,7 +25,8 @@ let sysinfo = {
|
|||||||
booting: false,
|
booting: false,
|
||||||
upgrading: false,
|
upgrading: false,
|
||||||
ui: {},
|
ui: {},
|
||||||
security: 0
|
security: 0,
|
||||||
|
trying: null
|
||||||
};
|
};
|
||||||
export const sysinfoStore = writable(sysinfo);
|
export const sysinfoStore = writable(sysinfo);
|
||||||
export async function getSysinfo() {
|
export async function getSysinfo() {
|
||||||
|
|||||||
@@ -15,6 +15,13 @@ export function ampcol(pct) {
|
|||||||
else return '#32d900';
|
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) {
|
export function metertype(mt) {
|
||||||
switch(mt) {
|
switch(mt) {
|
||||||
case 1:
|
case 1:
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import PowerGaugeSvg from './PowerGaugeSvg.svelte';
|
import PowerGaugeSvg from './PowerGaugeSvg.svelte';
|
||||||
import { ampcol } from './Helpers.js';
|
|
||||||
|
|
||||||
export let val;
|
export let val;
|
||||||
export let max;
|
export let max;
|
||||||
@@ -8,10 +7,16 @@
|
|||||||
export let label;
|
export let label;
|
||||||
export let sub = "";
|
export let sub = "";
|
||||||
export let subunit = "";
|
export let subunit = "";
|
||||||
|
export let colorFn;
|
||||||
|
|
||||||
|
let pct = 0;
|
||||||
|
$: {
|
||||||
|
pct = (Math.min(val,max)/max) * 100
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="pl-root">
|
<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-ov">
|
||||||
<span class="pl-lab">{label}</span>
|
<span class="pl-lab">{label}</span>
|
||||||
<br/>
|
<br/>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { sysinfoStore } from './DataStores.js';
|
import { sysinfoStore } from './DataStores.js';
|
||||||
import Mask from './Mask.svelte'
|
import Mask from './Mask.svelte'
|
||||||
|
import SubnetOptions from './SubnetOptions.svelte';
|
||||||
|
|
||||||
export let sysinfo = {}
|
export let sysinfo = {}
|
||||||
|
|
||||||
@@ -12,7 +13,15 @@
|
|||||||
var url = "";
|
var url = "";
|
||||||
tries++;
|
tries++;
|
||||||
|
|
||||||
|
var retry = function() {
|
||||||
|
setTimeout(scanForDevice, 1000);
|
||||||
|
};
|
||||||
|
|
||||||
if(sysinfo.net.ip && tries%3 == 0) {
|
if(sysinfo.net.ip && tries%3 == 0) {
|
||||||
|
if(sysinfo.net.ip == '0.0.0.0') {
|
||||||
|
retry();
|
||||||
|
return;
|
||||||
|
};
|
||||||
url = "http://" + sysinfo.net.ip;
|
url = "http://" + sysinfo.net.ip;
|
||||||
} else if(sysinfo.hostname && tries%3 == 1) {
|
} else if(sysinfo.hostname && tries%3 == 1) {
|
||||||
url = "http://" + sysinfo.hostname;
|
url = "http://" + sysinfo.hostname;
|
||||||
@@ -22,10 +31,10 @@
|
|||||||
url = "";
|
url = "";
|
||||||
}
|
}
|
||||||
if(console) console.log("Trying url " + url);
|
if(console) console.log("Trying url " + url);
|
||||||
|
sysinfoStore.update(s => {
|
||||||
var retry = function() {
|
s.trying = url;
|
||||||
setTimeout(scanForDevice, 1000);
|
return s;
|
||||||
};
|
});
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
xhr.timeout = 5000;
|
xhr.timeout = 5000;
|
||||||
@@ -42,12 +51,10 @@
|
|||||||
async function handleSubmit(e) {
|
async function handleSubmit(e) {
|
||||||
loadingOrSaving = true;
|
loadingOrSaving = true;
|
||||||
const formData = new FormData(e.target);
|
const formData = new FormData(e.target);
|
||||||
let hostname = sysinfo.hostname;
|
|
||||||
const data = new URLSearchParams();
|
const data = new URLSearchParams();
|
||||||
for (let field of formData) {
|
for (let field of formData) {
|
||||||
const [key, value] = field;
|
const [key, value] = field;
|
||||||
data.append(key, value)
|
data.append(key, value)
|
||||||
if(key == 'sh') hostname = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch('/save', {
|
const response = await fetch('/save', {
|
||||||
@@ -58,9 +65,15 @@
|
|||||||
loadingOrSaving = false;
|
loadingOrSaving = false;
|
||||||
|
|
||||||
sysinfoStore.update(s => {
|
sysinfoStore.update(s => {
|
||||||
s.hostname = hostname;
|
s.hostname = formData.get('sh');
|
||||||
s.usrcfg = res.success;
|
s.usrcfg = res.success;
|
||||||
s.booting = res.reboot;
|
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);
|
setTimeout(scanForDevice, 5000);
|
||||||
return s;
|
return s;
|
||||||
});
|
});
|
||||||
@@ -75,14 +88,14 @@
|
|||||||
<strong class="text-sm">Setup</strong>
|
<strong class="text-sm">Setup</strong>
|
||||||
<div class="my-3">
|
<div class="my-3">
|
||||||
SSID<br/>
|
SSID<br/>
|
||||||
<input name="ss" type="text" class="in-s"/>
|
<input name="ss" type="text" class="in-s" required/>
|
||||||
</div>
|
</div>
|
||||||
<div class="my-3">
|
<div class="my-3">
|
||||||
PSK<br/>
|
PSK<br/>
|
||||||
<input name="sp" type="password" class="in-s" autocomplete="off"/>
|
<input name="sp" type="password" class="in-s" autocomplete="off"/>
|
||||||
</div>
|
</div>
|
||||||
<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"/>
|
<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>
|
||||||
<div class="my-3">
|
<div class="my-3">
|
||||||
@@ -92,9 +105,7 @@
|
|||||||
<div class="flex">
|
<div class="flex">
|
||||||
<input name="si" type="text" class="in-f w-full" required={staticIp}/>
|
<input name="si" type="text" class="in-f w-full" required={staticIp}/>
|
||||||
<select name="su" class="in-l" required={staticIp}>
|
<select name="su" class="in-l" required={staticIp}>
|
||||||
<option value="255.255.255.0">/24</option>
|
<SubnetOptions/>
|
||||||
<option value="255.255.0.0">/16</option>
|
|
||||||
<option value="255.0.0.0">/8</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
16
lib/SvelteUi/app/src/lib/SubnetOptions.svelte
Normal file
16
lib/SvelteUi/app/src/lib/SubnetOptions.svelte
Normal 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>
|
||||||
|
|
||||||
@@ -31,7 +31,9 @@
|
|||||||
<option value={13}>GPIO13</option>
|
<option value={13}>GPIO13</option>
|
||||||
<option value={14}>GPIO14</option>
|
<option value={14}>GPIO14</option>
|
||||||
<option value={15}>GPIO15</option>
|
<option value={15}>GPIO15</option>
|
||||||
|
{#if chip == 'esp32s2'}
|
||||||
|
<option value={16}>GPIO16</option>
|
||||||
|
{/if}
|
||||||
{#if chip.startsWith('esp32')}
|
{#if chip.startsWith('esp32')}
|
||||||
<option value={17}>GPIO17</option>
|
<option value={17}>GPIO17</option>
|
||||||
{#if chip != 'esp32s2'}
|
{#if chip != 'esp32s2'}
|
||||||
|
|||||||
@@ -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_CONTROL_NO_CACHE[] PROGMEM = "no-cache, no-store, must-revalidate";
|
||||||
static const char CACHE_1HR[] PROGMEM = "public, max-age=3600";
|
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 PRAGMA_NO_CACHE[] PROGMEM = "no-cache";
|
||||||
static const char EXPIRES_OFF[] PROGMEM = "-1";
|
static const char EXPIRES_OFF[] PROGMEM = "-1";
|
||||||
static const char AUTHENTICATE_BASIC[] PROGMEM = "Basic realm=\"Secure Area\"";
|
static const char AUTHENTICATE_BASIC[] PROGMEM = "Basic realm=\"Secure Area\"";
|
||||||
|
|||||||
@@ -241,10 +241,12 @@ void AmsWebServer::sysinfoJson() {
|
|||||||
config->getUiConfig(ui);
|
config->getUiConfig(ui);
|
||||||
|
|
||||||
String meterModel = meterState->getMeterModel();
|
String meterModel = meterState->getMeterModel();
|
||||||
meterModel.replace("\\", "\\\\");
|
if(!meterModel.isEmpty())
|
||||||
|
meterModel.replace("\\", "\\\\");
|
||||||
|
|
||||||
String meterId = meterState->getMeterId();
|
String meterId = meterState->getMeterId();
|
||||||
meterId.replace("\\", "\\\\");
|
if(!meterId.isEmpty())
|
||||||
|
meterId.replace("\\", "\\\\");
|
||||||
|
|
||||||
int size = snprintf_P(buf, BufferSize, SYSINFO_JSON,
|
int size = snprintf_P(buf, BufferSize, SYSINFO_JSON,
|
||||||
VERSION,
|
VERSION,
|
||||||
@@ -356,7 +358,6 @@ void AmsWebServer::dataJson() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
uint8_t hanStatus;
|
uint8_t hanStatus;
|
||||||
if(meterState->getLastError() != 0) {
|
if(meterState->getLastError() != 0) {
|
||||||
hanStatus = 3;
|
hanStatus = 3;
|
||||||
@@ -405,7 +406,7 @@ void AmsWebServer::dataJson() {
|
|||||||
snprintf_P(buf, BufferSize, DATA_JSON,
|
snprintf_P(buf, BufferSize, DATA_JSON,
|
||||||
maxPwr == 0 ? meterState->isThreePhase() ? 20000 : 10000 : maxPwr,
|
maxPwr == 0 ? meterState->isThreePhase() ? 20000 : 10000 : maxPwr,
|
||||||
meterConfig->productionCapacity,
|
meterConfig->productionCapacity,
|
||||||
meterConfig->mainFuse == 0 ? 32 : meterConfig->mainFuse,
|
meterConfig->mainFuse == 0 ? 40 : meterConfig->mainFuse,
|
||||||
meterState->getActiveImportPower(),
|
meterState->getActiveImportPower(),
|
||||||
meterState->getActiveExportPower(),
|
meterState->getActiveExportPower(),
|
||||||
meterState->getReactiveImportPower(),
|
meterState->getReactiveImportPower(),
|
||||||
@@ -452,7 +453,7 @@ void AmsWebServer::dataJson() {
|
|||||||
ea->getCostThisMonth(),
|
ea->getCostThisMonth(),
|
||||||
ea->getProducedThisMonth(),
|
ea->getProducedThisMonth(),
|
||||||
ea->getIncomeThisMonth(),
|
ea->getIncomeThisMonth(),
|
||||||
priceRegion.c_str(),
|
eapi == NULL ? "" : priceRegion.c_str(),
|
||||||
meterState->getLastError(),
|
meterState->getLastError(),
|
||||||
eapi == NULL ? 0 : eapi->getLastError(),
|
eapi == NULL ? 0 : eapi->getLastError(),
|
||||||
(uint32_t) now,
|
(uint32_t) now,
|
||||||
@@ -732,7 +733,7 @@ void AmsWebServer::indexCss() {
|
|||||||
if(!checkSecurity(2))
|
if(!checkSecurity(2))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_1HR);
|
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_1MO);
|
||||||
server.setContentLength(INDEX_CSS_LEN);
|
server.setContentLength(INDEX_CSS_LEN);
|
||||||
server.send_P(200, MIME_CSS, INDEX_CSS);
|
server.send_P(200, MIME_CSS, INDEX_CSS);
|
||||||
}
|
}
|
||||||
@@ -743,7 +744,7 @@ void AmsWebServer::indexJs() {
|
|||||||
if(!checkSecurity(2))
|
if(!checkSecurity(2))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_1HR);
|
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_1MO);
|
||||||
server.setContentLength(INDEX_JS_LEN);
|
server.setContentLength(INDEX_JS_LEN);
|
||||||
server.send_P(200, MIME_JS, INDEX_JS);
|
server.send_P(200, MIME_JS, INDEX_JS);
|
||||||
}
|
}
|
||||||
@@ -1045,16 +1046,18 @@ void AmsWebServer::handleSave() {
|
|||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
config->setGpioConfig(*gpioConfig);
|
if(success) {
|
||||||
|
config->setGpioConfig(*gpioConfig);
|
||||||
|
|
||||||
SystemConfig sys;
|
SystemConfig sys;
|
||||||
config->getSystemConfig(sys);
|
config->getSystemConfig(sys);
|
||||||
sys.boardType = success ? boardType : 0xFF;
|
sys.boardType = success ? boardType : 0xFF;
|
||||||
sys.vendorConfigured = success;
|
sys.vendorConfigured = success;
|
||||||
config->setSystemConfig(sys);
|
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;
|
SystemConfig sys;
|
||||||
config->getSystemConfig(sys);
|
config->getSystemConfig(sys);
|
||||||
|
|
||||||
|
|||||||
@@ -122,6 +122,8 @@ DSMRParser *dsmrParser = NULL;
|
|||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
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)) {
|
if(!config.getGpioConfig(gpioConfig)) {
|
||||||
config.clearGpio(gpioConfig);
|
config.clearGpio(gpioConfig);
|
||||||
}
|
}
|
||||||
@@ -577,13 +579,14 @@ void loop() {
|
|||||||
if(readHanPort() || now - meterState.getLastUpdateMillis() > 30000) {
|
if(readHanPort() || now - meterState.getLastUpdateMillis() > 30000) {
|
||||||
if(now - lastTemperatureRead > 15000) {
|
if(now - lastTemperatureRead > 15000) {
|
||||||
unsigned long start = millis();
|
unsigned long start = millis();
|
||||||
hw.updateTemperatures();
|
if(hw.updateTemperatures()) {
|
||||||
lastTemperatureRead = now;
|
lastTemperatureRead = now;
|
||||||
|
|
||||||
if(mqtt != NULL && mqttHandler != NULL && WiFi.getMode() != WIFI_AP && WiFi.status() == WL_CONNECTED && mqtt->connected() && !topic.isEmpty()) {
|
if(mqtt != NULL && mqttHandler != NULL && WiFi.getMode() != WIFI_AP && WiFi.status() == WL_CONNECTED && mqtt->connected() && !topic.isEmpty()) {
|
||||||
mqttHandler->publishTemperatures(&config, &hw);
|
mqttHandler->publishTemperatures(&config, &hw);
|
||||||
|
}
|
||||||
|
debugD("Used %ld ms to update temperature", millis()-start);
|
||||||
}
|
}
|
||||||
debugD("Used %ld ms to update temperature", millis()-start);
|
|
||||||
}
|
}
|
||||||
if(now - lastSysupdate > 60000) {
|
if(now - lastSysupdate > 60000) {
|
||||||
if(mqtt != NULL && mqttHandler != NULL && WiFi.getMode() != WIFI_AP && WiFi.status() == WL_CONNECTED && mqtt->connected() && !topic.isEmpty()) {
|
if(mqtt != NULL && mqttHandler != NULL && WiFi.getMode() != WIFI_AP && WiFi.status() == WL_CONNECTED && mqtt->connected() && !topic.isEmpty()) {
|
||||||
@@ -597,8 +600,8 @@ void loop() {
|
|||||||
meterState.setLastError(98);
|
meterState.setLastError(98);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if(meterState.getLastError() > 0) {
|
if(meterState.getListType() == 0) {
|
||||||
if(now - meterAutodetectLastChange > 15000 && (meterConfig.baud == 0 || meterConfig.parity == 0)) {
|
if(now - meterAutodetectLastChange > 20000 && (meterConfig.baud == 0 || meterConfig.parity == 0)) {
|
||||||
meterAutodetect = true;
|
meterAutodetect = true;
|
||||||
meterAutoIndex++; // Default is to try the first one in setup()
|
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");
|
debugI("Meter serial autodetect, swapping to: %d, %d, %s", bauds[meterAutoIndex], parities[meterAutoIndex], inverts[meterAutoIndex] ? "true" : "false");
|
||||||
@@ -607,7 +610,6 @@ void loop() {
|
|||||||
meterAutodetectLastChange = now;
|
meterAutodetectLastChange = now;
|
||||||
}
|
}
|
||||||
} else if(meterAutodetect) {
|
} 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");
|
debugI("Meter serial autodetected, saving: %d, %d, %s", bauds[meterAutoIndex], parities[meterAutoIndex], inverts[meterAutoIndex] ? "true" : "false");
|
||||||
meterAutodetect = false;
|
meterAutodetect = false;
|
||||||
meterConfig.baud = bauds[meterAutoIndex];
|
meterConfig.baud = bauds[meterAutoIndex];
|
||||||
@@ -862,9 +864,9 @@ bool readHanPort() {
|
|||||||
pos = unwrapData((uint8_t *) hanBuffer, ctx);
|
pos = unwrapData((uint8_t *) hanBuffer, ctx);
|
||||||
if(ctx.type > 0 && pos >= 0) {
|
if(ctx.type > 0 && pos >= 0) {
|
||||||
if(ctx.type == DATA_TAG_DLMS) {
|
if(ctx.type == DATA_TAG_DLMS) {
|
||||||
debugV("Received valid DLMS at %d", pos);
|
debugD("Received valid DLMS at %d", pos);
|
||||||
} else if(ctx.type == DATA_TAG_DSMR) {
|
} else if(ctx.type == DATA_TAG_DSMR) {
|
||||||
debugV("Received valid DSMR at %d", pos);
|
debugD("Received valid DSMR at %d", pos);
|
||||||
} else {
|
} else {
|
||||||
// TODO: Move this so that payload is sent to MQTT
|
// TODO: Move this so that payload is sent to MQTT
|
||||||
debugE("Unknown tag %02X at pos %d", ctx.type, pos);
|
debugE("Unknown tag %02X at pos %d", ctx.type, pos);
|
||||||
@@ -879,7 +881,7 @@ bool readHanPort() {
|
|||||||
meterState.setLastError(pos);
|
meterState.setLastError(pos);
|
||||||
debugV("Unknown data payload:");
|
debugV("Unknown data payload:");
|
||||||
len = len + hanSerial->readBytes(hanBuffer+len, BUF_SIZE_HAN-len);
|
len = len + hanSerial->readBytes(hanBuffer+len, BUF_SIZE_HAN-len);
|
||||||
debugPrint(hanBuffer, 0, len);
|
if(Debug.isActive(RemoteDebug::VERBOSE)) debugPrint(hanBuffer, 0, len);
|
||||||
len = 0;
|
len = 0;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -962,7 +964,7 @@ bool readHanPort() {
|
|||||||
|
|
||||||
bool saveData = false;
|
bool saveData = false;
|
||||||
if(!ds.isHappy() && now > BUILD_EPOCH) {
|
if(!ds.isHappy() && now > BUILD_EPOCH) {
|
||||||
debugV("Its time to update data storage");
|
debugD("Its time to update data storage");
|
||||||
tmElements_t tm;
|
tmElements_t tm;
|
||||||
breakTime(now, tm);
|
breakTime(now, tm);
|
||||||
if(tm.Minute == 0) {
|
if(tm.Minute == 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user