mirror of
https://github.com/UtilitechAS/amsreader-firmware.git
synced 2026-03-28 02:53:12 +00:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32fa2f5632 | ||
|
|
026cd25c8c | ||
|
|
12be475b02 | ||
|
|
06ec97b42a | ||
|
|
b420a0e6f4 | ||
|
|
fe7be81f1e | ||
|
|
7674fc2ad0 | ||
|
|
d50181c347 | ||
|
|
8ca771fa5a | ||
|
|
7d557b2679 | ||
|
|
2f0c912388 | ||
|
|
57e6d0fbe3 | ||
|
|
e7c25fafda | ||
|
|
2a4772fe25 | ||
|
|
f1f7408208 | ||
|
|
feb8e5007b | ||
|
|
c4af1ee74f | ||
|
|
992e1b6121 | ||
|
|
9cc7529934 | ||
|
|
ef8715be6d | ||
|
|
44bcd386d1 | ||
|
|
01547f9a52 | ||
|
|
940d38af5c | ||
|
|
a055465ce0 |
@@ -43,3 +43,37 @@ FF // Last byte of OBIS in previous block
|
|||||||
0600000000 // Accumulated export
|
0600000000 // Accumulated export
|
||||||
8BA4
|
8BA4
|
||||||
7E
|
7E
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
7E A1 23 CE FF 03 13 21 55 E6 E7 00
|
||||||
|
|
||||||
|
0F 00 00 08 E2
|
||||||
|
0C 07 E5 07 13 01 0C 1A 0A FF 80 00 00
|
||||||
|
|
||||||
|
02 0B // 11
|
||||||
|
01 0B // 11
|
||||||
|
02 04 12 00 28 09 06 00 08 19 09 00 FF 0F 02 12 00 00
|
||||||
|
02 04 12 00 28 09 06 00 08 19 09 00 FF 0F 01 12 00 00
|
||||||
|
02 04 12 00 01 09 06 00 00 60 01 00 FF 0F 02 12 00 00
|
||||||
|
02 04 12 00 03 09 06 01 00 01 07 00 FF 0F 02 12 00 00
|
||||||
|
02 04 12 00 03 09 06 01 00 02 07 00 FF 0F 02 12 00 00
|
||||||
|
02 04 12 00 03 09 06 01 01 01 08 00 FF 0F 02 12 00 00
|
||||||
|
02 04 12 00 03 09 06 01 01 02 08 00 FF 0F 02 12 00 00
|
||||||
|
02 04 12 00 03 09 06 01 01 05 08 00 FF 0F 02 12 00 00
|
||||||
|
02 04 12 00 03 09 06 01 01 06 08 00 FF 0F 02 12 00 00
|
||||||
|
02 04 12 00 03 09 06 01 01 07 08 00 FF 0F 02 12 00 00
|
||||||
|
02 04 12 00 03 09 06 01 01 08 08 00 FF 0F 02 12 00 00
|
||||||
|
09 06 00 08 19 09 00 FF
|
||||||
|
09 08 34 33 30 39 34 33 35 31
|
||||||
|
06 00 00 00 0B
|
||||||
|
06 00 00 00 00
|
||||||
|
06 00 00 00 10
|
||||||
|
06 00 00 00 04
|
||||||
|
06 00 00 00 00
|
||||||
|
06 00 00 00 08
|
||||||
|
06 00 00 00 00
|
||||||
|
06 00 00 00 01
|
||||||
|
7C 8B
|
||||||
|
7E
|
||||||
@@ -19,6 +19,6 @@ hf = """
|
|||||||
#define VERSION "{}"
|
#define VERSION "{}"
|
||||||
#endif
|
#endif
|
||||||
#define BUILD_EPOCH {}
|
#define BUILD_EPOCH {}
|
||||||
""".format(version, time())
|
""".format(version, round(time()))
|
||||||
with open(FILENAME_VERSION_H, 'w+') as f:
|
with open(FILENAME_VERSION_H, 'w+') as f:
|
||||||
f.write(hf)
|
f.write(hf)
|
||||||
|
|||||||
@@ -517,6 +517,7 @@ bool AmsConfiguration::getEnergyAccountingConfig(EnergyAccountingConfig& config)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool AmsConfiguration::setEnergyAccountingConfig(EnergyAccountingConfig& config) {
|
bool AmsConfiguration::setEnergyAccountingConfig(EnergyAccountingConfig& config) {
|
||||||
|
if(config.hours > 5) config.hours = 5;
|
||||||
EnergyAccountingConfig existing;
|
EnergyAccountingConfig existing;
|
||||||
if(getEnergyAccountingConfig(existing)) {
|
if(getEnergyAccountingConfig(existing)) {
|
||||||
for(int i = 0; i < 9; i++) {
|
for(int i = 0; i < 9; i++) {
|
||||||
@@ -525,6 +526,7 @@ bool AmsConfiguration::setEnergyAccountingConfig(EnergyAccountingConfig& config)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
config.thresholds[9] = 255;
|
config.thresholds[9] = 255;
|
||||||
|
energyAccountingChanged |= config.hours != existing.hours;
|
||||||
} else {
|
} else {
|
||||||
energyAccountingChanged = true;
|
energyAccountingChanged = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ enum AmsType {
|
|||||||
AmsTypeIskra = 0x08,
|
AmsTypeIskra = 0x08,
|
||||||
AmsTypeLandis = 0x09,
|
AmsTypeLandis = 0x09,
|
||||||
AmsTypeSagemcom = 0x0A,
|
AmsTypeSagemcom = 0x0A,
|
||||||
|
AmsTypeLng = 0x0B,
|
||||||
AmsTypeCustom = 0x88,
|
AmsTypeCustom = 0x88,
|
||||||
AmsTypeUnknown = 0xFF
|
AmsTypeUnknown = 0xFF
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -65,10 +65,11 @@ ADC_MODE(ADC_VCC);
|
|||||||
#include "RemoteDebug.h"
|
#include "RemoteDebug.h"
|
||||||
|
|
||||||
#define BUF_SIZE_COMMON (2048)
|
#define BUF_SIZE_COMMON (2048)
|
||||||
#define BUF_SIZE_HAN (1024)
|
#define BUF_SIZE_HAN (1280)
|
||||||
|
|
||||||
#include "IEC6205621.h"
|
#include "IEC6205621.h"
|
||||||
#include "IEC6205675.h"
|
#include "IEC6205675.h"
|
||||||
|
#include "LNG.h"
|
||||||
|
|
||||||
#include "ams/DataParsers.h"
|
#include "ams/DataParsers.h"
|
||||||
|
|
||||||
@@ -512,6 +513,7 @@ void loop() {
|
|||||||
if (mqttEnabled || config.isMqttChanged()) {
|
if (mqttEnabled || config.isMqttChanged()) {
|
||||||
if(mqtt == NULL || !mqtt->connected() || config.isMqttChanged()) {
|
if(mqtt == NULL || !mqtt->connected() || config.isMqttChanged()) {
|
||||||
MQTT_connect();
|
MQTT_connect();
|
||||||
|
config.ackMqttChange();
|
||||||
}
|
}
|
||||||
} else if(mqtt != NULL && mqtt->connected()) {
|
} else if(mqtt != NULL && mqtt->connected()) {
|
||||||
mqttClient->stop();
|
mqttClient->stop();
|
||||||
@@ -593,7 +595,7 @@ void loop() {
|
|||||||
}
|
}
|
||||||
if(now - lastSysupdate > 10000) {
|
if(now - lastSysupdate > 10000) {
|
||||||
if(mqtt != NULL && mqttHandler != NULL && WiFi.getMode() != WIFI_AP && WiFi.status() == WL_CONNECTED && mqtt->connected() && !topic.isEmpty()) {
|
if(mqtt != NULL && mqttHandler != NULL && WiFi.getMode() != WIFI_AP && WiFi.status() == WL_CONNECTED && mqtt->connected() && !topic.isEmpty()) {
|
||||||
mqttHandler->publishSystem(&hw);
|
mqttHandler->publishSystem(&hw, eapi, &ea);
|
||||||
}
|
}
|
||||||
lastSysupdate = now;
|
lastSysupdate = now;
|
||||||
}
|
}
|
||||||
@@ -848,20 +850,26 @@ bool readHanPort() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AmsData data;
|
AmsData data;
|
||||||
|
char* payload = ((char *) (hanBuffer)) + pos;
|
||||||
if(ctx.type == DATA_TAG_DLMS) {
|
if(ctx.type == DATA_TAG_DLMS) {
|
||||||
// If MQTT bytestream payload is selected (mqttHandler == NULL), send the payload to MQTT
|
// If MQTT bytestream payload is selected (mqttHandler == NULL), send the payload to MQTT
|
||||||
if(mqttEnabled && mqtt != NULL && mqttHandler == NULL) {
|
if(mqttEnabled && mqtt != NULL && mqttHandler == NULL) {
|
||||||
mqtt->publish(topic.c_str(), toHex(hanBuffer+pos, ctx.length));
|
mqtt->publish(topic.c_str(), toHex((byte*) payload, ctx.length));
|
||||||
mqtt->loop();
|
mqtt->loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
debugV("Using application data:");
|
debugV("Using application data:");
|
||||||
if(Debug.isActive(RemoteDebug::VERBOSE)) debugPrint(hanBuffer+pos, 0, ctx.length);
|
if(Debug.isActive(RemoteDebug::VERBOSE)) debugPrint((byte*) payload, 0, ctx.length);
|
||||||
|
|
||||||
// TODO: Split IEC6205675 into DataParserKaifa and DataParserObis. This way we can add other means of parsing, for those other proprietary formats
|
// Rudimentary detector for L&G proprietary format
|
||||||
data = IEC6205675(((char *) (hanBuffer)) + pos, meterState.getMeterType(), &meterConfig, ctx);
|
if(payload[0] == CosemTypeStructure && payload[2] == CosemTypeArray && payload[1] == payload[3]) {
|
||||||
|
data = LNG(payload, meterState.getMeterType(), &meterConfig, ctx, &Debug);
|
||||||
|
} else {
|
||||||
|
// TODO: Split IEC6205675 into DataParserKaifa and DataParserObis. This way we can add other means of parsing, for those other proprietary formats
|
||||||
|
data = IEC6205675(payload, meterState.getMeterType(), &meterConfig, ctx);
|
||||||
|
}
|
||||||
} else if(ctx.type == DATA_TAG_DSMR) {
|
} else if(ctx.type == DATA_TAG_DSMR) {
|
||||||
data = IEC6205621(((char *) (hanBuffer)) + pos);
|
data = IEC6205621(payload);
|
||||||
}
|
}
|
||||||
len = 0;
|
len = 0;
|
||||||
|
|
||||||
@@ -1043,7 +1051,7 @@ void WiFi_connect() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
WiFi.mode(WIFI_STA);
|
WiFi.mode(WIFI_STA);
|
||||||
WiFi.setSleep(WIFI_PS_MIN_MODEM);
|
WiFi.setSleep(WIFI_PS_MAX_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);
|
||||||
@@ -1243,7 +1251,6 @@ void MQTT_connect() {
|
|||||||
if(Debug.isActive(RemoteDebug::WARNING)) debugW("No MQTT config");
|
if(Debug.isActive(RemoteDebug::WARNING)) debugW("No MQTT config");
|
||||||
mqttEnabled = false;
|
mqttEnabled = false;
|
||||||
ws.setMqttEnabled(false);
|
ws.setMqttEnabled(false);
|
||||||
config.ackMqttChange();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(mqtt != NULL) {
|
if(mqtt != NULL) {
|
||||||
@@ -1258,20 +1265,19 @@ void MQTT_connect() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mqtt->disconnect();
|
mqtt->disconnect();
|
||||||
|
if(config.isMqttChanged()) {
|
||||||
|
if(mqttSecureClient != NULL) {
|
||||||
|
mqttSecureClient->stop();
|
||||||
|
delete mqttSecureClient;
|
||||||
|
mqttSecureClient = NULL;
|
||||||
|
} else {
|
||||||
|
mqttClient->stop();
|
||||||
|
}
|
||||||
|
mqttClient = NULL;
|
||||||
|
}
|
||||||
yield();
|
yield();
|
||||||
} else {
|
} else {
|
||||||
uint16_t size = 256;
|
mqtt = new MQTTClient(1024);
|
||||||
switch(mqttConfig.payloadFormat) {
|
|
||||||
case 0: // JSON
|
|
||||||
case 4: // Home Assistant
|
|
||||||
size = 768;
|
|
||||||
break;
|
|
||||||
case 255: // Raw frame
|
|
||||||
size = 1024;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
mqtt = new MQTTClient(size);
|
|
||||||
ws.setMqtt(mqtt);
|
ws.setMqtt(mqtt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1306,54 +1312,54 @@ void MQTT_connect() {
|
|||||||
debugI("MQTT SSL is configured (%dkb free heap)", ESP.getFreeHeap());
|
debugI("MQTT SSL is configured (%dkb free heap)", ESP.getFreeHeap());
|
||||||
if(mqttSecureClient == NULL) {
|
if(mqttSecureClient == NULL) {
|
||||||
mqttSecureClient = new WiFiClientSecure();
|
mqttSecureClient = new WiFiClientSecure();
|
||||||
}
|
#if defined(ESP8266)
|
||||||
#if defined(ESP8266)
|
mqttSecureClient->setBufferSizes(512, 512);
|
||||||
mqttSecureClient->setBufferSizes(512, 512);
|
#endif
|
||||||
#endif
|
|
||||||
|
if(LittleFS.begin()) {
|
||||||
if(LittleFS.begin()) {
|
File file;
|
||||||
File file;
|
|
||||||
|
|
||||||
if(LittleFS.exists(FILE_MQTT_CA)) {
|
if(LittleFS.exists(FILE_MQTT_CA)) {
|
||||||
debugI("Found MQTT CA file (%dkb free heap)", ESP.getFreeHeap());
|
debugI("Found MQTT CA file (%dkb free heap)", ESP.getFreeHeap());
|
||||||
file = LittleFS.open(FILE_MQTT_CA, "r");
|
file = LittleFS.open(FILE_MQTT_CA, "r");
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
BearSSL::X509List *serverTrustedCA = new BearSSL::X509List(file);
|
BearSSL::X509List *serverTrustedCA = new BearSSL::X509List(file);
|
||||||
mqttSecureClient->setTrustAnchors(serverTrustedCA);
|
mqttSecureClient->setTrustAnchors(serverTrustedCA);
|
||||||
#elif defined(ESP32)
|
#elif defined(ESP32)
|
||||||
mqttSecureClient->loadCACert(file, file.size());
|
mqttSecureClient->loadCACert(file, file.size());
|
||||||
#endif
|
#endif
|
||||||
file.close();
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(LittleFS.exists(FILE_MQTT_CERT) && LittleFS.exists(FILE_MQTT_KEY)) {
|
||||||
|
#if defined(ESP8266)
|
||||||
|
debugI("Found MQTT certificate file (%dkb free heap)", ESP.getFreeHeap());
|
||||||
|
file = LittleFS.open(FILE_MQTT_CERT, "r");
|
||||||
|
BearSSL::X509List *serverCertList = new BearSSL::X509List(file);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
debugI("Found MQTT key file (%dkb free heap)", ESP.getFreeHeap());
|
||||||
|
file = LittleFS.open(FILE_MQTT_KEY, "r");
|
||||||
|
BearSSL::PrivateKey *serverPrivKey = new BearSSL::PrivateKey(file);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
debugD("Setting client certificates (%dkb free heap)", ESP.getFreeHeap());
|
||||||
|
mqttSecureClient->setClientRSACert(serverCertList, serverPrivKey);
|
||||||
|
#elif defined(ESP32)
|
||||||
|
debugI("Found MQTT certificate file (%dkb free heap)", ESP.getFreeHeap());
|
||||||
|
file = LittleFS.open(FILE_MQTT_CERT, "r");
|
||||||
|
mqttSecureClient->loadCertificate(file, file.size());
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
debugI("Found MQTT key file (%dkb free heap)", ESP.getFreeHeap());
|
||||||
|
file = LittleFS.open(FILE_MQTT_KEY, "r");
|
||||||
|
mqttSecureClient->loadPrivateKey(file, file.size());
|
||||||
|
file.close();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
LittleFS.end();
|
||||||
|
debugD("MQTT SSL setup complete (%dkb free heap)", ESP.getFreeHeap());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(LittleFS.exists(FILE_MQTT_CERT) && LittleFS.exists(FILE_MQTT_KEY)) {
|
|
||||||
#if defined(ESP8266)
|
|
||||||
debugI("Found MQTT certificate file (%dkb free heap)", ESP.getFreeHeap());
|
|
||||||
file = LittleFS.open(FILE_MQTT_CERT, "r");
|
|
||||||
BearSSL::X509List *serverCertList = new BearSSL::X509List(file);
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
debugI("Found MQTT key file (%dkb free heap)", ESP.getFreeHeap());
|
|
||||||
file = LittleFS.open(FILE_MQTT_KEY, "r");
|
|
||||||
BearSSL::PrivateKey *serverPrivKey = new BearSSL::PrivateKey(file);
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
debugD("Setting client certificates (%dkb free heap)", ESP.getFreeHeap());
|
|
||||||
mqttSecureClient->setClientRSACert(serverCertList, serverPrivKey);
|
|
||||||
#elif defined(ESP32)
|
|
||||||
debugI("Found MQTT certificate file (%dkb free heap)", ESP.getFreeHeap());
|
|
||||||
file = LittleFS.open(FILE_MQTT_CERT, "r");
|
|
||||||
mqttSecureClient->loadCertificate(file, file.size());
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
debugI("Found MQTT key file (%dkb free heap)", ESP.getFreeHeap());
|
|
||||||
file = LittleFS.open(FILE_MQTT_KEY, "r");
|
|
||||||
mqttSecureClient->loadPrivateKey(file, file.size());
|
|
||||||
file.close();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
LittleFS.end();
|
|
||||||
debugD("MQTT SSL setup complete (%dkb free heap)", ESP.getFreeHeap());
|
|
||||||
}
|
}
|
||||||
mqttClient = mqttSecureClient;
|
mqttClient = mqttSecureClient;
|
||||||
} else if(mqttClient == NULL) {
|
} else if(mqttClient == NULL) {
|
||||||
@@ -1378,10 +1384,9 @@ void MQTT_connect() {
|
|||||||
if ((strlen(mqttConfig.username) == 0 && mqtt->connect(mqttConfig.clientId)) ||
|
if ((strlen(mqttConfig.username) == 0 && mqtt->connect(mqttConfig.clientId)) ||
|
||||||
(strlen(mqttConfig.username) > 0 && mqtt->connect(mqttConfig.clientId, mqttConfig.username, mqttConfig.password))) {
|
(strlen(mqttConfig.username) > 0 && mqtt->connect(mqttConfig.clientId, mqttConfig.username, mqttConfig.password))) {
|
||||||
if (Debug.isActive(RemoteDebug::INFO)) debugI("Successfully connected to MQTT!");
|
if (Debug.isActive(RemoteDebug::INFO)) debugI("Successfully connected to MQTT!");
|
||||||
config.ackMqttChange();
|
|
||||||
|
|
||||||
if(mqttHandler != NULL) {
|
if(mqttHandler != NULL) {
|
||||||
mqttHandler->publishSystem(&hw);
|
mqttHandler->publishSystem(&hw, eapi, &ea);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscribe to the chosen MQTT topic, if set in configuration
|
// Subscribe to the chosen MQTT topic, if set in configuration
|
||||||
|
|||||||
@@ -237,11 +237,12 @@ float EnergyAccounting::getMonthMax() {
|
|||||||
uint32_t maxHour = 0.0;
|
uint32_t maxHour = 0.0;
|
||||||
bool included[5] = { false, false, false, false, false };
|
bool included[5] = { false, false, false, false, false };
|
||||||
|
|
||||||
while(count < config->hours) {
|
while(count < config->hours && count <= 5) {
|
||||||
uint8_t maxIdx = 0;
|
uint8_t maxIdx = 0;
|
||||||
uint16_t maxVal = 0;
|
uint16_t maxVal = 0;
|
||||||
for(uint8_t i = 0; i < 5; i++) {
|
for(uint8_t i = 0; i < 5; i++) {
|
||||||
if(included[i]) continue;
|
if(included[i]) continue;
|
||||||
|
if(data.peaks[i].day == 0) continue;
|
||||||
if(data.peaks[i].value > maxVal) {
|
if(data.peaks[i].value > maxVal) {
|
||||||
maxVal = data.peaks[i].value;
|
maxVal = data.peaks[i].value;
|
||||||
maxIdx = i;
|
maxIdx = i;
|
||||||
@@ -253,9 +254,7 @@ float EnergyAccounting::getMonthMax() {
|
|||||||
|
|
||||||
for(uint8_t i = 0; i < 5; i++) {
|
for(uint8_t i = 0; i < 5; i++) {
|
||||||
if(!included[i]) continue;
|
if(!included[i]) continue;
|
||||||
if(data.peaks[i].day > 0) {
|
maxHour += data.peaks[i].value;
|
||||||
maxHour += data.peaks[i].value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return maxHour > 0 ? maxHour / count / 100.0 : 0.0;
|
return maxHour > 0 ? maxHour / count / 100.0 : 0.0;
|
||||||
}
|
}
|
||||||
@@ -266,7 +265,7 @@ float EnergyAccounting::getPeak(uint8_t num) {
|
|||||||
uint8_t count = 0;
|
uint8_t count = 0;
|
||||||
bool included[5] = { false, false, false, false, false };
|
bool included[5] = { false, false, false, false, false };
|
||||||
|
|
||||||
while(count < config->hours) {
|
while(count < config->hours && count <= 5) {
|
||||||
uint8_t maxIdx = 0;
|
uint8_t maxIdx = 0;
|
||||||
uint16_t maxVal = 0;
|
uint16_t maxVal = 0;
|
||||||
for(uint8_t i = 0; i < 5; i++) {
|
for(uint8_t i = 0; i < 5; i++) {
|
||||||
@@ -341,7 +340,7 @@ bool EnergyAccounting::load() {
|
|||||||
this->data.peaks[b].day = b;
|
this->data.peaks[b].day = b;
|
||||||
memcpy(&this->data.peaks[b].value, buf+i, 2);
|
memcpy(&this->data.peaks[b].value, buf+i, 2);
|
||||||
b++;
|
b++;
|
||||||
if(b >= config->hours) break;
|
if(b >= config->hours || b >= 5) break;
|
||||||
}
|
}
|
||||||
ret = true;
|
ret = true;
|
||||||
} else if(buf[0] == 1) {
|
} else if(buf[0] == 1) {
|
||||||
|
|||||||
111
src/LNG.cpp
Normal file
111
src/LNG.cpp
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
#include "LNG.h"
|
||||||
|
#include "lwip/def.h"
|
||||||
|
#include "ams/Cosem.h"
|
||||||
|
|
||||||
|
LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, DataParserContext &ctx, RemoteDebug* debugger) {
|
||||||
|
LngHeader* h = (LngHeader*) payload;
|
||||||
|
if(h->tag == CosemTypeStructure && h->arrayTag == CosemTypeArray) {
|
||||||
|
meterType = AmsTypeLng;
|
||||||
|
this->packageTimestamp = ctx.timestamp;
|
||||||
|
|
||||||
|
uint8_t* ptr = (uint8_t*) &h[1];
|
||||||
|
uint8_t* data = ptr + (18*h->arrayLength); // Skip descriptors
|
||||||
|
|
||||||
|
uint16_t o170 = 0, o270 = 0;
|
||||||
|
uint16_t o181 = 0, o182 = 0;
|
||||||
|
uint16_t o281 = 0, o282 = 0;
|
||||||
|
LngObisDescriptor* descriptor = (LngObisDescriptor*) ptr;
|
||||||
|
for(uint8_t x = 0; x < h->arrayLength-1; x++) {
|
||||||
|
ptr = (uint8_t*) &descriptor[1];
|
||||||
|
descriptor = (LngObisDescriptor*) ptr;
|
||||||
|
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(L&G) OBIS %d.%d.%d with type 0x%02X", descriptor->obis[2], descriptor->obis[3], descriptor->obis[4], *data);
|
||||||
|
|
||||||
|
CosemData* item = (CosemData*) data;
|
||||||
|
if(descriptor->obis[2] == 1) {
|
||||||
|
if(descriptor->obis[3] == 7) {
|
||||||
|
if(descriptor->obis[4] == 0) {
|
||||||
|
o170 = ntohl(item->dlu.data);
|
||||||
|
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data));
|
||||||
|
}
|
||||||
|
} else if(descriptor->obis[3] == 8) {
|
||||||
|
if(descriptor->obis[4] == 0) {
|
||||||
|
activeImportCounter = ntohl(item->dlu.data) / 1000.0;
|
||||||
|
listType = listType >= 3 ? listType : 3;
|
||||||
|
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data));
|
||||||
|
} else if(descriptor->obis[4] == 1) {
|
||||||
|
o181 = ntohl(item->dlu.data);
|
||||||
|
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data));
|
||||||
|
} else if(descriptor->obis[4] == 2) {
|
||||||
|
o182 = ntohl(item->dlu.data);
|
||||||
|
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(descriptor->obis[2] == 2) {
|
||||||
|
if(descriptor->obis[3] == 7) {
|
||||||
|
if(descriptor->obis[4] == 0) {
|
||||||
|
o270 = ntohl(item->dlu.data);
|
||||||
|
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data));
|
||||||
|
}
|
||||||
|
} else if(descriptor->obis[3] == 8) {
|
||||||
|
if(descriptor->obis[4] == 0) {
|
||||||
|
activeExportCounter = ntohl(item->dlu.data) / 1000.0;
|
||||||
|
listType = listType >= 3 ? listType : 3;
|
||||||
|
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data));
|
||||||
|
} else if(descriptor->obis[4] == 1) {
|
||||||
|
o281 = ntohl(item->dlu.data);
|
||||||
|
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data));
|
||||||
|
} else if(descriptor->obis[4] == 2) {
|
||||||
|
o282 = ntohl(item->dlu.data);
|
||||||
|
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(descriptor->obis[2] == 96) {
|
||||||
|
if(descriptor->obis[3] == 1) {
|
||||||
|
if(descriptor->obis[4] == 0) {
|
||||||
|
char str[item->oct.length+1];
|
||||||
|
memcpy(str, item->oct.data, item->oct.length);
|
||||||
|
str[item->oct.length] = '\0';
|
||||||
|
meterId = String(str);
|
||||||
|
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %s (oct)", str);
|
||||||
|
} else if(descriptor->obis[4] == 1) {
|
||||||
|
char str[item->oct.length+1];
|
||||||
|
memcpy(str, item->oct.data, item->oct.length);
|
||||||
|
str[item->oct.length] = '\0';
|
||||||
|
meterModel = String(str);
|
||||||
|
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %s (oct)", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("\n");
|
||||||
|
|
||||||
|
if(o170 > 0 || o270 > 0) {
|
||||||
|
int32_t sum = o170-o270;
|
||||||
|
if(sum > 0) {
|
||||||
|
listType = listType >= 1 ? listType : 1;
|
||||||
|
activeImportPower = sum;
|
||||||
|
} else {
|
||||||
|
listType = listType >= 2 ? listType : 2;
|
||||||
|
activeExportPower = sum * -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(o181 > 0 || o182 > 0) {
|
||||||
|
activeImportCounter = (o181 + o182) / 1000.0;
|
||||||
|
listType = listType >= 3 ? listType : 3;
|
||||||
|
}
|
||||||
|
if(o281 > 0 || o282 > 0) {
|
||||||
|
activeExportCounter = (o281 + o282) / 1000.0;
|
||||||
|
listType = listType >= 3 ? listType : 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((*data) == 0x09) {
|
||||||
|
data += (*(data+1))+2;
|
||||||
|
} else {
|
||||||
|
data += 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastUpdateMillis = millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/LNG.h
Normal file
30
src/LNG.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#ifndef _LNG_H
|
||||||
|
#define _LNG_H
|
||||||
|
|
||||||
|
#include "AmsData.h"
|
||||||
|
#include "AmsConfiguration.h"
|
||||||
|
#include "ams/DataParser.h"
|
||||||
|
#include "RemoteDebug.h"
|
||||||
|
|
||||||
|
struct LngHeader {
|
||||||
|
uint8_t tag;
|
||||||
|
uint8_t values;
|
||||||
|
uint8_t arrayTag;
|
||||||
|
uint8_t arrayLength;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct LngObisDescriptor {
|
||||||
|
uint8_t ignore1[5];
|
||||||
|
uint8_t octetTag;
|
||||||
|
uint8_t octetLength;
|
||||||
|
uint8_t obis[6];
|
||||||
|
uint8_t ignore2[5];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
|
||||||
|
class LNG : public AmsData {
|
||||||
|
public:
|
||||||
|
LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, DataParserContext &ctx, RemoteDebug* debugger);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -19,7 +19,7 @@ public:
|
|||||||
virtual bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea);
|
virtual bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea);
|
||||||
virtual bool publishTemperatures(AmsConfiguration*, HwTools*);
|
virtual bool publishTemperatures(AmsConfiguration*, HwTools*);
|
||||||
virtual bool publishPrices(EntsoeApi* eapi);
|
virtual bool publishPrices(EntsoeApi* eapi);
|
||||||
virtual bool publishSystem(HwTools*);
|
virtual bool publishSystem(HwTools*, EntsoeApi*, EnergyAccounting*);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
MQTTClient* mqtt;
|
MQTTClient* mqtt;
|
||||||
|
|||||||
@@ -71,6 +71,6 @@ bool DomoticzMqttHandler::publishPrices(EntsoeApi* eapi) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DomoticzMqttHandler::publishSystem(HwTools* hw) {
|
bool DomoticzMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ public:
|
|||||||
bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea);
|
bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea);
|
||||||
bool publishTemperatures(AmsConfiguration*, HwTools*);
|
bool publishTemperatures(AmsConfiguration*, HwTools*);
|
||||||
bool publishPrices(EntsoeApi*);
|
bool publishPrices(EntsoeApi*);
|
||||||
bool publishSystem(HwTools*);
|
bool publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DomoticzConfig config;
|
DomoticzConfig config;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "web/root/jsonsys_json.h"
|
#include "web/root/jsonsys_json.h"
|
||||||
#include "web/root/jsonprices_json.h"
|
#include "web/root/jsonprices_json.h"
|
||||||
#include "web/root/hadiscover_json.h"
|
#include "web/root/hadiscover_json.h"
|
||||||
|
#include "web/root/realtime_json.h"
|
||||||
|
|
||||||
bool HomeAssistantMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea) {
|
bool HomeAssistantMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea) {
|
||||||
if(topic.isEmpty() || !mqtt->connected())
|
if(topic.isEmpty() || !mqtt->connected())
|
||||||
@@ -55,8 +56,7 @@ bool HomeAssistantMqttHandler::publish(AmsData* data, AmsData* previousState, En
|
|||||||
);
|
);
|
||||||
return mqtt->publish(topic + "/power", json);
|
return mqtt->publish(topic + "/power", json);
|
||||||
}
|
}
|
||||||
return false;
|
return false;}
|
||||||
}
|
|
||||||
|
|
||||||
bool HomeAssistantMqttHandler::publishTemperatures(AmsConfiguration* config, HwTools* hw) {
|
bool HomeAssistantMqttHandler::publishTemperatures(AmsConfiguration* config, HwTools* hw) {
|
||||||
int count = hw->getTempSensorCount();
|
int count = hw->getTempSensorCount();
|
||||||
@@ -190,7 +190,7 @@ bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
|
|||||||
return mqtt->publish(topic + "/prices", json);
|
return mqtt->publish(topic + "/prices", json);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HomeAssistantMqttHandler::publishSystem(HwTools* hw) {
|
bool HomeAssistantMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea) {
|
||||||
if(topic.isEmpty() || !mqtt->connected()){
|
if(topic.isEmpty() || !mqtt->connected()){
|
||||||
sequence = 0;
|
sequence = 0;
|
||||||
return false;
|
return false;
|
||||||
@@ -218,7 +218,7 @@ bool HomeAssistantMqttHandler::publishSystem(HwTools* hw) {
|
|||||||
String haUrl = "http://" + haUID + ".local/";
|
String haUrl = "http://" + haUID + ".local/";
|
||||||
// Could this be necessary? haUID.replace("-", "_");
|
// Could this be necessary? haUID.replace("-", "_");
|
||||||
|
|
||||||
for(int i=0;i<sensors;i++){
|
for(int i=0;i<17;i++){
|
||||||
snprintf_P(json, BufferSize, HADISCOVER_JSON,
|
snprintf_P(json, BufferSize, HADISCOVER_JSON,
|
||||||
FPSTR(HA_NAMES[i]),
|
FPSTR(HA_NAMES[i]),
|
||||||
topic.c_str(), FPSTR(HA_TOPICS[i]),
|
topic.c_str(), FPSTR(HA_TOPICS[i]),
|
||||||
@@ -241,5 +241,4 @@ bool HomeAssistantMqttHandler::publishSystem(HwTools* hw) {
|
|||||||
autodiscoverInit = true;
|
autodiscoverInit = true;
|
||||||
}
|
}
|
||||||
if(listType>0) sequence++;
|
if(listType>0) sequence++;
|
||||||
return true;
|
return true;}
|
||||||
}
|
|
||||||
|
|||||||
@@ -13,11 +13,9 @@ public:
|
|||||||
bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea);
|
bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea);
|
||||||
bool publishTemperatures(AmsConfiguration*, HwTools*);
|
bool publishTemperatures(AmsConfiguration*, HwTools*);
|
||||||
bool publishPrices(EntsoeApi*);
|
bool publishPrices(EntsoeApi*);
|
||||||
bool publishSystem(HwTools*);
|
bool publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const uint8_t sensors = 17;
|
|
||||||
|
|
||||||
String haTopic = "homeassistant/sensor/";
|
String haTopic = "homeassistant/sensor/";
|
||||||
|
|
||||||
String haName = "AMS reader";
|
String haName = "AMS reader";
|
||||||
|
|||||||
@@ -11,4 +11,4 @@ const char* HA_UOM[17] PROGMEM = {"dBm", "V", "C", "W", "W", "W", "W", "A", "A",
|
|||||||
const char* HA_DEVCL[17] PROGMEM = {"signal_strength", "voltage", "temperature", "power", "power", "power", "power", "current", "current", "current", "voltage", "voltage", "voltage", "energy", "energy", "energy", "energy"};
|
const char* HA_DEVCL[17] PROGMEM = {"signal_strength", "voltage", "temperature", "power", "power", "power", "power", "current", "current", "current", "voltage", "voltage", "voltage", "energy", "energy", "energy", "energy"};
|
||||||
const char* HA_STACL[17] PROGMEM = {"", "", "", "\"measurement\"", "\"measurement\"", "\"measurement\"", "\"measurement\"", "", "", "", "", "", "", "\"total_increasing\"", "\"total_increasing\"", "\"total_increasing\"", "\"total_increasing\""};
|
const char* HA_STACL[17] PROGMEM = {"", "", "", "\"measurement\"", "\"measurement\"", "\"measurement\"", "\"measurement\"", "", "", "", "", "", "", "\"total_increasing\"", "\"total_increasing\"", "\"total_increasing\"", "\"total_increasing\""};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -271,7 +271,7 @@ bool JsonMqttHandler::publishPrices(EntsoeApi* eapi) {
|
|||||||
return mqtt->publish(topic, json);
|
return mqtt->publish(topic, json);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JsonMqttHandler::publishSystem(HwTools* hw) {
|
bool JsonMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea) {
|
||||||
if(init || topic.isEmpty() || !mqtt->connected())
|
if(init || topic.isEmpty() || !mqtt->connected())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public:
|
|||||||
bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea);
|
bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea);
|
||||||
bool publishTemperatures(AmsConfiguration*, HwTools*);
|
bool publishTemperatures(AmsConfiguration*, HwTools*);
|
||||||
bool publishPrices(EntsoeApi*);
|
bool publishPrices(EntsoeApi*);
|
||||||
bool publishSystem(HwTools*);
|
bool publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
String clientId;
|
String clientId;
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ bool RawMqttHandler::publishPrices(EntsoeApi* eapi) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RawMqttHandler::publishSystem(HwTools* hw) {
|
bool RawMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea) {
|
||||||
if(topic.isEmpty() || !mqtt->connected())
|
if(topic.isEmpty() || !mqtt->connected())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ public:
|
|||||||
bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea);
|
bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea);
|
||||||
bool publishTemperatures(AmsConfiguration*, HwTools*);
|
bool publishTemperatures(AmsConfiguration*, HwTools*);
|
||||||
bool publishPrices(EntsoeApi*);
|
bool publishPrices(EntsoeApi*);
|
||||||
bool publishSystem(HwTools*);
|
bool publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
String topic;
|
String topic;
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ void AmsWebServer::setup(AmsConfiguration* config, GpioConfig* gpioConfig, Meter
|
|||||||
server.on("/debugging", HTTP_GET, std::bind(&AmsWebServer::configDebugHtml, this));
|
server.on("/debugging", HTTP_GET, std::bind(&AmsWebServer::configDebugHtml, this));
|
||||||
|
|
||||||
server.on("/firmware", HTTP_GET, std::bind(&AmsWebServer::firmwareHtml, this));
|
server.on("/firmware", HTTP_GET, std::bind(&AmsWebServer::firmwareHtml, this));
|
||||||
server.on("/firmware", HTTP_POST, std::bind(&AmsWebServer::uploadPost, this), std::bind(&AmsWebServer::firmwareUpload, this));
|
server.on("/firmware", HTTP_POST, std::bind(&AmsWebServer::firmwarePost, this), std::bind(&AmsWebServer::firmwareUpload, this));
|
||||||
server.on("/upgrade", HTTP_GET, std::bind(&AmsWebServer::firmwareDownload, this));
|
server.on("/upgrade", HTTP_GET, std::bind(&AmsWebServer::firmwareDownload, this));
|
||||||
server.on("/restart", HTTP_GET, std::bind(&AmsWebServer::restartHtml, this));
|
server.on("/restart", HTTP_GET, std::bind(&AmsWebServer::restartHtml, this));
|
||||||
server.on("/restart", HTTP_POST, std::bind(&AmsWebServer::restartPost, this));
|
server.on("/restart", HTTP_POST, std::bind(&AmsWebServer::restartPost, this));
|
||||||
@@ -336,6 +336,9 @@ void AmsWebServer::configMeterHtml() {
|
|||||||
case AmsTypeSagemcom:
|
case AmsTypeSagemcom:
|
||||||
manufacturer = "Sagemcom";
|
manufacturer = "Sagemcom";
|
||||||
break;
|
break;
|
||||||
|
case AmsTypeLng:
|
||||||
|
manufacturer = "L&G";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
manufacturer = "Unknown";
|
manufacturer = "Unknown";
|
||||||
break;
|
break;
|
||||||
@@ -567,6 +570,20 @@ void AmsWebServer::configEntsoeHtml() {
|
|||||||
html.replace("{eaDk1}", strcmp(entsoe.area, "10YDK-1--------W") == 0 ? "selected" : "");
|
html.replace("{eaDk1}", strcmp(entsoe.area, "10YDK-1--------W") == 0 ? "selected" : "");
|
||||||
html.replace("{eaDk2}", strcmp(entsoe.area, "10YDK-2--------M") == 0 ? "selected" : "");
|
html.replace("{eaDk2}", strcmp(entsoe.area, "10YDK-2--------M") == 0 ? "selected" : "");
|
||||||
|
|
||||||
|
html.replace("{at}", strcmp(entsoe.area, "10YAT-APG------L") == 0 ? "selected" : "");
|
||||||
|
html.replace("{be}", strcmp(entsoe.area, "10YBE----------2") == 0 ? "selected" : "");
|
||||||
|
html.replace("{cz}", strcmp(entsoe.area, "10YCZ-CEPS-----N") == 0 ? "selected" : "");
|
||||||
|
html.replace("{ee}", strcmp(entsoe.area, "10Y1001A1001A39I") == 0 ? "selected" : "");
|
||||||
|
html.replace("{fi}", strcmp(entsoe.area, "10YFI-1--------U") == 0 ? "selected" : "");
|
||||||
|
html.replace("{fr}", strcmp(entsoe.area, "10YFR-RTE------C") == 0 ? "selected" : "");
|
||||||
|
html.replace("{de}", strcmp(entsoe.area, "10Y1001A1001A83F") == 0 ? "selected" : "");
|
||||||
|
html.replace("{gb}", strcmp(entsoe.area, "10YGB----------A") == 0 ? "selected" : "");
|
||||||
|
html.replace("{lv}", strcmp(entsoe.area, "10YLV-1001A00074") == 0 ? "selected" : "");
|
||||||
|
html.replace("{lt}", strcmp(entsoe.area, "10YLT-1001A0008Q") == 0 ? "selected" : "");
|
||||||
|
html.replace("{nl}", strcmp(entsoe.area, "10YNL----------L") == 0 ? "selected" : "");
|
||||||
|
html.replace("{pl}", strcmp(entsoe.area, "10YPL-AREA-----S") == 0 ? "selected" : "");
|
||||||
|
html.replace("{ch}", strcmp(entsoe.area, "10YCH-SWISSGRIDZ") == 0 ? "selected" : "");
|
||||||
|
|
||||||
html.replace("{ecNOK}", strcmp(entsoe.currency, "NOK") == 0 ? "selected" : "");
|
html.replace("{ecNOK}", strcmp(entsoe.currency, "NOK") == 0 ? "selected" : "");
|
||||||
html.replace("{ecSEK}", strcmp(entsoe.currency, "SEK") == 0 ? "selected" : "");
|
html.replace("{ecSEK}", strcmp(entsoe.currency, "SEK") == 0 ? "selected" : "");
|
||||||
html.replace("{ecDKK}", strcmp(entsoe.currency, "DKK") == 0 ? "selected" : "");
|
html.replace("{ecDKK}", strcmp(entsoe.currency, "DKK") == 0 ? "selected" : "");
|
||||||
@@ -1649,13 +1666,40 @@ void AmsWebServer::firmwareHtml() {
|
|||||||
server.sendContent_P(FOOT_HTML);
|
server.sendContent_P(FOOT_HTML);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AmsWebServer::firmwarePost() {
|
||||||
|
printD("Handlling firmware post...");
|
||||||
|
if(!checkSecurity(1))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(rebootForUpgrade) {
|
||||||
|
server.send(200);
|
||||||
|
} else {
|
||||||
|
if(server.hasArg("url")) {
|
||||||
|
String url = server.arg("url");
|
||||||
|
if(!url.isEmpty() && (url.startsWith("http://") || url.startsWith("https://"))) {
|
||||||
|
printD("Custom firmware URL was provided");
|
||||||
|
customFirmwareUrl = url;
|
||||||
|
performUpgrade = true;
|
||||||
|
server.sendHeader("Location","/restart-wait");
|
||||||
|
server.send(303);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
server.sendHeader("Location","/firmware");
|
||||||
|
server.send(303);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AmsWebServer::firmwareUpload() {
|
void AmsWebServer::firmwareUpload() {
|
||||||
|
printD("Handlling firmware upload...");
|
||||||
if(!checkSecurity(1))
|
if(!checkSecurity(1))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
HTTPUpload& upload = server.upload();
|
HTTPUpload& upload = server.upload();
|
||||||
|
String filename = upload.filename;
|
||||||
|
if(filename.isEmpty()) return;
|
||||||
|
|
||||||
if(upload.status == UPLOAD_FILE_START) {
|
if(upload.status == UPLOAD_FILE_START) {
|
||||||
String filename = upload.filename;
|
|
||||||
if(!filename.endsWith(".bin")) {
|
if(!filename.endsWith(".bin")) {
|
||||||
server.send(500, MIME_PLAIN, "500: couldn't create file");
|
server.send(500, MIME_PLAIN, "500: couldn't create file");
|
||||||
} else {
|
} else {
|
||||||
@@ -1764,7 +1808,7 @@ void AmsWebServer::restartWaitHtml() {
|
|||||||
performRestart = false;
|
performRestart = false;
|
||||||
} else if(performUpgrade) {
|
} else if(performUpgrade) {
|
||||||
WiFiClient client;
|
WiFiClient client;
|
||||||
String url = "http://ams2mqtt.rewiredinvent.no/hub/firmware/update";
|
String url = customFirmwareUrl.isEmpty() || !customFirmwareUrl.startsWith("http") ? "http://ams2mqtt.rewiredinvent.no/hub/firmware/update" : customFirmwareUrl;
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
String chipType = "esp8266";
|
String chipType = "esp8266";
|
||||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
@@ -1778,9 +1822,11 @@ void AmsWebServer::restartWaitHtml() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
|
ESPhttpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||||
t_httpUpdate_return ret = ESPhttpUpdate.update(client, url, VERSION);
|
t_httpUpdate_return ret = ESPhttpUpdate.update(client, url, VERSION);
|
||||||
#elif defined(ESP32)
|
#elif defined(ESP32)
|
||||||
HTTPUpdate httpUpdate;
|
HTTPUpdate httpUpdate;
|
||||||
|
httpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||||
HTTPUpdateResult ret = httpUpdate.update(client, url, String(VERSION) + "-" + chipType);
|
HTTPUpdateResult ret = httpUpdate.update(client, url, String(VERSION) + "-" + chipType);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ private:
|
|||||||
bool performRestart = false;
|
bool performRestart = false;
|
||||||
bool performUpgrade = false;
|
bool performUpgrade = false;
|
||||||
bool rebootForUpgrade = false;
|
bool rebootForUpgrade = false;
|
||||||
|
String customFirmwareUrl;
|
||||||
|
|
||||||
static const uint16_t BufferSize = 2048;
|
static const uint16_t BufferSize = 2048;
|
||||||
char* buf;
|
char* buf;
|
||||||
@@ -104,6 +105,7 @@ private:
|
|||||||
|
|
||||||
String getSerialSelectOptions(int selected);
|
String getSerialSelectOptions(int selected);
|
||||||
void firmwareHtml();
|
void firmwareHtml();
|
||||||
|
void firmwarePost();
|
||||||
void firmwareUpload();
|
void firmwareUpload();
|
||||||
void firmwareDownload();
|
void firmwareDownload();
|
||||||
void restartHtml();
|
void restartHtml();
|
||||||
|
|||||||
@@ -316,6 +316,7 @@ $(function() {
|
|||||||
url: swv.data('url'),
|
url: swv.data('url'),
|
||||||
dataType: 'json'
|
dataType: 'json'
|
||||||
}).done(function(releases) {
|
}).done(function(releases) {
|
||||||
|
var isnew = false;
|
||||||
if(/^v\d{1,2}\.\d{1,2}\.\d{1,2}$/.test(swv.text()) && fwl.length == 0) {
|
if(/^v\d{1,2}\.\d{1,2}\.\d{1,2}$/.test(swv.text()) && fwl.length == 0) {
|
||||||
releases.reverse();
|
releases.reverse();
|
||||||
var next_patch;
|
var next_patch;
|
||||||
@@ -352,10 +353,13 @@ $(function() {
|
|||||||
});
|
});
|
||||||
if(next_minor) {
|
if(next_minor) {
|
||||||
nextVersion = next_minor;
|
nextVersion = next_minor;
|
||||||
|
isnew = true;
|
||||||
} else if(next_major) {
|
} else if(next_major) {
|
||||||
nextVersion = next_major;
|
nextVersion = next_major;
|
||||||
|
isnew = true;
|
||||||
} else if(next_patch) {
|
} else if(next_patch) {
|
||||||
nextVersion = next_patch;
|
nextVersion = next_patch;
|
||||||
|
isnew = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
nextVersion = releases[0];
|
nextVersion = releases[0];
|
||||||
@@ -375,9 +379,11 @@ $(function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$('#newVersionTag').text(nextVersion.tag_name);
|
if(isnew) {
|
||||||
$('#newVersionUrl').prop('href', nextVersion.html_url);
|
$('#newVersionTag').text(nextVersion.tag_name);
|
||||||
$('#newVersion').removeClass('d-none');
|
$('#newVersionUrl').prop('href', nextVersion.html_url);
|
||||||
|
$('#newVersion').removeClass('d-none');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -884,7 +890,7 @@ var fetch = function() {
|
|||||||
|
|
||||||
var upgrade = function() {
|
var upgrade = function() {
|
||||||
if(nextVersion) {
|
if(nextVersion) {
|
||||||
if(confirm("WARNING: Please keep USB power connected while upgrading. Are you sure you want to perform upgrade to " + nextVersion.tag_name + "?")) {
|
if(confirm("WARNING: If you have a M-BUS powered device (Pow-U), please keep USB power connected while upgrading.\n\nAre 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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,19 @@
|
|||||||
<option value="10YDK-1--------W" {eaDk1}>DK1</option>
|
<option value="10YDK-1--------W" {eaDk1}>DK1</option>
|
||||||
<option value="10YDK-2--------M" {eaDk2}>DK2</option>
|
<option value="10YDK-2--------M" {eaDk2}>DK2</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
|
<option value="10YAT-APG------L" {at}>Austria</option>
|
||||||
|
<option value="10YBE----------2" {be}>Belgium</option>
|
||||||
|
<option value="10YCZ-CEPS-----N" {cz}>Czech Republic</option>
|
||||||
|
<option value="10Y1001A1001A39I" {ee}>Estonia</option>
|
||||||
|
<option value="10YFI-1--------U" {fi}>Finland</option>
|
||||||
|
<option value="10YFR-RTE------C" {fr}>France</option>
|
||||||
|
<option value="10Y1001A1001A83F" {de}>Germany</option>
|
||||||
|
<option value="10YGB----------A" {gb}>Great Britain</option>
|
||||||
|
<option value="10YLV-1001A00074" {lv}>Latvia</option>
|
||||||
|
<option value="10YLT-1001A0008Q" {lt}>Lithuania</option>
|
||||||
|
<option value="10YNL----------L" {nl}>Netherland</option>
|
||||||
|
<option value="10YPL-AREA-----S" {pl}>Poland</option>
|
||||||
|
<option value="10YCH-SWISSGRIDZ" {ch}>Switzerland</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
<div class="alert alert-danger">
|
<div class="alert alert-danger">
|
||||||
WARNING: Units powered over M-bus must be connected to an external power supply during firmware upload. Failure to do so may cause power-down during upload resulting in non-functioning unit.
|
WARNING: Units powered by M-BUS (Pow-U) must be connected to an external power supply during firmware upload. Failure to do so may cause power-down during upload resulting in non-functioning unit.
|
||||||
</div>
|
</div>
|
||||||
<div class="alert alert-warning">
|
<div class="alert alert-warning">
|
||||||
Your board is using {chipset} chipset. Only upload firmware designed for this chipset. Failure to do so may result in non-functioning unit.
|
Your board is using {chipset} chipset. Only upload firmware designed for this chipset. Failure to do so may result in non-functioning unit.
|
||||||
<span id="fwDownload" style="display: none;"><br/>Download latest firmware file <a id="fwLink" href="#" data-chipset="{chipset}">here</a></span>
|
<span id="fwDownload" style="display: none;"><br/>Download latest firmware file <a id="fwLink" href="#" data-chipset="{chipset}">here</a></span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
When using URL, only a valid ESP OTA server response will be accepted.
|
||||||
|
</div>
|
||||||
|
|
||||||
<form method="post" enctype="multipart/form-data" class="upload-form">
|
<form method="post" enctype="multipart/form-data" class="upload-form">
|
||||||
<div class="my-3 p-3 bg-white rounded shadow">
|
<div class="my-3 p-3 bg-white rounded shadow">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<div class="input-group mb-3">
|
<div class="input-group">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text">Upload</span>
|
<span class="input-group-text">Upload file</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="custom-file">
|
<div class="custom-file">
|
||||||
<input name="file" type="file" class="custom-file-input" id="fileUploadField">
|
<input name="file" type="file" class="custom-file-input" id="fileUploadField">
|
||||||
@@ -21,6 +24,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6">or</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<span class="input-group-text">Use URL</span>
|
||||||
|
</div>
|
||||||
|
<input type="text" name="url" class="form-control"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
<div class="row form-group">
|
<div class="row form-group">
|
||||||
@@ -28,7 +44,7 @@
|
|||||||
<a href="javascript:history.back();" class="btn btn-outline-secondary">Back</a>
|
<a href="javascript:history.back();" class="btn btn-outline-secondary">Back</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 text-right">
|
<div class="col-6 text-right">
|
||||||
<button class="btn btn-primary">Upload</button>
|
<button class="btn btn-primary">Upgrade firmware</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
20
web/realtime.json
Normal file
20
web/realtime.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"max" : %.1f,
|
||||||
|
"peaks" : [ %s ],
|
||||||
|
"threshold" : %d,
|
||||||
|
"hour" : {
|
||||||
|
"use" : %.2f,
|
||||||
|
"cost" : %.2f,
|
||||||
|
"produced" : %.2f
|
||||||
|
},
|
||||||
|
"day" : {
|
||||||
|
"use" : %.2f,
|
||||||
|
"cost" : %.2f,
|
||||||
|
"produced" : %.2f
|
||||||
|
},
|
||||||
|
"month" : {
|
||||||
|
"use" : %.2f,
|
||||||
|
"cost" : %.2f,
|
||||||
|
"produced" : %.2f
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user