Compare commits

...

14 Commits

Author SHA1 Message Date
Gunnar Skjold
2978116207 Fixed formatting error in HA payload 2022-08-16 11:55:07 +02:00
Gunnar Skjold
998b986604 Adjusted modem sleep to a safer value 2022-08-12 20:48:34 +02:00
Gunnar Skjold
7799431405 Remove unused files 2022-08-12 20:42:13 +02:00
Gunnar Skjold
508c14a671 Fixed modem sleep on S2 and fixed default wifi power 2022-08-12 09:02:04 +02:00
Gunnar Skjold
4a7ef87269 Fixed realtime day calculation 2022-08-10 17:43:51 +02:00
Gunnar Skjold
3a4fc707b0 Show real time production 2022-08-10 10:53:16 +02:00
Gunnar Skjold
5d2e320b07 Fixed export labels on graph 2022-08-10 10:28:31 +02:00
Gunnar Skjold
d12613b91a Trying to fix HA accumulated 2022-08-10 10:01:24 +02:00
Gunnar Skjold
e6a02f34ab USB power warning when upgrading 2022-08-10 09:31:50 +02:00
Gunnar Skjold
0b2ffbfd77 Revert to arduino 2.0.3 because of #298 2022-08-10 09:30:31 +02:00
Gunnar Skjold
cab6c54ed9 Correct Vcc GPIO for Pow-K+ 2022-08-01 12:48:31 +02:00
Gunnar Skjold
313024f273 Support OTA upgrade from ESP8266 2022-08-01 08:17:24 +02:00
Gunnar Skjold
91fc078c5e Higher precision on prices 2022-07-31 20:26:38 +02:00
Gunnar Skjold
5b9d44a3e9 Fixed loading of energy accounting 2022-07-31 20:19:01 +02:00
20 changed files with 251 additions and 202 deletions

View File

@@ -17,8 +17,10 @@ extra_scripts =
pre:scripts/addversion.py pre:scripts/addversion.py
scripts/makeweb.py scripts/makeweb.py
# Sticking to v2.0.3 because of #298
[env:esp32] [env:esp32]
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.4/platform-espressif32-2.0.4.zip platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.3/platform-espressif32-2.0.3.zip
framework = arduino framework = arduino
board = esp32dev board = esp32dev
board_build.f_cpu = 160000000L board_build.f_cpu = 160000000L
@@ -33,8 +35,8 @@ extra_scripts =
# https://github.com/Jason2866/esp32-arduino-lib-builder # https://github.com/Jason2866/esp32-arduino-lib-builder
[env:esp32s2] [env:esp32s2]
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.4/platform-espressif32-2.0.4.zip platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.3/platform-espressif32-2.0.3.zip
platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.4 platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.3
framework = arduino framework = arduino
board = esp32dev board = esp32dev
board_build.mcu = esp32s2 board_build.mcu = esp32s2
@@ -50,7 +52,7 @@ extra_scripts =
scripts/makeweb.py scripts/makeweb.py
[env:esp32solo] [env:esp32solo]
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.4/platform-espressif32-solo1-2.0.4.zip platform = https://github.com/tasmota/platform-espressif32/releases/download/v.2.0.3/platform-espressif32-solo1-v.2.0.3.zip
framework = arduino framework = arduino
board = esp32dev board = esp32dev
board_build.f_cpu = 160000000L board_build.f_cpu = 160000000L

View File

