mirror of
https://github.com/UtilitechAS/amsreader-firmware.git
synced 2026-03-11 04:57:28 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0ccd2d007 | ||
|
|
bb2f74d1ca | ||
|
|
dfef18fa09 | ||
|
|
485c21dc69 | ||
|
|
9ceb84bc9c | ||
|
|
24e68428c4 | ||
|
|
c2c5855e6a | ||
|
|
de19de2129 | ||
|
|
1719263de0 | ||
|
|
e70b872c98 | ||
|
|
b7d28238ab | ||
|
|
6c9a8b0692 | ||
|
|
9f3dba3aab | ||
|
|
e4e4ad4107 | ||
|
|
e8fb9570bb | ||
|
|
bc42099962 | ||
|
|
be71cbe609 | ||
|
|
0d6df03c94 | ||
|
|
d777040c0a | ||
|
|
a5636a60f8 | ||
|
|
6f817b2ed5 |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -77,7 +77,7 @@ protected:
|
||||
unsigned long lastList2 = 0;
|
||||
uint8_t listType = 0, meterType = AmsTypeUnknown;
|
||||
time_t packageTimestamp = 0;
|
||||
String listId, meterId, meterModel;
|
||||
String listId = "", meterId = "", meterModel = "";
|
||||
time_t meterTimestamp = 0;
|
||||
uint16_t activeImportPower = 0, reactiveImportPower = 0, activeExportPower = 0, reactiveExportPower = 0;
|
||||
float l1voltage = 0, l2voltage = 0, l3voltage = 0, l1current = 0, l2current = 0, l3current = 0;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
@@ -362,7 +367,9 @@ bool EnergyAccounting::load() {
|
||||
} else if(buf[0] == 4) {
|
||||
EnergyAccountingData4* data = (EnergyAccountingData4*) buf;
|
||||
this->data = { 5, data->month,
|
||||
(uint16_t) (data->costYesterday / 10), (uint16_t) (data->costThisMonth / 100), (uint16_t) (data->costLastMonth / 100),
|
||||
data->costYesterday,
|
||||
data->costThisMonth,
|
||||
data->costLastMonth,
|
||||
0,0,0, // Income from production
|
||||
data->peaks[0].day, data->peaks[0].value,
|
||||
data->peaks[1].day, data->peaks[1].value,
|
||||
|
||||
@@ -284,7 +284,7 @@ PricesContainer* EntsoeApi::fetchPrices(time_t t) {
|
||||
breakTime(tz->toUTC(e2), d2);
|
||||
|
||||
snprintf(buf, BufferSize, "%s?securityToken=%s&documentType=A44&periodStart=%04d%02d%02d%02d%02d&periodEnd=%04d%02d%02d%02d%02d&in_Domain=%s&out_Domain=%s",
|
||||
"https://transparency.entsoe.eu/api", getToken(),
|
||||
"https://web-api.tp.entsoe.eu/api", getToken(),
|
||||
d1.Year+1970, d1.Month, d1.Day, d1.Hour, 00,
|
||||
d2.Year+1970, d2.Month, d2.Day, d2.Hour, 00,
|
||||
config->area, config->area);
|
||||
|
||||
@@ -119,68 +119,96 @@ void HwTools::getAdcChannel(uint8_t pin, AdcConfig& config) {
|
||||
config.unit = ADC_UNIT_1;
|
||||
config.channel = ADC1_CHANNEL_4;
|
||||
break;
|
||||
#if defined(ADC1_CHANNEL_5)
|
||||
case ADC1_CHANNEL_5_GPIO_NUM:
|
||||
config.unit = ADC_UNIT_1;
|
||||
config.channel = ADC1_CHANNEL_5;
|
||||
break;
|
||||
#endif
|
||||
#if defined(ADC1_CHANNEL_6)
|
||||
case ADC1_CHANNEL_6_GPIO_NUM:
|
||||
config.unit = ADC_UNIT_1;
|
||||
config.channel = ADC1_CHANNEL_6;
|
||||
break;
|
||||
#endif
|
||||
#if defined(ADC1_CHANNEL_7)
|
||||
case ADC1_CHANNEL_7_GPIO_NUM:
|
||||
config.unit = ADC_UNIT_1;
|
||||
config.channel = ADC1_CHANNEL_7;
|
||||
break;
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
#endif
|
||||
#if defined(ADC1_CHANNEL_8)
|
||||
case ADC1_CHANNEL_8_GPIO_NUM:
|
||||
config.unit = ADC_UNIT_1;
|
||||
config.channel = ADC1_CHANNEL_8;
|
||||
break;
|
||||
#endif
|
||||
#if defined(ADC1_CHANNEL_9)
|
||||
case ADC1_CHANNEL_9_GPIO_NUM:
|
||||
config.unit = ADC_UNIT_1;
|
||||
config.channel = ADC1_CHANNEL_9;
|
||||
break;
|
||||
#endif
|
||||
#if defined(ADC2_CHANNEL_0)
|
||||
case ADC2_CHANNEL_0_GPIO_NUM:
|
||||
config.unit = ADC_UNIT_2;
|
||||
config.channel = ADC2_CHANNEL_0;
|
||||
break;
|
||||
#endif
|
||||
#if defined(ADC2_CHANNEL_1)
|
||||
case ADC2_CHANNEL_1_GPIO_NUM:
|
||||
config.unit = ADC_UNIT_2;
|
||||
config.channel = ADC2_CHANNEL_1;
|
||||
break;
|
||||
#endif
|
||||
#if defined(ADC2_CHANNEL_2)
|
||||
case ADC2_CHANNEL_2_GPIO_NUM:
|
||||
config.unit = ADC_UNIT_2;
|
||||
config.channel = ADC2_CHANNEL_2;
|
||||
break;
|
||||
#endif
|
||||
#if defined(ADC2_CHANNEL_3)
|
||||
case ADC2_CHANNEL_3_GPIO_NUM:
|
||||
config.unit = ADC_UNIT_2;
|
||||
config.channel = ADC2_CHANNEL_3;
|
||||
break;
|
||||
#endif
|
||||
#if defined(ADC2_CHANNEL_4)
|
||||
case ADC2_CHANNEL_4_GPIO_NUM:
|
||||
config.unit = ADC_UNIT_2;
|
||||
config.channel = ADC2_CHANNEL_4;
|
||||
break;
|
||||
#endif
|
||||
#if defined(ADC2_CHANNEL_5)
|
||||
case ADC2_CHANNEL_5_GPIO_NUM:
|
||||
config.unit = ADC_UNIT_2;
|
||||
config.channel = ADC2_CHANNEL_5;
|
||||
break;
|
||||
#endif
|
||||
#if defined(ADC2_CHANNEL_6)
|
||||
case ADC2_CHANNEL_6_GPIO_NUM:
|
||||
config.unit = ADC_UNIT_2;
|
||||
config.channel = ADC2_CHANNEL_6;
|
||||
break;
|
||||
#endif
|
||||
#if defined(ADC2_CHANNEL_7)
|
||||
case ADC2_CHANNEL_7_GPIO_NUM:
|
||||
config.unit = ADC_UNIT_2;
|
||||
config.channel = ADC2_CHANNEL_7;
|
||||
break;
|
||||
#endif
|
||||
#if defined(ADC2_CHANNEL_8)
|
||||
case ADC2_CHANNEL_8_GPIO_NUM:
|
||||
config.unit = ADC_UNIT_2;
|
||||
config.channel = ADC2_CHANNEL_8;
|
||||
break;
|
||||
#endif
|
||||
#if defined(ADC2_CHANNEL_9)
|
||||
case ADC2_CHANNEL_9_GPIO_NUM:
|
||||
config.unit = ADC_UNIT_2;
|
||||
config.channel = ADC2_CHANNEL_9;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
2
lib/SvelteUi/app/.gitignore
vendored
2
lib/SvelteUi/app/.gitignore
vendored
@@ -8,8 +8,6 @@ pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
|
||||
19
lib/SvelteUi/app/dist/favicon.svg
vendored
Normal file
19
lib/SvelteUi/app/dist/favicon.svg
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52">
|
||||
<title>Amsleser</title>
|
||||
<g transform="translate(-29.5,-83)">
|
||||
<circle r="4.8016944" cy="123.56455" cx="55.064552"
|
||||
style="fill:none;stroke:#045c7c;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path d="m 41.298717,103.9049 a 24,24 0 0 1 27.531669,0"
|
||||
style="fill:none;stroke:#045c7c;stroke-width:3.3;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path d="m 35.562952,95.713384 a 34,34 0 0 1 39.003199,-2e-6"
|
||||
style="fill:none;stroke:#045c7c;stroke-width:3.3;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path d="m 47.034482,112.09642 a 14,14 0 0 1 16.06014,0"
|
||||
style="fill:none;stroke:#045c7c;stroke-width:3.3;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<circle r="3" cy="105.99158" cx="38.181862"
|
||||
style="fill:none;stroke:#045c7c;stroke-width:2.4;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<circle r="3" cy="97.959579" cx="77.491386"
|
||||
style="fill:none;stroke:#045c7c;stroke-width:2.4;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
6
lib/SvelteUi/app/dist/github.svg
vendored
Normal file
6
lib/SvelteUi/app/dist/github.svg
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 499.36" focusable="false">
|
||||
<title>GitHub</title>
|
||||
<path d="M256 0C114.64 0 0 114.61 0 256c0 113.09 73.34 209 175.08 242.9 12.8 2.35 17.47-5.56 17.47-12.34 0-6.08-.22-22.18-.35-43.54-71.2 15.49-86.2-34.34-86.2-34.34-11.64-29.57-28.42-37.45-28.42-37.45-23.27-15.84 1.73-15.55 1.73-15.55 25.69 1.81 39.21 26.38 39.21 26.38 22.84 39.12 59.92 27.82 74.5 21.27 2.33-16.54 8.94-27.82 16.25-34.22-56.84-6.43-116.6-28.43-116.6-126.49 0-27.95 10-50.8 26.35-68.69-2.63-6.48-11.42-32.5 2.51-67.75 0 0 21.49-6.88 70.4 26.24a242.65 242.65 0 0 1 128.18 0c48.87-33.13 70.33-26.24 70.33-26.24 14 35.25 5.18 61.27 2.55 67.75 16.41 17.9 26.31 40.75 26.31 68.69 0 98.35-59.85 120-116.88 126.32 9.19 7.9 17.38 23.53 17.38 47.41 0 34.22-.31 61.83-.31 70.23 0 6.85 4.61 14.81 17.6 12.31C438.72 464.97 512 369.08 512 256.02 512 114.62 397.37 0 256 0z" fill="#f8f9fa" fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
1
lib/SvelteUi/app/dist/index.css
vendored
Normal file
1
lib/SvelteUi/app/dist/index.css
vendored
Normal file
File diff suppressed because one or more lines are too long
16
lib/SvelteUi/app/dist/index.html
vendored
Normal file
16
lib/SvelteUi/app/dist/index.html
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="icon" href="/favicon.svg">
|
||||
<link rel="mask-icon" href="/favicon.svg" color="#000000">
|
||||
<title>AMS reader</title>
|
||||
<script type="module" crossorigin src="/index.js"></script>
|
||||
<link rel="stylesheet" href="/index.css">
|
||||
</head>
|
||||
<body class="bg-gray-100">
|
||||
<div id="app"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
13
lib/SvelteUi/app/dist/index.js
vendored
Normal file
13
lib/SvelteUi/app/dist/index.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -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>
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
@apply bg-white m-2 p-2 rounded shadow-lg
|
||||
}
|
||||
|
||||
.gwf {
|
||||
@apply 2xl:col-span-6 xl:col-span-5 lg:col-span-4 md:col-span-3 sm:col-span-2 h-64
|
||||
}
|
||||
|
||||
.in-pre {
|
||||
@apply flex items-center bg-gray-100 rounded-l-md border border-r-0 border-gray-300 px-3 whitespace-nowrap text-sm
|
||||
}
|
||||
@@ -38,25 +42,28 @@
|
||||
@apply text-right
|
||||
}
|
||||
|
||||
.bd-grn {
|
||||
.bd-green {
|
||||
@apply my-auto bg-green-500 text-green-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded
|
||||
}
|
||||
.bd-ylo {
|
||||
.bd-yellow {
|
||||
@apply my-auto bg-yellow-500 text-yellow-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded
|
||||
}
|
||||
.bd-red {
|
||||
@apply my-auto bg-red-500 text-red-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded
|
||||
}
|
||||
.bd-blu {
|
||||
.bd-blue {
|
||||
@apply my-auto bg-blue-500 text-blue-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded
|
||||
}
|
||||
.bd-gry {
|
||||
.bd-gray {
|
||||
@apply my-auto bg-gray-500 text-gray-100 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded
|
||||
}
|
||||
|
||||
.btn-pri {
|
||||
@apply py-2 px-4 rounded bg-blue-500 text-white mr-3
|
||||
}
|
||||
.btn-pri-sm {
|
||||
@apply text-xs py-1 px-2 rounded bg-blue-500 text-white mr-3
|
||||
}
|
||||
|
||||
.pl-root {
|
||||
position: relative;
|
||||
|
||||
@@ -1,54 +1,68 @@
|
||||
<script>
|
||||
import { fmtnum } from "./Helpers";
|
||||
|
||||
|
||||
export let data;
|
||||
export let currency;
|
||||
export let hasExport;
|
||||
|
||||
let hasExport = data && (data.om || data.e > 0);
|
||||
let cols = 3
|
||||
$: {
|
||||
cols = currency ? 3 : 2;
|
||||
}
|
||||
</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">
|
||||
<br/><br/>
|
||||
|
||||
{#if data}
|
||||
{#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>
|
||||
<strong>Import</strong>
|
||||
<div class="grid grid-cols-{cols} mb-3">
|
||||
<div>Hour</div>
|
||||
<div class="text-right">{fmtnum(data.h.u,2)} kWh</div>
|
||||
{#if currency}<div class="text-right">{fmtnum(data.h.c,2)} {currency}</div>{/if}
|
||||
<div>Day</div>
|
||||
<div class="text-right">{fmtnum(data.d.u,1)} kWh</div>
|
||||
{#if currency}<div class="text-right">{fmtnum(data.d.c,1)} {currency}</div>{/if}
|
||||
<div>Month</div>
|
||||
<div class="text-right">{fmtnum(data.m.u)} kWh</div>
|
||||
{#if currency}<div class="text-right">{fmtnum(data.m.c)} {currency}</div>{/if}
|
||||
</div>
|
||||
<strong>Export</strong>
|
||||
<div class="grid grid-cols-{cols}">
|
||||
<div>Hour</div>
|
||||
<div class="text-right">{fmtnum(data.h.p,2)} kWh</div>
|
||||
{#if currency}<div class="text-right">{fmtnum(data.h.i,2)} {currency}</div>{/if}
|
||||
<div>Day</div>
|
||||
<div class="text-right">{fmtnum(data.d.p,1)} kWh</div>
|
||||
{#if currency}<div class="text-right">{fmtnum(data.d.i,1)} {currency}</div>{/if}
|
||||
<div>Month</div>
|
||||
<div class="text-right">{fmtnum(data.m.p)} kWh</div>
|
||||
{#if currency}<div class="text-right">{fmtnum(data.m.i)} {currency}</div>{/if}
|
||||
</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">{fmtnum(data.h.u,2)} kWh</div>
|
||||
<div>Day</div>
|
||||
<div class="text-right">{fmtnum(data.d.u,1)} kWh</div>
|
||||
<div>Month</div>
|
||||
<div class="text-right">{fmtnum(data.m.u)} kWh</div>
|
||||
</div>
|
||||
{#if currency}
|
||||
<strong>Cost</strong>
|
||||
<div class="grid grid-cols-2">
|
||||
<div>Hour</div>
|
||||
<div class="text-right">{fmtnum(data.h.c,2)} {currency}</div>
|
||||
<div>Day</div>
|
||||
<div class="text-right">{fmtnum(data.d.c,1)} {currency}</div>
|
||||
<div>Month</div>
|
||||
<div class="text-right">{fmtnum(data.m.c)} {currency}</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import BarChart from './BarChart.svelte';
|
||||
import { ampcol } from './Helpers.js';
|
||||
import { ampcol, fmtnum } from './Helpers.js';
|
||||
|
||||
export let u1;
|
||||
export let u2;
|
||||
@@ -12,32 +12,28 @@
|
||||
|
||||
let config = {};
|
||||
|
||||
function point(v) {
|
||||
return {
|
||||
label: fmtnum(v) +'A',
|
||||
value: isNaN(v) ? 0 : v,
|
||||
color: ampcol(v ? (v)/(max)*100 : 0)
|
||||
};
|
||||
};
|
||||
|
||||
$: {
|
||||
let xTicks = [];
|
||||
let points = [];
|
||||
if(u1 > 0) {
|
||||
xTicks.push({ label: 'L1' });
|
||||
points.push({
|
||||
label: i1 ? (i1 > 10 ? i1.toFixed(0) : i1.toFixed(1)) + 'A' : '-',
|
||||
value: i1 ? i1 : 0,
|
||||
color: ampcol(i1 ? (i1)/(max)*100 : 0)
|
||||
});
|
||||
points.push(point(i1));
|
||||
}
|
||||
if(u2 > 0) {
|
||||
xTicks.push({ label: 'L2' });
|
||||
points.push({
|
||||
label: i2 ? (i2 > 10 ? i2.toFixed(0) : i2.toFixed(1)) + 'A' : '-',
|
||||
value: i2 ? i2 : 0,
|
||||
color: ampcol(i2 ? (i2)/(max)*100 : 0)
|
||||
});
|
||||
points.push(point(i2));
|
||||
}
|
||||
if(u3 > 0) {
|
||||
xTicks.push({ label: 'L3' });
|
||||
points.push({
|
||||
label: i3 ? (i3 > 10 ? i3.toFixed(0) : i3.toFixed(1)) + 'A' : '-',
|
||||
value: i3 ? i3 : 0,
|
||||
color: ampcol(i3 ? (i3)/(max)*100 : 0)
|
||||
});
|
||||
points.push(point(i3));
|
||||
}
|
||||
config = {
|
||||
padding: { top: 20, right: 15, bottom: 20, left: 35 },
|
||||
|
||||
@@ -3,14 +3,4 @@
|
||||
export let title;
|
||||
export let text;
|
||||
</script>
|
||||
{#if color == 'green'}
|
||||
<span title={title} class="bd-grn">{text}</span>
|
||||
{:else if color === `yellow`}
|
||||
<span title={title} class="bd-ylo">{text}</span>
|
||||
{:else if color === `red`}
|
||||
<span title={title} class="bd-red">{text}</span>
|
||||
{:else if color === `blue`}
|
||||
<span title={title} class="bd-blu">{text}</span>
|
||||
{:else if color === `gray`}
|
||||
<span title={title} class="bd-gry">{text}</span>
|
||||
{/if}
|
||||
<span title={title} class="bd-{color}">{text}</span>
|
||||
|
||||
@@ -1,16 +1,53 @@
|
||||
<script>
|
||||
import { getConfiguration, configurationStore } from './ConfigurationStore'
|
||||
import { sysinfoStore } from './DataStores.js';
|
||||
import { wiki } from './Helpers.js';
|
||||
import UartSelectOptions from './UartSelectOptions.svelte';
|
||||
import Mask from './Mask.svelte'
|
||||
import Badge from './Badge.svelte';
|
||||
import HelpIcon from './HelpIcon.svelte';
|
||||
import CountrySelectOptions from './CountrySelectOptions.svelte';
|
||||
import { Link, navigate } from 'svelte-navigator';
|
||||
import SubnetOptions from './SubnetOptions.svelte';
|
||||
|
||||
|
||||
export let sysinfo = {}
|
||||
|
||||
let uiElements = [{
|
||||
name: 'Import gauge',
|
||||
key: 'i'
|
||||
},{
|
||||
name: 'Export gauge',
|
||||
key: 'e'
|
||||
},{
|
||||
name: 'Voltage',
|
||||
key: 'v'
|
||||
},{
|
||||
name: 'Amperage',
|
||||
key: 'a'
|
||||
},{
|
||||
name: 'Reactive',
|
||||
key: 'r'
|
||||
},{
|
||||
name: 'Realtime',
|
||||
key: 'c'
|
||||
},{
|
||||
name: 'Peaks',
|
||||
key: 't'
|
||||
},{
|
||||
name: 'Price',
|
||||
key: 'p'
|
||||
},{
|
||||
name: 'Day plot',
|
||||
key: 'd'
|
||||
},{
|
||||
name: 'Month plot',
|
||||
key: 'm'
|
||||
},{
|
||||
name: 'Temperature plot',
|
||||
key: 's'
|
||||
}];
|
||||
|
||||
let loading = true;
|
||||
let saving = false;
|
||||
|
||||
@@ -67,8 +104,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 +115,8 @@
|
||||
body: data
|
||||
});
|
||||
let res = (await response.json());
|
||||
isFactoryReset = res.success;
|
||||
isFactoryReset = false;
|
||||
isFactoryResetComplete = res.success;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,13 +169,18 @@
|
||||
if(configuration.q.p == 8883) configuration.q.p = 1883;
|
||||
}
|
||||
}
|
||||
|
||||
let gpioMax = 44;
|
||||
$: {
|
||||
gpioMax = sysinfo.chip == 'esp8266' ? 16 : sysinfo.chip == 'esp32s2' ? 44 : 39;
|
||||
}
|
||||
</script>
|
||||
|
||||
<form on:submit|preventDefault={handleSubmit} autocomplete="off">
|
||||
<div class="grid xl:grid-cols-4 lg:grid-cols-2 md:grid-cols-2">
|
||||
<div class="cnt">
|
||||
<strong class="text-sm">General</strong>
|
||||
<a href="https://github.com/UtilitechAS/amsreader-firmware/wiki/General-configuration" target="_blank" class="float-right"><HelpIcon/></a>
|
||||
<a href="{wiki('General-configuration')}" target="_blank" class="float-right"><HelpIcon/></a>
|
||||
<input type="hidden" name="g" value="true"/>
|
||||
<div class="my-1">
|
||||
<div class="flex">
|
||||
@@ -145,7 +190,7 @@
|
||||
</div>
|
||||
<div>
|
||||
Time zone<br/>
|
||||
<select name="gt" bind:value={configuration.g.t} class="in-l">
|
||||
<select name="gt" bind:value={configuration.g.t} class="in-l w-full">
|
||||
<CountrySelectOptions/>
|
||||
</select>
|
||||
</div>
|
||||
@@ -192,10 +237,9 @@
|
||||
<div class="w-1/2">
|
||||
Currency<br/>
|
||||
<select name="pc" bind:value={configuration.p.c} class="in-f w-full">
|
||||
<option value="NOK">NOK</option>
|
||||
<option value="SEK">SEK</option>
|
||||
<option value="DKK">DKK</option>
|
||||
<option value="EUR">EUR</option>
|
||||
{#each ["NOK","SEK","DKK","EUR"] as c}
|
||||
<option value={c}>{c}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
@@ -231,20 +275,16 @@
|
||||
</div>
|
||||
<div class="cnt">
|
||||
<strong class="text-sm">Meter</strong>
|
||||
<a href="https://github.com/UtilitechAS/amsreader-firmware/wiki/Meter-configuration" target="_blank" class="float-right"><HelpIcon/></a>
|
||||
<a href="{wiki('Meter-configuration')}" target="_blank" class="float-right"><HelpIcon/></a>
|
||||
<input type="hidden" name="m" value="true"/>
|
||||
<div class="my-1">
|
||||
<span>Serial configuration</span>
|
||||
<div class="flex">
|
||||
<select name="mb" bind:value={configuration.m.b} class="in-f">
|
||||
<option value={0} disabled={configuration.m.b != 0}>Autodetect</option>
|
||||
<option value={2400}>2400</option>
|
||||
<option value={4800}>4800</option>
|
||||
<option value={9600}>9600</option>
|
||||
<option value={19200}>19200</option>
|
||||
<option value={38400}>38400</option>
|
||||
<option value={57600}>57600</option>
|
||||
<option value={115200}>115200</option>
|
||||
{#each [24,48,96,192,384,576,1152] as b}
|
||||
<option value={b*100}>{b*100}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<select name="mp" bind:value={configuration.m.p} class="in-l" disabled={configuration.m.b == 0}>
|
||||
<option value={0} disabled={configuration.m.b != 0}>-</option>
|
||||
@@ -320,7 +360,7 @@
|
||||
</div>
|
||||
<div class="cnt">
|
||||
<strong class="text-sm">WiFi</strong>
|
||||
<a href="https://github.com/UtilitechAS/amsreader-firmware/wiki/WiFi-configuration" target="_blank" class="float-right"><HelpIcon/></a>
|
||||
<a href="{wiki('WiFi-configuration')}" target="_blank" class="float-right"><HelpIcon/></a>
|
||||
<input type="hidden" name="w" value="true"/>
|
||||
<div class="my-1">
|
||||
SSID<br/>
|
||||
@@ -354,7 +394,7 @@
|
||||
</div>
|
||||
<div class="cnt">
|
||||
<strong class="text-sm">Network</strong>
|
||||
<a href="https://github.com/UtilitechAS/amsreader-firmware/wiki/Network-configuration" target="_blank" class="float-right"><HelpIcon/></a>
|
||||
<a href="{wiki('Network-configuration')}" target="_blank" class="float-right"><HelpIcon/></a>
|
||||
<div class="my-1">
|
||||
IP<br/>
|
||||
<div class="flex">
|
||||
@@ -362,11 +402,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>
|
||||
@@ -396,7 +434,7 @@
|
||||
</div>
|
||||
<div class="cnt">
|
||||
<strong class="text-sm">MQTT</strong>
|
||||
<a href="https://github.com/UtilitechAS/amsreader-firmware/wiki/MQTT-configuration" target="_blank" class="float-right"><HelpIcon/></a>
|
||||
<a href="{wiki('MQTT-configuration')}" target="_blank" class="float-right"><HelpIcon/></a>
|
||||
<input type="hidden" name="q" value="true"/>
|
||||
<div class="my-1">
|
||||
Server
|
||||
@@ -471,7 +509,7 @@
|
||||
{#if configuration.q.m == 3}
|
||||
<div class="cnt">
|
||||
<strong class="text-sm">Domoticz</strong>
|
||||
<a href="https://github.com/UtilitechAS/amsreader-firmware/wiki/MQTT-configuration#domoticz" target="_blank" class="float-right"><HelpIcon/></a>
|
||||
<a href="{wiki('MQTT-configuration#domoticz')}" target="_blank" class="float-right"><HelpIcon/></a>
|
||||
<input type="hidden" name="o" value="true"/>
|
||||
<div class="my-1 flex">
|
||||
<div class="w-1/2">
|
||||
@@ -496,54 +534,16 @@
|
||||
{#if configuration.p.r.startsWith("10YNO") || configuration.p.r == '10Y1001A1001A48H'}
|
||||
<div class="cnt">
|
||||
<strong class="text-sm">Tariff thresholds</strong>
|
||||
<a href="https://github.com/UtilitechAS/amsreader-firmware/wiki/Threshold-configuration" target="_blank" class="float-right"><HelpIcon/></a>
|
||||
<a href="{wiki('Threshold-configuration')}" target="_blank" class="float-right"><HelpIcon/></a>
|
||||
<input type="hidden" name="t" value="true"/>
|
||||
<div class="flex flex-wrap my-1">
|
||||
{#each {length: 9} as _, i}
|
||||
<label class="flex w-40 m-1">
|
||||
<span class="in-pre">1</span>
|
||||
<input name="t0" bind:value={configuration.t.t[0]} type="number" min="0" max="255" class="in-txt w-full"/>
|
||||
<span class="in-post">kWh</span>
|
||||
</label>
|
||||
<label class="flex w-40 m-1">
|
||||
<span class="in-pre">2</span>
|
||||
<input name="t1" bind:value={configuration.t.t[1]} type="number" min="0" max="255" class="in-txt w-full"/>
|
||||
<span class="in-post">kWh</span>
|
||||
</label>
|
||||
<label class="flex w-40 m-1">
|
||||
<span class="in-pre">3</span>
|
||||
<input name="t2" bind:value={configuration.t.t[2]} type="number" min="0" max="255" class="in-txt w-full"/>
|
||||
<span class="in-post">kWh</span>
|
||||
</label>
|
||||
<label class="flex w-40 m-1">
|
||||
<span class="in-pre">4</span>
|
||||
<input name="t3" bind:value={configuration.t.t[3]} type="number" min="0" max="255" class="in-txt w-full"/>
|
||||
<span class="in-post">kWh</span>
|
||||
</label>
|
||||
<label class="flex w-40 m-1">
|
||||
<span class="in-pre">5</span>
|
||||
<input name="t4" bind:value={configuration.t.t[4]} type="number" min="0" max="255" class="in-txt w-full"/>
|
||||
<span class="in-post">kWh</span>
|
||||
</label>
|
||||
<label class="flex w-40 m-1">
|
||||
<span class="in-pre">6</span>
|
||||
<input name="t5" bind:value={configuration.t.t[5]} type="number" min="0" max="255" class="in-txt w-full"/>
|
||||
<span class="in-post">kWh</span>
|
||||
</label>
|
||||
<label class="flex w-40 m-1">
|
||||
<span class="in-pre">7</span>
|
||||
<input name="t6" bind:value={configuration.t.t[6]} type="number" min="0" max="255" class="in-txt w-full"/>
|
||||
<span class="in-post">kWh</span>
|
||||
</label>
|
||||
<label class="flex w-40 m-1">
|
||||
<span class="in-pre">8</span>
|
||||
<input name="t7" bind:value={configuration.t.t[7]} type="number" min="0" max="255" class="in-txt w-full"/>
|
||||
<span class="in-post">kWh</span>
|
||||
</label>
|
||||
<label class="flex w-40 m-1">
|
||||
<span class="in-pre">9</span>
|
||||
<input name="t8" bind:value={configuration.t.t[8]} type="number" min="0" max="255" class="in-txt w-full"/>
|
||||
<span class="in-pre">{i+1}</span>
|
||||
<input name="t{i}" bind:value={configuration.t.t[i]} type="number" min="0" max="255" class="in-txt w-full"/>
|
||||
<span class="in-post">kWh</span>
|
||||
</label>
|
||||
{/each}
|
||||
</div>
|
||||
<label class="flex m-1">
|
||||
<span class="in-pre">Average of</span>
|
||||
@@ -554,103 +554,25 @@
|
||||
{/if}
|
||||
<div class="cnt">
|
||||
<strong class="text-sm">User interface</strong>
|
||||
<a href="https://github.com/UtilitechAS/amsreader-firmware/wiki/User-interface" target="_blank" class="float-right"><HelpIcon/></a>
|
||||
<a href="{wiki('User-interface')}" target="_blank" class="float-right"><HelpIcon/></a>
|
||||
<input type="hidden" name="u" value="true"/>
|
||||
<div class="flex flex-wrap">
|
||||
<div class="w-1/2">
|
||||
Import gauge<br/>
|
||||
<select name="ui" bind:value={configuration.u.i} class="in-s">
|
||||
<option value={0}>Hide</option>
|
||||
<option value={1}>Show</option>
|
||||
<option value={2}>Dynamic</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
Export gauge<br/>
|
||||
<select name="ue" bind:value={configuration.u.e} class="in-s">
|
||||
<option value={0}>Hide</option>
|
||||
<option value={1}>Show</option>
|
||||
<option value={2}>Dynamic</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
Voltage<br/>
|
||||
<select name="uv" bind:value={configuration.u.v} class="in-s">
|
||||
<option value={0}>Hide</option>
|
||||
<option value={1}>Show</option>
|
||||
<option value={2}>Dynamic</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
Amperage<br/>
|
||||
<select name="ua" bind:value={configuration.u.a} class="in-s">
|
||||
<option value={0}>Hide</option>
|
||||
<option value={1}>Show</option>
|
||||
<option value={2}>Dynamic</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
Reactive<br/>
|
||||
<select name="ur" bind:value={configuration.u.r} class="in-s">
|
||||
<option value={0}>Hide</option>
|
||||
<option value={1}>Show</option>
|
||||
<option value={2}>Dynamic</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
Realtime<br/>
|
||||
<select name="uc" bind:value={configuration.u.c} class="in-s">
|
||||
<option value={0}>Hide</option>
|
||||
<option value={1}>Show</option>
|
||||
<option value={2}>Dynamic</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
Peaks<br/>
|
||||
<select name="ut" bind:value={configuration.u.t} class="in-s">
|
||||
<option value={0}>Hide</option>
|
||||
<option value={1}>Show</option>
|
||||
<option value={2}>Dynamic</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
Price<br/>
|
||||
<select name="up" bind:value={configuration.u.p} class="in-s">
|
||||
<option value={0}>Hide</option>
|
||||
<option value={1}>Show</option>
|
||||
<option value={2}>Dynamic</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
Day plot<br/>
|
||||
<select name="ud" bind:value={configuration.u.d} class="in-s">
|
||||
<option value={0}>Hide</option>
|
||||
<option value={1}>Show</option>
|
||||
<option value={2}>Dynamic</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
Month plot<br/>
|
||||
<select name="um" bind:value={configuration.u.m} class="in-s">
|
||||
<option value={0}>Hide</option>
|
||||
<option value={1}>Show</option>
|
||||
<option value={2}>Dynamic</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
Temperature plot<br/>
|
||||
<select name="us" bind:value={configuration.u.s} class="in-s">
|
||||
<option value={0}>Hide</option>
|
||||
<option value={1}>Show</option>
|
||||
<option value={2}>Dynamic</option>
|
||||
</select>
|
||||
</div>
|
||||
{#each uiElements as el}
|
||||
<div class="w-1/2">
|
||||
{el.name}<br/>
|
||||
<select name="u{el.key}" bind:value={configuration.u[el.key]} class="in-s">
|
||||
<option value={0}>Hide</option>
|
||||
<option value={1}>Show</option>
|
||||
<option value={2}>Dynamic</option>
|
||||
</select>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{#if sysinfo.board > 20 || sysinfo.chip == 'esp8266'}
|
||||
<div class="cnt">
|
||||
<strong class="text-sm">Hardware</strong>
|
||||
<a href="https://github.com/UtilitechAS/amsreader-firmware/wiki/GPIO-configuration" target="_blank" class="float-right"><HelpIcon/></a>
|
||||
<a href="{wiki('GPIO-configuration')}" target="_blank" class="float-right"><HelpIcon/></a>
|
||||
{#if sysinfo.board > 20}
|
||||
<input type="hidden" name="i" value="true"/>
|
||||
<div class="flex flex-wrap">
|
||||
@@ -662,34 +584,34 @@
|
||||
</div>
|
||||
<div class="w-1/3">
|
||||
AP button<br/>
|
||||
<input name="ia" bind:value={configuration.i.a} type="number" min="0" max={sysinfo.chip == 'esp8266' ? 16 : sysinfo.chip == 'esp32s2' ? 44 : 39} class="in-m tr w-full"/>
|
||||
<input name="ia" bind:value={configuration.i.a} type="number" min="0" max={gpioMax} class="in-m tr w-full"/>
|
||||
</div>
|
||||
<div class="w-1/3">
|
||||
LED<label class="ml-4"><input name="ili" value="true" bind:checked={configuration.i.l.i} type="checkbox" class="rounded mb-1"/> inv</label><br/>
|
||||
<div class="flex">
|
||||
<input name="ilp" bind:value={configuration.i.l.p} type="number" min="0" max={sysinfo.chip == 'esp8266' ? 16 : sysinfo.chip == 'esp32s2' ? 44 : 39} class="in-l tr w-full"/>
|
||||
<input name="ilp" bind:value={configuration.i.l.p} type="number" min="0" max={gpioMax} class="in-l tr w-full"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
RGB<label class="ml-4"><input name="iri" value="true" bind:checked={configuration.i.r.i} type="checkbox" class="rounded mb-1"/> inverted</label><br/>
|
||||
<div class="flex">
|
||||
<input name="irr" bind:value={configuration.i.r.r} type="number" min="0" max={sysinfo.chip == 'esp8266' ? 16 : sysinfo.chip == 'esp32s2' ? 44 : 39} class="in-f tr w-1/3"/>
|
||||
<input name="irg" bind:value={configuration.i.r.g} type="number" min="0" max={sysinfo.chip == 'esp8266' ? 16 : sysinfo.chip == 'esp32s2' ? 44 : 39} class="in-m tr w-1/3"/>
|
||||
<input name="irb" bind:value={configuration.i.r.b} type="number" min="0" max={sysinfo.chip == 'esp8266' ? 16 : sysinfo.chip == 'esp32s2' ? 44 : 39} class="in-l tr w-1/3"/>
|
||||
<input name="irr" bind:value={configuration.i.r.r} type="number" min="0" max={gpioMax} class="in-f tr w-1/3"/>
|
||||
<input name="irg" bind:value={configuration.i.r.g} type="number" min="0" max={gpioMax} class="in-m tr w-1/3"/>
|
||||
<input name="irb" bind:value={configuration.i.r.b} type="number" min="0" max={gpioMax} class="in-l tr w-1/3"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-1 w-1/3">
|
||||
Temperature<br/>
|
||||
<input name="itd" bind:value={configuration.i.t.d} type="number" min="0" max={sysinfo.chip == 'esp8266' ? 16 : sysinfo.chip == 'esp32s2' ? 44 : 39} class="in-f tr w-full"/>
|
||||
<input name="itd" bind:value={configuration.i.t.d} type="number" min="0" max={gpioMax} class="in-f tr w-full"/>
|
||||
</div>
|
||||
<div class="my-1 pr-1 w-1/3">
|
||||
Analog temp<br/>
|
||||
<input name="ita" bind:value={configuration.i.t.a} type="number" min="0" max={sysinfo.chip == 'esp8266' ? 16 : sysinfo.chip == 'esp32s2' ? 44 : 39} class="in-l tr w-full"/>
|
||||
<input name="ita" bind:value={configuration.i.t.a} type="number" min="0" max={gpioMax} class="in-l tr w-full"/>
|
||||
</div>
|
||||
{#if sysinfo.chip != 'esp8266'}
|
||||
<div class="my-1 pl-1 w-1/3">
|
||||
Vcc<br/>
|
||||
<input name="ivp" bind:value={configuration.i.v.p} type="number" min="0" max={sysinfo.chip == 'esp8266' ? 16 : sysinfo.chip == 'esp32s2' ? 44 : 39} class="in-s tr w-full"/>
|
||||
<input name="ivp" bind:value={configuration.i.v.p} type="number" min="0" max={gpioMax} class="in-s tr w-full"/>
|
||||
</div>
|
||||
{/if}
|
||||
{#if configuration.i.v.p > 0}
|
||||
@@ -765,4 +687,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"/>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { sysinfoStore } from './DataStores.js';
|
||||
import Mask from './Mask.svelte'
|
||||
import { navigate } from 'svelte-navigator';
|
||||
import { wiki } from './Helpers';
|
||||
|
||||
export let sysinfo = {}
|
||||
|
||||
@@ -41,7 +42,7 @@
|
||||
<hr/>
|
||||
<div class="my-3">
|
||||
Enable one-click upgrade? (implies data collection)<br/>
|
||||
<a href="https://github.com/UtilitechAS/amsreader-firmware/wiki/Data-collection-on-one-click-firmware-upgrade" target="_blank" class="text-blue-600 hover:text-blue-800">Read more</a><br/>
|
||||
<a href="{wiki('Data-collection-on-one-click-firmware-upgrade')}" target="_blank" class="text-blue-600 hover:text-blue-800">Read more</a><br/>
|
||||
<label><input type="radio" name="sf" value={1} checked={sysinfo.fwconsent === 1} class="rounded m-2" required/> Yes</label><label><input type="radio" name="sf" value={2} checked={sysinfo.fwconsent === 2} class="rounded m-2" required/> No</label><br/>
|
||||
</div>
|
||||
<div class="my-3">
|
||||
|
||||
@@ -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 gwf">
|
||||
<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 gwf">
|
||||
<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 gwf">
|
||||
<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 gwf">
|
||||
<TemperaturePlot json={temperatures} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -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() {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script>
|
||||
import { Link } from "svelte-navigator";
|
||||
import { sysinfoStore, getGitHubReleases, gitHubReleaseStore } from './DataStores.js';
|
||||
import { upgrade, getNextVersion } from './UpgradeHelper';
|
||||
import { boardtype, hanError, mqttError, priceError, isBusPowered } from './Helpers.js';
|
||||
import { upgrade, getNextVersion, upgradeWarningText } from './UpgradeHelper';
|
||||
import { boardtype, hanError, mqttError, priceError, isBusPowered, wiki, bcol } from './Helpers.js';
|
||||
import AmsleserSvg from "./../assets/favicon.svg";
|
||||
import GitHubLogo from './../assets/github.svg';
|
||||
import Uptime from "./Uptime.svelte";
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
function askUpgrade() {
|
||||
if(confirm('Do you want to upgrade this device to ' + nextVersion.tag_name + '?')) {
|
||||
if(!isBusPowered(sysinfo.board) || confirm('WARNING: ' + boardtype(sysinfo.chip, sysinfo.board) + ' must be connected to an external power supply during firmware upgrade. Failure to do so may cause power-down during upload resulting in non-functioning unit.')) {
|
||||
if(!isBusPowered(sysinfo.board) || confirm(upgradeWarningText(boardtype(sysinfo.chip, sysinfo.board)))) {
|
||||
sysinfoStore.update(s => {
|
||||
s.upgrading = true;
|
||||
return s;
|
||||
@@ -54,10 +54,10 @@
|
||||
<div class="flex-none my-auto">Free mem: {data.m ? (data.m/1000).toFixed(1) : '-'}kb</div>
|
||||
</div>
|
||||
<div class="flex-auto flex-wrap my-auto justify-center p-2">
|
||||
<Badge title="ESP" text={sysinfo.booting ? 'Booting' : data.v > 2.0 ? data.v.toFixed(2)+"V" : "ESP"} color={sysinfo.booting ? 'yellow' : data.em === 1 ? 'green' : data.em === 2 ? 'yellow' : data.em === 3 ? 'red' : 'gray'}/>
|
||||
<Badge title="HAN" text="HAN" color={sysinfo.booting ? 'gray' : data.hm === 1 ? 'green' : data.hm === 2 ? 'yellow' : data.hm === 3 ? 'red' : 'gray'}/>
|
||||
<Badge title="WiFi" text={data.r ? data.r.toFixed(0)+"dBm" : "WiFi"} color={sysinfo.booting ? 'gray' : data.wm === 1 ? 'green' : data.wm === 2 ? 'yellow' : data.wm === 3 ? 'red' : 'gray'}/>
|
||||
<Badge title="MQTT" text="MQTT" color={sysinfo.booting ? 'gray' : data.mm === 1 ? 'green' : data.mm === 2 ? 'yellow' : data.mm === 3 ? 'red' : 'gray'}/>
|
||||
<Badge title="ESP" text={sysinfo.booting ? 'Booting' : data.v > 2.0 ? data.v.toFixed(2)+"V" : "ESP"} color={bcol(sysinfo.booting ? 2 : data.em)}/>
|
||||
<Badge title="HAN" text="HAN" color={bcol(sysinfo.booting ? 9 : data.hm)}/>
|
||||
<Badge title="WiFi" text={data.r ? data.r.toFixed(0)+"dBm" : "WiFi"} color={bcol(sysinfo.booting ? 9 : data.wm)}/>
|
||||
<Badge title="MQTT" text="MQTT" color={bcol(sysinfo.booting ? 9 : data.mm)}/>
|
||||
</div>
|
||||
{#if data.he < 0 || data.he > 0}
|
||||
<div class="bd-red">{ 'HAN: ' + hanError(data.he) }</div>
|
||||
@@ -84,7 +84,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex-none px-1 mt-1" title="Documentation">
|
||||
<a href="https://github.com/UtilitechAS/amsreader-firmware/wiki" target='_blank' rel="noreferrer"><HelpIcon/></a>
|
||||
<a href={wiki('')} target='_blank' rel="noreferrer"><HelpIcon/></a>
|
||||
</div>
|
||||
{#if sysinfo.fwconsent === 1 && nextVersion}
|
||||
<div class="flex-none mr-3 text-yellow-500" title="New version: {nextVersion.tag_name}">
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
export let monthnames = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
|
||||
|
||||
export function bcol(num) {
|
||||
return num === 1 ? 'green' : num === 2 ? 'yellow' : num === 3 ? 'red' : 'gray';
|
||||
}
|
||||
|
||||
export function voltcol(volt) {
|
||||
if(volt > 218 && volt < 242) return '#32d900';
|
||||
if(volt > 212 && volt < 248) return '#b1d900';
|
||||
@@ -15,6 +19,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:
|
||||
@@ -153,3 +164,14 @@ export function isBusPowered(boardType) {
|
||||
export function uiVisibility(choice, state) {
|
||||
return choice == 1 || (choice == 2 && state);
|
||||
}
|
||||
|
||||
export function wiki(page) {
|
||||
return "https://github.com/UtilitechAS/amsreader-firmware/wiki/" + page;
|
||||
}
|
||||
|
||||
export function fmtnum(v,d) {
|
||||
if(isNaN(v)) return '-';
|
||||
if(isNaN(d))
|
||||
d = v < 10 ? 1 : 0;
|
||||
return v.toFixed(d);
|
||||
}
|
||||
@@ -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/>
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
export let exportTotal;
|
||||
</script>
|
||||
|
||||
<div class="mx-2">
|
||||
<strong class="text-sm">Reactive</strong>
|
||||
<div class="mx-2 text-sm">
|
||||
<strong>Reactive</strong>
|
||||
|
||||
<div class="grid grid-cols-2 mt-4">
|
||||
<div>Instant in</div>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -1,13 +1,42 @@
|
||||
<script>
|
||||
import { metertype, boardtype, isBusPowered } from './Helpers.js';
|
||||
import { getSysinfo, gitHubReleaseStore, sysinfoStore } from './DataStores.js';
|
||||
import { upgrade, getNextVersion } from './UpgradeHelper';
|
||||
import { upgrade, getNextVersion, upgradeWarningText } from './UpgradeHelper';
|
||||
import DownloadIcon from './DownloadIcon.svelte';
|
||||
import { Link } from 'svelte-navigator';
|
||||
import Mask from './Mask.svelte';
|
||||
|
||||
export let data;
|
||||
export let sysinfo;
|
||||
|
||||
let cfgItems = [{
|
||||
name: 'WiFi',
|
||||
key: 'iw'
|
||||
},{
|
||||
name: 'MQTT',
|
||||
key: 'im'
|
||||
},{
|
||||
name: 'Web',
|
||||
key: 'ie'
|
||||
},{
|
||||
name: 'Meter',
|
||||
key: 'it'
|
||||
},{
|
||||
name: 'Thresholds',
|
||||
key: 'ih'
|
||||
},{
|
||||
name: 'GPIO',
|
||||
key: 'ig'
|
||||
},{
|
||||
name: 'Domoticz',
|
||||
key: 'id'
|
||||
},{
|
||||
name: 'NTP',
|
||||
key: 'in'
|
||||
},{
|
||||
name: 'Price API',
|
||||
key: 'is'
|
||||
}];
|
||||
|
||||
let nextVersion = {};
|
||||
gitHubReleaseStore.subscribe(releases => {
|
||||
@@ -19,7 +48,7 @@
|
||||
|
||||
function askUpgrade() {
|
||||
if(confirm('Do you want to upgrade this device to ' + nextVersion.tag_name + '?')) {
|
||||
if((sysinfo.board != 2 && sysinfo.board != 4 && sysinfo.board != 7) || confirm('WARNING: ' + boardtype(sysinfo.chip, sysinfo.board) + ' must be connected to an external power supply during firmware upgrade. Failure to do so may cause power-down during upload resulting in non-functioning unit.')) {
|
||||
if((sysinfo.board != 2 && sysinfo.board != 4 && sysinfo.board != 7) || confirm(upgradeWarningText(boardtype(sysinfo.chip, sysinfo.board)))) {
|
||||
sysinfoStore.update(s => {
|
||||
s.upgrading = true;
|
||||
return s;
|
||||
@@ -64,7 +93,7 @@
|
||||
Chip: {sysinfo.chip}
|
||||
</div>
|
||||
<div class="my-2">
|
||||
Device: {boardtype(sysinfo.chip, sysinfo.board)}
|
||||
Device: <Link to="/vendor">{boardtype(sysinfo.chip, sysinfo.board)}</Link>
|
||||
</div>
|
||||
<div class="my-2">
|
||||
MAC: {sysinfo.mac}
|
||||
@@ -76,7 +105,7 @@
|
||||
{/if}
|
||||
<div class="my-2">
|
||||
<Link to="/consent">
|
||||
<span class="text-xs py-1 px-2 rounded bg-blue-500 text-white mr-3 ">Update consents</span>
|
||||
<span class="btn-pri-sm">Update consents</span>
|
||||
</Link>
|
||||
<button on:click={askReboot} class="text-xs py-1 px-2 rounded bg-yellow-500 text-white mr-3 float-right">Reboot</button>
|
||||
</div>
|
||||
@@ -129,13 +158,13 @@
|
||||
</div>
|
||||
{#if sysinfo.fwconsent === 2}
|
||||
<div class="my-2">
|
||||
<div class="bd-ylo">You have disabled one-click firmware upgrade, link to self-upgrade is disabled</div>
|
||||
<div class="bd-yellow">You have disabled one-click firmware upgrade, link to self-upgrade is disabled</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if (sysinfo.security == 0 || data.a) && isBusPowered(sysinfo.board) }
|
||||
<div class="bd-red">
|
||||
{boardtype(sysinfo.chip, sysinfo.board)} must be connected to an external power supply during firmware upgrade. Failure to do so may cause power-down during upload resulting in non-functioning unit.
|
||||
{upgradeWarningText(boardtype(sysinfo.chip, sysinfo.board))}
|
||||
</div>
|
||||
{/if}
|
||||
{#if sysinfo.security == 0 || data.a}
|
||||
@@ -143,10 +172,10 @@
|
||||
<form action="/firmware" enctype="multipart/form-data" method="post" on:submit={() => firmwareUploading=true} autocomplete="off">
|
||||
<input style="display:none" name="file" type="file" accept=".bin" bind:this={firmwareFileInput} bind:files={firmwareFiles}>
|
||||
{#if firmwareFiles.length == 0}
|
||||
<button type="button" on:click={()=>{firmwareFileInput.click();}} class="text-xs py-1 px-2 rounded bg-blue-500 text-white float-right mr-3">Select firmware file for upgrade</button>
|
||||
<button type="button" on:click={()=>{firmwareFileInput.click();}} class="btn-pri-sm float-right">Select firmware file for upgrade</button>
|
||||
{:else}
|
||||
{firmwareFiles[0].name}
|
||||
<button type="submit" class="ml-2 text-xs py-1 px-2 rounded bg-blue-500 text-white float-right mr-3">Upload</button>
|
||||
<button type="submit" class="btn-pri-sm float-right">Upload</button>
|
||||
{/if}
|
||||
</form>
|
||||
</div>
|
||||
@@ -157,28 +186,22 @@
|
||||
<strong class="text-sm">Configuration</strong>
|
||||
<form method="get" action="/configfile.cfg" autocomplete="off">
|
||||
<div class="grid grid-cols-2">
|
||||
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="iw" value="true" checked/> WiFi</label>
|
||||
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="im" value="true" checked/> MQTT</label>
|
||||
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="ie" value="true" checked/> Web</label>
|
||||
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="it" value="true" checked/> Meter</label>
|
||||
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="ih" value="true" checked/> Thresholds</label>
|
||||
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="ig" value="true" checked/> GPIO</label>
|
||||
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="id" value="true" checked/> Domoticz</label>
|
||||
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="in" value="true" checked/> NTP</label>
|
||||
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="is" value="true" checked/> Price API</label>
|
||||
{#each cfgItems as el}
|
||||
<label class="my-1 mx-3"><input type="checkbox" class="rounded" name="{el.key}" value="true" checked/> {el.name}</label>
|
||||
{/each}
|
||||
<label class="my-1 mx-3 col-span-2"><input type="checkbox" class="rounded" name="ic" value="true"/> Include Secrets<br/><small>(SSID, PSK, passwords and tokens)</small></label>
|
||||
</div>
|
||||
{#if configFiles.length == 0}
|
||||
<button type="submit" class="ml-2 text-xs py-1 px-2 rounded bg-blue-500 text-white float-right mr-3">Download</button>
|
||||
<button type="submit" class="btn-pri-sm float-right">Download</button>
|
||||
{/if}
|
||||
</form>
|
||||
<form action="/configfile" enctype="multipart/form-data" method="post" on:submit={() => configUploading=true} autocomplete="off">
|
||||
<input style="display:none" name="file" type="file" accept=".cfg" bind:this={configFileInput} bind:files={configFiles}>
|
||||
{#if configFiles.length == 0}
|
||||
<button type="button" on:click={()=>{configFileInput.click();}} class="text-xs py-1 px-2 rounded bg-blue-500 text-white mr-3">Select file...</button>
|
||||
<button type="button" on:click={()=>{configFileInput.click();}} class="btn-pri-sm">Select file...</button>
|
||||
{:else}
|
||||
{configFiles[0].name}
|
||||
<button type="submit" class="ml-2 text-xs py-1 px-2 rounded bg-blue-500 text-white mr-3">Upload</button>
|
||||
<button type="submit" class="btn-pri-sm">Upload</button>
|
||||
{/if}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
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>
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<script>
|
||||
export let chip;
|
||||
|
||||
let gpioMax = 44;
|
||||
$: {
|
||||
gpioMax = chip == 'esp8266' ? 16 : chip == 'esp32s2' ? 44 : 39;
|
||||
}
|
||||
</script>
|
||||
|
||||
<option value={3}>UART0</option>
|
||||
@@ -13,46 +18,15 @@
|
||||
{#if chip == 'esp32s2'}
|
||||
<option value={18}>UART1</option>
|
||||
{/if}
|
||||
<option value={4}>GPIO4</option>
|
||||
<option value={5}>GPIO5</option>
|
||||
{#if chip.startsWith('esp32')}
|
||||
<option value={6}>GPIO6</option>
|
||||
<option value={7}>GPIO7</option>
|
||||
<option value={8}>GPIO8</option>
|
||||
{/if}
|
||||
{#if chip == 'esp8266'}
|
||||
<option value={9}>GPIO9</option>
|
||||
{/if}
|
||||
<option value={10}>GPIO10</option>
|
||||
{#if chip.startsWith('esp32')}
|
||||
<option value={11}>GPIO11</option>
|
||||
{/if}
|
||||
<option value={12}>GPIO12</option>
|
||||
<option value={13}>GPIO13</option>
|
||||
<option value={14}>GPIO14</option>
|
||||
<option value={15}>GPIO15</option>
|
||||
|
||||
{#if chip.startsWith('esp32')}
|
||||
<option value={17}>GPIO17</option>
|
||||
{#if chip != 'esp32s2'}
|
||||
<option value={18}>GPIO18</option>
|
||||
{/if}
|
||||
<option value={19}>GPIO19</option>
|
||||
<option value={21}>GPIO21</option>
|
||||
<option value={22}>GPIO22</option>
|
||||
<option value={23}>GPIO23</option>
|
||||
<option value={25}>GPIO25</option>
|
||||
<option value={32}>GPIO32</option>
|
||||
<option value={33}>GPIO33</option>
|
||||
<option value={34}>GPIO34</option>
|
||||
<option value={35}>GPIO35</option>
|
||||
<option value={36}>GPIO36</option>
|
||||
<option value={39}>GPIO39</option>
|
||||
{/if}
|
||||
{#if chip == 'esp32s2'}
|
||||
<option value={40}>GPIO40</option>
|
||||
<option value={41}>GPIO41</option>
|
||||
<option value={42}>GPIO42</option>
|
||||
<option value={43}>GPIO43</option>
|
||||
<option value={44}>GPIO44</option>
|
||||
{#each {length: gpioMax+1} as _, i}
|
||||
{#if i > 3
|
||||
&& !(chip == 'esp32' && (i == 9 || i == 16))
|
||||
&& !(chip == 'esp32s2' && i == 18)
|
||||
&& !(chip == 'esp8266' && (i == 3 || i == 113))
|
||||
}
|
||||
<option value={i}>GPIO{i}</option>
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
@@ -1,31 +1,33 @@
|
||||
export async function upgrade(version) {
|
||||
const data = new URLSearchParams()
|
||||
data.append('version', version.tag_name);
|
||||
export function upgradeWarningText(board) {
|
||||
return 'WARNING: ' + board + ' must be connected to an external power supply during firmware upgrade. Failure to do so may cause power-down during upload resulting in non-functioning unit.'
|
||||
}
|
||||
|
||||
export async function upgrade() {
|
||||
const response = await fetch('/upgrade', {
|
||||
method: 'POST',
|
||||
body: data
|
||||
method: 'POST'
|
||||
});
|
||||
let res = (await response.json())
|
||||
await response.json();
|
||||
}
|
||||
|
||||
export function getNextVersion(currentVersion, releases) {
|
||||
export function getNextVersion(currentVersion, releases_orig) {
|
||||
if(/^v\d{1,2}\.\d{1,2}\.\d{1,2}$/.test(currentVersion)) {
|
||||
var v = currentVersion.substring(1).split('.');
|
||||
var v_major = parseInt(v[0]);
|
||||
var v_minor = parseInt(v[1]);
|
||||
var v_patch = parseInt(v[2]);
|
||||
let v = currentVersion.substring(1).split('.');
|
||||
let v_major = parseInt(v[0]);
|
||||
let v_minor = parseInt(v[1]);
|
||||
let v_patch = parseInt(v[2]);
|
||||
|
||||
let releases = [...releases_orig];
|
||||
releases.reverse();
|
||||
var next_patch;
|
||||
var next_minor;
|
||||
var next_major;
|
||||
for(var i = 0; i < releases.length; i++) {
|
||||
var release = releases[i];
|
||||
var ver2 = release.tag_name;
|
||||
var v2 = ver2.substring(1).split('.');
|
||||
var v2_major = parseInt(v2[0]);
|
||||
var v2_minor = parseInt(v2[1]);
|
||||
var v2_patch = parseInt(v2[2]);
|
||||
let next_patch;
|
||||
let next_minor;
|
||||
let next_major;
|
||||
for(let i = 0; i < releases.length; i++) {
|
||||
let release = releases[i];
|
||||
let ver2 = release.tag_name;
|
||||
let v2 = ver2.substring(1).split('.');
|
||||
let v2_major = parseInt(v2[0]);
|
||||
let v2_minor = parseInt(v2[1]);
|
||||
let v2_patch = parseInt(v2[2]);
|
||||
|
||||
if(v2_major == v_major) {
|
||||
if(v2_minor == v_minor) {
|
||||
@@ -37,10 +39,10 @@ export function getNextVersion(currentVersion, releases) {
|
||||
}
|
||||
} else if(v2_major == v_major+1) {
|
||||
if(next_major) {
|
||||
var mv = next_major.tag_name.substring(1).split('.');
|
||||
var mv_major = parseInt(mv[0]);
|
||||
var mv_minor = parseInt(mv[1]);
|
||||
var mv_patch = parseInt(mv[2]);
|
||||
let mv = next_major.tag_name.substring(1).split('.');
|
||||
let mv_major = parseInt(mv[0]);
|
||||
let mv_minor = parseInt(mv[1]);
|
||||
let mv_patch = parseInt(mv[2]);
|
||||
if(v2_minor == mv_minor) {
|
||||
next_major = release;
|
||||
}
|
||||
@@ -58,6 +60,6 @@ export function getNextVersion(currentVersion, releases) {
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return releases[0];
|
||||
return releases_orig[0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,11 @@
|
||||
});
|
||||
navigate(sysinfo.usrcfg ? "/" : "/setup");
|
||||
}
|
||||
|
||||
let cc = false;
|
||||
$: {
|
||||
cc = !sysinfo.usrcfg;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="grid xl:grid-cols-4 lg:grid-cols-3 md:grid-cols-2">
|
||||
@@ -38,6 +43,9 @@
|
||||
<form on:submit|preventDefault={handleSubmit} autocomplete="off">
|
||||
<input type="hidden" name="v" value="true"/>
|
||||
<strong class="text-sm">Initial configuration</strong>
|
||||
{#if sysinfo.usrcfg}
|
||||
<div class="bd-red">WARNING: Changing this configuration will affect basic configuration of your device. Only make changes here if instructed by vendor</div>
|
||||
{/if}
|
||||
<div class="my-3">
|
||||
Board type<br/>
|
||||
<select name="vb" bind:value={sysinfo.board} class="in-s">
|
||||
@@ -53,7 +61,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
<div class="my-3">
|
||||
<label><input type="checkbox" name="vr" value="true" class="rounded mb-1" checked /> Clear all other configuration</label>
|
||||
<label><input type="checkbox" name="vr" value="true" class="rounded mb-1" bind:checked={cc} /> Clear all other configuration</label>
|
||||
</div>
|
||||
<div class="my-3">
|
||||
<button type="submit" class="btn-pri">Save</button>
|
||||
|
||||
@@ -1,48 +1,42 @@
|
||||
<script>
|
||||
import BarChart from './BarChart.svelte';
|
||||
import { voltcol } from './Helpers.js';
|
||||
import { fmtnum, voltcol } from './Helpers.js';
|
||||
|
||||
export let u1;
|
||||
export let u2;
|
||||
export let u3;
|
||||
export let ds;
|
||||
|
||||
let min = 200;
|
||||
let max = 260;
|
||||
let config = {};
|
||||
|
||||
function point(v) {
|
||||
return {
|
||||
label: fmtnum(v) + 'V',
|
||||
value: isNaN(v) ? 0 : v,
|
||||
color: voltcol(v ? v : 0)
|
||||
};
|
||||
};
|
||||
|
||||
$: {
|
||||
let xTicks = [];
|
||||
let points = [];
|
||||
if(u1 > 0) {
|
||||
xTicks.push({ label: ds === 1 ? 'L1-L2' : 'L1' });
|
||||
points.push({
|
||||
label: u1 ? u1.toFixed(0) + 'V' : '-',
|
||||
value: u1 ? u1 : 0,
|
||||
color: voltcol(u1 ? u1 : 0)
|
||||
});
|
||||
points.push(point(u1));
|
||||
}
|
||||
if(u2 > 0) {
|
||||
xTicks.push({ label: ds === 1 ? 'L1-L3' : 'L2' });
|
||||
points.push({
|
||||
label: u2 ? u2.toFixed(0) + 'V' : '-',
|
||||
value: u2 ? u2 : 0,
|
||||
color: voltcol(u2 ? u2 : 0)
|
||||
});
|
||||
points.push(point(u2));
|
||||
}
|
||||
if(u3 > 0) {
|
||||
xTicks.push({ label: ds === 1 ? 'L2-L3' : 'L3' });
|
||||
points.push({
|
||||
label: u3 ? u3.toFixed(0) + 'V' : '-',
|
||||
value: u3 ? u3 : 0,
|
||||
color: voltcol(u3 ? u3 : 0)
|
||||
});
|
||||
points.push(point(u3));
|
||||
}
|
||||
config = {
|
||||
padding: { top: 20, right: 15, bottom: 20, left: 35 },
|
||||
y: {
|
||||
min: min,
|
||||
max: max,
|
||||
min: 200,
|
||||
max: 260,
|
||||
ticks: [
|
||||
{ value: 207, label: '-10%' },
|
||||
{ value: 230, label: '230v' },
|
||||
|
||||
@@ -17,18 +17,18 @@ export default defineConfig({
|
||||
plugins: [svelte()],
|
||||
server: {
|
||||
proxy: {
|
||||
"/data.json": "http://192.168.233.244",
|
||||
"/energyprice.json": "http://192.168.233.244",
|
||||
"/dayplot.json": "http://192.168.233.244",
|
||||
"/monthplot.json": "http://192.168.233.244",
|
||||
"/temperature.json": "http://192.168.233.244",
|
||||
"/sysinfo.json": "http://192.168.233.244",
|
||||
"/configuration.json": "http://192.168.233.244",
|
||||
"/tariff.json": "http://192.168.233.244",
|
||||
"/save": "http://192.168.233.244",
|
||||
"/reboot": "http://192.168.233.244",
|
||||
"/configfile": "http://192.168.233.244",
|
||||
"/upgrade": "http://192.168.233.244"
|
||||
"/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"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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\"";
|
||||
|
||||
@@ -240,12 +240,22 @@ void AmsWebServer::sysinfoJson() {
|
||||
UiConfig ui;
|
||||
config->getUiConfig(ui);
|
||||
|
||||
String meterModel = meterState->getMeterModel();
|
||||
if(!meterModel.isEmpty())
|
||||
meterModel.replace("\\", "\\\\");
|
||||
|
||||
String meterId = meterState->getMeterId();
|
||||
if(!meterId.isEmpty())
|
||||
meterId.replace("\\", "\\\\");
|
||||
|
||||
int size = snprintf_P(buf, BufferSize, SYSINFO_JSON,
|
||||
VERSION,
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
"esp32s2",
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
"esp32c3",
|
||||
#elif defined(CONFIG_FREERTOS_UNICORE)
|
||||
"esp32solo",
|
||||
#elif defined(ESP32)
|
||||
"esp32",
|
||||
#elif defined(ESP8266)
|
||||
@@ -272,8 +282,8 @@ void AmsWebServer::sysinfoJson() {
|
||||
dns2.toString().c_str(),
|
||||
#endif
|
||||
meterState->getMeterType(),
|
||||
meterState->getMeterModel().c_str(),
|
||||
meterState->getMeterId().c_str(),
|
||||
meterModel.c_str(),
|
||||
meterId.c_str(),
|
||||
ui.showImport,
|
||||
ui.showExport,
|
||||
ui.showVoltage,
|
||||
@@ -348,7 +358,6 @@ void AmsWebServer::dataJson() {
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
uint8_t hanStatus;
|
||||
if(meterState->getLastError() != 0) {
|
||||
hanStatus = 3;
|
||||
@@ -397,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(),
|
||||
@@ -444,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,
|
||||
@@ -724,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);
|
||||
}
|
||||
@@ -735,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);
|
||||
}
|
||||
@@ -1037,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);
|
||||
|
||||
|
||||
@@ -73,3 +73,15 @@ lib_compat_mode = off
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
lib_ignore = ${common.lib_ignore}
|
||||
extra_scripts = ${common.extra_scripts}
|
||||
|
||||
[env:esp32c3]
|
||||
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.5.3/platform-espressif32-2.0.5.3.zip
|
||||
framework = arduino
|
||||
board = esp32-c3-devkitm-1
|
||||
board_build.mcu = esp32c3
|
||||
build_flags = ${common.build_flags}
|
||||
lib_ldf_mode = off
|
||||
lib_compat_mode = off
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
lib_ignore = ${common.lib_ignore}
|
||||
extra_scripts = ${common.extra_scripts}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -577,13 +579,14 @@ void loop() {
|
||||
if(readHanPort() || now - meterState.getLastUpdateMillis() > 30000) {
|
||||
if(now - lastTemperatureRead > 15000) {
|
||||
unsigned long start = millis();
|
||||
hw.updateTemperatures();
|
||||
lastTemperatureRead = now;
|
||||
if(hw.updateTemperatures()) {
|
||||
lastTemperatureRead = now;
|
||||
|
||||
if(mqtt != NULL && mqttHandler != NULL && WiFi.getMode() != WIFI_AP && WiFi.status() == WL_CONNECTED && mqtt->connected() && !topic.isEmpty()) {
|
||||
mqttHandler->publishTemperatures(&config, &hw);
|
||||
if(mqtt != NULL && mqttHandler != NULL && WiFi.getMode() != WIFI_AP && WiFi.status() == WL_CONNECTED && mqtt->connected() && !topic.isEmpty()) {
|
||||
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(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);
|
||||
}
|
||||
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");
|
||||
@@ -607,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];
|
||||
@@ -862,9 +864,9 @@ bool readHanPort() {
|
||||
pos = unwrapData((uint8_t *) hanBuffer, ctx);
|
||||
if(ctx.type > 0 && pos >= 0) {
|
||||
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) {
|
||||
debugV("Received valid DSMR at %d", pos);
|
||||
debugD("Received valid DSMR at %d", pos);
|
||||
} else {
|
||||
// TODO: Move this so that payload is sent to MQTT
|
||||
debugE("Unknown tag %02X at pos %d", ctx.type, pos);
|
||||
@@ -879,7 +881,7 @@ bool readHanPort() {
|
||||
meterState.setLastError(pos);
|
||||
debugV("Unknown data payload:");
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
@@ -962,7 +964,7 @@ bool readHanPort() {
|
||||
|
||||
bool saveData = false;
|
||||
if(!ds.isHappy() && now > BUILD_EPOCH) {
|
||||
debugV("Its time to update data storage");
|
||||
debugD("Its time to update data storage");
|
||||
tmElements_t tm;
|
||||
breakTime(now, tm);
|
||||
if(tm.Minute == 0) {
|
||||
@@ -1598,7 +1600,7 @@ void configFileParse() {
|
||||
fromHex(meter.encryptionKey, String(buf+19), 16);
|
||||
} else if(strncmp_P(buf, PSTR("meterAuthenticationKey "), 23) == 0) {
|
||||
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
||||
fromHex(meter.authenticationKey, String(buf+19), 16);
|
||||
fromHex(meter.authenticationKey, String(buf+23), 16);
|
||||
} else if(strncmp_P(buf, PSTR("gpioHanPin "), 11) == 0) {
|
||||
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
||||
gpio.hanPin = String(buf+11).toInt();
|
||||
|
||||
Reference in New Issue
Block a user