mirror of
https://github.com/UtilitechAS/amsreader-firmware.git
synced 2026-03-11 13:05:31 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e232b875fa | ||
|
|
f18171fecc | ||
|
|
a3c7a09211 | ||
|
|
a6f3bc3f71 | ||
|
|
4e451c51e1 | ||
|
|
43def1c311 | ||
|
|
2978116207 | ||
|
|
998b986604 | ||
|
|
7799431405 | ||
|
|
508c14a671 | ||
|
|
4a7ef87269 | ||
|
|
3a4fc707b0 | ||
|
|
5d2e320b07 | ||
|
|
d12613b91a | ||
|
|
e6a02f34ab | ||
|
|
0b2ffbfd77 | ||
|
|
cab6c54ed9 | ||
|
|
313024f273 |
@@ -17,8 +17,10 @@ extra_scripts =
|
||||
pre:scripts/addversion.py
|
||||
scripts/makeweb.py
|
||||
|
||||
# Sticking to v2.0.3 because of #298
|
||||
|
||||
[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
|
||||
board = esp32dev
|
||||
board_build.f_cpu = 160000000L
|
||||
@@ -33,8 +35,8 @@ extra_scripts =
|
||||
# https://github.com/Jason2866/esp32-arduino-lib-builder
|
||||
|
||||
[env:esp32s2]
|
||||
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.4/platform-espressif32-2.0.4.zip
|
||||
platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.4
|
||||
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.3
|
||||
framework = arduino
|
||||
board = esp32dev
|
||||
board_build.mcu = esp32s2
|
||||
@@ -50,7 +52,7 @@ extra_scripts =
|
||||
scripts/makeweb.py
|
||||
|
||||
[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
|
||||
board = esp32dev
|
||||
board_build.f_cpu = 160000000L
|
||||
|
||||
@@ -63,8 +63,10 @@ void AmsConfiguration::clearWifi(WiFiConfig& config) {
|
||||
uint16_t chipId;
|
||||
#if defined(ESP32)
|
||||
chipId = ESP.getEfuseMac();
|
||||
config.power = 195;
|
||||
#else
|
||||
chipId = ESP.getChipId();
|
||||
config.power = 205;
|
||||
#endif
|
||||
strcpy(config.hostname, (String("ams-") + String(chipId, HEX)).c_str());
|
||||
config.mdns = true;
|
||||
|
||||
@@ -32,6 +32,10 @@ ADC_MODE(ADC_VCC);
|
||||
#endif
|
||||
#define WDT_TIMEOUT 60
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
#include <driver/uart.h>
|
||||
#endif
|
||||
|
||||
#include "version.h"
|
||||
|
||||
#include "AmsToMqttBridge.h"
|
||||
@@ -148,6 +152,8 @@ void setup() {
|
||||
gpioConfig.hanPin = 3;
|
||||
gpioConfig.ledPin = 2;
|
||||
gpioConfig.ledInverted = true;
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
gpioConfig.hanPin = 18;
|
||||
#elif defined(ESP32)
|
||||
gpioConfig.hanPin = 16;
|
||||
gpioConfig.ledPin = 2;
|
||||
@@ -155,6 +161,7 @@ void setup() {
|
||||
gpioConfig.tempSensorPin = 14;
|
||||
#endif
|
||||
}
|
||||
|
||||
delay(1);
|
||||
config.loadTempSensors();
|
||||
hw.setup(&gpioConfig, &config);
|
||||
@@ -616,9 +623,7 @@ void setupHanPort(uint8_t pin, uint32_t baud, uint8_t parityOrdinal, bool invert
|
||||
hwSerial = &Serial2;
|
||||
}
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
if(pin == 18) {
|
||||
hwSerial = &Serial1;
|
||||
}
|
||||
hwSerial = &Serial1;
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#endif
|
||||
#endif
|
||||
@@ -651,7 +656,11 @@ void setupHanPort(uint8_t pin, uint32_t baud, uint8_t parityOrdinal, bool invert
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(ESP32)
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
hwSerial->begin(baud, serialConfig, -1, -1, invert);
|
||||
hwSerial->setRxBufferSize(768);
|
||||
uart_set_pin(UART_NUM_1, UART_PIN_NO_CHANGE, pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
|
||||
#elif defined(ESP32)
|
||||
hwSerial->begin(baud, serialConfig, -1, -1, invert);
|
||||
hwSerial->setRxBufferSize(768);
|
||||
#else
|
||||
@@ -826,6 +835,7 @@ bool readHanPort() {
|
||||
len += hanSerial->readBytes(hanBuffer+len, BUF_SIZE_HAN-len);
|
||||
if(mqttEnabled && mqtt != NULL && mqttHandler == NULL) {
|
||||
mqtt->publish(topic.c_str(), toHex(hanBuffer+pos, len));
|
||||
mqtt->loop();
|
||||
}
|
||||
while(hanSerial->available()) hanSerial->read(); // Make sure it is all empty, in case we overflowed buffer above
|
||||
len = 0;
|
||||
@@ -842,6 +852,7 @@ bool readHanPort() {
|
||||
// If MQTT bytestream payload is selected (mqttHandler == NULL), send the payload to MQTT
|
||||
if(mqttEnabled && mqtt != NULL && mqttHandler == NULL) {
|
||||
mqtt->publish(topic.c_str(), toHex(hanBuffer+pos, ctx.length));
|
||||
mqtt->loop();
|
||||
}
|
||||
|
||||
debugV("Using application data:");
|
||||
@@ -1032,6 +1043,7 @@ void WiFi_connect() {
|
||||
}
|
||||
#endif
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.setSleep(WIFI_PS_MIN_MODEM);
|
||||
#if defined(ESP32)
|
||||
if(wifi.power >= 195)
|
||||
WiFi.setTxPower(WIFI_POWER_19_5dBm);
|
||||
@@ -1055,6 +1067,8 @@ void WiFi_connect() {
|
||||
WiFi.setTxPower(WIFI_POWER_5dBm);
|
||||
else if(wifi.power >= 20)
|
||||
WiFi.setTxPower(WIFI_POWER_2dBm);
|
||||
else
|
||||
WiFi.setTxPower(WIFI_POWER_MINUS_1dBm);
|
||||
#elif defined(ESP8266)
|
||||
WiFi.setOutputPower(wifi.power / 10.0);
|
||||
#endif
|
||||
@@ -1163,6 +1177,7 @@ int16_t unwrapData(uint8_t *buf, DataParserContext &context) {
|
||||
// If MQTT bytestream payload is selected (mqttHandler == NULL), send the payload to MQTT
|
||||
if(mqttEnabled && mqtt != NULL && mqttHandler == NULL) {
|
||||
mqtt->publish(topic.c_str(), toHex(buf, curLen));
|
||||
mqtt->loop();
|
||||
}
|
||||
break;
|
||||
case DATA_TAG_MBUS:
|
||||
@@ -1170,6 +1185,7 @@ int16_t unwrapData(uint8_t *buf, DataParserContext &context) {
|
||||
// If MQTT bytestream payload is selected (mqttHandler == NULL), send the payload to MQTT
|
||||
if(mqttEnabled && mqtt != NULL && mqttHandler == NULL) {
|
||||
mqtt->publish(topic.c_str(), toHex(buf, curLen));
|
||||
mqtt->loop();
|
||||
}
|
||||
break;
|
||||
case DATA_TAG_GBT:
|
||||
@@ -1188,6 +1204,7 @@ int16_t unwrapData(uint8_t *buf, DataParserContext &context) {
|
||||
debugV("DSMR frame:");
|
||||
if(mqttEnabled && mqtt != NULL && mqttHandler == NULL) {
|
||||
mqtt->publish(topic.c_str(), (char*) buf);
|
||||
mqtt->loop();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ bool EnergyAccounting::update(AmsData* amsData) {
|
||||
}
|
||||
|
||||
use = 0;
|
||||
produce = 0;
|
||||
costHour = 0;
|
||||
currentHour = local.Hour;
|
||||
|
||||
@@ -106,19 +107,24 @@ bool EnergyAccounting::update(AmsData* amsData) {
|
||||
}
|
||||
|
||||
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();
|
||||
if(kwh > 0) {
|
||||
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) Adding %.4f kWh\n", kwh);
|
||||
use += kwh;
|
||||
if(kwhi > 0) {
|
||||
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) Adding %.4f kWh import\n", kwhi);
|
||||
use += kwhi;
|
||||
if(eapi != NULL && eapi->getValueForHour(0) != ENTSOE_NO_VALUE) {
|
||||
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());
|
||||
costHour += 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(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(initPrice) costDay = 0;
|
||||
for(int i = 0; i < local.Hour; i++) {
|
||||
float price = eapi->getValueForHour(i - local.Hour);
|
||||
for(int i = 0; i < currentHour; i++) {
|
||||
float price = eapi->getValueForHour(i - currentHour);
|
||||
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);
|
||||
costDay += price * (wh / 1000.0);
|
||||
}
|
||||
@@ -151,23 +157,59 @@ double EnergyAccounting::getUseThisHour() {
|
||||
return use;
|
||||
}
|
||||
|
||||
double EnergyAccounting::getCostThisHour() {
|
||||
return costHour;
|
||||
}
|
||||
|
||||
double EnergyAccounting::getUseToday() {
|
||||
float ret = 0.0;
|
||||
time_t now = time(nullptr);
|
||||
if(now < BUILD_EPOCH) return 0;
|
||||
tmElements_t local, utc;
|
||||
breakTime(tz->toLocal(now), local);
|
||||
for(int i = 0; i < local.Hour; i++) {
|
||||
breakTime(now - ((local.Hour - i) * 3600), utc);
|
||||
tmElements_t utc;
|
||||
for(int i = 0; i < currentHour; i++) {
|
||||
breakTime(now - ((currentHour - i) * 3600), utc);
|
||||
ret += ds->getHourImport(utc.Hour) / 1000.0;
|
||||
}
|
||||
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() {
|
||||
return costDay;
|
||||
}
|
||||
@@ -176,21 +218,6 @@ double EnergyAccounting::getCostYesterday() {
|
||||
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() {
|
||||
return data.costThisMonth + getCostToday();
|
||||
}
|
||||
@@ -233,6 +260,37 @@ float EnergyAccounting::getMonthMax() {
|
||||
return maxHour > 0 ? maxHour / count / 100.0 : 0.0;
|
||||
}
|
||||
|
||||
float EnergyAccounting::getPeak(uint8_t num) {
|
||||
if(num < 1 || num > 5) return 0.0;
|
||||
|
||||
uint8_t count = 0;
|
||||
bool included[5] = { false, false, false, false, false };
|
||||
|
||||
while(count < config->hours) {
|
||||
uint8_t maxIdx = 0;
|
||||
uint16_t maxVal = 0;
|
||||
for(uint8_t i = 0; i < 5; i++) {
|
||||
if(included[i]) continue;
|
||||
if(data.peaks[i].value > maxVal) {
|
||||
maxVal = data.peaks[i].value;
|
||||
maxIdx = i;
|
||||
}
|
||||
}
|
||||
included[maxIdx] = true;
|
||||
count++;
|
||||
}
|
||||
|
||||
uint8_t pos = 0;
|
||||
for(uint8_t i = 0; i < 5; i++) {
|
||||
if(!included[i]) continue;
|
||||
pos++;
|
||||
if(pos == num) {
|
||||
return data.peaks[i].value / 100.0;
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
bool EnergyAccounting::load() {
|
||||
if(!LittleFS.begin()) {
|
||||
if(debugger->isActive(RemoteDebug::ERROR)) {
|
||||
|
||||
@@ -42,16 +42,22 @@ public:
|
||||
bool save();
|
||||
|
||||
double getUseThisHour();
|
||||
double getCostThisHour();
|
||||
double getUseToday();
|
||||
double getUseThisMonth();
|
||||
|
||||
double getProducedThisHour();
|
||||
double getProducedToday();
|
||||
double getProducedThisMonth();
|
||||
|
||||
double getCostThisHour();
|
||||
double getCostToday();
|
||||
double getCostYesterday();
|
||||
double getUseThisMonth();
|
||||
double getCostThisMonth();
|
||||
uint16_t getCostLastMonth();
|
||||
|
||||
float getMonthMax();
|
||||
uint8_t getCurrentThreshold();
|
||||
float getPeak(uint8_t);
|
||||
|
||||
EnergyAccountingData getData();
|
||||
void setData(EnergyAccountingData&);
|
||||
@@ -66,6 +72,7 @@ private:
|
||||
Timezone *tz = NULL;
|
||||
uint8_t currentHour = 0, currentDay = 0, currentThresholdIdx = 0;
|
||||
double use, costHour, costDay;
|
||||
double produce;
|
||||
EnergyAccountingData data = { 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
void calcDayCost();
|
||||
|
||||
@@ -266,7 +266,9 @@ IEC6205675::IEC6205675(const char* d, uint8_t useMeterType, MeterConfig* meterCo
|
||||
if(meterTs != NULL) {
|
||||
AmsOctetTimestamp* amst = (AmsOctetTimestamp*) meterTs;
|
||||
time_t ts = decodeCosemDateTime(amst->dt);
|
||||
if(meterType == AmsTypeKamstrup || meterType == AmsTypeAidon) {
|
||||
if(meterType == AmsTypeAidon) {
|
||||
meterTimestamp = ts - 3600;
|
||||
} else if(meterType == AmsTypeKamstrup) {
|
||||
meterTimestamp = tz.toUTC(ts);
|
||||
} else {
|
||||
meterTimestamp = ts;
|
||||
|
||||
@@ -24,6 +24,7 @@ bool HomeAssistantMqttHandler::publish(AmsData* data, AmsData* previousState, En
|
||||
data->getMeterTimestamp()
|
||||
);
|
||||
mqtt->publish(topic + "/energy", json);
|
||||
mqtt->loop();
|
||||
}
|
||||
String meterModel = data->getMeterModel();
|
||||
meterModel.replace("\\", "\\\\");
|
||||
@@ -202,7 +203,8 @@ bool HomeAssistantMqttHandler::publishSystem(HwTools* hw) {
|
||||
(uint32_t) (millis64()/1000),
|
||||
hw->getVcc(),
|
||||
hw->getWifiRssi(),
|
||||
hw->getTemperature()
|
||||
hw->getTemperature(),
|
||||
VERSION
|
||||
);
|
||||
mqtt->publish(topic + "/state", json);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "JsonMqttHandler.h"
|
||||
#include "version.h"
|
||||
#include "hexutils.h"
|
||||
#include "Uptime.h"
|
||||
#include "web/root/json1_json.h"
|
||||
@@ -27,7 +28,9 @@ bool JsonMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyAccou
|
||||
ea->getUseThisHour(),
|
||||
ea->getUseToday(),
|
||||
ea->getCurrentThreshold(),
|
||||
ea->getMonthMax()
|
||||
ea->getMonthMax(),
|
||||
ea->getProducedThisHour(),
|
||||
ea->getProducedToday()
|
||||
);
|
||||
return mqtt->publish(topic, json);
|
||||
} else if(data->getListType() == 2) {
|
||||
@@ -55,7 +58,9 @@ bool JsonMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyAccou
|
||||
ea->getUseThisHour(),
|
||||
ea->getUseToday(),
|
||||
ea->getCurrentThreshold(),
|
||||
ea->getMonthMax()
|
||||
ea->getMonthMax(),
|
||||
ea->getProducedThisHour(),
|
||||
ea->getProducedToday()
|
||||
);
|
||||
return mqtt->publish(topic, json);
|
||||
} else if(data->getListType() == 3) {
|
||||
@@ -88,7 +93,9 @@ bool JsonMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyAccou
|
||||
ea->getUseThisHour(),
|
||||
ea->getUseToday(),
|
||||
ea->getCurrentThreshold(),
|
||||
ea->getMonthMax()
|
||||
ea->getMonthMax(),
|
||||
ea->getProducedThisHour(),
|
||||
ea->getProducedToday()
|
||||
);
|
||||
return mqtt->publish(topic, json);
|
||||
} else if(data->getListType() == 4) {
|
||||
@@ -125,7 +132,9 @@ bool JsonMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyAccou
|
||||
ea->getUseThisHour(),
|
||||
ea->getUseToday(),
|
||||
ea->getCurrentThreshold(),
|
||||
ea->getMonthMax()
|
||||
ea->getMonthMax(),
|
||||
ea->getProducedThisHour(),
|
||||
ea->getProducedToday()
|
||||
);
|
||||
return mqtt->publish(topic, json);
|
||||
}
|
||||
@@ -272,7 +281,8 @@ bool JsonMqttHandler::publishSystem(HwTools* hw) {
|
||||
(uint32_t) (millis64()/1000),
|
||||
hw->getVcc(),
|
||||
hw->getWifiRssi(),
|
||||
hw->getTemperature()
|
||||
hw->getTemperature(),
|
||||
VERSION
|
||||
);
|
||||
init = mqtt->publish(topic, json);
|
||||
return init;
|
||||
|
||||
@@ -74,8 +74,13 @@ bool RawMqttHandler::publish(AmsData* data, AmsData* meterState, EnergyAccountin
|
||||
}
|
||||
mqtt->publish(topic + "/realtime/import/hour", String(ea->getUseThisHour(), 3));
|
||||
mqtt->publish(topic + "/realtime/import/day", String(ea->getUseToday(), 2));
|
||||
for(uint8_t i = 1; i <= ea->getConfig()->hours; i++) {
|
||||
mqtt->publish(topic + "/realtime/import/peak/" + String(i, 10), String(ea->getPeak(i), 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/export/hour", String(ea->getProducedThisHour(), 3));
|
||||
mqtt->publish(topic + "/realtime/export/day", String(ea->getProducedToday(), 2));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -591,9 +591,9 @@ void AmsWebServer::configThresholdsHtml() {
|
||||
|
||||
String html = String((const __FlashStringHelper*) THRESHOLDS_HTML);
|
||||
for(int i = 0; i < 9; i++) {
|
||||
html.replace("{t" + String(i) + "}", String(config->thresholds[i]));
|
||||
html.replace("{t" + String(i) + "}", String(config->thresholds[i], 10));
|
||||
}
|
||||
html.replace("{h}", String(config->hours));
|
||||
html.replace("{h}", String(config->hours, 10));
|
||||
|
||||
server.setContentLength(html.length() + HEAD_HTML_LEN + FOOT_HTML_LEN);
|
||||
server.send_P(200, MIME_HTML, HEAD_HTML);
|
||||
@@ -708,6 +708,12 @@ void AmsWebServer::dataJson() {
|
||||
if(eapi != NULL && strlen(eapi->getToken()) > 0)
|
||||
price = eapi->getValueForHour(0);
|
||||
|
||||
String peaks = "";
|
||||
for(uint8_t i = 1; i <= ea->getConfig()->hours; i++) {
|
||||
if(!peaks.isEmpty()) peaks += ",";
|
||||
peaks += String(ea->getPeak(i));
|
||||
}
|
||||
|
||||
snprintf_P(buf, BufferSize, DATA_JSON,
|
||||
maxPwr == 0 ? meterState->isThreePhase() ? 20000 : 10000 : maxPwr,
|
||||
meterConfig->productionCapacity,
|
||||
@@ -744,13 +750,17 @@ void AmsWebServer::dataJson() {
|
||||
meterState->getMeterType(),
|
||||
meterConfig->distributionSystem,
|
||||
ea->getMonthMax(),
|
||||
peaks.c_str(),
|
||||
ea->getCurrentThreshold(),
|
||||
ea->getUseThisHour(),
|
||||
ea->getCostThisHour(),
|
||||
ea->getProducedThisHour(),
|
||||
ea->getUseToday(),
|
||||
ea->getCostToday(),
|
||||
ea->getProducedToday(),
|
||||
ea->getUseThisMonth(),
|
||||
ea->getCostThisMonth(),
|
||||
ea->getProducedThisMonth(),
|
||||
(uint32_t) time(nullptr)
|
||||
);
|
||||
|
||||
@@ -1036,12 +1046,12 @@ void AmsWebServer::handleSetup() {
|
||||
gpioConfig->ledPinRed = 13;
|
||||
gpioConfig->ledPinGreen = 14;
|
||||
gpioConfig->ledRgbInverted = true;
|
||||
gpioConfig->vccPin = 35;
|
||||
gpioConfig->vccPin = 10;
|
||||
gpioConfig->vccResistorGnd = 22;
|
||||
gpioConfig->vccResistorVcc = 33;
|
||||
break;
|
||||
case 6: // Pow-P1
|
||||
gpioConfig->hanPin = 18;
|
||||
gpioConfig->hanPin = 16;
|
||||
gpioConfig->apPin = 0;
|
||||
gpioConfig->ledPinRed = 13;
|
||||
gpioConfig->ledPinGreen = 14;
|
||||
@@ -1659,7 +1669,7 @@ void AmsWebServer::firmwareUpload() {
|
||||
}
|
||||
uploadFile(FILE_FIRMWARE);
|
||||
if(upload.status == UPLOAD_FILE_END) {
|
||||
performRestart = true;
|
||||
rebootForUpgrade = true;
|
||||
server.sendHeader("Location","/restart-wait");
|
||||
server.send(303);
|
||||
}
|
||||
@@ -1672,102 +1682,9 @@ void AmsWebServer::firmwareDownload() {
|
||||
return;
|
||||
|
||||
printD("Firmware download URL triggered");
|
||||
if(server.hasArg("version")) {
|
||||
String version = server.arg("version");
|
||||
String versionStripped = version.substring(1);
|
||||
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/hub/firmware/update";
|
||||
server.sendHeader("Location","/");
|
||||
server.send(303);
|
||||
t_httpUpdate_return ret = ESPhttpUpdate.update(client, url, VERSION);
|
||||
switch(ret) {
|
||||
case HTTP_UPDATE_FAILED:
|
||||
printE("[update] Update failed.");
|
||||
break;
|
||||
case HTTP_UPDATE_NO_UPDATES:
|
||||
printI("[update] Update no Update.");
|
||||
break;
|
||||
case HTTP_UPDATE_OK:
|
||||
printI("[update] Update ok.");
|
||||
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);
|
||||
}
|
||||
performUpgrade = true;
|
||||
server.sendHeader("Location","/restart-wait");
|
||||
server.send(303);
|
||||
}
|
||||
|
||||
void AmsWebServer::restartHtml() {
|
||||
@@ -1817,6 +1734,14 @@ void AmsWebServer::restartWaitHtml() {
|
||||
}
|
||||
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_PRAGMA, PRAGMA_NO_CACHE);
|
||||
server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF);
|
||||
@@ -1825,7 +1750,7 @@ void AmsWebServer::restartWaitHtml() {
|
||||
server.send(200, MIME_HTML, html);
|
||||
|
||||
yield();
|
||||
if(performRestart) {
|
||||
if(performRestart || rebootForUpgrade) {
|
||||
if(ds != NULL) {
|
||||
ds->save();
|
||||
}
|
||||
@@ -1837,6 +1762,40 @@ void AmsWebServer::restartWaitHtml() {
|
||||
ESP.restart();
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <WiFi.h>
|
||||
#include <WebServer.h>
|
||||
#include <HTTPClient.h>
|
||||
#include <HTTPUpdate.h>
|
||||
#else
|
||||
#warning "Unsupported board type"
|
||||
#endif
|
||||
@@ -58,6 +59,8 @@ private:
|
||||
bool uploading = false;
|
||||
File file;
|
||||
bool performRestart = false;
|
||||
bool performUpgrade = false;
|
||||
bool rebootForUpgrade = false;
|
||||
|
||||
static const uint16_t BufferSize = 2048;
|
||||
char* buf;
|
||||
|
||||
@@ -478,7 +478,7 @@ var drawDay = function() {
|
||||
timeout: 30000,
|
||||
dataType: '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 hour = moment.utc().hours();
|
||||
var offset = moment().utcOffset()/60;
|
||||
@@ -486,13 +486,13 @@ var drawDay = function() {
|
||||
for(var i = hour; i<24; i++) {
|
||||
var imp = json["i"+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);
|
||||
};
|
||||
for(var i = 0; i < hour; i++) {
|
||||
var imp = json["i"+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);
|
||||
};
|
||||
ea = google.visualization.arrayToDataTable(data);
|
||||
@@ -511,7 +511,7 @@ var drawMonth = function() {
|
||||
timeout: 30000,
|
||||
dataType: '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 day = moment().date();
|
||||
var eom = moment().subtract(1, 'months').endOf('month').date();
|
||||
@@ -519,13 +519,13 @@ var drawMonth = function() {
|
||||
for(var i = day; i<=eom; i++) {
|
||||
var imp = json["i"+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);
|
||||
}
|
||||
for(var i = 1; i < day; i++) {
|
||||
var imp = json["i"+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);
|
||||
}
|
||||
ma = google.visualization.arrayToDataTable(data);
|
||||
@@ -790,6 +790,12 @@ var fetch = function() {
|
||||
if(currency) {
|
||||
$('.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) {
|
||||
@@ -878,7 +884,7 @@ var fetch = function() {
|
||||
|
||||
var upgrade = function() {
|
||||
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();
|
||||
window.location.href="/upgrade?version=" + nextVersion.tag_name;
|
||||
}
|
||||
|
||||
@@ -35,18 +35,22 @@
|
||||
"ds" : %d,
|
||||
"ea" : {
|
||||
"x" : %.1f,
|
||||
"p" : [ %s ],
|
||||
"t" : %d,
|
||||
"h" : {
|
||||
"u" : %.2f,
|
||||
"c" : %.2f
|
||||
"c" : %.2f,
|
||||
"p" : %.2f
|
||||
},
|
||||
"d" : {
|
||||
"u" : %.2f,
|
||||
"c" : %.2f
|
||||
"c" : %.2f,
|
||||
"p" : %.2f
|
||||
},
|
||||
"m" : {
|
||||
"u" : %.2f,
|
||||
"c" : %.2f
|
||||
"c" : %.2f,
|
||||
"p" : %.2f
|
||||
}
|
||||
},
|
||||
"c" : %lu
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<div id="newVersion" class="alert alert-info d-none">New version <span id="newVersionTag"></span>!
|
||||
<a id="newVersionUrl" href="#" target="_blank">view</a>
|
||||
<span class="d-none ssl-capable"> or <a href="javascript:upgrade();">upgrade</a></span>
|
||||
<a id="newVersionUrl" href="#" target="_blank">view</a> or <a href="javascript:upgrade();">click here to upgrade</a>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
"tPO" : %.2f,
|
||||
"tQI" : %.2f,
|
||||
"tQO" : %.2f,
|
||||
"rtc" : %llu
|
||||
"rtc" : %lu
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
|
||||
<div class="col-xl-12 mb-3">
|
||||
<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="col-lg-3 col-sm-6">
|
||||
<div class="mr-3 ml-3 d-flex">
|
||||
@@ -151,6 +151,35 @@
|
||||
</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>
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
"h" : %.2f,
|
||||
"d" : %.1f,
|
||||
"t" : %d,
|
||||
"x" : %.2f
|
||||
"x" : %.2f,
|
||||
"he" : %.2f,
|
||||
"de" : %.1f
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
"h" : %.2f,
|
||||
"d" : %.1f,
|
||||
"t" : %d,
|
||||
"x" : %.2f
|
||||
"x" : %.2f,
|
||||
"he" : %.2f,
|
||||
"de" : %.1f
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
"h" : %.2f,
|
||||
"d" : %.1f,
|
||||
"t" : %d,
|
||||
"x" : %.2f
|
||||
"x" : %.2f,
|
||||
"he" : %.2f,
|
||||
"de" : %.1f
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
"h" : %.2f,
|
||||
"d" : %.1f,
|
||||
"t" : %d,
|
||||
"x" : %.2f
|
||||
"x" : %.2f,
|
||||
"he" : %.2f,
|
||||
"de" : %.1f
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,5 +4,6 @@
|
||||
"up" : %d,
|
||||
"vcc" : %.3f,
|
||||
"rssi": %d,
|
||||
"temp": %.2f
|
||||
"temp": %.2f,
|
||||
"version": "%s"
|
||||
}
|
||||
|
||||
@@ -18,9 +18,10 @@
|
||||
</li>
|
||||
</ul>
|
||||
</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.
|
||||
</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>
|
||||
<script>
|
||||
var tries = 0;
|
||||
|
||||
Reference in New Issue
Block a user