@@ -63,8 +63,10 @@ void AmsConfiguration::clearWifi(WiFiConfig& config) {
uint16_t chipId; uint16_t chipId;
#if defined(ESP32) #if defined(ESP32)
chipId = ESP.getEfuseMac(); chipId = ESP.getEfuseMac();
config.power = 195;
#else #else
chipId = ESP.getChipId(); chipId = ESP.getChipId();
config.power = 205;
#endif #endif
strcpy(config.hostname, (String("ams-") + String(chipId, HEX)).c_str()); strcpy(config.hostname, (String("ams-") + String(chipId, HEX)).c_str());
config.mdns = true; config.mdns = true;

View File

@@ -148,6 +148,8 @@ void setup() {
gpioConfig.hanPin = 3; gpioConfig.hanPin = 3;
gpioConfig.ledPin = 2; gpioConfig.ledPin = 2;
gpioConfig.ledInverted = true; gpioConfig.ledInverted = true;
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
gpioConfig.hanPin = 18;
#elif defined(ESP32) #elif defined(ESP32)
gpioConfig.hanPin = 16; gpioConfig.hanPin = 16;
gpioConfig.ledPin = 2; gpioConfig.ledPin = 2;
@@ -155,6 +157,7 @@ void setup() {
gpioConfig.tempSensorPin = 14; gpioConfig.tempSensorPin = 14;
#endif #endif
} }
delay(1); delay(1);
config.loadTempSensors(); config.loadTempSensors();
hw.setup(&gpioConfig, &config); hw.setup(&gpioConfig, &config);
@@ -1032,6 +1035,7 @@ void WiFi_connect() {
} }
#endif #endif
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
WiFi.setSleep(WIFI_PS_MIN_MODEM);
#if defined(ESP32) #if defined(ESP32)
if(wifi.power >= 195) if(wifi.power >= 195)
WiFi.setTxPower(WIFI_POWER_19_5dBm); WiFi.setTxPower(WIFI_POWER_19_5dBm);
@@ -1055,6 +1059,8 @@ void WiFi_connect() {
WiFi.setTxPower(WIFI_POWER_5dBm); WiFi.setTxPower(WIFI_POWER_5dBm);
else if(wifi.power >= 20) else if(wifi.power >= 20)
WiFi.setTxPower(WIFI_POWER_2dBm); WiFi.setTxPower(WIFI_POWER_2dBm);
else
WiFi.setTxPower(WIFI_POWER_MINUS_1dBm);
#elif defined(ESP8266) #elif defined(ESP8266)
WiFi.setOutputPower(wifi.power / 10.0); WiFi.setOutputPower(wifi.power / 10.0);
#endif #endif

View File

@@ -80,6 +80,7 @@ bool EnergyAccounting::update(AmsData* amsData) {
} }
use = 0; use = 0;
produce = 0;
costHour = 0; costHour = 0;
currentHour = local.Hour; currentHour = local.Hour;
@@ -106,19 +107,24 @@ bool EnergyAccounting::update(AmsData* amsData) {
} }
unsigned long ms = this->lastUpdateMillis > amsData->getLastUpdateMillis() ? 0 : amsData->getLastUpdateMillis() - this->lastUpdateMillis; unsigned long ms = this->lastUpdateMillis > amsData->getLastUpdateMillis() ? 0 : amsData->getLastUpdateMillis() - this->lastUpdateMillis;
float kwh = (amsData->getActiveImportPower() * (((float) ms) / 3600000.0)) / 1000.0; float kwhi = (amsData->getActiveImportPower() * (((float) ms) / 3600000.0)) / 1000.0;
float kwhe = (amsData->getActiveExportPower() * (((float) ms) / 3600000.0)) / 1000.0;
lastUpdateMillis = amsData->getLastUpdateMillis(); lastUpdateMillis = amsData->getLastUpdateMillis();
if(kwh > 0) { if(kwhi > 0) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) Adding %.4f kWh\n", kwh); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) Adding %.4f kWh import\n", kwhi);
use += kwh; use += kwhi;
if(eapi != NULL && eapi->getValueForHour(0) != ENTSOE_NO_VALUE) { if(eapi != NULL && eapi->getValueForHour(0) != ENTSOE_NO_VALUE) {
float price = eapi->getValueForHour(0); float price = eapi->getValueForHour(0);
float cost = price * kwh; float cost = price * kwhi;
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) and %.4f %s\n", cost / 100.0, eapi->getCurrency()); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) and %.4f %s\n", cost / 100.0, eapi->getCurrency());
costHour += cost; costHour += cost;
costDay += cost; costDay += cost;
} }
} }
if(kwhe > 0) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) Adding %.4f kWh export\n", kwhe);
produce += kwhe;
}
if(config != NULL) { if(config != NULL) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) calculating threshold, currently at %d\n", currentThresholdIdx); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) calculating threshold, currently at %d\n", currentThresholdIdx);
@@ -136,10 +142,10 @@ void EnergyAccounting::calcDayCost() {
if(eapi != NULL && eapi->getValueForHour(0) != ENTSOE_NO_VALUE) { if(eapi != NULL && eapi->getValueForHour(0) != ENTSOE_NO_VALUE) {
if(initPrice) costDay = 0; if(initPrice) costDay = 0;
for(int i = 0; i < local.Hour; i++) { for(int i = 0; i < currentHour; i++) {
float price = eapi->getValueForHour(i - local.Hour); float price = eapi->getValueForHour(i - currentHour);
if(price == ENTSOE_NO_VALUE) break; if(price == ENTSOE_NO_VALUE) break;
breakTime(now - ((local.Hour - i) * 3600), utc); breakTime(now - ((currentHour - i) * 3600), utc);
int16_t wh = ds->getHourImport(utc.Hour); int16_t wh = ds->getHourImport(utc.Hour);
costDay += price * (wh / 1000.0); costDay += price * (wh / 1000.0);
} }
@@ -151,23 +157,59 @@ double EnergyAccounting::getUseThisHour() {
return use; return use;
} }
double EnergyAccounting::getCostThisHour() {
return costHour;
}
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;
tmElements_t local, utc; tmElements_t utc;
breakTime(tz->toLocal(now), local); for(int i = 0; i < currentHour; i++) {
for(int i = 0; i < local.Hour; i++) { breakTime(now - ((currentHour - i) * 3600), utc);
breakTime(now - ((local.Hour - i) * 3600), utc);
ret += ds->getHourImport(utc.Hour) / 1000.0; ret += ds->getHourImport(utc.Hour) / 1000.0;
} }
return ret + getUseThisHour(); return ret + getUseThisHour();
} }
double EnergyAccounting::getUseThisMonth() {
time_t now = time(nullptr);
if(now < BUILD_EPOCH) return 0;
float ret = 0;
for(int i = 0; i < currentDay; i++) {
ret += ds->getDayImport(i) / 1000.0;
}
return ret + getUseToday();
}
double EnergyAccounting::getProducedThisHour() {
return produce;
}
double EnergyAccounting::getProducedToday() {
float ret = 0.0;
time_t now = time(nullptr);
if(now < BUILD_EPOCH) return 0;
tmElements_t utc;
for(int i = 0; i < currentHour; i++) {
breakTime(now - ((currentHour - i) * 3600), utc);
ret += ds->getHourExport(utc.Hour) / 1000.0;
}
return ret + getProducedThisHour();
}
double EnergyAccounting::getProducedThisMonth() {
time_t now = time(nullptr);
if(now < BUILD_EPOCH) return 0;
float ret = 0;
for(int i = 0; i < currentDay; i++) {
ret += ds->getDayExport(i) / 1000.0;
}
return ret + getProducedToday();
}
double EnergyAccounting::getCostThisHour() {
return costHour;
}
double EnergyAccounting::getCostToday() { double EnergyAccounting::getCostToday() {
return costDay; return costDay;
} }
@@ -176,21 +218,6 @@ double EnergyAccounting::getCostYesterday() {
return data.costYesterday / 10.0; return data.costYesterday / 10.0;
} }
double EnergyAccounting::getUseThisMonth() {
time_t now = time(nullptr);
if(now < BUILD_EPOCH) return 0;
tmElements_t tm;
if(tz != NULL)
breakTime(tz->toLocal(now), tm);
else
breakTime(now, tm);
float ret = 0;
for(int i = 0; i < tm.Day; i++) {
ret += ds->getDayImport(i) / 1000.0;
}
return ret + getUseToday();
}
double EnergyAccounting::getCostThisMonth() { double EnergyAccounting::getCostThisMonth() {
return data.costThisMonth + getCostToday(); return data.costThisMonth + getCostToday();
} }
@@ -251,6 +278,7 @@ bool EnergyAccounting::load() {
if(buf[0] == 4) { if(buf[0] == 4) {
EnergyAccountingData* data = (EnergyAccountingData*) buf; EnergyAccountingData* data = (EnergyAccountingData*) buf;
memcpy(&this->data, data, sizeof(this->data)); memcpy(&this->data, data, sizeof(this->data));
ret = true;
} else if(buf[0] == 3) { } else if(buf[0] == 3) {
EnergyAccountingData* data = (EnergyAccountingData*) buf; EnergyAccountingData* data = (EnergyAccountingData*) buf;
this->data = { 4, data->month, this->data = { 4, data->month,

View File

@@ -42,11 +42,16 @@ public:
bool save(); bool save();
double getUseThisHour(); double getUseThisHour();
double getCostThisHour();
double getUseToday(); double getUseToday();
double getUseThisMonth();
double getProducedThisHour();
double getProducedToday();
double getProducedThisMonth();
double getCostThisHour();
double getCostToday(); double getCostToday();
double getCostYesterday(); double getCostYesterday();
double getUseThisMonth();
double getCostThisMonth(); double getCostThisMonth();
uint16_t getCostLastMonth(); uint16_t getCostLastMonth();
@@ -66,6 +71,7 @@ private:
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, costHour, costDay;
double produce;
EnergyAccountingData data = { 0, 0, 0, 0, 0, 0 }; EnergyAccountingData data = { 0, 0, 0, 0, 0, 0 };
void calcDayCost(); void calcDayCost();

View File

@@ -24,6 +24,7 @@ bool HomeAssistantMqttHandler::publish(AmsData* data, AmsData* previousState, En
data->getMeterTimestamp() data->getMeterTimestamp()
); );
mqtt->publish(topic + "/energy", json); mqtt->publish(topic + "/energy", json);
mqtt->loop();
} }
String meterModel = data->getMeterModel(); String meterModel = data->getMeterModel();
meterModel.replace("\\", "\\\\"); meterModel.replace("\\", "\\\\");

View File

@@ -27,7 +27,9 @@ bool JsonMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyAccou
ea->getUseThisHour(), ea->getUseThisHour(),
ea->getUseToday(), ea->getUseToday(),
ea->getCurrentThreshold(), ea->getCurrentThreshold(),
ea->getMonthMax() ea->getMonthMax(),
ea->getProducedThisHour(),
ea->getProducedToday()
); );
return mqtt->publish(topic, json); return mqtt->publish(topic, json);
} else if(data->getListType() == 2) { } else if(data->getListType() == 2) {
@@ -55,7 +57,9 @@ bool JsonMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyAccou
ea->getUseThisHour(), ea->getUseThisHour(),
ea->getUseToday(), ea->getUseToday(),
ea->getCurrentThreshold(), ea->getCurrentThreshold(),
ea->getMonthMax() ea->getMonthMax(),
ea->getProducedThisHour(),
ea->getProducedToday()
); );
return mqtt->publish(topic, json); return mqtt->publish(topic, json);
} else if(data->getListType() == 3) { } else if(data->getListType() == 3) {
@@ -88,7 +92,9 @@ bool JsonMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyAccou
ea->getUseThisHour(), ea->getUseThisHour(),
ea->getUseToday(), ea->getUseToday(),
ea->getCurrentThreshold(), ea->getCurrentThreshold(),
ea->getMonthMax() ea->getMonthMax(),
ea->getProducedThisHour(),
ea->getProducedToday()
); );
return mqtt->publish(topic, json); return mqtt->publish(topic, json);
} else if(data->getListType() == 4) { } else if(data->getListType() == 4) {
@@ -125,7 +131,9 @@ bool JsonMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyAccou
ea->getUseThisHour(), ea->getUseThisHour(),
ea->getUseToday(), ea->getUseToday(),
ea->getCurrentThreshold(), ea->getCurrentThreshold(),
ea->getMonthMax() ea->getMonthMax(),
ea->getProducedThisHour(),
ea->getProducedToday()
); );
return mqtt->publish(topic, json); return mqtt->publish(topic, json);
} }

