mirror of
https://github.com/UtilitechAS/amsreader-firmware.git
synced 2026-03-21 00:57:46 +00:00
Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
538de5ea99 | ||
|
|
042e2bcc85 | ||
|
|
775e5a0881 | ||
|
|
6c0d5fcc09 | ||
|
|
69d8fa9254 | ||
|
|
c38c305bab | ||
|
|
bfee2a1d64 | ||
|
|
ec7edae9a1 | ||
|
|
d8e265b7ac | ||
|
|
1987cddab7 | ||
|
|
e18be5f97c | ||
|
|
537597d6d1 | ||
|
|
a89013cec3 | ||
|
|
9c7a0cb7ff | ||
|
|
ade12199b9 | ||
|
|
39b68aca51 | ||
|
|
8ac1e034b1 | ||
|
|
f446dff865 | ||
|
|
34ebe9601a | ||
|
|
fa299198fc | ||
|
|
6d81b0a856 | ||
|
|
dd095da97b | ||
|
|
1b6ce203b7 | ||
|
|
2850be4e48 | ||
|
|
7eca31de84 | ||
|
|
a64f960cc7 | ||
|
|
ce3a47a7e6 | ||
|
|
8395e1dc77 | ||
|
|
39a4761415 | ||
|
|
867ab9d6c2 | ||
|
|
5e03e3d3c2 | ||
|
|
adb5050621 | ||
|
|
026cd25c8c | ||
|
|
06ec97b42a | ||
|
|
b420a0e6f4 | ||
|
|
7cd52d5689 | ||
|
|
ad78ff3082 | ||
|
|
fe7be81f1e | ||
|
|
7674fc2ad0 | ||
|
|
d50181c347 | ||
|
|
8ca771fa5a | ||
|
|
7d557b2679 | ||
|
|
2f0c912388 | ||
|
|
57e6d0fbe3 | ||
|
|
e7c25fafda | ||
|
|
2a4772fe25 | ||
|
|
f1f7408208 | ||
|
|
feb8e5007b | ||
|
|
c4af1ee74f | ||
|
|
992e1b6121 | ||
|
|
9cc7529934 | ||
|
|
ef8715be6d | ||
|
|
e232b875fa | ||
|
|
f18171fecc | ||
|
|
a3c7a09211 | ||
|
|
a6f3bc3f71 | ||
|
|
44bcd386d1 | ||
|
|
01547f9a52 | ||
|
|
940d38af5c | ||
|
|
4e451c51e1 | ||
|
|
43def1c311 | ||
|
|
a055465ce0 |
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -33,6 +33,7 @@ If applicable, add screenshots to help explain your problem.
|
|||||||
**Relevant firmware information:**
|
**Relevant firmware information:**
|
||||||
- Version: [e.g. 2.1.0]
|
- Version: [e.g. 2.1.0]
|
||||||
- MQTT: [yes/no]
|
- MQTT: [yes/no]
|
||||||
|
- MQTT payload type: [e.g. JSON]
|
||||||
- HAN GPIO: [e.g. GPIO5]
|
- HAN GPIO: [e.g. GPIO5]
|
||||||
- HAN baud and parity: [e.g. 2400 8E1]
|
- HAN baud and parity: [e.g. 2400 8E1]
|
||||||
- Temperature sensors [e.g. 3xDS18B20]
|
- Temperature sensors [e.g. 3xDS18B20]
|
||||||
|
|||||||
1
.github/ISSUE_TEMPLATE/support.md
vendored
1
.github/ISSUE_TEMPLATE/support.md
vendored
@@ -20,6 +20,7 @@ A clear and concise description of what the problem is.
|
|||||||
**Relevant firmware information:**
|
**Relevant firmware information:**
|
||||||
- Version: [e.g. 2.1.0]
|
- Version: [e.g. 2.1.0]
|
||||||
- MQTT: [yes/no]
|
- MQTT: [yes/no]
|
||||||
|
- MQTT payload type: [e.g. JSON]
|
||||||
- HAN GPIO: [e.g. GPIO5]
|
- HAN GPIO: [e.g. GPIO5]
|
||||||
- HAN baud and parity: [e.g. 2400 8E1]
|
- HAN baud and parity: [e.g. 2400 8E1]
|
||||||
- Temperature sensors [e.g. 3xDS18B20]
|
- Temperature sensors [e.g. 3xDS18B20]
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ bool AmsConfiguration::getWiFiConfig(WiFiConfig& config) {
|
|||||||
EEPROM.begin(EEPROM_SIZE);
|
EEPROM.begin(EEPROM_SIZE);
|
||||||
EEPROM.get(CONFIG_WIFI_START, config);
|
EEPROM.get(CONFIG_WIFI_START, config);
|
||||||
EEPROM.end();
|
EEPROM.end();
|
||||||
|
if(config.sleep > 2) config.sleep = 1;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
clearWifi(config);
|
clearWifi(config);
|
||||||
@@ -33,6 +34,7 @@ bool AmsConfiguration::getWiFiConfig(WiFiConfig& config) {
|
|||||||
|
|
||||||
bool AmsConfiguration::setWiFiConfig(WiFiConfig& config) {
|
bool AmsConfiguration::setWiFiConfig(WiFiConfig& config) {
|
||||||
WiFiConfig existing;
|
WiFiConfig existing;
|
||||||
|
if(config.sleep > 2) config.sleep = 1;
|
||||||
if(getWiFiConfig(existing)) {
|
if(getWiFiConfig(existing)) {
|
||||||
wifiChanged |= strcmp(config.ssid, existing.ssid) != 0;
|
wifiChanged |= strcmp(config.ssid, existing.ssid) != 0;
|
||||||
wifiChanged |= strcmp(config.psk, existing.psk) != 0;
|
wifiChanged |= strcmp(config.psk, existing.psk) != 0;
|
||||||
@@ -45,6 +47,7 @@ bool AmsConfiguration::setWiFiConfig(WiFiConfig& config) {
|
|||||||
}
|
}
|
||||||
wifiChanged |= strcmp(config.hostname, existing.hostname) != 0;
|
wifiChanged |= strcmp(config.hostname, existing.hostname) != 0;
|
||||||
wifiChanged |= config.power != existing.power;
|
wifiChanged |= config.power != existing.power;
|
||||||
|
wifiChanged |= config.sleep != existing.sleep;
|
||||||
} else {
|
} else {
|
||||||
wifiChanged = true;
|
wifiChanged = true;
|
||||||
}
|
}
|
||||||
@@ -70,6 +73,7 @@ void AmsConfiguration::clearWifi(WiFiConfig& config) {
|
|||||||
#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;
|
||||||
|
config.sleep = 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AmsConfiguration::clearWifiIp(WiFiConfig& config) {
|
void AmsConfiguration::clearWifiIp(WiFiConfig& config) {
|
||||||
@@ -510,6 +514,7 @@ bool AmsConfiguration::getEnergyAccountingConfig(EnergyAccountingConfig& config)
|
|||||||
if(config.thresholds[9] != 255) {
|
if(config.thresholds[9] != 255) {
|
||||||
clearEnergyAccountingConfig(config);
|
clearEnergyAccountingConfig(config);
|
||||||
}
|
}
|
||||||
|
if(config.hours > 5) config.hours = 5;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@@ -517,6 +522,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 +531,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;
|
||||||
}
|
}
|
||||||
@@ -547,6 +554,7 @@ void AmsConfiguration::clearEnergyAccountingConfig(EnergyAccountingConfig& confi
|
|||||||
config.thresholds[7] = 100;
|
config.thresholds[7] = 100;
|
||||||
config.thresholds[8] = 150;
|
config.thresholds[8] = 150;
|
||||||
config.thresholds[9] = 255;
|
config.thresholds[9] = 255;
|
||||||
|
config.hours = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AmsConfiguration::isEnergyAccountingChanged() {
|
bool AmsConfiguration::isEnergyAccountingChanged() {
|
||||||
@@ -667,6 +675,14 @@ bool AmsConfiguration::hasConfig() {
|
|||||||
configVersion = 0;
|
configVersion = 0;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
case 95:
|
||||||
|
configVersion = -1; // Prevent loop
|
||||||
|
if(relocateConfig95()) {
|
||||||
|
configVersion = 96;
|
||||||
|
} else {
|
||||||
|
configVersion = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
case EEPROM_CHECK_SUM:
|
case EEPROM_CHECK_SUM:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
@@ -741,7 +757,7 @@ bool AmsConfiguration::relocateConfig86() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool AmsConfiguration::relocateConfig87() {
|
bool AmsConfiguration::relocateConfig87() {
|
||||||
MeterConfig87 meter87;
|
MeterConfig87 meter87 = {0,0,0,0,0,0,0};
|
||||||
MeterConfig meter;
|
MeterConfig meter;
|
||||||
EEPROM.begin(EEPROM_SIZE);
|
EEPROM.begin(EEPROM_SIZE);
|
||||||
EEPROM.get(CONFIG_METER_START_87, meter87);
|
EEPROM.get(CONFIG_METER_START_87, meter87);
|
||||||
@@ -844,6 +860,23 @@ bool AmsConfiguration::relocateConfig94() {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AmsConfiguration::relocateConfig95() {
|
||||||
|
MeterConfig meter;
|
||||||
|
MeterConfig95 meter95;
|
||||||
|
EEPROM.begin(EEPROM_SIZE);
|
||||||
|
EEPROM.get(CONFIG_METER_START, meter);
|
||||||
|
EEPROM.get(CONFIG_METER_START, meter95);
|
||||||
|
meter.wattageMultiplier = meter95.wattageMultiplier;
|
||||||
|
meter.voltageMultiplier = meter95.voltageMultiplier;
|
||||||
|
meter.amperageMultiplier = meter95.amperageMultiplier;
|
||||||
|
meter.accumulatedMultiplier = meter95.accumulatedMultiplier;
|
||||||
|
EEPROM.put(CONFIG_METER_START, meter);
|
||||||
|
EEPROM.put(EEPROM_CONFIG_ADDRESS, 96);
|
||||||
|
bool ret = EEPROM.commit();
|
||||||
|
EEPROM.end();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
bool AmsConfiguration::save() {
|
bool AmsConfiguration::save() {
|
||||||
EEPROM.begin(EEPROM_SIZE);
|
EEPROM.begin(EEPROM_SIZE);
|
||||||
EEPROM.put(EEPROM_CONFIG_ADDRESS, EEPROM_CHECK_SUM);
|
EEPROM.put(EEPROM_CONFIG_ADDRESS, EEPROM_CHECK_SUM);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
|
|
||||||
#define EEPROM_SIZE 1024*3
|
#define EEPROM_SIZE 1024*3
|
||||||
#define EEPROM_CHECK_SUM 95 // Used to check if config is stored. Change if structure changes
|
#define EEPROM_CHECK_SUM 96 // Used to check if config is stored. Change if structure changes
|
||||||
#define EEPROM_CONFIG_ADDRESS 0
|
#define EEPROM_CONFIG_ADDRESS 0
|
||||||
#define EEPROM_TEMP_CONFIG_ADDRESS 2048
|
#define EEPROM_TEMP_CONFIG_ADDRESS 2048
|
||||||
|
|
||||||
@@ -54,7 +54,8 @@ struct WiFiConfig {
|
|||||||
char hostname[32];
|
char hostname[32];
|
||||||
bool mdns;
|
bool mdns;
|
||||||
uint8_t power;
|
uint8_t power;
|
||||||
}; // 210
|
uint8_t sleep;
|
||||||
|
}; // 211
|
||||||
|
|
||||||
struct MqttConfig86 {
|
struct MqttConfig86 {
|
||||||
char host[128];
|
char host[128];
|
||||||
@@ -87,6 +88,23 @@ struct WebConfig {
|
|||||||
}; // 129
|
}; // 129
|
||||||
|
|
||||||
struct MeterConfig {
|
struct MeterConfig {
|
||||||
|
uint32_t baud;
|
||||||
|
uint8_t parity;
|
||||||
|
bool invert;
|
||||||
|
uint8_t distributionSystem;
|
||||||
|
uint8_t mainFuse;
|
||||||
|
uint8_t productionCapacity;
|
||||||
|
uint8_t encryptionKey[16];
|
||||||
|
uint8_t authenticationKey[16];
|
||||||
|
uint32_t wattageMultiplier;
|
||||||
|
uint32_t voltageMultiplier;
|
||||||
|
uint32_t amperageMultiplier;
|
||||||
|
uint32_t accumulatedMultiplier;
|
||||||
|
uint8_t source;
|
||||||
|
uint8_t parser;
|
||||||
|
}; // 50
|
||||||
|
|
||||||
|
struct MeterConfig95 {
|
||||||
uint32_t baud;
|
uint32_t baud;
|
||||||
uint8_t parity;
|
uint8_t parity;
|
||||||
bool invert;
|
bool invert;
|
||||||
@@ -269,6 +287,7 @@ private:
|
|||||||
bool relocateConfig92(); // 2.0.3
|
bool relocateConfig92(); // 2.0.3
|
||||||
bool relocateConfig93(); // 2.1.0
|
bool relocateConfig93(); // 2.1.0
|
||||||
bool relocateConfig94(); // 2.1.4
|
bool relocateConfig94(); // 2.1.4
|
||||||
|
bool relocateConfig95(); // 2.1.13
|
||||||
|
|
||||||
void saveToFs();
|
void saveToFs();
|
||||||
bool loadFromFs(uint8_t version);
|
bool loadFromFs(uint8_t version);
|
||||||
|
|||||||
@@ -45,6 +45,12 @@ void AmsData::apply(AmsData& other) {
|
|||||||
this->l1PowerFactor = other.getL1PowerFactor();
|
this->l1PowerFactor = other.getL1PowerFactor();
|
||||||
this->l2PowerFactor = other.getL2PowerFactor();
|
this->l2PowerFactor = other.getL2PowerFactor();
|
||||||
this->l3PowerFactor = other.getL3PowerFactor();
|
this->l3PowerFactor = other.getL3PowerFactor();
|
||||||
|
this->l1activeImportPower = other.getL1ActiveImportPower();
|
||||||
|
this->l2activeImportPower = other.getL2ActiveImportPower();
|
||||||
|
this->l3activeImportPower = other.getL3ActiveImportPower();
|
||||||
|
this->l1activeExportPower = other.getL1ActiveExportPower();
|
||||||
|
this->l2activeExportPower = other.getL2ActiveExportPower();
|
||||||
|
this->l3activeExportPower = other.getL3ActiveExportPower();
|
||||||
case 3:
|
case 3:
|
||||||
this->meterTimestamp = other.getMeterTimestamp();
|
this->meterTimestamp = other.getMeterTimestamp();
|
||||||
this->activeImportCounter = other.getActiveImportCounter();
|
this->activeImportCounter = other.getActiveImportCounter();
|
||||||
@@ -161,6 +167,30 @@ float AmsData::getL3PowerFactor() {
|
|||||||
return this->l3PowerFactor;
|
return this->l3PowerFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float AmsData::getL1ActiveImportPower() {
|
||||||
|
return this->l1activeImportPower;
|
||||||
|
}
|
||||||
|
|
||||||
|
float AmsData::getL2ActiveImportPower() {
|
||||||
|
return this->l2activeImportPower;
|
||||||
|
}
|
||||||
|
|
||||||
|
float AmsData::getL3ActiveImportPower() {
|
||||||
|
return this->l3activeImportPower;
|
||||||
|
}
|
||||||
|
|
||||||
|
float AmsData::getL1ActiveExportPower() {
|
||||||
|
return this->l1activeExportPower;
|
||||||
|
}
|
||||||
|
|
||||||
|
float AmsData::getL2ActiveExportPower() {
|
||||||
|
return this->l2activeExportPower;
|
||||||
|
}
|
||||||
|
|
||||||
|
float AmsData::getL3ActiveExportPower() {
|
||||||
|
return this->l3activeExportPower;
|
||||||
|
}
|
||||||
|
|
||||||
double AmsData::getActiveImportCounter() {
|
double AmsData::getActiveImportCounter() {
|
||||||
return this->activeImportCounter;
|
return this->activeImportCounter;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
};
|
};
|
||||||
@@ -53,6 +54,14 @@ public:
|
|||||||
float getL2PowerFactor();
|
float getL2PowerFactor();
|
||||||
float getL3PowerFactor();
|
float getL3PowerFactor();
|
||||||
|
|
||||||
|
float getL1ActiveImportPower();
|
||||||
|
float getL2ActiveImportPower();
|
||||||
|
float getL3ActiveImportPower();
|
||||||
|
|
||||||
|
float getL1ActiveExportPower();
|
||||||
|
float getL2ActiveExportPower();
|
||||||
|
float getL3ActiveExportPower();
|
||||||
|
|
||||||
double getActiveImportCounter();
|
double getActiveImportCounter();
|
||||||
double getReactiveImportCounter();
|
double getReactiveImportCounter();
|
||||||
double getActiveExportCounter();
|
double getActiveExportCounter();
|
||||||
@@ -70,6 +79,8 @@ protected:
|
|||||||
time_t meterTimestamp = 0;
|
time_t meterTimestamp = 0;
|
||||||
uint16_t activeImportPower = 0, reactiveImportPower = 0, activeExportPower = 0, reactiveExportPower = 0;
|
uint16_t activeImportPower = 0, reactiveImportPower = 0, activeExportPower = 0, reactiveExportPower = 0;
|
||||||
float l1voltage = 0, l2voltage = 0, l3voltage = 0, l1current = 0, l2current = 0, l3current = 0;
|
float l1voltage = 0, l2voltage = 0, l3voltage = 0, l1current = 0, l2current = 0, l3current = 0;
|
||||||
|
float l1activeImportPower = 0, l2activeImportPower = 0, l3activeImportPower = 0;
|
||||||
|
float l1activeExportPower = 0, l2activeExportPower = 0, l3activeExportPower = 0;
|
||||||
float powerFactor = 0, l1PowerFactor = 0, l2PowerFactor = 0, l3PowerFactor = 0;
|
float powerFactor = 0, l1PowerFactor = 0, l2PowerFactor = 0, l3PowerFactor = 0;
|
||||||
double activeImportCounter = 0, reactiveImportCounter = 0, activeExportCounter = 0, reactiveExportCounter = 0;
|
double activeImportCounter = 0, reactiveImportCounter = 0, activeExportCounter = 0, reactiveExportCounter = 0;
|
||||||
bool threePhase = false, twoPhase = false, counterEstimated = false;
|
bool threePhase = false, twoPhase = false, counterEstimated = false;
|
||||||
|
|||||||
@@ -7,20 +7,20 @@
|
|||||||
|
|
||||||
struct DayDataPoints {
|
struct DayDataPoints {
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
int16_t hImport[24];
|
uint16_t hImport[24];
|
||||||
time_t lastMeterReadTime;
|
time_t lastMeterReadTime;
|
||||||
uint32_t activeImport;
|
uint32_t activeImport;
|
||||||
uint32_t activeExport;
|
uint32_t activeExport;
|
||||||
int16_t hExport[24];
|
uint16_t hExport[24];
|
||||||
}; // 112 bytes
|
}; // 112 bytes
|
||||||
|
|
||||||
struct MonthDataPoints {
|
struct MonthDataPoints {
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
int16_t dImport[31];
|
uint16_t dImport[31];
|
||||||
time_t lastMeterReadTime;
|
time_t lastMeterReadTime;
|
||||||
uint32_t activeImport;
|
uint32_t activeImport;
|
||||||
uint32_t activeExport;
|
uint32_t activeExport;
|
||||||
int16_t dExport[31];
|
uint16_t dExport[31];
|
||||||
}; // 141 bytes
|
}; // 141 bytes
|
||||||
|
|
||||||
class AmsDataStorage {
|
class AmsDataStorage {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef _AMSTOMQTTBRIDGE_H
|
#ifndef _AMSTOMQTTBRIDGE_H
|
||||||
#define _AMSTOMQTTBRIDGE_H
|
#define _AMSTOMQTTBRIDGE_H
|
||||||
|
|
||||||
#define WIFI_CONNECTION_TIMEOUT 30000;
|
#define WIFI_CONNECTION_TIMEOUT 30000
|
||||||
|
|
||||||
#define INVALID_BUTTON_PIN 0xFFFFFFFF
|
#define INVALID_BUTTON_PIN 0xFFFFFFFF
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ ADC_MODE(ADC_VCC);
|
|||||||
#endif
|
#endif
|
||||||
#define WDT_TIMEOUT 60
|
#define WDT_TIMEOUT 60
|
||||||
|
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
|
#include <driver/uart.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
#include "AmsToMqttBridge.h"
|
#include "AmsToMqttBridge.h"
|
||||||
@@ -61,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"
|
||||||
|
|
||||||
@@ -114,7 +119,6 @@ DLMSParser *dlmsParser = NULL;
|
|||||||
DSMRParser *dsmrParser = NULL;
|
DSMRParser *dsmrParser = NULL;
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
WiFiConfig wifi;
|
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
|
|
||||||
if(!config.getGpioConfig(gpioConfig)) {
|
if(!config.getGpioConfig(gpioConfig)) {
|
||||||
@@ -241,7 +245,6 @@ void setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Debug.setSerialEnabled(true);
|
Debug.setSerialEnabled(true);
|
||||||
DebugConfig debug;
|
|
||||||
delay(1);
|
delay(1);
|
||||||
|
|
||||||
float vcc = hw.getVcc();
|
float vcc = hw.getVcc();
|
||||||
@@ -304,7 +307,7 @@ void setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
debugI(" flashing");
|
debugI(" flashing");
|
||||||
File firmwareFile = LittleFS.open(FILE_FIRMWARE, "r");
|
File firmwareFile = LittleFS.open(FILE_FIRMWARE, (char*) "r");
|
||||||
debugD(" firmware size: %d", firmwareFile.size());
|
debugD(" firmware size: %d", firmwareFile.size());
|
||||||
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
|
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
|
||||||
debugD(" available: %d", maxSketchSpace);
|
debugD(" available: %d", maxSketchSpace);
|
||||||
@@ -355,7 +358,7 @@ void setup() {
|
|||||||
|
|
||||||
NtpConfig ntp;
|
NtpConfig ntp;
|
||||||
if(config.getNtpConfig(ntp)) {
|
if(config.getNtpConfig(ntp)) {
|
||||||
configTime(ntp.offset*10, ntp.summerOffset*10, ntp.enable ? strlen(ntp.server) > 0 ? ntp.server : "pool.ntp.org" : ""); // Add NTP server by default if none is configured
|
configTime(ntp.offset*10, ntp.summerOffset*10, ntp.enable ? strlen(ntp.server) > 0 ? ntp.server : (char*) F("pool.ntp.org") : (char*) F("")); // Add NTP server by default if none is configured
|
||||||
sntp_servermode_dhcp(ntp.enable && ntp.dhcp ? 1 : 0);
|
sntp_servermode_dhcp(ntp.enable && ntp.dhcp ? 1 : 0);
|
||||||
ntpEnabled = ntp.enable;
|
ntpEnabled = ntp.enable;
|
||||||
TimeChangeRule std = {"STD", Last, Sun, Oct, 3, ntp.offset / 6};
|
TimeChangeRule std = {"STD", Last, Sun, Oct, 3, ntp.offset / 6};
|
||||||
@@ -431,6 +434,10 @@ void loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(now > 10000 && now - lastErrorBlink > 3000) {
|
||||||
|
errorBlink();
|
||||||
|
}
|
||||||
|
|
||||||
// Only do normal stuff if we're not booted as AP
|
// Only do normal stuff if we're not booted as AP
|
||||||
if (WiFi.getMode() != WIFI_AP) {
|
if (WiFi.getMode() != WIFI_AP) {
|
||||||
if (WiFi.status() != WL_CONNECTED) {
|
if (WiFi.status() != WL_CONNECTED) {
|
||||||
@@ -466,7 +473,7 @@ void loop() {
|
|||||||
if(strlen(wifi.hostname) > 0 && wifi.mdns) {
|
if(strlen(wifi.hostname) > 0 && wifi.mdns) {
|
||||||
debugD("mDNS is enabled, using host: %s", wifi.hostname);
|
debugD("mDNS is enabled, using host: %s", wifi.hostname);
|
||||||
if(MDNS.begin(wifi.hostname)) {
|
if(MDNS.begin(wifi.hostname)) {
|
||||||
MDNS.addService("http", "tcp", 80);
|
MDNS.addService(F("http"), F("tcp"), 80);
|
||||||
} else {
|
} else {
|
||||||
debugE("Failed to set up mDNS!");
|
debugE("Failed to set up mDNS!");
|
||||||
}
|
}
|
||||||
@@ -501,13 +508,10 @@ void loop() {
|
|||||||
MDNS.update();
|
MDNS.update();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(now > 10000 && now - lastErrorBlink > 3000) {
|
|
||||||
errorBlink();
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
||||||
@@ -585,11 +589,11 @@ void loop() {
|
|||||||
if(mqtt != NULL && mqttHandler != NULL && WiFi.getMode() != WIFI_AP && WiFi.status() == WL_CONNECTED && mqtt->connected() && !topic.isEmpty()) {
|
if(mqtt != NULL && mqttHandler != NULL && WiFi.getMode() != WIFI_AP && WiFi.status() == WL_CONNECTED && mqtt->connected() && !topic.isEmpty()) {
|
||||||
mqttHandler->publishTemperatures(&config, &hw);
|
mqttHandler->publishTemperatures(&config, &hw);
|
||||||
}
|
}
|
||||||
debugD("Used %d ms to update temperature", millis()-start);
|
debugD("Used %ld ms to update temperature", millis()-start);
|
||||||
}
|
}
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -603,7 +607,7 @@ void loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setupHanPort(uint8_t pin, uint32_t baud, uint8_t parityOrdinal, bool invert) {
|
void setupHanPort(uint8_t pin, uint32_t baud, uint8_t parityOrdinal, bool invert) {
|
||||||
if(Debug.isActive(RemoteDebug::INFO)) Debug.printf("(setupHanPort) Setting up HAN on pin %d with baud %d and parity %d\n", pin, baud, parityOrdinal);
|
if(Debug.isActive(RemoteDebug::INFO)) Debug.printf((char*) F("(setupHanPort) Setting up HAN on pin %d with baud %d and parity %d\n"), pin, baud, parityOrdinal);
|
||||||
|
|
||||||
HardwareSerial *hwSerial = NULL;
|
HardwareSerial *hwSerial = NULL;
|
||||||
if(pin == 3 || pin == 113) {
|
if(pin == 3 || pin == 113) {
|
||||||
@@ -619,9 +623,7 @@ void setupHanPort(uint8_t pin, uint32_t baud, uint8_t parityOrdinal, bool invert
|
|||||||
hwSerial = &Serial2;
|
hwSerial = &Serial2;
|
||||||
}
|
}
|
||||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
if(pin == 18) {
|
hwSerial = &Serial1;
|
||||||
hwSerial = &Serial1;
|
|
||||||
}
|
|
||||||
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
@@ -654,12 +656,13 @@ void setupHanPort(uint8_t pin, uint32_t baud, uint8_t parityOrdinal, bool invert
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(ESP32)
|
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||||
|
hwSerial->begin(baud, serialConfig, -1, -1, invert);
|
||||||
|
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->begin(baud, serialConfig, -1, -1, invert);
|
||||||
hwSerial->setRxBufferSize(768);
|
|
||||||
#else
|
#else
|
||||||
hwSerial->begin(baud, serialConfig, SERIAL_FULL, 1, invert);
|
hwSerial->begin(baud, serialConfig, SERIAL_FULL, 1, invert);
|
||||||
hwSerial->setRxBufferSize(768);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
@@ -717,22 +720,25 @@ void errorBlink() {
|
|||||||
if(lastError == 3)
|
if(lastError == 3)
|
||||||
lastError = 0;
|
lastError = 0;
|
||||||
lastErrorBlink = millis();
|
lastErrorBlink = millis();
|
||||||
for(;lastError < 3;lastError++) {
|
while(lastError < 3) {
|
||||||
switch(lastError) {
|
switch(lastError++) {
|
||||||
case 0:
|
case 0:
|
||||||
if(lastErrorBlink - meterState.getLastUpdateMillis() > 30000) {
|
if(lastErrorBlink - meterState.getLastUpdateMillis() > 30000) {
|
||||||
|
debugW("No HAN data received last 30s, single blink");
|
||||||
hw.ledBlink(LED_RED, 1); // If no message received from AMS in 30 sec, blink once
|
hw.ledBlink(LED_RED, 1); // If no message received from AMS in 30 sec, blink once
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
if(mqttEnabled && mqtt != NULL && mqtt->lastError() != 0) {
|
if(mqttEnabled && mqtt != NULL && mqtt->lastError() != 0) {
|
||||||
|
debugW("MQTT connection not available, double blink");
|
||||||
hw.ledBlink(LED_RED, 2); // If MQTT error, blink twice
|
hw.ledBlink(LED_RED, 2); // If MQTT error, blink twice
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
if(WiFi.getMode() != WIFI_AP && WiFi.status() != WL_CONNECTED) {
|
if(WiFi.getMode() != WIFI_AP && WiFi.status() != WL_CONNECTED) {
|
||||||
|
debugW("WiFi not connected, tripe blink");
|
||||||
hw.ledBlink(LED_RED, 3); // If WiFi not connected, blink three times
|
hw.ledBlink(LED_RED, 3); // If WiFi not connected, blink three times
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -756,14 +762,14 @@ void swapWifiMode() {
|
|||||||
|
|
||||||
if (mode != WIFI_AP || !config.hasConfig()) {
|
if (mode != WIFI_AP || !config.hasConfig()) {
|
||||||
if(Debug.isActive(RemoteDebug::INFO)) debugI("Swapping to AP mode");
|
if(Debug.isActive(RemoteDebug::INFO)) debugI("Swapping to AP mode");
|
||||||
WiFi.softAP("AMS2MQTT");
|
WiFi.softAP((char*) F("AMS2MQTT"));
|
||||||
WiFi.mode(WIFI_AP);
|
WiFi.mode(WIFI_AP);
|
||||||
|
|
||||||
if(dnsServer == NULL) {
|
if(dnsServer == NULL) {
|
||||||
dnsServer = new DNSServer();
|
dnsServer = new DNSServer();
|
||||||
}
|
}
|
||||||
dnsServer->setErrorReplyCode(DNSReplyCode::NoError);
|
dnsServer->setErrorReplyCode(DNSReplyCode::NoError);
|
||||||
dnsServer->start(53, "*", WiFi.softAPIP());
|
dnsServer->start(53, (char*) F("*"), WiFi.softAPIP());
|
||||||
} else {
|
} else {
|
||||||
if(Debug.isActive(RemoteDebug::INFO)) debugI("Swapping to STA mode");
|
if(Debug.isActive(RemoteDebug::INFO)) debugI("Swapping to STA mode");
|
||||||
if(dnsServer != NULL) {
|
if(dnsServer != NULL) {
|
||||||
@@ -804,7 +810,7 @@ bool readHanPort() {
|
|||||||
hanBuffer[len++] = hanSerial->read();
|
hanBuffer[len++] = hanSerial->read();
|
||||||
ctx.length = len;
|
ctx.length = len;
|
||||||
pos = unwrapData((uint8_t *) hanBuffer, ctx);
|
pos = unwrapData((uint8_t *) hanBuffer, ctx);
|
||||||
if(pos >= 0) {
|
if(ctx.type > 0 && pos >= 0) {
|
||||||
if(ctx.type == DATA_TAG_DLMS) {
|
if(ctx.type == DATA_TAG_DLMS) {
|
||||||
debugV("Received valid DLMS at %d", pos);
|
debugV("Received valid DLMS at %d", pos);
|
||||||
} else if(ctx.type == DATA_TAG_DSMR) {
|
} else if(ctx.type == DATA_TAG_DSMR) {
|
||||||
@@ -819,6 +825,12 @@ bool readHanPort() {
|
|||||||
}
|
}
|
||||||
if(pos == DATA_PARSE_INCOMPLETE) {
|
if(pos == DATA_PARSE_INCOMPLETE) {
|
||||||
return false;
|
return false;
|
||||||
|
} else if(pos == DATA_PARSE_UNKNOWN_DATA) {
|
||||||
|
debugV("Unknown data payload:");
|
||||||
|
len = len + hanSerial->readBytes(hanBuffer+len, BUF_SIZE_HAN-len);
|
||||||
|
debugPrint(hanBuffer, 0, len);
|
||||||
|
len = 0;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(pos == DATA_PARSE_INTERMEDIATE_SEGMENT) {
|
if(pos == DATA_PARSE_INTERMEDIATE_SEGMENT) {
|
||||||
@@ -829,6 +841,7 @@ bool readHanPort() {
|
|||||||
len += hanSerial->readBytes(hanBuffer+len, BUF_SIZE_HAN-len);
|
len += hanSerial->readBytes(hanBuffer+len, BUF_SIZE_HAN-len);
|
||||||
if(mqttEnabled && mqtt != NULL && mqttHandler == NULL) {
|
if(mqttEnabled && mqtt != NULL && mqttHandler == NULL) {
|
||||||
mqtt->publish(topic.c_str(), toHex(hanBuffer+pos, len));
|
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
|
while(hanSerial->available()) hanSerial->read(); // Make sure it is all empty, in case we overflowed buffer above
|
||||||
len = 0;
|
len = 0;
|
||||||
@@ -841,19 +854,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
@@ -956,13 +976,13 @@ void printHanReadError(int pos) {
|
|||||||
void debugPrint(byte *buffer, int start, int length) {
|
void debugPrint(byte *buffer, int start, int length) {
|
||||||
for (int i = start; i < start + length; i++) {
|
for (int i = start; i < start + length; i++) {
|
||||||
if (buffer[i] < 0x10)
|
if (buffer[i] < 0x10)
|
||||||
Debug.print("0");
|
Debug.print(F("0"));
|
||||||
Debug.print(buffer[i], HEX);
|
Debug.print(buffer[i], HEX);
|
||||||
Debug.print(" ");
|
Debug.print(F(" "));
|
||||||
if ((i - start + 1) % 16 == 0)
|
if ((i - start + 1) % 16 == 0)
|
||||||
Debug.println("");
|
Debug.println("");
|
||||||
else if ((i - start + 1) % 4 == 0)
|
else if ((i - start + 1) % 4 == 0)
|
||||||
Debug.print(" ");
|
Debug.print(F(" "));
|
||||||
|
|
||||||
yield(); // Let other get some resources too
|
yield(); // Let other get some resources too
|
||||||
}
|
}
|
||||||
@@ -1035,7 +1055,6 @@ 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);
|
||||||
@@ -1077,7 +1096,7 @@ void WiFi_connect() {
|
|||||||
if(strlen(wifi.dns2) > 0) {
|
if(strlen(wifi.dns2) > 0) {
|
||||||
dns2.fromString(wifi.dns2);
|
dns2.fromString(wifi.dns2);
|
||||||
} else if(dns1.toString().isEmpty()) {
|
} else if(dns1.toString().isEmpty()) {
|
||||||
dns2.fromString("208.67.220.220"); // Add OpenDNS as second by default if nothing is configured
|
dns2.fromString(F("208.67.220.220")); // Add OpenDNS as second by default if nothing is configured
|
||||||
}
|
}
|
||||||
if(!WiFi.config(ip, gw, sn, dns1, dns2)) {
|
if(!WiFi.config(ip, gw, sn, dns1, dns2)) {
|
||||||
debugE("Static IP configuration is invalid, not using");
|
debugE("Static IP configuration is invalid, not using");
|
||||||
@@ -1096,6 +1115,19 @@ void WiFi_connect() {
|
|||||||
WiFi.setAutoReconnect(true);
|
WiFi.setAutoReconnect(true);
|
||||||
WiFi.persistent(true);
|
WiFi.persistent(true);
|
||||||
if(WiFi.begin(wifi.ssid, wifi.psk)) {
|
if(WiFi.begin(wifi.ssid, wifi.psk)) {
|
||||||
|
if(wifi.sleep <= 2) {
|
||||||
|
switch(wifi.sleep) {
|
||||||
|
case 0:
|
||||||
|
WiFi.setSleep(WIFI_PS_NONE);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
WiFi.setSleep(WIFI_PS_MIN_MODEM);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
WiFi.setSleep(WIFI_PS_MAX_MODEM);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
yield();
|
yield();
|
||||||
} else {
|
} else {
|
||||||
if (Debug.isActive(RemoteDebug::ERROR)) debugI("Unable to start WiFi");
|
if (Debug.isActive(RemoteDebug::ERROR)) debugI("Unable to start WiFi");
|
||||||
@@ -1105,16 +1137,16 @@ void WiFi_connect() {
|
|||||||
|
|
||||||
void mqttMessageReceived(String &topic, String &payload) {
|
void mqttMessageReceived(String &topic, String &payload) {
|
||||||
debugI("Received message for topic %s", topic.c_str() );
|
debugI("Received message for topic %s", topic.c_str() );
|
||||||
if(meterConfig.source == METER_SOURCE_MQTT) {
|
//if(meterConfig.source == METER_SOURCE_MQTT) {
|
||||||
DataParserContext ctx = {payload.length()/2};
|
//DataParserContext ctx = {static_cast<uint8_t>(payload.length()/2)};
|
||||||
fromHex(hanBuffer, payload, ctx.length);
|
//fromHex(hanBuffer, payload, ctx.length);
|
||||||
uint16_t pos = unwrapData(hanBuffer, ctx);
|
//uint16_t pos = unwrapData(hanBuffer, ctx);
|
||||||
// TODO: Run through DLMS/DMSR parser and apply AmsData
|
// TODO: Run through DLMS/DMSR parser and apply AmsData
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t unwrapData(uint8_t *buf, DataParserContext &context) {
|
int16_t unwrapData(uint8_t *buf, DataParserContext &context) {
|
||||||
int16_t ret;
|
int16_t ret = 0;
|
||||||
bool doRet = false;
|
bool doRet = false;
|
||||||
uint16_t end = BUF_SIZE_HAN;
|
uint16_t end = BUF_SIZE_HAN;
|
||||||
uint8_t tag = (*buf);
|
uint8_t tag = (*buf);
|
||||||
@@ -1154,7 +1186,7 @@ int16_t unwrapData(uint8_t *buf, DataParserContext &context) {
|
|||||||
if(res >= 0) doRet = true;
|
if(res >= 0) doRet = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
debugE("Ended up in default case while unwrapping...");
|
debugE("Ended up in default case while unwrapping...(tag %02X)", tag);
|
||||||
return DATA_PARSE_UNKNOWN_DATA;
|
return DATA_PARSE_UNKNOWN_DATA;
|
||||||
}
|
}
|
||||||
lastTag = tag;
|
lastTag = tag;
|
||||||
@@ -1169,6 +1201,7 @@ int16_t unwrapData(uint8_t *buf, DataParserContext &context) {
|
|||||||
// 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(buf, curLen));
|
mqtt->publish(topic.c_str(), toHex(buf, curLen));
|
||||||
|
mqtt->loop();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DATA_TAG_MBUS:
|
case DATA_TAG_MBUS:
|
||||||
@@ -1176,6 +1209,7 @@ int16_t unwrapData(uint8_t *buf, DataParserContext &context) {
|
|||||||
// 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(buf, curLen));
|
mqtt->publish(topic.c_str(), toHex(buf, curLen));
|
||||||
|
mqtt->loop();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DATA_TAG_GBT:
|
case DATA_TAG_GBT:
|
||||||
@@ -1194,6 +1228,7 @@ int16_t unwrapData(uint8_t *buf, DataParserContext &context) {
|
|||||||
debugV("DSMR frame:");
|
debugV("DSMR frame:");
|
||||||
if(mqttEnabled && mqtt != NULL && mqttHandler == NULL) {
|
if(mqttEnabled && mqtt != NULL && mqttHandler == NULL) {
|
||||||
mqtt->publish(topic.c_str(), (char*) buf);
|
mqtt->publish(topic.c_str(), (char*) buf);
|
||||||
|
mqtt->loop();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1232,7 +1267,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) {
|
||||||
@@ -1247,20 +1281,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1295,54 +1328,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, (char*) "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, (char*) "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, (char*) "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, (char*) "r");
|
||||||
|
mqttSecureClient->loadCertificate(file, file.size());
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
debugI("Found MQTT key file (%dkb free heap)", ESP.getFreeHeap());
|
||||||
|
file = LittleFS.open(FILE_MQTT_KEY, (char*) "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) {
|
||||||
@@ -1358,7 +1391,7 @@ void MQTT_connect() {
|
|||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
if(mqttSecureClient) {
|
if(mqttSecureClient) {
|
||||||
time_t epoch = time(nullptr);
|
time_t epoch = time(nullptr);
|
||||||
debugD("Setting NTP time %i for secure MQTT connection", epoch);
|
debugD("Setting NTP time %lld for secure MQTT connection", epoch);
|
||||||
mqttSecureClient->setX509Time(epoch);
|
mqttSecureClient->setX509Time(epoch);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1367,10 +1400,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
|
||||||
@@ -1402,7 +1434,7 @@ void configFileParse() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
File file = LittleFS.open(FILE_CFG, "r");
|
File file = LittleFS.open(FILE_CFG, (char*) "r");
|
||||||
|
|
||||||
bool lSys = false;
|
bool lSys = false;
|
||||||
bool lWiFi = false;
|
bool lWiFi = false;
|
||||||
@@ -1432,195 +1464,196 @@ void configFileParse() {
|
|||||||
char* buf = (char*) commonBuffer;
|
char* buf = (char*) commonBuffer;
|
||||||
memset(buf, 0, 1024);
|
memset(buf, 0, 1024);
|
||||||
while((size = file.readBytesUntil('\n', buf, 1024)) > 0) {
|
while((size = file.readBytesUntil('\n', buf, 1024)) > 0) {
|
||||||
if(strncmp(buf, "boardType ", 10) == 0) {
|
if(strncmp_P(buf, PSTR("boardType "), 10) == 0) {
|
||||||
if(!lSys) { config.getSystemConfig(sys); lSys = true; };
|
if(!lSys) { config.getSystemConfig(sys); lSys = true; };
|
||||||
sys.boardType = String(buf+10).toInt();
|
sys.boardType = String(buf+10).toInt();
|
||||||
} else if(strncmp(buf, "ssid ", 5) == 0) {
|
} else if(strncmp_P(buf, PSTR("ssid "), 5) == 0) {
|
||||||
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
|
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
|
||||||
memcpy(wifi.ssid, buf+5, size-5);
|
memcpy(wifi.ssid, buf+5, size-5);
|
||||||
} else if(strncmp(buf, "psk ", 4) == 0) {
|
} else if(strncmp_P(buf, PSTR("psk "), 4) == 0) {
|
||||||
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
|
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
|
||||||
memcpy(wifi.psk, buf+4, size-4);
|
memcpy(wifi.psk, buf+4, size-4);
|
||||||
} else if(strncmp(buf, "ip ", 3) == 0) {
|
} else if(strncmp_P(buf, PSTR("ip "), 3) == 0) {
|
||||||
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
|
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
|
||||||
memcpy(wifi.ip, buf+3, size-3);
|
memcpy(wifi.ip, buf+3, size-3);
|
||||||
} else if(strncmp(buf, "gateway ", 8) == 0) {
|
} else if(strncmp_P(buf, PSTR("gateway "), 8) == 0) {
|
||||||
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
|
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
|
||||||
memcpy(wifi.gateway, buf+8, size-8);
|
memcpy(wifi.gateway, buf+8, size-8);
|
||||||
} else if(strncmp(buf, "subnet ", 7) == 0) {
|
} else if(strncmp_P(buf, PSTR("subnet "), 7) == 0) {
|
||||||
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
|
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
|
||||||
memcpy(wifi.subnet, buf+7, size-7);
|
memcpy(wifi.subnet, buf+7, size-7);
|
||||||
} else if(strncmp(buf, "dns1 ", 5) == 0) {
|
} else if(strncmp_P(buf, PSTR("dns1 "), 5) == 0) {
|
||||||
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
|
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
|
||||||
memcpy(wifi.dns1, buf+5, size-5);
|
memcpy(wifi.dns1, buf+5, size-5);
|
||||||
} else if(strncmp(buf, "dns2 ", 5) == 0) {
|
} else if(strncmp_P(buf, PSTR("dns2 "), 5) == 0) {
|
||||||
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
|
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
|
||||||
memcpy(wifi.dns2, buf+5, size-5);
|
memcpy(wifi.dns2, buf+5, size-5);
|
||||||
} else if(strncmp(buf, "hostname ", 9) == 0) {
|
} else if(strncmp_P(buf, PSTR("hostname "), 9) == 0) {
|
||||||
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
|
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
|
||||||
memcpy(wifi.hostname, buf+9, size-9);
|
memcpy(wifi.hostname, buf+9, size-9);
|
||||||
} else if(strncmp(buf, "mdns ", 5) == 0) {
|
} else if(strncmp_P(buf, PSTR("mdns "), 5) == 0) {
|
||||||
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
|
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
|
||||||
wifi.mdns = String(buf+5).toInt() == 1;;
|
wifi.mdns = String(buf+5).toInt() == 1;;
|
||||||
} else if(strncmp(buf, "mqttHost ", 9) == 0) {
|
} else if(strncmp_P(buf, PSTR("mqttHost "), 9) == 0) {
|
||||||
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
|
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
|
||||||
memcpy(mqtt.host, buf+9, size-9);
|
memcpy(mqtt.host, buf+9, size-9);
|
||||||
} else if(strncmp(buf, "mqttPort ", 9) == 0) {
|
} else if(strncmp_P(buf, PSTR("mqttPort "), 9) == 0) {
|
||||||
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
|
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
|
||||||
mqtt.port = String(buf+9).toInt();
|
mqtt.port = String(buf+9).toInt();
|
||||||
} else if(strncmp(buf, "mqttClientId ", 13) == 0) {
|
} else if(strncmp_P(buf, PSTR("mqttClientId "), 13) == 0) {
|
||||||
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
|
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
|
||||||
memcpy(mqtt.clientId, buf+13, size-13);
|
memcpy(mqtt.clientId, buf+13, size-13);
|
||||||
} else if(strncmp(buf, "mqttPublishTopic ", 17) == 0) {
|
} else if(strncmp_P(buf, PSTR("mqttPublishTopic "), 17) == 0) {
|
||||||
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
|
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
|
||||||
memcpy(mqtt.publishTopic, buf+17, size-17);
|
memcpy(mqtt.publishTopic, buf+17, size-17);
|
||||||
} else if(strncmp(buf, "mqttUsername ", 13) == 0) {
|
} else if(strncmp_P(buf, PSTR("mqttUsername "), 13) == 0) {
|
||||||
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
|
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
|
||||||
memcpy(mqtt.username, buf+13, size-13);
|
memcpy(mqtt.username, buf+13, size-13);
|
||||||
} else if(strncmp(buf, "mqttPassword ", 13) == 0) {
|
} else if(strncmp_P(buf, PSTR("mqttPassword "), 13) == 0) {
|
||||||
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
|
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
|
||||||
memcpy(mqtt.password, buf+13, size-13);
|
memcpy(mqtt.password, buf+13, size-13);
|
||||||
} else if(strncmp(buf, "mqttPayloadFormat ", 18) == 0) {
|
} else if(strncmp_P(buf, PSTR("mqttPayloadFormat "), 18) == 0) {
|
||||||
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
|
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
|
||||||
mqtt.payloadFormat = String(buf+18).toInt();
|
mqtt.payloadFormat = String(buf+18).toInt();
|
||||||
} else if(strncmp(buf, "mqttSsl ", 8) == 0) {
|
} else if(strncmp_P(buf, PSTR("mqttSsl "), 8) == 0) {
|
||||||
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
|
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
|
||||||
mqtt.ssl = String(buf+8).toInt() == 1;;
|
mqtt.ssl = String(buf+8).toInt() == 1;;
|
||||||
} else if(strncmp(buf, "webSecurity ", 12) == 0) {
|
} else if(strncmp_P(buf, PSTR("webSecurity "), 12) == 0) {
|
||||||
if(!lWeb) { config.getWebConfig(web); lWeb = true; };
|
if(!lWeb) { config.getWebConfig(web); lWeb = true; };
|
||||||
web.security = String(buf+12).toInt();
|
web.security = String(buf+12).toInt();
|
||||||
} else if(strncmp(buf, "webUsername ", 12) == 0) {
|
} else if(strncmp_P(buf, PSTR("webUsername "), 12) == 0) {
|
||||||
if(!lWeb) { config.getWebConfig(web); lWeb = true; };
|
if(!lWeb) { config.getWebConfig(web); lWeb = true; };
|
||||||
memcpy(web.username, buf+12, size-12);
|
memcpy(web.username, buf+12, size-12);
|
||||||
} else if(strncmp(buf, "webPassword ", 12) == 0) {
|
} else if(strncmp_P(buf, PSTR("webPassword "), 12) == 0) {
|
||||||
if(!lWeb) { config.getWebConfig(web); lWeb = true; };
|
if(!lWeb) { config.getWebConfig(web); lWeb = true; };
|
||||||
memcpy(web.username, buf+12, size-12);
|
memcpy(web.username, buf+12, size-12);
|
||||||
} else if(strncmp(buf, "meterBaud ", 10) == 0) {
|
} else if(strncmp_P(buf, PSTR("meterBaud "), 10) == 0) {
|
||||||
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
||||||
meter.baud = String(buf+10).toInt();
|
meter.baud = String(buf+10).toInt();
|
||||||
} else if(strncmp(buf, "meterParity ", 12) == 0) {
|
} else if(strncmp_P(buf, PSTR("meterParity "), 12) == 0) {
|
||||||
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
||||||
if(strncmp(buf+12, "7N1", 3) == 0) meter.parity = 2;
|
if(strncmp_P(buf+12, PSTR("7N1"), 3) == 0) meter.parity = 2;
|
||||||
if(strncmp(buf+12, "8N1", 3) == 0) meter.parity = 3;
|
if(strncmp_P(buf+12, PSTR("8N1"), 3) == 0) meter.parity = 3;
|
||||||
if(strncmp(buf+12, "7E1", 3) == 0) meter.parity = 10;
|
if(strncmp_P(buf+12, PSTR("7E1"), 3) == 0) meter.parity = 10;
|
||||||
if(strncmp(buf+12, "8E1", 3) == 0) meter.parity = 11;
|
if(strncmp_P(buf+12, PSTR("8E1"), 3) == 0) meter.parity = 11;
|
||||||
} else if(strncmp(buf, "meterInvert ", 12) == 0) {
|
} else if(strncmp_P(buf, PSTR("meterInvert "), 12) == 0) {
|
||||||
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
||||||
meter.invert = String(buf+12).toInt() == 1;;
|
meter.invert = String(buf+12).toInt() == 1;;
|
||||||
} else if(strncmp(buf, "meterDistributionSystem ", 24) == 0) {
|
} else if(strncmp_P(buf, PSTR("meterDistributionSystem "), 24) == 0) {
|
||||||
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
||||||
meter.distributionSystem = String(buf+24).toInt();
|
meter.distributionSystem = String(buf+24).toInt();
|
||||||
} else if(strncmp(buf, "meterMainFuse ", 14) == 0) {
|
} else if(strncmp_P(buf, PSTR("meterMainFuse "), 14) == 0) {
|
||||||
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
||||||
meter.mainFuse = String(buf+14).toInt();
|
meter.mainFuse = String(buf+14).toInt();
|
||||||
} else if(strncmp(buf, "meterProductionCapacity ", 24) == 0) {
|
} else if(strncmp_P(buf, PSTR("meterProductionCapacity "), 24) == 0) {
|
||||||
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
||||||
meter.productionCapacity = String(buf+24).toInt();
|
meter.productionCapacity = String(buf+24).toInt();
|
||||||
} else if(strncmp(buf, "meterEncryptionKey ", 19) == 0) {
|
} else if(strncmp_P(buf, PSTR("meterEncryptionKey "), 19) == 0) {
|
||||||
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
||||||
fromHex(meter.encryptionKey, String(buf+19), 16);
|
fromHex(meter.encryptionKey, String(buf+19), 16);
|
||||||
} else if(strncmp(buf, "meterAuthenticationKey ", 23) == 0) {
|
} else if(strncmp_P(buf, PSTR("meterAuthenticationKey "), 23) == 0) {
|
||||||
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
||||||
fromHex(meter.authenticationKey, String(buf+19), 16);
|
fromHex(meter.authenticationKey, String(buf+19), 16);
|
||||||
} else if(strncmp(buf, "gpioHanPin ", 11) == 0) {
|
} else if(strncmp_P(buf, PSTR("gpioHanPin "), 11) == 0) {
|
||||||
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
||||||
gpio.hanPin = String(buf+11).toInt();
|
gpio.hanPin = String(buf+11).toInt();
|
||||||
} else if(strncmp(buf, "gpioApPin ", 10) == 0) {
|
} else if(strncmp_P(buf, PSTR("gpioApPin "), 10) == 0) {
|
||||||
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
||||||
gpio.apPin = String(buf+10).toInt();
|
gpio.apPin = String(buf+10).toInt();
|
||||||
} else if(strncmp(buf, "gpioLedPin ", 11) == 0) {
|
} else if(strncmp_P(buf, PSTR("gpioLedPin "), 11) == 0) {
|
||||||
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
||||||
gpio.ledPin = String(buf+11).toInt();
|
gpio.ledPin = String(buf+11).toInt();
|
||||||
} else if(strncmp(buf, "gpioLedInverted ", 16) == 0) {
|
} else if(strncmp_P(buf, PSTR("gpioLedInverted "), 16) == 0) {
|
||||||
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
||||||
gpio.ledInverted = String(buf+16).toInt() == 1;
|
gpio.ledInverted = String(buf+16).toInt() == 1;
|
||||||
} else if(strncmp(buf, "gpioLedPinRed ", 14) == 0) {
|
} else if(strncmp_P(buf, PSTR("gpioLedPinRed "), 14) == 0) {
|
||||||
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
||||||
gpio.ledPinRed = String(buf+14).toInt();
|
gpio.ledPinRed = String(buf+14).toInt();
|
||||||
} else if(strncmp(buf, "gpioLedPinGreen ", 16) == 0) {
|
} else if(strncmp_P(buf, PSTR("gpioLedPinGreen "), 16) == 0) {
|
||||||
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
||||||
gpio.ledPinGreen = String(buf+16).toInt();
|
gpio.ledPinGreen = String(buf+16).toInt();
|
||||||
} else if(strncmp(buf, "gpioLedPinBlue ", 15) == 0) {
|
} else if(strncmp_P(buf, PSTR("gpioLedPinBlue "), 15) == 0) {
|
||||||
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
||||||
gpio.ledPinBlue = String(buf+15).toInt();
|
gpio.ledPinBlue = String(buf+15).toInt();
|
||||||
} else if(strncmp(buf, "gpioLedRgbInverted ", 19) == 0) {
|
} else if(strncmp_P(buf, PSTR("gpioLedRgbInverted "), 19) == 0) {
|
||||||
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
||||||
gpio.ledRgbInverted = String(buf+19).toInt() == 1;
|
gpio.ledRgbInverted = String(buf+19).toInt() == 1;
|
||||||
} else if(strncmp(buf, "gpioTempSensorPin ", 18) == 0) {
|
} else if(strncmp_P(buf, PSTR("gpioTempSensorPin "), 18) == 0) {
|
||||||
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
||||||
gpio.tempSensorPin = String(buf+18).toInt();
|
gpio.tempSensorPin = String(buf+18).toInt();
|
||||||
} else if(strncmp(buf, "gpioTempAnalogSensorPin ", 24) == 0) {
|
} else if(strncmp_P(buf, PSTR("gpioTempAnalogSensorPin "), 24) == 0) {
|
||||||
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
||||||
gpio.tempAnalogSensorPin = String(buf+24).toInt();
|
gpio.tempAnalogSensorPin = String(buf+24).toInt();
|
||||||
} else if(strncmp(buf, "gpioVccPin ", 11) == 0) {
|
} else if(strncmp_P(buf, PSTR("gpioVccPin "), 11) == 0) {
|
||||||
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
||||||
gpio.vccPin = String(buf+11).toInt();
|
gpio.vccPin = String(buf+11).toInt();
|
||||||
} else if(strncmp(buf, "gpioVccOffset ", 14) == 0) {
|
} else if(strncmp_P(buf, PSTR("gpioVccOffset "), 14) == 0) {
|
||||||
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
||||||
gpio.vccOffset = String(buf+14).toDouble() * 100;
|
gpio.vccOffset = String(buf+14).toDouble() * 100;
|
||||||
} else if(strncmp(buf, "gpioVccMultiplier ", 18) == 0) {
|
} else if(strncmp_P(buf, PSTR("gpioVccMultiplier "), 18) == 0) {
|
||||||
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
||||||
gpio.vccMultiplier = String(buf+18).toDouble() * 1000;
|
gpio.vccMultiplier = String(buf+18).toDouble() * 1000;
|
||||||
} else if(strncmp(buf, "gpioVccBootLimit ", 17) == 0) {
|
} else if(strncmp_P(buf, PSTR("gpioVccBootLimit "), 17) == 0) {
|
||||||
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
||||||
gpio.vccBootLimit = String(buf+17).toDouble() * 10;
|
gpio.vccBootLimit = String(buf+17).toDouble() * 10;
|
||||||
} else if(strncmp(buf, "gpioVccResistorGnd ", 19) == 0) {
|
} else if(strncmp_P(buf, PSTR("gpioVccResistorGnd "), 19) == 0) {
|
||||||
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
||||||
gpio.vccResistorGnd = String(buf+19).toInt();
|
gpio.vccResistorGnd = String(buf+19).toInt();
|
||||||
} else if(strncmp(buf, "gpioVccResistorVcc ", 19) == 0) {
|
} else if(strncmp_P(buf, PSTR("gpioVccResistorVcc "), 19) == 0) {
|
||||||
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
|
||||||
gpio.vccResistorVcc = String(buf+19).toInt();
|
gpio.vccResistorVcc = String(buf+19).toInt();
|
||||||
} else if(strncmp(buf, "domoticzElidx ", 14) == 0) {
|
} else if(strncmp_P(buf, PSTR("domoticzElidx "), 14) == 0) {
|
||||||
if(!lDomo) { config.getDomoticzConfig(domo); lDomo = true; };
|
if(!lDomo) { config.getDomoticzConfig(domo); lDomo = true; };
|
||||||
domo.elidx = String(buf+14).toInt();
|
domo.elidx = String(buf+14).toInt();
|
||||||
} else if(strncmp(buf, "domoticzVl1idx ", 15) == 0) {
|
} else if(strncmp_P(buf, PSTR("domoticzVl1idx "), 15) == 0) {
|
||||||
if(!lDomo) { config.getDomoticzConfig(domo); lDomo = true; };
|
if(!lDomo) { config.getDomoticzConfig(domo); lDomo = true; };
|
||||||
domo.vl1idx = String(buf+15).toInt();
|
domo.vl1idx = String(buf+15).toInt();
|
||||||
} else if(strncmp(buf, "domoticzVl2idx ", 15) == 0) {
|
} else if(strncmp_P(buf, PSTR("domoticzVl2idx "), 15) == 0) {
|
||||||
if(!lDomo) { config.getDomoticzConfig(domo); lDomo = true; };
|
if(!lDomo) { config.getDomoticzConfig(domo); lDomo = true; };
|
||||||
domo.vl2idx = String(buf+15).toInt();
|
domo.vl2idx = String(buf+15).toInt();
|
||||||
} else if(strncmp(buf, "domoticzVl3idx ", 15) == 0) {
|
} else if(strncmp_P(buf, PSTR("domoticzVl3idx "), 15) == 0) {
|
||||||
if(!lDomo) { config.getDomoticzConfig(domo); lDomo = true; };
|
if(!lDomo) { config.getDomoticzConfig(domo); lDomo = true; };
|
||||||
domo.vl3idx = String(buf+15).toInt();
|
domo.vl3idx = String(buf+15).toInt();
|
||||||
} else if(strncmp(buf, "domoticzCl1idx ", 15) == 0) {
|
} else if(strncmp_P(buf, PSTR("domoticzCl1idx "), 15) == 0) {
|
||||||
if(!lDomo) { config.getDomoticzConfig(domo); lDomo = true; };
|
if(!lDomo) { config.getDomoticzConfig(domo); lDomo = true; };
|
||||||
domo.cl1idx = String(buf+15).toInt();
|
domo.cl1idx = String(buf+15).toInt();
|
||||||
} else if(strncmp(buf, "ntpEnable ", 10) == 0) {
|
} else if(strncmp_P(buf, PSTR("ntpEnable "), 10) == 0) {
|
||||||
if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; };
|
if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; };
|
||||||
ntp.enable = String(buf+10).toInt() == 1;
|
ntp.enable = String(buf+10).toInt() == 1;
|
||||||
} else if(strncmp(buf, "ntpDhcp ", 8) == 0) {
|
} else if(strncmp_P(buf, PSTR("ntpDhcp "), 8) == 0) {
|
||||||
if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; };
|
if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; };
|
||||||
ntp.dhcp = String(buf+8).toInt() == 1;
|
ntp.dhcp = String(buf+8).toInt() == 1;
|
||||||
} else if(strncmp(buf, "ntpOffset ", 10) == 0) {
|
} else if(strncmp_P(buf, PSTR("ntpOffset "), 10) == 0) {
|
||||||
if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; };
|
if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; };
|
||||||
ntp.offset = String(buf+10).toInt() / 10;
|
ntp.offset = String(buf+10).toInt() / 10;
|
||||||
} else if(strncmp(buf, "ntpSummerOffset ", 16) == 0) {
|
} else if(strncmp_P(buf, PSTR("ntpSummerOffset "), 16) == 0) {
|
||||||
if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; };
|
if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; };
|
||||||
ntp.summerOffset = String(buf+16).toInt() / 10;
|
ntp.summerOffset = String(buf+16).toInt() / 10;
|
||||||
} else if(strncmp(buf, "ntpServer ", 10) == 0) {
|
} else if(strncmp_P(buf, PSTR("ntpServer "), 10) == 0) {
|
||||||
if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; };
|
if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; };
|
||||||
memcpy(ntp.server, buf+10, size-10);
|
memcpy(ntp.server, buf+10, size-10);
|
||||||
} else if(strncmp(buf, "entsoeToken ", 12) == 0) {
|
} else if(strncmp_P(buf, PSTR("entsoeToken "), 12) == 0) {
|
||||||
if(!lEntsoe) { config.getEntsoeConfig(entsoe); lEntsoe = true; };
|
if(!lEntsoe) { config.getEntsoeConfig(entsoe); lEntsoe = true; };
|
||||||
memcpy(entsoe.token, buf+12, size-12);
|
memcpy(entsoe.token, buf+12, size-12);
|
||||||
} else if(strncmp(buf, "entsoeArea ", 11) == 0) {
|
} else if(strncmp_P(buf, PSTR("entsoeArea "), 11) == 0) {
|
||||||
if(!lEntsoe) { config.getEntsoeConfig(entsoe); lEntsoe = true; };
|
if(!lEntsoe) { config.getEntsoeConfig(entsoe); lEntsoe = true; };
|
||||||
memcpy(entsoe.area, buf+11, size-11);
|
memcpy(entsoe.area, buf+11, size-11);
|
||||||
} else if(strncmp(buf, "entsoeCurrency ", 15) == 0) {
|
} else if(strncmp_P(buf, PSTR("entsoeCurrency "), 15) == 0) {
|
||||||
if(!lEntsoe) { config.getEntsoeConfig(entsoe); lEntsoe = true; };
|
if(!lEntsoe) { config.getEntsoeConfig(entsoe); lEntsoe = true; };
|
||||||
memcpy(entsoe.currency, buf+15, size-15);
|
memcpy(entsoe.currency, buf+15, size-15);
|
||||||
} else if(strncmp(buf, "entsoeMultiplier ", 17) == 0) {
|
} else if(strncmp_P(buf, PSTR("entsoeMultiplier "), 17) == 0) {
|
||||||
if(!lEntsoe) { config.getEntsoeConfig(entsoe); lEntsoe = true; };
|
if(!lEntsoe) { config.getEntsoeConfig(entsoe); lEntsoe = true; };
|
||||||
entsoe.multiplier = String(buf+17).toDouble() * 1000;
|
entsoe.multiplier = String(buf+17).toDouble() * 1000;
|
||||||
} else if(strncmp(buf, "thresholds ", 11) == 0) {
|
} else if(strncmp_P(buf, PSTR("thresholds "), 11) == 0) {
|
||||||
if(!lEac) { config.getEnergyAccountingConfig(eac); lEac = true; };
|
if(!lEac) { config.getEnergyAccountingConfig(eac); lEac = true; };
|
||||||
int i = 0;
|
int i = 0;
|
||||||
char * pch = strtok (buf+11," ");
|
char * pch = strtok (buf+11," ");
|
||||||
while (pch != NULL) {
|
while (pch != NULL && i < 10) {
|
||||||
eac.thresholds[i++] = String(pch).toInt();
|
eac.thresholds[i++] = String(pch).toInt();
|
||||||
pch = strtok (NULL, " ");
|
pch = strtok (NULL, " ");
|
||||||
}
|
}
|
||||||
} else if(strncmp(buf, "dayplot ", 8) == 0) {
|
eac.hours = String(pch).toInt();
|
||||||
|
} else if(strncmp_P(buf, PSTR("dayplot "), 8) == 0) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
DayDataPoints day = { 4 }; // Use a version we know the multiplier of the data points
|
DayDataPoints day = { 4 }; // Use a version we know the multiplier of the data points
|
||||||
char * pch = strtok (buf+8," ");
|
char * pch = strtok (buf+8," ");
|
||||||
@@ -1643,7 +1676,7 @@ void configFileParse() {
|
|||||||
}
|
}
|
||||||
ds.setDayData(day);
|
ds.setDayData(day);
|
||||||
sDs = true;
|
sDs = true;
|
||||||
} else if(strncmp(buf, "monthplot ", 10) == 0) {
|
} else if(strncmp_P(buf, PSTR("monthplot "), 10) == 0) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
MonthDataPoints month = { 5 }; // Use a version we know the multiplier of the data points
|
MonthDataPoints month = { 5 }; // Use a version we know the multiplier of the data points
|
||||||
char * pch = strtok (buf+10," ");
|
char * pch = strtok (buf+10," ");
|
||||||
@@ -1666,7 +1699,7 @@ void configFileParse() {
|
|||||||
}
|
}
|
||||||
ds.setMonthData(month);
|
ds.setMonthData(month);
|
||||||
sDs = true;
|
sDs = true;
|
||||||
} else if(strncmp(buf, "energyaccounting ", 17) == 0) {
|
} else if(strncmp_P(buf, PSTR("energyaccounting "), 17) == 0) {
|
||||||
uint8_t i = 0;
|
uint8_t i = 0;
|
||||||
EnergyAccountingData ead = { 4, 0,
|
EnergyAccountingData ead = { 4, 0,
|
||||||
0, 0, 0,
|
0, 0, 0,
|
||||||
@@ -1731,6 +1764,7 @@ void configFileParse() {
|
|||||||
if(lDomo) config.setDomoticzConfig(domo);
|
if(lDomo) config.setDomoticzConfig(domo);
|
||||||
if(lNtp) config.setNtpConfig(ntp);
|
if(lNtp) config.setNtpConfig(ntp);
|
||||||
if(lEntsoe) config.setEntsoeConfig(entsoe);
|
if(lEntsoe) config.setEntsoeConfig(entsoe);
|
||||||
|
if(lEac) config.setEnergyAccountingConfig(eac);
|
||||||
if(sDs) ds.save();
|
if(sDs) ds.save();
|
||||||
if(sEa) ea.save();
|
if(sEa) ea.save();
|
||||||
config.save();
|
config.save();
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ bool EnergyAccounting::update(AmsData* amsData) {
|
|||||||
for(uint8_t i = 0; i < 5; i++) {
|
for(uint8_t i = 0; i < 5; i++) {
|
||||||
debugger->printf("(EnergyAccounting) Peak hour from day %d: %d\n", data.peaks[i].day, data.peaks[i].value*10);
|
debugger->printf("(EnergyAccounting) Peak hour from day %d: %d\n", data.peaks[i].day, data.peaks[i].value*10);
|
||||||
}
|
}
|
||||||
debugger->printf("(EnergyAccounting) Loaded cost yesterday: %d, this month: %d, last month: %d\n", data.costYesterday / 10.0, data.costThisMonth, data.costLastMonth);
|
debugger->printf("(EnergyAccounting) Loaded cost yesterday: %.2f, this month: %d, last month: %d\n", data.costYesterday / 10.0, data.costThisMonth, data.costLastMonth);
|
||||||
}
|
}
|
||||||
init = true;
|
init = true;
|
||||||
}
|
}
|
||||||
@@ -237,7 +237,37 @@ 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) {
|
for(uint8_t x = 0;x < min((uint8_t) 5, config->hours); x++) {
|
||||||
|
uint8_t maxIdx = 0;
|
||||||
|
uint16_t maxVal = 0;
|
||||||
|
for(uint8_t i = 0; i < 5; i++) {
|
||||||
|
if(included[i]) continue;
|
||||||
|
if(data.peaks[i].day == 0) continue;
|
||||||
|
if(data.peaks[i].value > maxVal) {
|
||||||
|
maxVal = data.peaks[i].value;
|
||||||
|
maxIdx = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(maxVal > 0) {
|
||||||
|
included[maxIdx] = true;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(uint8_t i = 0; i < 5; i++) {
|
||||||
|
if(!included[i]) continue;
|
||||||
|
maxHour += data.peaks[i].value;
|
||||||
|
}
|
||||||
|
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 };
|
||||||
|
|
||||||
|
for(uint8_t x = 0;x < min((uint8_t) 5, config->hours); x++) {
|
||||||
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++) {
|
||||||
@@ -247,17 +277,21 @@ float EnergyAccounting::getMonthMax() {
|
|||||||
maxIdx = i;
|
maxIdx = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
included[maxIdx] = true;
|
if(maxVal > 0) {
|
||||||
count++;
|
included[maxIdx] = true;
|
||||||
}
|
count++;
|
||||||
|
|
||||||
for(uint8_t i = 0; i < 5; i++) {
|
|
||||||
if(!included[i]) continue;
|
|
||||||
if(data.peaks[i].day > 0) {
|
|
||||||
maxHour += data.peaks[i].value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return maxHour > 0 ? maxHour / count / 100.0 : 0.0;
|
|
||||||
|
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() {
|
bool EnergyAccounting::load() {
|
||||||
@@ -310,7 +344,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) {
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ public:
|
|||||||
|
|
||||||
float getMonthMax();
|
float getMonthMax();
|
||||||
uint8_t getCurrentThreshold();
|
uint8_t getCurrentThreshold();
|
||||||
|
float getPeak(uint8_t);
|
||||||
|
|
||||||
EnergyAccountingData getData();
|
EnergyAccountingData getData();
|
||||||
void setData(EnergyAccountingData&);
|
void setData(EnergyAccountingData&);
|
||||||
|
|||||||
@@ -373,8 +373,7 @@ bool HwTools::ledBlink(uint8_t color, uint8_t blink) {
|
|||||||
if(!ledOn(color)) return false;
|
if(!ledOn(color)) return false;
|
||||||
delay(50);
|
delay(50);
|
||||||
ledOff(color);
|
ledOff(color);
|
||||||
if(i != blink)
|
delay(200);
|
||||||
delay(50);
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -266,7 +266,9 @@ IEC6205675::IEC6205675(const char* d, uint8_t useMeterType, MeterConfig* meterCo
|
|||||||
if(meterTs != NULL) {
|
if(meterTs != NULL) {
|
||||||
AmsOctetTimestamp* amst = (AmsOctetTimestamp*) meterTs;
|
AmsOctetTimestamp* amst = (AmsOctetTimestamp*) meterTs;
|
||||||
time_t ts = decodeCosemDateTime(amst->dt);
|
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);
|
meterTimestamp = tz.toUTC(ts);
|
||||||
} else {
|
} else {
|
||||||
meterTimestamp = ts;
|
meterTimestamp = ts;
|
||||||
@@ -294,6 +296,38 @@ IEC6205675::IEC6205675(const char* d, uint8_t useMeterType, MeterConfig* meterCo
|
|||||||
l3PowerFactor = val;
|
l3PowerFactor = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val = getNumber(AMS_OBIS_ACTIVE_IMPORT_L1, sizeof(AMS_OBIS_ACTIVE_IMPORT_L1), ((char *) (d)));
|
||||||
|
if (val != NOVALUE) {
|
||||||
|
listType = 4;
|
||||||
|
l1activeImportPower = val;
|
||||||
|
}
|
||||||
|
val = getNumber(AMS_OBIS_ACTIVE_IMPORT_L2, sizeof(AMS_OBIS_ACTIVE_IMPORT_L2), ((char *) (d)));
|
||||||
|
if (val != NOVALUE) {
|
||||||
|
listType = 4;
|
||||||
|
l2activeImportPower = val;
|
||||||
|
}
|
||||||
|
val = getNumber(AMS_OBIS_ACTIVE_IMPORT_L3, sizeof(AMS_OBIS_ACTIVE_IMPORT_L3), ((char *) (d)));
|
||||||
|
if (val != NOVALUE) {
|
||||||
|
listType = 4;
|
||||||
|
l3activeImportPower = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = getNumber(AMS_OBIS_ACTIVE_EXPORT_L1, sizeof(AMS_OBIS_ACTIVE_EXPORT_L1), ((char *) (d)));
|
||||||
|
if (val != NOVALUE) {
|
||||||
|
listType = 4;
|
||||||
|
l1activeExportPower = val;
|
||||||
|
}
|
||||||
|
val = getNumber(AMS_OBIS_ACTIVE_EXPORT_L2, sizeof(AMS_OBIS_ACTIVE_EXPORT_L2), ((char *) (d)));
|
||||||
|
if (val != NOVALUE) {
|
||||||
|
listType = 4;
|
||||||
|
l2activeExportPower = val;
|
||||||
|
}
|
||||||
|
val = getNumber(AMS_OBIS_ACTIVE_EXPORT_L3, sizeof(AMS_OBIS_ACTIVE_EXPORT_L3), ((char *) (d)));
|
||||||
|
if (val != NOVALUE) {
|
||||||
|
listType = 4;
|
||||||
|
l3activeExportPower = val;
|
||||||
|
}
|
||||||
|
|
||||||
if(meterType == AmsTypeKamstrup) {
|
if(meterType == AmsTypeKamstrup) {
|
||||||
if(listType >= 3) {
|
if(listType >= 3) {
|
||||||
activeImportCounter *= 10;
|
activeImportCounter *= 10;
|
||||||
@@ -334,12 +368,14 @@ IEC6205675::IEC6205675(const char* d, uint8_t useMeterType, MeterConfig* meterCo
|
|||||||
if(mid != NULL) {
|
if(mid != NULL) {
|
||||||
switch(mid->base.type) {
|
switch(mid->base.type) {
|
||||||
case CosemTypeString:
|
case CosemTypeString:
|
||||||
memcpy(&meterId, mid->str.data, mid->str.length);
|
memcpy(str, mid->oct.data, mid->oct.length);
|
||||||
meterId[mid->str.length] = 0;
|
str[mid->oct.length] = 0x00;
|
||||||
|
meterId = String(str);
|
||||||
break;
|
break;
|
||||||
case CosemTypeOctetString:
|
case CosemTypeOctetString:
|
||||||
memcpy(&meterId, mid->oct.data, mid->oct.length);
|
memcpy(str, mid->str.data, mid->str.length);
|
||||||
meterId[mid->oct.length] = 0;
|
str[mid->str.length] = 0x00;
|
||||||
|
meterId = String(str);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,12 @@ private:
|
|||||||
uint8_t AMS_OBIS_POWER_FACTOR_L1[4] = { 33, 7, 0, 255 };
|
uint8_t AMS_OBIS_POWER_FACTOR_L1[4] = { 33, 7, 0, 255 };
|
||||||
uint8_t AMS_OBIS_POWER_FACTOR_L2[4] = { 53, 7, 0, 255 };
|
uint8_t AMS_OBIS_POWER_FACTOR_L2[4] = { 53, 7, 0, 255 };
|
||||||
uint8_t AMS_OBIS_POWER_FACTOR_L3[4] = { 73, 7, 0, 255 };
|
uint8_t AMS_OBIS_POWER_FACTOR_L3[4] = { 73, 7, 0, 255 };
|
||||||
|
uint8_t AMS_OBIS_ACTIVE_IMPORT_L1[4] = { 21, 7, 0, 255 };
|
||||||
|
uint8_t AMS_OBIS_ACTIVE_IMPORT_L2[4] = { 41, 7, 0, 255 };
|
||||||
|
uint8_t AMS_OBIS_ACTIVE_IMPORT_L3[4] = { 61, 7, 0, 255 };
|
||||||
|
uint8_t AMS_OBIS_ACTIVE_EXPORT_L1[4] = { 22, 7, 0, 255 };
|
||||||
|
uint8_t AMS_OBIS_ACTIVE_EXPORT_L2[4] = { 42, 7, 0, 255 };
|
||||||
|
uint8_t AMS_OBIS_ACTIVE_EXPORT_L3[4] = { 62, 7, 0, 255 };
|
||||||
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
152
src/LNG.cpp
Normal file
152
src/LNG.cpp
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
#include "LNG.h"
|
||||||
|
#include "lwip/def.h"
|
||||||
|
#include "ams/ntohll.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
|
||||||
|
|
||||||
|
uint64_t o170 = 0, o270 = 0;
|
||||||
|
uint64_t o180 = 0, o280 = 0;
|
||||||
|
uint64_t o181 = 0, o182 = 0;
|
||||||
|
uint64_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 = getNumber(item);
|
||||||
|
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o170);
|
||||||
|
}
|
||||||
|
} else if(descriptor->obis[3] == 8) {
|
||||||
|
if(descriptor->obis[4] == 0) {
|
||||||
|
o180 = getNumber(item);
|
||||||
|
listType = listType >= 3 ? listType : 3;
|
||||||
|
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o180);
|
||||||
|
activeImportCounter = o180 / 1000.0;
|
||||||
|
} else if(descriptor->obis[4] == 1) {
|
||||||
|
o181 = getNumber(item);
|
||||||
|
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o181);
|
||||||
|
} else if(descriptor->obis[4] == 2) {
|
||||||
|
o182 = getNumber(item);
|
||||||
|
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o182);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(descriptor->obis[2] == 2) {
|
||||||
|
if(descriptor->obis[3] == 7) {
|
||||||
|
if(descriptor->obis[4] == 0) {
|
||||||
|
o270 = getNumber(item);
|
||||||
|
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o270);
|
||||||
|
}
|
||||||
|
} else if(descriptor->obis[3] == 8) {
|
||||||
|
if(descriptor->obis[4] == 0) {
|
||||||
|
o280 = getNumber(item);
|
||||||
|
listType = listType >= 3 ? listType : 3;
|
||||||
|
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o280);
|
||||||
|
activeExportCounter = o280 / 1000.0;
|
||||||
|
} else if(descriptor->obis[4] == 1) {
|
||||||
|
o281 = getNumber(item);
|
||||||
|
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o281);
|
||||||
|
} else if(descriptor->obis[4] == 2) {
|
||||||
|
o282 = getNumber(item);
|
||||||
|
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o282);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} 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 if((*data) == 0x15) {
|
||||||
|
data += 9;
|
||||||
|
} else if((*data) == 0x06) {
|
||||||
|
data += 5;
|
||||||
|
} else if((*data) == 0x12) {
|
||||||
|
data += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastUpdateMillis = millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t LNG::getNumber(CosemData* item) {
|
||||||
|
if(item != NULL) {
|
||||||
|
uint64_t ret = 0.0;
|
||||||
|
switch(item->base.type) {
|
||||||
|
case CosemTypeLongSigned: {
|
||||||
|
int16_t i16 = ntohs(item->ls.data);
|
||||||
|
return i16;
|
||||||
|
}
|
||||||
|
case CosemTypeLongUnsigned: {
|
||||||
|
uint16_t u16 = ntohs(item->lu.data);
|
||||||
|
return u16;
|
||||||
|
}
|
||||||
|
case CosemTypeDLongSigned: {
|
||||||
|
int32_t i32 = ntohl(item->dlu.data);
|
||||||
|
return i32;
|
||||||
|
}
|
||||||
|
case CosemTypeDLongUnsigned: {
|
||||||
|
uint32_t u32 = ntohl(item->dlu.data);
|
||||||
|
return u32;
|
||||||
|
}
|
||||||
|
case CosemTypeLong64Signed: {
|
||||||
|
int64_t i64 = ntohll(item->l64s.data);
|
||||||
|
return i64;
|
||||||
|
}
|
||||||
|
case CosemTypeLong64Unsigned: {
|
||||||
|
uint64_t u64 = ntohll(item->l64u.data);
|
||||||
|
return u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
32
src/LNG.h
Normal file
32
src/LNG.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#ifndef _LNG_H
|
||||||
|
#define _LNG_H
|
||||||
|
|
||||||
|
#include "AmsData.h"
|
||||||
|
#include "AmsConfiguration.h"
|
||||||
|
#include "ams/DataParser.h"
|
||||||
|
#include "ams/Cosem.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);
|
||||||
|
uint64_t getNumber(CosemData* item);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
#include "Uptime.h"
|
#include "Uptime.h"
|
||||||
|
|
||||||
|
uint32_t _uptime_last_value = 0;
|
||||||
|
uint32_t _uptime_rollovers = 0;
|
||||||
|
|
||||||
uint64_t millis64() {
|
uint64_t millis64() {
|
||||||
uint32_t new_low32 = millis();
|
uint32_t new_low32 = millis();
|
||||||
if (new_low32 < _uptime_last_value) _uptime_rollovers++;
|
if (new_low32 < _uptime_last_value) _uptime_rollovers++;
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
|
|
||||||
static uint32_t _uptime_last_value = 0;
|
|
||||||
static uint32_t _uptime_rollovers = 0;
|
|
||||||
uint64_t millis64();
|
uint64_t millis64();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ int8_t DSMRParser::parse(uint8_t *buf, DataParserContext &ctx, bool verified) {
|
|||||||
uint16_t crcPos = 0;
|
uint16_t crcPos = 0;
|
||||||
bool reachedEnd = verified;
|
bool reachedEnd = verified;
|
||||||
uint8_t lastByte = 0x00;
|
uint8_t lastByte = 0x00;
|
||||||
int c = 0;
|
|
||||||
for(int pos = 0; pos < ctx.length; pos++) {
|
for(int pos = 0; pos < ctx.length; pos++) {
|
||||||
uint8_t b = *(buf+pos);
|
uint8_t b = *(buf+pos);
|
||||||
if(pos == 0 && b != '/') return DATA_PARSE_BOUNDRY_FLAG_MISSING;
|
if(pos == 0 && b != '/') return DATA_PARSE_BOUNDRY_FLAG_MISSING;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ int8_t GCMParser::parse(uint8_t *d, DataParserContext &ctx) {
|
|||||||
memcpy(ctx.system_title, ptr, systemTitleLength);
|
memcpy(ctx.system_title, ptr, systemTitleLength);
|
||||||
memcpy(initialization_vector, ctx.system_title, systemTitleLength);
|
memcpy(initialization_vector, ctx.system_title, systemTitleLength);
|
||||||
|
|
||||||
int len;
|
int len = 0;
|
||||||
int headersize = 2 + systemTitleLength;
|
int headersize = 2 + systemTitleLength;
|
||||||
ptr += systemTitleLength;
|
ptr += systemTitleLength;
|
||||||
if(((*ptr) & 0xFF) == 0x81) {
|
if(((*ptr) & 0xFF) == 0x81) {
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
int8_t HDLCParser::parse(uint8_t *d, DataParserContext &ctx) {
|
int8_t HDLCParser::parse(uint8_t *d, DataParserContext &ctx) {
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
uint8_t flag = *d;
|
|
||||||
|
|
||||||
uint8_t* ptr;
|
uint8_t* ptr;
|
||||||
if(ctx.length < 3)
|
if(ctx.length < 3)
|
||||||
return DATA_PARSE_INCOMPLETE;
|
return DATA_PARSE_INCOMPLETE;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "LlcParser.h"
|
#include "LlcParser.h"
|
||||||
|
|
||||||
int8_t LLCParser::parse(uint8_t *buf, DataParserContext &ctx) {
|
int8_t LLCParser::parse(uint8_t *buf, DataParserContext &ctx) {
|
||||||
LLCHeader* llc = (LLCHeader*) buf;
|
|
||||||
ctx.length -= 3;
|
ctx.length -= 3;
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
@@ -5,8 +5,6 @@ int8_t MBUSParser::parse(uint8_t *d, DataParserContext &ctx) {
|
|||||||
int headersize = 3;
|
int headersize = 3;
|
||||||
int footersize = 1;
|
int footersize = 1;
|
||||||
|
|
||||||
uint8_t flag = *d;
|
|
||||||
|
|
||||||
uint8_t* ptr;
|
uint8_t* ptr;
|
||||||
|
|
||||||
// https://m-bus.com/documentation-wired/06-application-layer
|
// https://m-bus.com/documentation-wired/06-application-layer
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ void DnbCurrParser::flush() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t DnbCurrParser::write(const uint8_t *buffer, size_t size) {
|
size_t DnbCurrParser::write(const uint8_t *buffer, size_t size) {
|
||||||
for(int i = 0; i < size; i++) {
|
for(size_t i = 0; i < size; i++) {
|
||||||
write(buffer[i]);
|
write(buffer[i]);
|
||||||
}
|
}
|
||||||
return size;
|
return size;
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ EntsoeA44Parser::EntsoeA44Parser() {
|
|||||||
for(int i = 0; i < 24; i++) points[i] = ENTSOE_NO_VALUE;
|
for(int i = 0; i < 24; i++) points[i] = ENTSOE_NO_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EntsoeA44Parser::~EntsoeA44Parser() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
char* EntsoeA44Parser::getCurrency() {
|
char* EntsoeA44Parser::getCurrency() {
|
||||||
return currency;
|
return currency;
|
||||||
}
|
}
|
||||||
@@ -35,7 +39,7 @@ void EntsoeA44Parser::flush() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t EntsoeA44Parser::write(const uint8_t *buffer, size_t size) {
|
size_t EntsoeA44Parser::write(const uint8_t *buffer, size_t size) {
|
||||||
for(int i = 0; i < size; i++) {
|
for(size_t i = 0; i < size; i++) {
|
||||||
write(buffer[i]);
|
write(buffer[i]);
|
||||||
}
|
}
|
||||||
return size;
|
return size;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
class EntsoeA44Parser: public Stream {
|
class EntsoeA44Parser: public Stream {
|
||||||
public:
|
public:
|
||||||
EntsoeA44Parser();
|
EntsoeA44Parser();
|
||||||
|
virtual ~EntsoeA44Parser();
|
||||||
|
|
||||||
char* getCurrency();
|
char* getCurrency();
|
||||||
char* getMeasurementUnit();
|
char* getMeasurementUnit();
|
||||||
|
|||||||
@@ -106,11 +106,11 @@ bool EntsoeApi::loop() {
|
|||||||
if(midnightMillis == 0) {
|
if(midnightMillis == 0) {
|
||||||
uint32_t curDayMillis = (((((tm.Hour * 60) + tm.Minute) * 60) + tm.Second) * 1000);
|
uint32_t curDayMillis = (((((tm.Hour * 60) + tm.Minute) * 60) + tm.Second) * 1000);
|
||||||
midnightMillis = now + (SECS_PER_DAY * 1000) - curDayMillis;
|
midnightMillis = now + (SECS_PER_DAY * 1000) - curDayMillis;
|
||||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EntsoeApi) Setting midnight millis %lu\n", midnightMillis);
|
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EntsoeApi) Setting midnight millis %llu\n", midnightMillis);
|
||||||
currentDay = tm.Day;
|
currentDay = tm.Day;
|
||||||
return false;
|
return false;
|
||||||
} else if(now > midnightMillis && currentDay != tm.Day) {
|
} else if(now > midnightMillis && currentDay != tm.Day) {
|
||||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EntsoeApi) Rotating price objects at %lu\n", t);
|
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EntsoeApi) Rotating price objects at %lld\n", t);
|
||||||
if(today != NULL) delete today;
|
if(today != NULL) delete today;
|
||||||
if(tomorrow != NULL) {
|
if(tomorrow != NULL) {
|
||||||
today = tomorrow;
|
today = tomorrow;
|
||||||
|
|||||||
@@ -15,11 +15,12 @@ public:
|
|||||||
this->mqtt = mqtt;
|
this->mqtt = mqtt;
|
||||||
this->json = buf;
|
this->json = buf;
|
||||||
};
|
};
|
||||||
|
virtual ~AmsMqttHandler() {};
|
||||||
|
|
||||||
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;
|
||||||
|
|||||||
@@ -6,9 +6,11 @@
|
|||||||
#include "web/root/ha1_json.h"
|
#include "web/root/ha1_json.h"
|
||||||
#include "web/root/ha2_json.h"
|
#include "web/root/ha2_json.h"
|
||||||
#include "web/root/ha3_json.h"
|
#include "web/root/ha3_json.h"
|
||||||
|
#include "web/root/ha4_json.h"
|
||||||
#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())
|
||||||
@@ -32,8 +34,8 @@ bool HomeAssistantMqttHandler::publish(AmsData* data, AmsData* previousState, En
|
|||||||
snprintf_P(json, BufferSize, HA1_JSON,
|
snprintf_P(json, BufferSize, HA1_JSON,
|
||||||
data->getActiveImportPower()
|
data->getActiveImportPower()
|
||||||
);
|
);
|
||||||
return mqtt->publish(topic + "/power", json);
|
mqtt->publish(topic + "/power", json);
|
||||||
} else if(data->getListType() >= 2) { // publish power counts and volts/amps
|
} else if(data->getListType() <= 3) { // publish power counts and volts/amps
|
||||||
snprintf_P(json, BufferSize, HA3_JSON,
|
snprintf_P(json, BufferSize, HA3_JSON,
|
||||||
data->getListId().c_str(),
|
data->getListId().c_str(),
|
||||||
data->getMeterId().c_str(),
|
data->getMeterId().c_str(),
|
||||||
@@ -47,15 +49,62 @@ bool HomeAssistantMqttHandler::publish(AmsData* data, AmsData* previousState, En
|
|||||||
data->getL3Current(),
|
data->getL3Current(),
|
||||||
data->getL1Voltage(),
|
data->getL1Voltage(),
|
||||||
data->getL2Voltage(),
|
data->getL2Voltage(),
|
||||||
|
data->getL3Voltage()
|
||||||
|
);
|
||||||
|
mqtt->publish(topic + "/power", json);
|
||||||
|
} else if(data->getListType() == 4) { // publish power counts and volts/amps/phase power and PF
|
||||||
|
snprintf_P(json, BufferSize, HA4_JSON,
|
||||||
|
data->getListId().c_str(),
|
||||||
|
data->getMeterId().c_str(),
|
||||||
|
meterModel.c_str(),
|
||||||
|
data->getActiveImportPower(),
|
||||||
|
data->getL1ActiveImportPower(),
|
||||||
|
data->getL2ActiveImportPower(),
|
||||||
|
data->getL3ActiveImportPower(),
|
||||||
|
data->getReactiveImportPower(),
|
||||||
|
data->getActiveExportPower(),
|
||||||
|
data->getL1ActiveExportPower(),
|
||||||
|
data->getL2ActiveExportPower(),
|
||||||
|
data->getL3ActiveExportPower(),
|
||||||
|
data->getReactiveExportPower(),
|
||||||
|
data->getL1Current(),
|
||||||
|
data->getL2Current(),
|
||||||
|
data->getL3Current(),
|
||||||
|
data->getL1Voltage(),
|
||||||
|
data->getL2Voltage(),
|
||||||
data->getL3Voltage(),
|
data->getL3Voltage(),
|
||||||
data->getPowerFactor() == 0 ? 1 : data->getPowerFactor(),
|
data->getPowerFactor() == 0 ? 1 : data->getPowerFactor(),
|
||||||
data->getPowerFactor() == 0 ? 1 : data->getL1PowerFactor(),
|
data->getPowerFactor() == 0 ? 1 : data->getL1PowerFactor(),
|
||||||
data->getPowerFactor() == 0 ? 1 : data->getL2PowerFactor(),
|
data->getPowerFactor() == 0 ? 1 : data->getL2PowerFactor(),
|
||||||
data->getPowerFactor() == 0 ? 1 : data->getL3PowerFactor()
|
data->getPowerFactor() == 0 ? 1 : data->getL3PowerFactor()
|
||||||
);
|
);
|
||||||
return mqtt->publish(topic + "/power", json);
|
mqtt->publish(topic + "/power", json);
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
String peaks = "";
|
||||||
|
uint8_t peakCount = ea->getConfig()->hours;
|
||||||
|
if(peakCount > 5) peakCount = 5;
|
||||||
|
for(uint8_t i = 1; i <= peakCount; i++) {
|
||||||
|
if(!peaks.isEmpty()) peaks += ",";
|
||||||
|
peaks += String(ea->getPeak(i), 2);
|
||||||
|
}
|
||||||
|
snprintf_P(json, BufferSize, REALTIME_JSON,
|
||||||
|
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()
|
||||||
|
);
|
||||||
|
mqtt->publish(topic + "/realtime", json);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HomeAssistantMqttHandler::publishTemperatures(AmsConfiguration* config, HwTools* hw) {
|
bool HomeAssistantMqttHandler::publishTemperatures(AmsConfiguration* config, HwTools* hw) {
|
||||||
@@ -92,7 +141,7 @@ bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
|
|||||||
|
|
||||||
time_t now = time(nullptr);
|
time_t now = time(nullptr);
|
||||||
|
|
||||||
float min1hr, min3hr, min6hr;
|
float min1hr = 0.0, min3hr = 0.0, min6hr = 0.0;
|
||||||
int8_t min1hrIdx = -1, min3hrIdx = -1, min6hrIdx = -1;
|
int8_t min1hrIdx = -1, min3hrIdx = -1, min6hrIdx = -1;
|
||||||
float min = INT16_MAX, max = INT16_MIN;
|
float min = INT16_MAX, max = INT16_MIN;
|
||||||
float values[24];
|
float values[24];
|
||||||
@@ -142,7 +191,7 @@ bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char ts1hr[21];
|
char ts1hr[24];
|
||||||
if(min1hrIdx > -1) {
|
if(min1hrIdx > -1) {
|
||||||
time_t ts = now + (SECS_PER_HOUR * min1hrIdx);
|
time_t ts = now + (SECS_PER_HOUR * min1hrIdx);
|
||||||
//Serial.printf("1hr: %d %lu\n", min1hrIdx, ts);
|
//Serial.printf("1hr: %d %lu\n", min1hrIdx, ts);
|
||||||
@@ -150,7 +199,7 @@ bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
|
|||||||
breakTime(ts, tm);
|
breakTime(ts, tm);
|
||||||
sprintf(ts1hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
|
sprintf(ts1hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
|
||||||
}
|
}
|
||||||
char ts3hr[21];
|
char ts3hr[24];
|
||||||
if(min3hrIdx > -1) {
|
if(min3hrIdx > -1) {
|
||||||
time_t ts = now + (SECS_PER_HOUR * min3hrIdx);
|
time_t ts = now + (SECS_PER_HOUR * min3hrIdx);
|
||||||
//Serial.printf("3hr: %d %lu\n", min3hrIdx, ts);
|
//Serial.printf("3hr: %d %lu\n", min3hrIdx, ts);
|
||||||
@@ -158,7 +207,7 @@ bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
|
|||||||
breakTime(ts, tm);
|
breakTime(ts, tm);
|
||||||
sprintf(ts3hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
|
sprintf(ts3hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
|
||||||
}
|
}
|
||||||
char ts6hr[21];
|
char ts6hr[24];
|
||||||
if(min6hrIdx > -1) {
|
if(min6hrIdx > -1) {
|
||||||
time_t ts = now + (SECS_PER_HOUR * min6hrIdx);
|
time_t ts = now + (SECS_PER_HOUR * min6hrIdx);
|
||||||
//Serial.printf("6hr: %d %lu\n", min6hrIdx, ts);
|
//Serial.printf("6hr: %d %lu\n", min6hrIdx, ts);
|
||||||
@@ -187,10 +236,10 @@ bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
|
|||||||
ts3hr,
|
ts3hr,
|
||||||
ts6hr
|
ts6hr
|
||||||
);
|
);
|
||||||
return mqtt->publish(topic + "/prices", json);
|
return mqtt->publish(topic + "/prices", json, true, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
@@ -203,7 +252,8 @@ bool HomeAssistantMqttHandler::publishSystem(HwTools* hw) {
|
|||||||
(uint32_t) (millis64()/1000),
|
(uint32_t) (millis64()/1000),
|
||||||
hw->getVcc(),
|
hw->getVcc(),
|
||||||
hw->getWifiRssi(),
|
hw->getWifiRssi(),
|
||||||
hw->getTemperature()
|
hw->getTemperature(),
|
||||||
|
VERSION
|
||||||
);
|
);
|
||||||
mqtt->publish(topic + "/state", json);
|
mqtt->publish(topic + "/state", json);
|
||||||
}
|
}
|
||||||
@@ -216,27 +266,46 @@ bool HomeAssistantMqttHandler::publishSystem(HwTools* hw) {
|
|||||||
#endif
|
#endif
|
||||||
String haUrl = "http://" + haUID + ".local/";
|
String haUrl = "http://" + haUID + ".local/";
|
||||||
// Could this be necessary? haUID.replace("-", "_");
|
// Could this be necessary? haUID.replace("-", "_");
|
||||||
|
uint8_t peakCount = ea->getConfig()->hours;
|
||||||
|
if(peakCount > 5) peakCount = 5;
|
||||||
|
|
||||||
for(int i=0;i<sensors;i++){
|
uint8_t peaks = 0;
|
||||||
|
for(int i=0;i<HA_SENSOR_COUNT;i++) {
|
||||||
|
HomeAssistantSensor sensor = HA_SENSORS[i];
|
||||||
|
String uid = String(sensor.path);
|
||||||
|
uid.replace(".", "");
|
||||||
|
uid.replace("[", "");
|
||||||
|
uid.replace("]", "");
|
||||||
|
uid.replace("'", "");
|
||||||
|
String uom = String(sensor.uom);
|
||||||
|
if(strncmp(sensor.devcl, "monetary", 8) == 0) {
|
||||||
|
if(eapi == NULL) continue;
|
||||||
|
uom = String(eapi->getCurrency());
|
||||||
|
}
|
||||||
|
if(strncmp(sensor.path, "peaks[", 6) == 0) {
|
||||||
|
if(peaks >= peakCount) continue;
|
||||||
|
peaks++;
|
||||||
|
}
|
||||||
snprintf_P(json, BufferSize, HADISCOVER_JSON,
|
snprintf_P(json, BufferSize, HADISCOVER_JSON,
|
||||||
FPSTR(HA_NAMES[i]),
|
sensor.name,
|
||||||
topic.c_str(), FPSTR(HA_TOPICS[i]),
|
topic.c_str(), sensor.topic,
|
||||||
haUID.c_str(), FPSTR(HA_PARAMS[i]),
|
haUID.c_str(), uid.c_str(),
|
||||||
haUID.c_str(), FPSTR(HA_PARAMS[i]),
|
haUID.c_str(), uid.c_str(),
|
||||||
FPSTR(HA_UOM[i]),
|
uom.c_str(),
|
||||||
FPSTR(HA_PARAMS[i]),
|
sensor.path,
|
||||||
FPSTR(HA_DEVCL[i]),
|
sensor.devcl,
|
||||||
haUID.c_str(),
|
haUID.c_str(),
|
||||||
haName.c_str(),
|
haName.c_str(),
|
||||||
haModel.c_str(),
|
haModel.c_str(),
|
||||||
VERSION,
|
VERSION,
|
||||||
haManuf.c_str(),
|
haManuf.c_str(),
|
||||||
haUrl.c_str(),
|
haUrl.c_str(),
|
||||||
strlen_P(HA_STACL[i]) > 0 ? ", \"stat_cla\" :" : "",
|
strlen_P(sensor.stacl) > 0 ? ", \"stat_cla\" :" : "",
|
||||||
strlen_P(HA_STACL[i]) > 0 ? (char *) FPSTR(HA_STACL[i]) : ""
|
strlen_P(sensor.stacl) > 0 ? (char *) FPSTR(sensor.stacl) : ""
|
||||||
);
|
);
|
||||||
mqtt->publish(haTopic + haUID + "_" + FPSTR(HA_PARAMS[i]) + "/config", json, true, 0);
|
mqtt->publish(haTopic + haUID + "_" + uid.c_str() + "/config", json, true, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
autodiscoverInit = true;
|
autodiscoverInit = true;
|
||||||
}
|
}
|
||||||
if(listType>0) sequence++;
|
if(listType>0) sequence++;
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -3,12 +3,79 @@
|
|||||||
|
|
||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
|
|
||||||
const char* HA_TOPICS[17] PROGMEM = {"/state", "/state", "/state", "/power", "/power", "/power", "/power", "/power", "/power", "/power", "/power", "/power", "/power", "/energy", "/energy", "/energy", "/energy"};
|
struct HomeAssistantSensor {
|
||||||
const char* HA_NAMES[17] PROGMEM = {"Status", "Supply volt", "Temperature", "Active import", "Reactive import", "Active export", "Reactive export", "L1 current", "L2 current", "L3 current",
|
const char* name;
|
||||||
"L1 voltage", "L2 voltage", "L3 voltage", "Accumulated active import", "Accumulated active export", "Accumulated reactive import", "Accumulated reactive export"};
|
const char* topic;
|
||||||
const char* HA_PARAMS[17] PROGMEM = {"rssi", "vcc", "temp", "P", "Q", "PO", "QO", "I1", "I2", "I3", "U1", "U2", "U3", "tPI", "tPO", "tQI", "tQO"};
|
const char* path;
|
||||||
const char* HA_UOM[17] PROGMEM = {"dBm", "V", "C", "W", "W", "W", "W", "A", "A", "A", "V", "V", "V", "kWh", "kWh", "kWh", "kWh"};
|
const char* uom;
|
||||||
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* devcl;
|
||||||
const char* HA_STACL[17] PROGMEM = {"", "", "", "\"measurement\"", "\"measurement\"", "\"measurement\"", "\"measurement\"", "", "", "", "", "", "", "\"total_increasing\"", "\"total_increasing\"", "\"total_increasing\"", "\"total_increasing\""};
|
const char* stacl;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const uint8_t HA_SENSOR_COUNT PROGMEM = 60;
|
||||||
|
HomeAssistantSensor HA_SENSORS[HA_SENSOR_COUNT] PROGMEM = {
|
||||||
|
{"Status", "/state", "rssi", "dBm", "signal_strength", "\"measurement\""},
|
||||||
|
{"Supply volt", "/state", "vcc", "V", "voltage", "\"measurement\""},
|
||||||
|
{"Temperature", "/state", "temp", "C", "temperature", "\"measurement\""},
|
||||||
|
{"Active import", "/power", "P", "W", "power", "\"measurement\""},
|
||||||
|
{"L1 active import", "/power", "P1", "W", "power", "\"measurement\""},
|
||||||
|
{"L2 active import", "/power", "P2", "W", "power", "\"measurement\""},
|
||||||
|
{"L3 active import", "/power", "P3", "W", "power", "\"measurement\""},
|
||||||
|
{"Reactive import", "/power", "Q", "VAr", "reactive_power", "\"measurement\""},
|
||||||
|
{"Active export", "/power", "PO", "W", "power", "\"measurement\""},
|
||||||
|
{"L1 active export", "/power", "PO1", "W", "power", "\"measurement\""},
|
||||||
|
{"L2 active export", "/power", "PO2", "W", "power", "\"measurement\""},
|
||||||
|
{"L3 active export", "/power", "PO3", "W", "power", "\"measurement\""},
|
||||||
|
{"Reactive export", "/power", "QO", "VAr", "reactive_power", "\"measurement\""},
|
||||||
|
{"L1 current", "/power", "I1", "A", "current", "\"measurement\""},
|
||||||
|
{"L2 current", "/power", "I2", "A", "current", "\"measurement\""},
|
||||||
|
{"L3 current", "/power", "I3", "A", "current", "\"measurement\""},
|
||||||
|
{"L1 voltage", "/power", "U1", "V", "voltage", "\"measurement\""},
|
||||||
|
{"L2 voltage", "/power", "U2", "V", "voltage", "\"measurement\""},
|
||||||
|
{"L3 voltage", "/power", "U3", "V", "voltage", "\"measurement\""},
|
||||||
|
{"Accumulated active import", "/energy", "tPI", "kWh", "energy", "\"total_increasing\""},
|
||||||
|
{"Accumulated active export", "/energy", "tPO", "kWh", "energy", "\"total_increasing\""},
|
||||||
|
{"Accumulated reactive import","/energy", "tQI", "kVArh","energy", "\"total_increasing\""},
|
||||||
|
{"Accumulated reactive export","/energy", "tQO", "kVArh","energy", "\"total_increasing\""},
|
||||||
|
{"Power factor", "/power", "PF", "", "power_factor", "\"measurement\""},
|
||||||
|
{"L1 power factor", "/power", "PF1", "", "power_factor", "\"measurement\""},
|
||||||
|
{"L2 power factor", "/power", "PF2", "", "power_factor", "\"measurement\""},
|
||||||
|
{"L3 power factor", "/power", "PF3", "", "power_factor", "\"measurement\""},
|
||||||
|
{"Price current hour", "/prices", "prices['0']", "", "monetary", ""},
|
||||||
|
{"Price next hour", "/prices", "prices['1']", "", "monetary", ""},
|
||||||
|
{"Price in two hour", "/prices", "prices['2']", "", "monetary", ""},
|
||||||
|
{"Price in three hour", "/prices", "prices['3']", "", "monetary", ""},
|
||||||
|
{"Price in four hour", "/prices", "prices['4']", "", "monetary", ""},
|
||||||
|
{"Price in five hour", "/prices", "prices['5']", "", "monetary", ""},
|
||||||
|
{"Price in six hour", "/prices", "prices['6']", "", "monetary", ""},
|
||||||
|
{"Price in seven hour", "/prices", "prices['7']", "", "monetary", ""},
|
||||||
|
{"Price in eight hour", "/prices", "prices['8']", "", "monetary", ""},
|
||||||
|
{"Price in nine hour", "/prices", "prices['9']", "", "monetary", ""},
|
||||||
|
{"Price in ten hour", "/prices", "prices['10']", "", "monetary", ""},
|
||||||
|
{"Price in eleven hour", "/prices", "prices['11']", "", "monetary", ""},
|
||||||
|
{"Minimum price ahead", "/prices", "prices.min", "", "monetary", ""},
|
||||||
|
{"Maximum price ahead", "/prices", "prices.max", "", "monetary", ""},
|
||||||
|
{"Cheapest 1hr period ahead", "/prices", "prices.cheapest1hr","", "timestamp", ""},
|
||||||
|
{"Cheapest 3hr period ahead", "/prices", "prices.cheapest3hr","", "timestamp", ""},
|
||||||
|
{"Cheapest 6hr period ahead", "/prices", "prices.cheapest6hr","", "timestamp", ""},
|
||||||
|
{"Month max", "/realtime","max", "kWh", "energy", "\"total_increasing\""},
|
||||||
|
{"Tariff threshold", "/realtime","threshold", "kWh", "energy", "\"total_increasing\""},
|
||||||
|
{"Current hour used", "/realtime","hour.use", "kWh", "energy", "\"total_increasing\""},
|
||||||
|
{"Current hour cost", "/realtime","hour.cost", "", "monetary", "\"total_increasing\""},
|
||||||
|
{"Current hour produced", "/realtime","hour.produced", "kWh", "energy", "\"total_increasing\""},
|
||||||
|
{"Current day used", "/realtime","day.use", "kWh", "energy", "\"total_increasing\""},
|
||||||
|
{"Current day cost", "/realtime","day.cost", "", "monetary", "\"total_increasing\""},
|
||||||
|
{"Current day produced", "/realtime","day.produced", "kWh", "energy", "\"total_increasing\""},
|
||||||
|
{"Current month used", "/realtime","month.use", "kWh", "energy", "\"total_increasing\""},
|
||||||
|
{"Current month cost", "/realtime","month.cost", "", "monetary", "\"total_increasing\""},
|
||||||
|
{"Current month produced", "/realtime","month.produced", "kWh", "energy", "\"total_increasing\""},
|
||||||
|
{"Current month peak 1", "/realtime","peaks[0]", "kWh", "energy", ""},
|
||||||
|
{"Current month peak 2", "/realtime","peaks[1]", "kWh", "energy", ""},
|
||||||
|
{"Current month peak 3", "/realtime","peaks[2]", "kWh", "energy", ""},
|
||||||
|
{"Current month peak 4", "/realtime","peaks[3]", "kWh", "energy", ""},
|
||||||
|
{"Current month peak 5", "/realtime","peaks[4]", "kWh", "energy", ""},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "JsonMqttHandler.h"
|
#include "JsonMqttHandler.h"
|
||||||
|
#include "version.h"
|
||||||
#include "hexutils.h"
|
#include "hexutils.h"
|
||||||
#include "Uptime.h"
|
#include "Uptime.h"
|
||||||
#include "web/root/json1_json.h"
|
#include "web/root/json1_json.h"
|
||||||
@@ -110,8 +111,14 @@ bool JsonMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyAccou
|
|||||||
data->getMeterId().c_str(),
|
data->getMeterId().c_str(),
|
||||||
meterModel.c_str(),
|
meterModel.c_str(),
|
||||||
data->getActiveImportPower(),
|
data->getActiveImportPower(),
|
||||||
|
data->getL1ActiveImportPower(),
|
||||||
|
data->getL2ActiveImportPower(),
|
||||||
|
data->getL3ActiveImportPower(),
|
||||||
data->getReactiveImportPower(),
|
data->getReactiveImportPower(),
|
||||||
data->getActiveExportPower(),
|
data->getActiveExportPower(),
|
||||||
|
data->getL1ActiveExportPower(),
|
||||||
|
data->getL2ActiveExportPower(),
|
||||||
|
data->getL3ActiveExportPower(),
|
||||||
data->getReactiveExportPower(),
|
data->getReactiveExportPower(),
|
||||||
data->getL1Current(),
|
data->getL1Current(),
|
||||||
data->getL2Current(),
|
data->getL2Current(),
|
||||||
@@ -142,8 +149,9 @@ bool JsonMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyAccou
|
|||||||
|
|
||||||
bool JsonMqttHandler::publishTemperatures(AmsConfiguration* config, HwTools* hw) {
|
bool JsonMqttHandler::publishTemperatures(AmsConfiguration* config, HwTools* hw) {
|
||||||
int count = hw->getTempSensorCount();
|
int count = hw->getTempSensorCount();
|
||||||
if(count < 2)
|
if(count < 2) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
snprintf(json, 24, "{\"temperatures\":{");
|
snprintf(json, 24, "{\"temperatures\":{");
|
||||||
|
|
||||||
@@ -172,7 +180,7 @@ bool JsonMqttHandler::publishPrices(EntsoeApi* eapi) {
|
|||||||
|
|
||||||
time_t now = time(nullptr);
|
time_t now = time(nullptr);
|
||||||
|
|
||||||
float min1hr, min3hr, min6hr;
|
float min1hr = 0.0, min3hr = 0.0, min6hr = 0.0;
|
||||||
int8_t min1hrIdx = -1, min3hrIdx = -1, min6hrIdx = -1;
|
int8_t min1hrIdx = -1, min3hrIdx = -1, min6hrIdx = -1;
|
||||||
float min = INT16_MAX, max = INT16_MIN;
|
float min = INT16_MAX, max = INT16_MIN;
|
||||||
float values[24];
|
float values[24];
|
||||||
@@ -222,7 +230,7 @@ bool JsonMqttHandler::publishPrices(EntsoeApi* eapi) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char ts1hr[21];
|
char ts1hr[24];
|
||||||
if(min1hrIdx > -1) {
|
if(min1hrIdx > -1) {
|
||||||
time_t ts = now + (SECS_PER_HOUR * min1hrIdx);
|
time_t ts = now + (SECS_PER_HOUR * min1hrIdx);
|
||||||
//Serial.printf("1hr: %d %lu\n", min1hrIdx, ts);
|
//Serial.printf("1hr: %d %lu\n", min1hrIdx, ts);
|
||||||
@@ -230,7 +238,7 @@ bool JsonMqttHandler::publishPrices(EntsoeApi* eapi) {
|
|||||||
breakTime(ts, tm);
|
breakTime(ts, tm);
|
||||||
sprintf(ts1hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
|
sprintf(ts1hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
|
||||||
}
|
}
|
||||||
char ts3hr[21];
|
char ts3hr[24];
|
||||||
if(min3hrIdx > -1) {
|
if(min3hrIdx > -1) {
|
||||||
time_t ts = now + (SECS_PER_HOUR * min3hrIdx);
|
time_t ts = now + (SECS_PER_HOUR * min3hrIdx);
|
||||||
//Serial.printf("3hr: %d %lu\n", min3hrIdx, ts);
|
//Serial.printf("3hr: %d %lu\n", min3hrIdx, ts);
|
||||||
@@ -238,7 +246,7 @@ bool JsonMqttHandler::publishPrices(EntsoeApi* eapi) {
|
|||||||
breakTime(ts, tm);
|
breakTime(ts, tm);
|
||||||
sprintf(ts3hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
|
sprintf(ts3hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
|
||||||
}
|
}
|
||||||
char ts6hr[21];
|
char ts6hr[24];
|
||||||
if(min6hrIdx > -1) {
|
if(min6hrIdx > -1) {
|
||||||
time_t ts = now + (SECS_PER_HOUR * min6hrIdx);
|
time_t ts = now + (SECS_PER_HOUR * min6hrIdx);
|
||||||
//Serial.printf("6hr: %d %lu\n", min6hrIdx, ts);
|
//Serial.printf("6hr: %d %lu\n", min6hrIdx, ts);
|
||||||
@@ -270,7 +278,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;
|
||||||
|
|
||||||
@@ -280,7 +288,8 @@ bool JsonMqttHandler::publishSystem(HwTools* hw) {
|
|||||||
(uint32_t) (millis64()/1000),
|
(uint32_t) (millis64()/1000),
|
||||||
hw->getVcc(),
|
hw->getVcc(),
|
||||||
hw->getWifiRssi(),
|
hw->getWifiRssi(),
|
||||||
hw->getTemperature()
|
hw->getTemperature(),
|
||||||
|
VERSION
|
||||||
);
|
);
|
||||||
init = mqtt->publish(topic, json);
|
init = mqtt->publish(topic, json);
|
||||||
return init;
|
return init;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -11,6 +11,24 @@ bool RawMqttHandler::publish(AmsData* data, AmsData* meterState, EnergyAccountin
|
|||||||
}
|
}
|
||||||
switch(data->getListType()) {
|
switch(data->getListType()) {
|
||||||
case 4:
|
case 4:
|
||||||
|
if(full || meterState->getL1ActiveImportPower() != data->getL1ActiveImportPower()) {
|
||||||
|
mqtt->publish(topic + "/meter/import/l1", String(data->getL1ActiveImportPower(), 2));
|
||||||
|
}
|
||||||
|
if(full || meterState->getL2ActiveImportPower() != data->getL2ActiveImportPower()) {
|
||||||
|
mqtt->publish(topic + "/meter/import/l2", String(data->getL2ActiveImportPower(), 2));
|
||||||
|
}
|
||||||
|
if(full || meterState->getL3ActiveImportPower() != data->getL3ActiveImportPower()) {
|
||||||
|
mqtt->publish(topic + "/meter/import/l3", String(data->getL3ActiveImportPower(), 2));
|
||||||
|
}
|
||||||
|
if(full || meterState->getL1ActiveExportPower() != data->getL1ActiveExportPower()) {
|
||||||
|
mqtt->publish(topic + "/meter/export/l1", String(data->getL1ActiveExportPower(), 2));
|
||||||
|
}
|
||||||
|
if(full || meterState->getL2ActiveExportPower() != data->getL2ActiveExportPower()) {
|
||||||
|
mqtt->publish(topic + "/meter/export/l2", String(data->getL2ActiveExportPower(), 2));
|
||||||
|
}
|
||||||
|
if(full || meterState->getL3ActiveExportPower() != data->getL3ActiveExportPower()) {
|
||||||
|
mqtt->publish(topic + "/meter/export/l3", String(data->getL3ActiveExportPower(), 2));
|
||||||
|
}
|
||||||
if(full || meterState->getPowerFactor() != data->getPowerFactor()) {
|
if(full || meterState->getPowerFactor() != data->getPowerFactor()) {
|
||||||
mqtt->publish(topic + "/meter/powerfactor", String(data->getPowerFactor(), 2));
|
mqtt->publish(topic + "/meter/powerfactor", String(data->getPowerFactor(), 2));
|
||||||
}
|
}
|
||||||
@@ -74,6 +92,11 @@ bool RawMqttHandler::publish(AmsData* data, AmsData* meterState, EnergyAccountin
|
|||||||
}
|
}
|
||||||
mqtt->publish(topic + "/realtime/import/hour", String(ea->getUseThisHour(), 3));
|
mqtt->publish(topic + "/realtime/import/hour", String(ea->getUseThisHour(), 3));
|
||||||
mqtt->publish(topic + "/realtime/import/day", String(ea->getUseToday(), 2));
|
mqtt->publish(topic + "/realtime/import/day", String(ea->getUseToday(), 2));
|
||||||
|
uint8_t peakCount = ea->getConfig()->hours;
|
||||||
|
if(peakCount > 5) peakCount = 5;
|
||||||
|
for(uint8_t i = 1; i <= peakCount; 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/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/hour", String(ea->getProducedThisHour(), 3));
|
||||||
@@ -103,7 +126,7 @@ bool RawMqttHandler::publishPrices(EntsoeApi* eapi) {
|
|||||||
|
|
||||||
time_t now = time(nullptr);
|
time_t now = time(nullptr);
|
||||||
|
|
||||||
float min1hr, min3hr, min6hr;
|
float min1hr = 0.0, min3hr = 0.0, min6hr = 0.0;
|
||||||
int8_t min1hrIdx = -1, min3hrIdx = -1, min6hrIdx = -1;
|
int8_t min1hrIdx = -1, min3hrIdx = -1, min6hrIdx = -1;
|
||||||
float min = INT16_MAX, max = INT16_MIN;
|
float min = INT16_MAX, max = INT16_MIN;
|
||||||
float values[34];
|
float values[34];
|
||||||
@@ -154,7 +177,7 @@ bool RawMqttHandler::publishPrices(EntsoeApi* eapi) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char ts1hr[21];
|
char ts1hr[24];
|
||||||
if(min1hrIdx > -1) {
|
if(min1hrIdx > -1) {
|
||||||
time_t ts = now + (SECS_PER_HOUR * min1hrIdx);
|
time_t ts = now + (SECS_PER_HOUR * min1hrIdx);
|
||||||
//Serial.printf("1hr: %d %lu\n", min1hrIdx, ts);
|
//Serial.printf("1hr: %d %lu\n", min1hrIdx, ts);
|
||||||
@@ -162,7 +185,7 @@ bool RawMqttHandler::publishPrices(EntsoeApi* eapi) {
|
|||||||
breakTime(ts, tm);
|
breakTime(ts, tm);
|
||||||
sprintf(ts1hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
|
sprintf(ts1hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
|
||||||
}
|
}
|
||||||
char ts3hr[21];
|
char ts3hr[24];
|
||||||
if(min3hrIdx > -1) {
|
if(min3hrIdx > -1) {
|
||||||
time_t ts = now + (SECS_PER_HOUR * min3hrIdx);
|
time_t ts = now + (SECS_PER_HOUR * min3hrIdx);
|
||||||
//Serial.printf("3hr: %d %lu\n", min3hrIdx, ts);
|
//Serial.printf("3hr: %d %lu\n", min3hrIdx, ts);
|
||||||
@@ -170,7 +193,7 @@ bool RawMqttHandler::publishPrices(EntsoeApi* eapi) {
|
|||||||
breakTime(ts, tm);
|
breakTime(ts, tm);
|
||||||
sprintf(ts3hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
|
sprintf(ts3hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
|
||||||
}
|
}
|
||||||
char ts6hr[21];
|
char ts6hr[24];
|
||||||
if(min6hrIdx > -1) {
|
if(min6hrIdx > -1) {
|
||||||
time_t ts = now + (SECS_PER_HOUR * min6hrIdx);
|
time_t ts = now + (SECS_PER_HOUR * min6hrIdx);
|
||||||
//Serial.printf("6hr: %d %lu\n", min6hrIdx, ts);
|
//Serial.printf("6hr: %d %lu\n", min6hrIdx, ts);
|
||||||
@@ -208,7 +231,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;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ static const char HEADER_CACHE_CONTROL[] PROGMEM = "Cache-Control";
|
|||||||
static const char HEADER_PRAGMA[] PROGMEM = "Pragma";
|
static const char HEADER_PRAGMA[] PROGMEM = "Pragma";
|
||||||
static const char HEADER_EXPIRES[] PROGMEM = "Expires";
|
static const char HEADER_EXPIRES[] PROGMEM = "Expires";
|
||||||
static const char HEADER_AUTHENTICATE[] PROGMEM = "WWW-Authenticate";
|
static const char HEADER_AUTHENTICATE[] PROGMEM = "WWW-Authenticate";
|
||||||
|
static const char HEADER_LOCATION[] PROGMEM = "Location";
|
||||||
|
|
||||||
static const char CACHE_CONTROL_NO_CACHE[] PROGMEM = "no-cache, no-store, must-revalidate";
|
static const char CACHE_CONTROL_NO_CACHE[] PROGMEM = "no-cache, no-store, must-revalidate";
|
||||||
static const char CACHE_1HR[] PROGMEM = "public, max-age=3600";
|
static const char CACHE_1HR[] PROGMEM = "public, max-age=3600";
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -61,6 +61,11 @@ private:
|
|||||||
bool performRestart = false;
|
bool performRestart = false;
|
||||||
bool performUpgrade = false;
|
bool performUpgrade = false;
|
||||||
bool rebootForUpgrade = false;
|
bool rebootForUpgrade = false;
|
||||||
|
#if defined(AMS2MQTT_FIRMWARE_URL)
|
||||||
|
String customFirmwareUrl = AMS2MQTT_FIRMWARE_URL;
|
||||||
|
#else
|
||||||
|
String customFirmwareUrl;
|
||||||
|
#endif
|
||||||
|
|
||||||
static const uint16_t BufferSize = 2048;
|
static const uint16_t BufferSize = 2048;
|
||||||
char* buf;
|
char* buf;
|
||||||
@@ -104,6 +109,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');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -651,7 +657,7 @@ var fetch = function() {
|
|||||||
|
|
||||||
if(ip) {
|
if(ip) {
|
||||||
var v = parseInt(json.i);
|
var v = parseInt(json.i);
|
||||||
var pct = (v*100)/parseInt(json.im);
|
var pct = Math.min((v*100)/parseInt(json.im), 100);
|
||||||
var append = "W";
|
var append = "W";
|
||||||
if(v > 1000 && !swatt) {
|
if(v > 1000 && !swatt) {
|
||||||
v = (v/1000).toFixed(1);
|
v = (v/1000).toFixed(1);
|
||||||
@@ -677,7 +683,7 @@ var fetch = function() {
|
|||||||
$('.rim').hide();
|
$('.rim').hide();
|
||||||
if(xp) {
|
if(xp) {
|
||||||
var v = parseInt(json.e);
|
var v = parseInt(json.e);
|
||||||
var pct = (v*100)/(om*1000);
|
var pct = Math.min((v*100)/(om*1000), 100);
|
||||||
var append = "W";
|
var append = "W";
|
||||||
if(v > 1000 && !swatt) {
|
if(v > 1000 && !swatt) {
|
||||||
v = (v/1000).toFixed(1);
|
v = (v/1000).toFixed(1);
|
||||||
@@ -717,21 +723,21 @@ var fetch = function() {
|
|||||||
var u1 = parseFloat(json.u1);
|
var u1 = parseFloat(json.u1);
|
||||||
t += u1;
|
t += u1;
|
||||||
c++;
|
c++;
|
||||||
var pct = (Math.max(parseFloat(json.u1)-195.5, 1)*100/69);
|
var pct = Math.min(Math.max(parseFloat(json.u1)-195.5, 1)*100/69, 100);
|
||||||
arr[r++] = [ds == 1 ? 'L1-L2' : 'L1', u1, "color: " + voltcol(pct) + ";opacity: 0.9;", u1 + "V"];
|
arr[r++] = [ds == 1 ? 'L1-L2' : 'L1', u1, "color: " + voltcol(pct) + ";opacity: 0.9;", u1 + "V"];
|
||||||
}
|
}
|
||||||
if(json.u2) {
|
if(json.u2) {
|
||||||
var u2 = parseFloat(json.u2);
|
var u2 = parseFloat(json.u2);
|
||||||
t += u2;
|
t += u2;
|
||||||
c++;
|
c++;
|
||||||
var pct = (Math.max(parseFloat(json.u2)-195.5, 1)*100/69);
|
var pct = Math.min(Math.max(parseFloat(json.u2)-195.5, 1)*100/69, 100);
|
||||||
arr[r++] = [ds == 1 ? 'L1-L3' : 'L2', u2, "color: " + voltcol(pct) + ";opacity: 0.9;", u2 + "V"];
|
arr[r++] = [ds == 1 ? 'L1-L3' : 'L2', u2, "color: " + voltcol(pct) + ";opacity: 0.9;", u2 + "V"];
|
||||||
}
|
}
|
||||||
if(json.u3) {
|
if(json.u3) {
|
||||||
var u3 = parseFloat(json.u3);
|
var u3 = parseFloat(json.u3);
|
||||||
t += u3;
|
t += u3;
|
||||||
c++;
|
c++;
|
||||||
var pct = (Math.max(parseFloat(json.u3)-195.5, 1)*100/69);
|
var pct = Math.min(Math.max(parseFloat(json.u3)-195.5, 1)*100/69, 100);
|
||||||
arr[r++] = [ds == 1 ? 'L2-L3' : 'L3', u3, "color: " + voltcol(pct) + ";opacity: 0.9;", u3 + "V"];
|
arr[r++] = [ds == 1 ? 'L2-L3' : 'L3', u3, "color: " + voltcol(pct) + ";opacity: 0.9;", u3 + "V"];
|
||||||
}
|
}
|
||||||
v = t/c;
|
v = t/c;
|
||||||
@@ -756,19 +762,19 @@ var fetch = function() {
|
|||||||
if(json.i1 || json.u1) {
|
if(json.i1 || json.u1) {
|
||||||
var i1 = parseFloat(json.i1);
|
var i1 = parseFloat(json.i1);
|
||||||
dA = true;
|
dA = true;
|
||||||
var pct = (parseFloat(json.i1)/parseInt(json.mf))*100;
|
var pct = Math.min((parseFloat(json.i1)/parseInt(json.mf))*100, 100);
|
||||||
arr[r++] = ['L1', pct, "color: " + ampcol(pct) + ";opacity: 0.9;", i1 + "A"];
|
arr[r++] = ['L1', pct, "color: " + ampcol(pct) + ";opacity: 0.9;", i1 + "A"];
|
||||||
}
|
}
|
||||||
if(json.i2 || json.u2) {
|
if(json.i2 || json.u2) {
|
||||||
var i2 = parseFloat(json.i2);
|
var i2 = parseFloat(json.i2);
|
||||||
dA = true;
|
dA = true;
|
||||||
var pct = (parseFloat(json.i2)/parseInt(json.mf))*100;
|
var pct = Math.min((parseFloat(json.i2)/parseInt(json.mf))*100, 100);
|
||||||
arr[r++] = ['L2', pct, "color: " + ampcol(pct) + ";opacity: 0.9;", i2 + "A"];
|
arr[r++] = ['L2', pct, "color: " + ampcol(pct) + ";opacity: 0.9;", i2 + "A"];
|
||||||
}
|
}
|
||||||
if(json.i3 || json.u3) {
|
if(json.i3 || json.u3) {
|
||||||
var i3 = parseFloat(json.i3);
|
var i3 = parseFloat(json.i3);
|
||||||
dA = true;
|
dA = true;
|
||||||
var pct = (parseFloat(json.i3)/parseInt(json.mf))*100;
|
var pct = Math.min((parseFloat(json.i3)/parseInt(json.mf))*100, 100);
|
||||||
arr[r++] = ['L3', pct, "color: " + ampcol(pct) + ";opacity: 0.9;", i3 + "A"];
|
arr[r++] = ['L3', pct, "color: " + ampcol(pct) + ";opacity: 0.9;", i3 + "A"];
|
||||||
}
|
}
|
||||||
if(dA) {
|
if(dA) {
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,8 @@
|
|||||||
"v" : %.3f,
|
"v" : %.3f,
|
||||||
"r" : %d,
|
"r" : %d,
|
||||||
"t" : %.2f,
|
"t" : %.2f,
|
||||||
"u" : %lu,
|
"u" : %u,
|
||||||
"m" : %lu,
|
"m" : %u,
|
||||||
"em" : %d,
|
"em" : %d,
|
||||||
"hm" : %d,
|
"hm" : %d,
|
||||||
"wm" : %d,
|
"wm" : %d,
|
||||||
@@ -35,6 +35,7 @@
|
|||||||
"ds" : %d,
|
"ds" : %d,
|
||||||
"ea" : {
|
"ea" : {
|
||||||
"x" : %.1f,
|
"x" : %.1f,
|
||||||
|
"p" : [ %s ],
|
||||||
"t" : %d,
|
"t" : %d,
|
||||||
"h" : {
|
"h" : {
|
||||||
"u" : %.2f,
|
"u" : %.2f,
|
||||||
@@ -52,5 +53,5 @@
|
|||||||
"p" : %.2f
|
"p" : %.2f
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"c" : %lu
|
"c" : %u
|
||||||
}
|
}
|
||||||
@@ -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>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<div class="alert alert-warning">!!WARNING!!<br/>Do not change anything here unless you know exactly what you are doing! Changing things here could cause the device to stop responding</div>
|
<div class="alert alert-warning">!!WARNING!!<br/>Do not change anything here unless you know exactly what you are doing! Changing things here could cause the device to stop responding</div>
|
||||||
<form method="post" action="/save">
|
<form method="post" action="/save">
|
||||||
<input type="hidden" name="gpioConfig" value="true"/>
|
<input type="hidden" name="gc" value="true"/>
|
||||||
<div class="my-3 p-3 bg-white rounded shadow">
|
<div class="my-3 p-3 bg-white rounded shadow">
|
||||||
<h6>GPIO settings</h6>
|
<h6>GPIO settings</h6>
|
||||||
<div class="d-flex flex-row flex-wrap">
|
<div class="d-flex flex-row flex-wrap">
|
||||||
@@ -8,18 +8,18 @@
|
|||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text">HAN</span>
|
<span class="input-group-text">HAN</span>
|
||||||
</div>
|
</div>
|
||||||
<select name="hanPin" class="form-control">
|
<select name="h" class="form-control">
|
||||||
${options.han}
|
${h}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="m-2 input-group input-group-sm" style="width: 150px;">
|
<div class="m-2 input-group input-group-sm" style="width: 150px;">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text">LED</span>
|
<span class="input-group-text">LED</span>
|
||||||
</div>
|
</div>
|
||||||
<input name="ledPin" type="number" min="2" max="${gpio.max}" class="form-control" value="${config.ledPin}"/>
|
<input name="l" type="number" min="2" max="${g}" class="form-control" value="${l}"/>
|
||||||
<div class="input-group-append" title="Inverted">
|
<div class="input-group-append" title="Inverted">
|
||||||
<label class="input-group-text">
|
<label class="input-group-text">
|
||||||
<input type="checkbox" name="ledInverted" value="true" ${config.ledInverted}/> inv
|
<input type="checkbox" name="i" value="true" ${i}/> inv
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -27,12 +27,12 @@
|
|||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text">RGB</span>
|
<span class="input-group-text">RGB</span>
|
||||||
</div>
|
</div>
|
||||||
<input name="ledPinRed" type="number" min="2" max="${gpio.max}" class="form-control" value="${config.ledPinRed}"/>
|
<input name="r" type="number" min="2" max="${g}" class="form-control" value="${r}"/>
|
||||||
<input name="ledPinGreen" type="number" min="2" max="${gpio.max}" class="form-control" value="${config.ledPinGreen}"/>
|
<input name="e" type="number" min="2" max="${g}" class="form-control" value="${e}"/>
|
||||||
<input name="ledPinBlue" type="number" min="2" max="${gpio.max}" class="form-control" value="${config.ledPinBlue}"/>
|
<input name="b" type="number" min="2" max="${g}" class="form-control" value="${b}"/>
|
||||||
<div class="input-group-append" title="Inverted">
|
<div class="input-group-append" title="Inverted">
|
||||||
<label class="input-group-text">
|
<label class="input-group-text">
|
||||||
<input type="checkbox" name="ledRgbInverted" value="true" ${config.ledRgbInverted}/> inv
|
<input type="checkbox" name="n" value="true" ${n}/> inv
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -40,31 +40,31 @@
|
|||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text">AP button</span>
|
<span class="input-group-text">AP button</span>
|
||||||
</div>
|
</div>
|
||||||
<input name="apPin" type="number" min="0" max="${gpio.max}" class="form-control" value="${config.apPin}"/>
|
<input name="a" type="number" min="0" max="${g}" class="form-control" value="${a}"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="m-2 input-group input-group-sm" style="width: 150px;">
|
<div class="m-2 input-group input-group-sm" style="width: 150px;">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text">Temperature</span>
|
<span class="input-group-text">Temperature</span>
|
||||||
</div>
|
</div>
|
||||||
<input name="tempSensorPin" type="number" min="0" max="${gpio.max}" class="form-control" value="${config.tempSensorPin}"/>
|
<input name="t" type="number" min="0" max="${g}" class="form-control" value="${t}"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="m-2 input-group input-group-sm" style="width: 150px;">
|
<div class="m-2 input-group input-group-sm" style="width: 150px;">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text">Analog temp</span>
|
<span class="input-group-text">Analog temp</span>
|
||||||
</div>
|
</div>
|
||||||
<input name="tempAnalogSensorPin" type="number" min="0" max="${gpio.max}" class="form-control" value="${config.tempAnalogSensorPin}"/>
|
<input name="m" type="number" min="0" max="${g}" class="form-control" value="${m}"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="m-2 input-group input-group-sm" style="width: 100px;">
|
<div class="m-2 input-group input-group-sm" style="width: 100px;">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text">Vcc</span>
|
<span class="input-group-text">Vcc</span>
|
||||||
</div>
|
</div>
|
||||||
<input name="vccPin" type="number" min="0" max="${gpio.max}" class="form-control" value="${config.vccPin}"/>
|
<input name="v" type="number" min="0" max="${g}" class="form-control" value="${v}"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="m-2 input-group input-group-sm" style="width: 200px;">
|
<div class="m-2 input-group input-group-sm" style="width: 200px;">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text">GND resistor</span>
|
<span class="input-group-text">GND resistor</span>
|
||||||
</div>
|
</div>
|
||||||
<input type="number" min="1" max="1000" step="1" class="form-control" name="vccResistorGnd" value="${config.vccResistorGnd}" />
|
<input type="number" min="1" max="1000" step="1" class="form-control" name="d" value="${d}" />
|
||||||
<div class="input-group-append" title="Inverted">
|
<div class="input-group-append" title="Inverted">
|
||||||
<label class="input-group-text">kΩ</label>
|
<label class="input-group-text">kΩ</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text">Vcc resistor</span>
|
<span class="input-group-text">Vcc resistor</span>
|
||||||
</div>
|
</div>
|
||||||
<input type="number" min="1" max="1000" step="1" class="form-control" name="vccResistorVcc" value="${config.vccResistorVcc}" />
|
<input type="number" min="1" max="1000" step="1" class="form-control" name="s" value="${s}" />
|
||||||
<div class="input-group-append" title="Inverted">
|
<div class="input-group-append" title="Inverted">
|
||||||
<label class="input-group-text">kΩ</label>
|
<label class="input-group-text">kΩ</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -82,19 +82,19 @@
|
|||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text">Multiplier</span>
|
<span class="input-group-text">Multiplier</span>
|
||||||
</div>
|
</div>
|
||||||
<input type="number" min="0.1" max="10" step="0.01" class="form-control" name="vccMultiplier" value="${config.vccMultiplier}" />
|
<input type="number" min="0.1" max="10" step="0.01" class="form-control" name="u" value="${u}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="m-2 input-group input-group-sm" style="width: 120px;">
|
<div class="m-2 input-group input-group-sm" style="width: 120px;">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text">Offset</span>
|
<span class="input-group-text">Offset</span>
|
||||||
</div>
|
</div>
|
||||||
<input type="number" min="0.0" max="3.5" step="0.01" class="form-control" name="vccOffset" value="${config.vccOffset}" />
|
<input type="number" min="0.0" max="3.5" step="0.01" class="form-control" name="o" value="${o}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="m-2 input-group input-group-sm" style="width: 130px;">
|
<div class="m-2 input-group input-group-sm" style="width: 130px;">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text">Boot limit</span>
|
<span class="input-group-text">Boot limit</span>
|
||||||
</div>
|
</div>
|
||||||
<input type="number" min="2.5" max="3.5" step="0.1" class="form-control" name="vccBootLimit" value="${config.vccBootLimit}" />
|
<input type="number" min="2.5" max="3.5" step="0.1" class="form-control" name="c" value="${c}" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,9 +11,5 @@
|
|||||||
"I3" : %.2f,
|
"I3" : %.2f,
|
||||||
"U1" : %.2f,
|
"U1" : %.2f,
|
||||||
"U2" : %.2f,
|
"U2" : %.2f,
|
||||||
"U3" : %.2f,
|
"U3" : %.2f
|
||||||
"PF" : %.2f,
|
|
||||||
"PF1" : %.2f,
|
|
||||||
"PF2" : %.2f,
|
|
||||||
"PF3" : %.2f
|
|
||||||
}
|
}
|
||||||
|
|||||||
25
web/ha4.json
Normal file
25
web/ha4.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"lv" : "%s",
|
||||||
|
"id" : "%s",
|
||||||
|
"type" : "%s",
|
||||||
|
"P" : %d,
|
||||||
|
"P1" : %.2f,
|
||||||
|
"P2" : %.2f,
|
||||||
|
"P3" : %.2f,
|
||||||
|
"Q" : %d,
|
||||||
|
"PO" : %d,
|
||||||
|
"PO1" : %.2f,
|
||||||
|
"PO2" : %.2f,
|
||||||
|
"PO3" : %.2f,
|
||||||
|
"QO" : %d,
|
||||||
|
"I1" : %.2f,
|
||||||
|
"I2" : %.2f,
|
||||||
|
"I3" : %.2f,
|
||||||
|
"U1" : %.2f,
|
||||||
|
"U2" : %.2f,
|
||||||
|
"U3" : %.2f,
|
||||||
|
"PF" : %.2f,
|
||||||
|
"PF1" : %.2f,
|
||||||
|
"PF2" : %.2f,
|
||||||
|
"PF3" : %.2f
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id" : "%s",
|
"id" : "%s",
|
||||||
"name" : "%s",
|
"name" : "%s",
|
||||||
"up" : %lu,
|
"up" : %u,
|
||||||
"t" : %lu,
|
"t" : %lu,
|
||||||
"vcc" : %.3f,
|
"vcc" : %.3f,
|
||||||
"rssi": %d,
|
"rssi": %d,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id" : "%s",
|
"id" : "%s",
|
||||||
"name" : "%s",
|
"name" : "%s",
|
||||||
"up" : %lu,
|
"up" : %u,
|
||||||
"t" : %lu,
|
"t" : %lu,
|
||||||
"vcc" : %.3f,
|
"vcc" : %.3f,
|
||||||
"rssi": %d,
|
"rssi": %d,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id" : "%s",
|
"id" : "%s",
|
||||||
"name" : "%s",
|
"name" : "%s",
|
||||||
"up" : %lu,
|
"up" : %u,
|
||||||
"t" : %lu,
|
"t" : %lu,
|
||||||
"vcc" : %.3f,
|
"vcc" : %.3f,
|
||||||
"rssi": %d,
|
"rssi": %d,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id" : "%s",
|
"id" : "%s",
|
||||||
"name" : "%s",
|
"name" : "%s",
|
||||||
"up" : %lu,
|
"up" : %u,
|
||||||
"t" : %lu,
|
"t" : %lu,
|
||||||
"vcc" : %.3f,
|
"vcc" : %.3f,
|
||||||
"rssi": %d,
|
"rssi": %d,
|
||||||
@@ -11,8 +11,14 @@
|
|||||||
"id" : "%s",
|
"id" : "%s",
|
||||||
"type" : "%s",
|
"type" : "%s",
|
||||||
"P" : %d,
|
"P" : %d,
|
||||||
|
"P1" : %.2f,
|
||||||
|
"P2" : %.2f,
|
||||||
|
"P3" : %.2f,
|
||||||
"Q" : %d,
|
"Q" : %d,
|
||||||
"PO" : %d,
|
"PO" : %d,
|
||||||
|
"PO1" : %.2f,
|
||||||
|
"PO2" : %.2f,
|
||||||
|
"PO3" : %.2f,
|
||||||
"QO" : %d,
|
"QO" : %d,
|
||||||
"I1" : %.2f,
|
"I1" : %.2f,
|
||||||
"I2" : %.2f,
|
"I2" : %.2f,
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
"up" : %d,
|
"up" : %d,
|
||||||
"vcc" : %.3f,
|
"vcc" : %.3f,
|
||||||
"rssi": %d,
|
"rssi": %d,
|
||||||
"temp": %.2f
|
"temp": %.2f,
|
||||||
|
"version": "%s"
|
||||||
}
|
}
|
||||||
|
|||||||
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
<select name="board" class="form-control" required>
|
<select name="board" class="form-control" required>
|
||||||
<option value=""></option>
|
<option value=""></option>
|
||||||
<optgroup label="Custom hardware">
|
<optgroup label="Custom hardware">
|
||||||
|
<option value="7" ${config.boardType7}>Pow-U+ (ESP32) from amsleser.no</option>
|
||||||
<option value="6" ${config.boardType6}>Pow-P1 from amsleser.no</option>
|
<option value="6" ${config.boardType6}>Pow-P1 from amsleser.no</option>
|
||||||
<option value="5" ${config.boardType5}>Pow-K+ (ESP32) from amsleser.no</option>
|
<option value="5" ${config.boardType5}>Pow-K+ (ESP32) from amsleser.no</option>
|
||||||
<option value="4" ${config.boardType4}>Pow-U or Pow-K from amsleser.no (GPIO12)</option>
|
<option value="4" ${config.boardType4}>Pow-U or Pow-K from amsleser.no (GPIO12)</option>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row" id="i">
|
<div class="row" id="i">
|
||||||
<div class="col-xl-3 col-lg-4 form-group">
|
<div class="col-xl-3 col-lg-4 col-md-6 form-group">
|
||||||
<div class="input-group input-group-sm">
|
<div class="input-group input-group-sm">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text">IP</span>
|
<span class="input-group-text">IP</span>
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
<input type="text" name="i" class="form-control sip" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" value="{i}"/>
|
<input type="text" name="i" class="form-control sip" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" value="{i}"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xl-3 col-lg-4 form-group">
|
<div class="col-xl-3 col-lg-4 col-md-6 form-group">
|
||||||
<div class="input-group input-group-sm">
|
<div class="input-group input-group-sm">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text">Netmask</span>
|
<span class="input-group-text">Netmask</span>
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
<input type="text" name="sn" class="form-control sip" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" value="{sn}"/>
|
<input type="text" name="sn" class="form-control sip" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" value="{sn}"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xl-3 col-lg-4 form-group">
|
<div class="col-xl-3 col-lg-4 col-md-6 form-group">
|
||||||
<div class="input-group input-group-sm">
|
<div class="input-group input-group-sm">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text">Gateway</span>
|
<span class="input-group-text">Gateway</span>
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
<input type="text" name="g" class="form-control sip" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" value="{g}"/>
|
<input type="text" name="g" class="form-control sip" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" value="{g}"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xl-4 col-lg-5 form-group">
|
<div class="col-xl-4 col-lg-5 col-md-6 form-group">
|
||||||
<div class="input-group input-group-sm">
|
<div class="input-group input-group-sm">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text">DNS 1</span>
|
<span class="input-group-text">DNS 1</span>
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
<input type="text" name="d1" class="form-control sip" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" value="{d1}"/>
|
<input type="text" name="d1" class="form-control sip" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" value="{d1}"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xl-4 col-lg-5 form-group">
|
<div class="col-xl-4 col-lg-5 col-md-6 form-group">
|
||||||
<div class="input-group input-group-sm">
|
<div class="input-group input-group-sm">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text">DNS 2</span>
|
<span class="input-group-text">DNS 2</span>
|
||||||
@@ -89,7 +89,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
<div class="col-lg-3 col-md-4 col-sm-6 form-group">
|
||||||
|
<div class="input-group input-group-sm">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<span class="input-group-text">Power saving</span>
|
||||||
|
</div>
|
||||||
|
<select name="z" class="form-control">
|
||||||
|
<option value="255">Default</option>
|
||||||
|
<option value="0" {z0}>Off</option>
|
||||||
|
<option value="1" {z1}>Minimum</option>
|
||||||
|
<option value="2" {z2}>Maximum</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
<div class="row form-group">
|
<div class="row form-group">
|
||||||
|
|||||||
Reference in New Issue
Block a user