View File

@@ -76,6 +76,8 @@ bool RawMqttHandler::publish(AmsData* data, AmsData* meterState, EnergyAccountin
mqtt->publish(topic + "/realtime/import/day", String(ea->getUseToday(), 2)); mqtt->publish(topic + "/realtime/import/day", String(ea->getUseToday(), 2));
mqtt->publish(topic + "/realtime/import/threshold", String(ea->getCurrentThreshold(), 10), true, 0); mqtt->publish(topic + "/realtime/import/threshold", String(ea->getCurrentThreshold(), 10), true, 0);
mqtt->publish(topic + "/realtime/import/monthmax", String(ea->getMonthMax(), 3), true, 0); mqtt->publish(topic + "/realtime/import/monthmax", String(ea->getMonthMax(), 3), true, 0);
mqtt->publish(topic + "/realtime/export/hour", String(ea->getProducedThisHour(), 3));
mqtt->publish(topic + "/realtime/export/day", String(ea->getProducedToday(), 2));
return true; return true;
} }

View File

@@ -747,10 +747,13 @@ void AmsWebServer::dataJson() {
ea->getCurrentThreshold(), ea->getCurrentThreshold(),
ea->getUseThisHour(), ea->getUseThisHour(),
ea->getCostThisHour(), ea->getCostThisHour(),
ea->getProducedThisHour(),
ea->getUseToday(), ea->getUseToday(),
ea->getCostToday(), ea->getCostToday(),
ea->getProducedToday(),
ea->getUseThisMonth(), ea->getUseThisMonth(),
ea->getCostThisMonth(), ea->getCostThisMonth(),
ea->getProducedThisMonth(),
(uint32_t) time(nullptr) (uint32_t) time(nullptr)
); );
@@ -927,42 +930,42 @@ void AmsWebServer::energyPriceJson() {
snprintf_P(buf, BufferSize, ENERGYPRICE_JSON, snprintf_P(buf, BufferSize, ENERGYPRICE_JSON,
eapi == NULL ? "" : eapi->getCurrency(), eapi == NULL ? "" : eapi->getCurrency(),
prices[0] == ENTSOE_NO_VALUE ? "null" : String(prices[0], 2).c_str(), prices[0] == ENTSOE_NO_VALUE ? "null" : String(prices[0], 4).c_str(),
prices[1] == ENTSOE_NO_VALUE ? "null" : String(prices[1], 2).c_str(), prices[1] == ENTSOE_NO_VALUE ? "null" : String(prices[1], 4).c_str(),
prices[2] == ENTSOE_NO_VALUE ? "null" : String(prices[2], 2).c_str(), prices[2] == ENTSOE_NO_VALUE ? "null" : String(prices[2], 4).c_str(),
prices[3] == ENTSOE_NO_VALUE ? "null" : String(prices[3], 2).c_str(), prices[3] == ENTSOE_NO_VALUE ? "null" : String(prices[3], 4).c_str(),
prices[4] == ENTSOE_NO_VALUE ? "null" : String(prices[4], 2).c_str(), prices[4] == ENTSOE_NO_VALUE ? "null" : String(prices[4], 4).c_str(),
prices[5] == ENTSOE_NO_VALUE ? "null" : String(prices[5], 2).c_str(), prices[5] == ENTSOE_NO_VALUE ? "null" : String(prices[5], 4).c_str(),
prices[6] == ENTSOE_NO_VALUE ? "null" : String(prices[6], 2).c_str(), prices[6] == ENTSOE_NO_VALUE ? "null" : String(prices[6], 4).c_str(),
prices[7] == ENTSOE_NO_VALUE ? "null" : String(prices[7], 2).c_str(), prices[7] == ENTSOE_NO_VALUE ? "null" : String(prices[7], 4).c_str(),
prices[8] == ENTSOE_NO_VALUE ? "null" : String(prices[8], 2).c_str(), prices[8] == ENTSOE_NO_VALUE ? "null" : String(prices[8], 4).c_str(),
prices[9] == ENTSOE_NO_VALUE ? "null" : String(prices[9], 2).c_str(), prices[9] == ENTSOE_NO_VALUE ? "null" : String(prices[9], 4).c_str(),
prices[10] == ENTSOE_NO_VALUE ? "null" : String(prices[10], 2).c_str(), prices[10] == ENTSOE_NO_VALUE ? "null" : String(prices[10], 4).c_str(),
prices[11] == ENTSOE_NO_VALUE ? "null" : String(prices[11], 2).c_str(), prices[11] == ENTSOE_NO_VALUE ? "null" : String(prices[11], 4).c_str(),
prices[12] == ENTSOE_NO_VALUE ? "null" : String(prices[12], 2).c_str(), prices[12] == ENTSOE_NO_VALUE ? "null" : String(prices[12], 4).c_str(),
prices[13] == ENTSOE_NO_VALUE ? "null" : String(prices[13], 2).c_str(), prices[13] == ENTSOE_NO_VALUE ? "null" : String(prices[13], 4).c_str(),
prices[14] == ENTSOE_NO_VALUE ? "null" : String(prices[14], 2).c_str(), prices[14] == ENTSOE_NO_VALUE ? "null" : String(prices[14], 4).c_str(),
prices[15] == ENTSOE_NO_VALUE ? "null" : String(prices[15], 2).c_str(), prices[15] == ENTSOE_NO_VALUE ? "null" : String(prices[15], 4).c_str(),
prices[16] == ENTSOE_NO_VALUE ? "null" : String(prices[16], 2).c_str(), prices[16] == ENTSOE_NO_VALUE ? "null" : String(prices[16], 4).c_str(),
prices[17] == ENTSOE_NO_VALUE ? "null" : String(prices[17], 2).c_str(), prices[17] == ENTSOE_NO_VALUE ? "null" : String(prices[17], 4).c_str(),
prices[18] == ENTSOE_NO_VALUE ? "null" : String(prices[18], 2).c_str(), prices[18] == ENTSOE_NO_VALUE ? "null" : String(prices[18], 4).c_str(),
prices[19] == ENTSOE_NO_VALUE ? "null" : String(prices[19], 2).c_str(), prices[19] == ENTSOE_NO_VALUE ? "null" : String(prices[19], 4).c_str(),
prices[20] == ENTSOE_NO_VALUE ? "null" : String(prices[20], 2).c_str(), prices[20] == ENTSOE_NO_VALUE ? "null" : String(prices[20], 4).c_str(),
prices[21] == ENTSOE_NO_VALUE ? "null" : String(prices[21], 2).c_str(), prices[21] == ENTSOE_NO_VALUE ? "null" : String(prices[21], 4).c_str(),
prices[22] == ENTSOE_NO_VALUE ? "null" : String(prices[22], 2).c_str(), prices[22] == ENTSOE_NO_VALUE ? "null" : String(prices[22], 4).c_str(),
prices[23] == ENTSOE_NO_VALUE ? "null" : String(prices[23], 2).c_str(), prices[23] == ENTSOE_NO_VALUE ? "null" : String(prices[23], 4).c_str(),
prices[24] == ENTSOE_NO_VALUE ? "null" : String(prices[24], 2).c_str(), prices[24] == ENTSOE_NO_VALUE ? "null" : String(prices[24], 4).c_str(),
prices[25] == ENTSOE_NO_VALUE ? "null" : String(prices[25], 2).c_str(), prices[25] == ENTSOE_NO_VALUE ? "null" : String(prices[25], 4).c_str(),
prices[26] == ENTSOE_NO_VALUE ? "null" : String(prices[26], 2).c_str(), prices[26] == ENTSOE_NO_VALUE ? "null" : String(prices[26], 4).c_str(),
prices[27] == ENTSOE_NO_VALUE ? "null" : String(prices[27], 2).c_str(), prices[27] == ENTSOE_NO_VALUE ? "null" : String(prices[27], 4).c_str(),
prices[28] == ENTSOE_NO_VALUE ? "null" : String(prices[28], 2).c_str(), prices[28] == ENTSOE_NO_VALUE ? "null" : String(prices[28], 4).c_str(),
prices[29] == ENTSOE_NO_VALUE ? "null" : String(prices[29], 2).c_str(), prices[29] == ENTSOE_NO_VALUE ? "null" : String(prices[29], 4).c_str(),
prices[30] == ENTSOE_NO_VALUE ? "null" : String(prices[30], 2).c_str(), prices[30] == ENTSOE_NO_VALUE ? "null" : String(prices[30], 4).c_str(),
prices[31] == ENTSOE_NO_VALUE ? "null" : String(prices[31], 2).c_str(), prices[31] == ENTSOE_NO_VALUE ? "null" : String(prices[31], 4).c_str(),
prices[32] == ENTSOE_NO_VALUE ? "null" : String(prices[32], 2).c_str(), prices[32] == ENTSOE_NO_VALUE ? "null" : String(prices[32], 4).c_str(),
prices[33] == ENTSOE_NO_VALUE ? "null" : String(prices[33], 2).c_str(), prices[33] == ENTSOE_NO_VALUE ? "null" : String(prices[33], 4).c_str(),
prices[34] == ENTSOE_NO_VALUE ? "null" : String(prices[34], 2).c_str(), prices[34] == ENTSOE_NO_VALUE ? "null" : String(prices[34], 4).c_str(),
prices[35] == ENTSOE_NO_VALUE ? "null" : String(prices[35], 2).c_str() prices[35] == ENTSOE_NO_VALUE ? "null" : String(prices[35], 4).c_str()
); );
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE);
@@ -1036,7 +1039,7 @@ void AmsWebServer::handleSetup() {
gpioConfig->ledPinRed = 13; gpioConfig->ledPinRed = 13;
gpioConfig->ledPinGreen = 14; gpioConfig->ledPinGreen = 14;
gpioConfig->ledRgbInverted = true; gpioConfig->ledRgbInverted = true;
gpioConfig->vccPin = 35; gpioConfig->vccPin = 10;
gpioConfig->vccResistorGnd = 22; gpioConfig->vccResistorGnd = 22;
gpioConfig->vccResistorVcc = 33; gpioConfig->vccResistorVcc = 33;
break; break;
@@ -1659,7 +1662,7 @@ void AmsWebServer::firmwareUpload() {
} }
uploadFile(FILE_FIRMWARE); uploadFile(FILE_FIRMWARE);
if(upload.status == UPLOAD_FILE_END) { if(upload.status == UPLOAD_FILE_END) {
performRestart = true; rebootForUpgrade = true;
server.sendHeader("Location","/restart-wait"); server.sendHeader("Location","/restart-wait");
server.send(303); server.send(303);
} }
@@ -1672,109 +1675,9 @@ void AmsWebServer::firmwareDownload() {
return; return;
printD("Firmware download URL triggered"); printD("Firmware download URL triggered");
if(server.hasArg("version")) { performUpgrade = true;
String version = server.arg("version"); server.sendHeader("Location","/restart-wait");
String versionStripped = version.substring(1); server.send(303);
printI("Downloading firmware...");
HTTPClient httpClient;
httpClient.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
httpClient.setReuse(false);
httpClient.setTimeout(60000);
httpClient.setUserAgent("ams2mqtt/" + String(VERSION));
#if defined(ESP8266)
WiFiClient client;
String url = "http://ams2mqtt.no23.cc/releases/download/" + version + "/ams2mqtt-esp8266-" + versionStripped + ".bin";
/*
t_httpUpdate_return ret = ESPhttpUpdate.update(client, url, VERSION);
switch(ret) {
case HTTP_UPDATE_FAILED:
printE("[update] Update failed.");
server.sendHeader("Location","/");
server.send(303);
break;
case HTTP_UPDATE_NO_UPDATES:
printI("[update] Update no Update.");
server.sendHeader("Location","/");
server.send(303);
break;
case HTTP_UPDATE_OK:
printI("[update] Update ok."); // may not be called since we reboot the ESP
performRestart = true;
server.sendHeader("Location","/restart-wait");
server.send(303);
break;
}
return;
*/
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
httpClient.setConnectTimeout(60000);
String url = "https://github.com/gskjold/AmsToMqttBridge/releases/download/" + version + "/ams2mqtt-esp32s2-" + versionStripped + ".bin";
httpClient.addHeader("Referer", "https://github.com/gskjold/AmsToMqttBridge/releases");
#elif defined(ESP32)
httpClient.setConnectTimeout(60000);
#if defined(CONFIG_FREERTOS_UNICORE)
String url = "https://github.com/gskjold/AmsToMqttBridge/releases/download/" + version + "/ams2mqtt-esp32solo-" + versionStripped + ".bin";
#else
String url = "https://github.com/gskjold/AmsToMqttBridge/releases/download/" + version + "/ams2mqtt-esp32-" + versionStripped + ".bin";
#endif
httpClient.addHeader("Referer", "https://github.com/gskjold/AmsToMqttBridge/releases");
#endif
printD("Downloading from URL:");
printD(url);
#if defined(ESP8266)
if(httpClient.begin(client, url)) {
#elif defined(ESP32)
if(httpClient.begin(url)) {
#endif
printD("HTTP client setup successful");
int status = httpClient.GET();
if(status == HTTP_CODE_OK) {
printD("Received OK from server");
if(LittleFS.begin()) {
#if defined(ESP32)
esp_task_wdt_delete(NULL);
esp_task_wdt_deinit();
#elif defined(ESP8266)
ESP.wdtDisable();
#endif
printI("Downloading firmware to LittleFS");
file = LittleFS.open(FILE_FIRMWARE, "w");
int len = httpClient.writeToStream(&file);
file.close();
LittleFS.end();
performRestart = true;
server.sendHeader("Location","/restart-wait");
server.send(303);
} else {
printE("Unable to open LittleFS for writing");
server.sendHeader("Location","/");
server.send(303);
}
} else {
printE("Communication error: ");
debugger->printf("%d\n", status);
printE(httpClient.errorToString(status));
printD(httpClient.getString());
server.sendHeader("Location","/");
server.send(303);
}
} else {
printE("Unable to configure HTTP client");
server.sendHeader("Location","/");
server.send(303);
}
httpClient.end();
} else {
printI("No firmware version specified...");
server.sendHeader("Location","/");
server.send(303);
}
} }
void AmsWebServer::restartHtml() { void AmsWebServer::restartHtml() {
@@ -1824,6 +1727,14 @@ void AmsWebServer::restartWaitHtml() {
} }
html.replace("${hostname}", wifi.hostname); html.replace("${hostname}", wifi.hostname);
if(performUpgrade || rebootForUpgrade) {
html.replace("{rs}", "d-none");
html.replace("{us}", "");
} else {
html.replace("{rs}", "");
html.replace("{us}", "d-none");
}
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE);
server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE);
server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF);
@@ -1832,7 +1743,7 @@ void AmsWebServer::restartWaitHtml() {
server.send(200, MIME_HTML, html); server.send(200, MIME_HTML, html);
yield(); yield();
if(performRestart) { if(performRestart || rebootForUpgrade) {
if(ds != NULL) { if(ds != NULL) {
ds->save(); ds->save();
} }
@@ -1844,6 +1755,40 @@ void AmsWebServer::restartWaitHtml() {
ESP.restart(); ESP.restart();
#endif #endif
performRestart = false; performRestart = false;
} else if(performUpgrade) {
WiFiClient client;
String url = "http://ams2mqtt.rewiredinvent.no/hub/firmware/update";
#if defined(ESP8266)
String chipType = "esp8266";
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
String chipType = "esp32s2";
#elif defined(ESP32)
#if defined(CONFIG_FREERTOS_UNICORE)
String chipType = "esp32solo";
#else
String chipType = "esp32";
#endif
#endif
#if defined(ESP8266)
t_httpUpdate_return ret = ESPhttpUpdate.update(client, url, VERSION);
#elif defined(ESP32)
HTTPUpdate httpUpdate;
HTTPUpdateResult ret = httpUpdate.update(client, url, String(VERSION) + "-" + chipType);
#endif
switch(ret) {
case HTTP_UPDATE_FAILED:
printE("Update failed");
break;
case HTTP_UPDATE_NO_UPDATES:
printI("No Update");
break;
case HTTP_UPDATE_OK:
printI("Update OK");
break;
}
performUpgrade = false;
} }
} }

View File

@@ -24,6 +24,7 @@
#include <WiFi.h> #include <WiFi.h>
#include <WebServer.h> #include <WebServer.h>
#include <HTTPClient.h> #include <HTTPClient.h>
#include <HTTPUpdate.h>
#else #else
#warning "Unsupported board type" #warning "Unsupported board type"
#endif #endif
@@ -58,6 +59,8 @@ private:
bool uploading = false; bool uploading = false;
File file; File file;
bool performRestart = false; bool performRestart = false;
bool performUpgrade = false;
bool rebootForUpgrade = false;
static const uint16_t BufferSize = 2048; static const uint16_t BufferSize = 2048;
char* buf; char* buf;

View File

@@ -478,7 +478,7 @@ var drawDay = function() {
timeout: 30000, timeout: 30000,
dataType: 'json', dataType: 'json',
}).done(function(json) { }).done(function(json) {
data = [['Hour', 'Import', { role: 'style' }, { role: 'annotation' }, 'Export', { role: 'style' }]]; data = [['Hour', 'Import', { role: 'style' }, { role: 'annotation' }, 'Export', { role: 'style' }, { role: 'annotation' }]];
var r = 1; var r = 1;
var hour = moment.utc().hours(); var hour = moment.utc().hours();
var offset = moment().utcOffset()/60; var offset = moment().utcOffset()/60;
@@ -486,13 +486,13 @@ var drawDay = function() {
for(var i = hour; i<24; i++) { for(var i = hour; i<24; i++) {
var imp = json["i"+zeropad(i)]; var imp = json["i"+zeropad(i)];
var exp = json["e"+zeropad(i)]; var exp = json["e"+zeropad(i)];
data[r++] = [zeropad((i+offset)%24), imp, "opacity: 0.9;", exp == 0 ? imp.toFixed(1) : imp.toFixed(1) + '\n' + -exp.toFixed(1), exp == 0 ? 0 : -exp, "opacity: 0.9;"]; data[r++] = [zeropad((i+offset)%24), imp, "opacity: 0.9;", imp == 0 ? "" : imp.toFixed(1), exp == 0 ? 0 : -exp, "opacity: 0.9;", exp == 0 ? "" : -exp.toFixed(1)];
min = Math.min(0, -exp); min = Math.min(0, -exp);
}; };
for(var i = 0; i < hour; i++) { for(var i = 0; i < hour; i++) {
var imp = json["i"+zeropad(i)]; var imp = json["i"+zeropad(i)];
var exp = json["e"+zeropad(i)]; var exp = json["e"+zeropad(i)];
data[r++] = [zeropad((i+offset)%24), imp, "opacity: 0.9;", exp == 0 ? imp.toFixed(1) : imp.toFixed(1) + '\n' + -exp.toFixed(1), exp == 0 ? 0 : -exp, "opacity: 0.9;"]; data[r++] = [zeropad((i+offset)%24), imp, "opacity: 0.9;", imp == 0 ? "" : imp.toFixed(1), exp == 0 ? 0 : -exp, "opacity: 0.9;", exp == 0 ? "" : -exp.toFixed(1)];
min = Math.min(0, -exp); min = Math.min(0, -exp);
}; };
ea = google.visualization.arrayToDataTable(data); ea = google.visualization.arrayToDataTable(data);
@@ -511,7 +511,7 @@ var drawMonth = function() {
timeout: 30000, timeout: 30000,
dataType: 'json', dataType: 'json',
}).done(function(json) { }).done(function(json) {
data = [['Hour', 'Import', { role: 'style' }, { role: 'annotation' }, 'Export', { role: 'style' }]]; data = [['Hour', 'Import', { role: 'style' }, { role: 'annotation' }, 'Export', { role: 'style' }, { role: 'annotation' }]];
var r = 1; var r = 1;
var day = moment().date(); var day = moment().date();
var eom = moment().subtract(1, 'months').endOf('month').date(); var eom = moment().subtract(1, 'months').endOf('month').date();
@@ -519,13 +519,13 @@ var drawMonth = function() {
for(var i = day; i<=eom; i++) { for(var i = day; i<=eom; i++) {
var imp = json["i"+zeropad(i)]; var imp = json["i"+zeropad(i)];
var exp = json["e"+zeropad(i)]; var exp = json["e"+zeropad(i)];
data[r++] = [zeropad(i), imp, "opacity: 0.9;", exp == 0 ? imp.toFixed(0) : imp.toFixed(0) + '\n' + -exp.toFixed(0), exp == 0 ? 0 : -exp, "opacity: 0.9;"]; data[r++] = [zeropad(i), imp, "opacity: 0.9;", imp == 0 ? "" : imp.toFixed(0), exp == 0 ? 0 : -exp, "opacity: 0.9;", exp == 0 ? "" : -exp.toFixed(0)];
min = Math.min(0, -exp); min = Math.min(0, -exp);
} }
for(var i = 1; i < day; i++) { for(var i = 1; i < day; i++) {
var imp = json["i"+zeropad(i)]; var imp = json["i"+zeropad(i)];
var exp = json["e"+zeropad(i)]; var exp = json["e"+zeropad(i)];
data[r++] = [zeropad(i), imp, "opacity: 0.9;", exp == 0 ? imp.toFixed(0) : imp.toFixed(0) + '\n' + -exp.toFixed(0), exp == 0 ? 0 : -exp, "opacity: 0.9;"]; data[r++] = [zeropad(i), imp, "opacity: 0.9;", imp == 0 ? "" : imp.toFixed(0), exp == 0 ? 0 : -exp, "opacity: 0.9;", exp == 0 ? "" : -exp.toFixed(0)];
min = Math.min(0, -exp); min = Math.min(0, -exp);
} }
ma = google.visualization.arrayToDataTable(data); ma = google.visualization.arrayToDataTable(data);
@@ -790,6 +790,12 @@ var fetch = function() {
if(currency) { if(currency) {
$('.sp').show(); $('.sp').show();
} }
if(om > 0) {
$('.se').removeClass('d-none');
$('#eache').html(json.ea.h.p.toFixed(2));
$('#eacde').html(json.ea.d.p.toFixed(1));
$('#eacme').html(json.ea.m.p.toFixed(0));
}
} }
if(json.me) { if(json.me) {
@@ -878,7 +884,7 @@ var fetch = function() {
var upgrade = function() { var upgrade = function() {
if(nextVersion) { if(nextVersion) {
if(confirm("Are you sure you want to perform upgrade to " + nextVersion.tag_name + "?")) { if(confirm("WARNING: Please keep USB power connected while upgrading. Are you sure you want to perform upgrade to " + nextVersion.tag_name + "?")) {
$('#loading-indicator').show(); $('#loading-indicator').show();
window.location.href="/upgrade?version=" + nextVersion.tag_name; window.location.href="/upgrade?version=" + nextVersion.tag_name;
} }

View File

@@ -38,15 +38,18 @@
"t" : %d, "t" : %d,
"h" : { "h" : {
"u" : %.2f, "u" : %.2f,
"c" : %.2f "c" : %.2f,
"p" : %.2f
}, },
"d" : { "d" : {
"u" : %.2f, "u" : %.2f,
"c" : %.2f "c" : %.2f,
"p" : %.2f
}, },
"m" : { "m" : {
"u" : %.2f, "u" : %.2f,
"c" : %.2f "c" : %.2f,
"p" : %.2f
} }
}, },
"c" : %lu "c" : %lu

View File

@@ -1,6 +1,5 @@
<div id="newVersion" class="alert alert-info d-none">New version <span id="newVersionTag"></span>! <div id="newVersion" class="alert alert-info d-none">New version <span id="newVersionTag"></span>!
<a id="newVersionUrl" href="#" target="_blank">view</a> <a id="newVersionUrl" href="#" target="_blank">view</a> or <a href="javascript:upgrade();">click here to upgrade</a>
<span class="d-none ssl-capable"> or <a href="javascript:upgrade();">upgrade</a></span>
</div> </div>
</main> </main>

View File

@@ -3,5 +3,5 @@
"tPO" : %.2f, "tPO" : %.2f,
"tQI" : %.2f, "tQI" : %.2f,
"tQO" : %.2f, "tQO" : %.2f,
"rtc" : %llu "rtc" : %lu
} }

View File

@@ -113,7 +113,7 @@
<div class="col-xl-12 mb-3"> <div class="col-xl-12 mb-3">
<div class="bg-white rounded shadow pt-3 pb-3" style="font-size: 14px;"> <div class="bg-white rounded shadow pt-3 pb-3" style="font-size: 14px;">
<strong class="mr-3 ml-3">Real time calculation</strong><br/> <strong class="mr-3 ml-3">Real time consumption</strong><br/>
<div class="row"> <div class="row">
<div class="col-lg-3 col-sm-6"> <div class="col-lg-3 col-sm-6">
<div class="mr-3 ml-3 d-flex"> <div class="mr-3 ml-3 d-flex">
@@ -151,6 +151,35 @@
</div> </div>
</div> </div>
</div> </div>
<strong class="mr-3 ml-3 se d-none">Real time production</strong><br/>
<div class="row se d-none">
<div class="col-lg-3 col-sm-6">
<div class="mr-3 ml-3 d-flex">
<div>Hour</div>
<div class="flex-fill text-right">
<span id="eache"></span> kWh
</div>
</div>
</div>
<div class="col-lg-3 col-sm-6">
<div class="mr-3 ml-3 d-flex">
<div>Day</div>
<div class="flex-fill text-right">
<span id="eacde"></span> kWh
</div>
</div>
</div>
<div class="col-lg-3 col-sm-6">
<div class="mr-3 ml-3 d-flex">
<div>Month</div>
<div class="flex-fill text-right">
<span id="eacme"></span> kWh
</div>
</div>
</div>
<div class="col-lg-3 col-sm-6"></div>
</div>
</div> </div>
</div> </div>

View File

@@ -13,6 +13,8 @@
"h" : %.2f, "h" : %.2f,
"d" : %.1f, "d" : %.1f,
"t" : %d, "t" : %d,
"x" : %.2f "x" : %.2f,
"he" : %.2f,
"de" : %.1f
} }
} }

View File

@@ -25,6 +25,8 @@
"h" : %.2f, "h" : %.2f,
"d" : %.1f, "d" : %.1f,
"t" : %d, "t" : %d,
"x" : %.2f "x" : %.2f,
"he" : %.2f,
"de" : %.1f
} }
} }

View File

@@ -30,6 +30,8 @@
"h" : %.2f, "h" : %.2f,
"d" : %.1f, "d" : %.1f,
"t" : %d, "t" : %d,
"x" : %.2f "x" : %.2f,
"he" : %.2f,
"de" : %.1f
} }
} }

View File

@@ -34,6 +34,8 @@
"h" : %.2f, "h" : %.2f,
"d" : %.1f, "d" : %.1f,
"t" : %d, "t" : %d,
"x" : %.2f "x" : %.2f,
"he" : %.2f,
"de" : %.1f
} }
} }

View File

@@ -18,9 +18,10 @@
</li> </li>
</ul> </ul>
</header> </header>
<div class="my-3 p-3 bg-white rounded shadow"> <div class="my-3 p-3 bg-white rounded shadow {rs}">
Device is rebooting. You will be redirected to the main page when it is available again. Device is rebooting. You will be redirected to the main page when it is available again.
</div> </div>
<div class="alert alert-warning shadow {us}">Firmware upgrade in progress, DO NOT disconnect power from the device. You will be redirected to the main page when firmware upgrade is complete.</div>
</main> </main>
<script> <script>
var tries = 0; var tries = 0;