mirror of
https://github.com/UtilitechAS/amsreader-firmware.git
synced 2026-04-02 12:59:14 +00:00
Merge branch 'main' into dev-v2.3
This commit is contained in:
@@ -247,6 +247,9 @@ bool AmsConfiguration::getMeterConfig(MeterConfig& config) {
|
||||
}
|
||||
|
||||
bool AmsConfiguration::setMeterConfig(MeterConfig& config) {
|
||||
if(config.bufferSize < 1) config.bufferSize = 1;
|
||||
if(config.bufferSize > 64) config.bufferSize = 64;
|
||||
|
||||
MeterConfig existing;
|
||||
if(getMeterConfig(existing)) {
|
||||
meterChanged |= config.baud != existing.baud;
|
||||
|
||||
@@ -77,7 +77,7 @@ public:
|
||||
bool isThreePhase();
|
||||
bool isTwoPhase();
|
||||
bool isCounterEstimated();
|
||||
bool isL2currentEstimated();
|
||||
bool isL2currentMissing();
|
||||
|
||||
int8_t getLastError();
|
||||
void setLastError(int8_t);
|
||||
@@ -95,7 +95,7 @@ protected:
|
||||
float l1activeExportPower = 0, l2activeExportPower = 0, l3activeExportPower = 0;
|
||||
float powerFactor = 0, l1PowerFactor = 0, l2PowerFactor = 0, l3PowerFactor = 0;
|
||||
double activeImportCounter = 0, reactiveImportCounter = 0, activeExportCounter = 0, reactiveExportCounter = 0;
|
||||
bool threePhase = false, twoPhase = false, counterEstimated = false, l2currentEstimated = false;
|
||||
bool threePhase = false, twoPhase = false, counterEstimated = false, l2currentMissing = false;;
|
||||
|
||||
int8_t lastError = 0x00;
|
||||
uint8_t lastErrorCount = 0;
|
||||
|
||||
@@ -73,7 +73,7 @@ void AmsData::apply(AmsData& other) {
|
||||
this->reactiveExportPower = other.getReactiveExportPower();
|
||||
this->l1current = other.getL1Current();
|
||||
this->l2current = other.getL2Current();
|
||||
this->l2currentEstimated = other.isL2currentEstimated();
|
||||
this->l2currentMissing = other.isL2currentMissing();
|
||||
this->l3current = other.getL3Current();
|
||||
this->l1voltage = other.getL1Voltage();
|
||||
this->l2voltage = other.getL2Voltage();
|
||||
@@ -314,8 +314,8 @@ bool AmsData::isCounterEstimated() {
|
||||
return this->counterEstimated;
|
||||
}
|
||||
|
||||
bool AmsData::isL2currentEstimated() {
|
||||
return this->l2currentEstimated;
|
||||
bool AmsData::isL2currentMissing() {
|
||||
return this->l2currentMissing;
|
||||
}
|
||||
|
||||
int8_t AmsData::getLastError() {
|
||||
|
||||
@@ -13,7 +13,11 @@
|
||||
class DSMRParser {
|
||||
public:
|
||||
int8_t parse(uint8_t *buf, DataParserContext &ctx, bool verified);
|
||||
uint16_t getCrc();
|
||||
uint16_t getCrcCalc();
|
||||
private:
|
||||
uint16_t crc;
|
||||
uint16_t crc_calc;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,8 +23,8 @@ int8_t DSMRParser::parse(uint8_t *buf, DataParserContext &ctx, bool verified) {
|
||||
if(!reachedEnd) return DATA_PARSE_INCOMPLETE;
|
||||
buf[ctx.length+1] = '\0';
|
||||
if(crcPos > 0) {
|
||||
uint16_t crc_calc = crc16(buf, crcPos);
|
||||
uint16_t crc = 0x0000;
|
||||
crc_calc = crc16(buf, crcPos);
|
||||
crc = 0x0000;
|
||||
fromHex((uint8_t*) &crc, String((char*) buf+crcPos), 2);
|
||||
crc = ntohs(crc);
|
||||
|
||||
@@ -32,4 +32,11 @@ int8_t DSMRParser::parse(uint8_t *buf, DataParserContext &ctx, bool verified) {
|
||||
return DATA_PARSE_FOOTER_CHECKSUM_ERROR;
|
||||
}
|
||||
return DATA_PARSE_OK;
|
||||
}
|
||||
|
||||
uint16_t DSMRParser::getCrc() {
|
||||
return crc;
|
||||
}
|
||||
uint16_t DSMRParser::getCrcCalc() {
|
||||
return crc_calc;
|
||||
}
|
||||
@@ -23,12 +23,14 @@ class AmsMqttHandler {
|
||||
public:
|
||||
AmsMqttHandler(MqttConfig& mqttConfig, RemoteDebug* debugger, char* buf) {
|
||||
this->mqttConfig = mqttConfig;
|
||||
this->mqttConfigChanged = true;
|
||||
this->debugger = debugger;
|
||||
this->json = buf;
|
||||
mqtt.dropOverflow(true);
|
||||
};
|
||||
|
||||
void setCaVerification(bool);
|
||||
void setConfig(MqttConfig& mqttConfig);
|
||||
|
||||
bool connect();
|
||||
void disconnect();
|
||||
@@ -43,6 +45,7 @@ public:
|
||||
virtual bool publishPrices(PriceService* ps) { return false; };
|
||||
virtual bool publishSystem(HwTools*, PriceService*, EnergyAccounting*) { return false; };
|
||||
virtual bool publishRaw(String data) { return false; };
|
||||
virtual void onMessage(String &topic, String &payload) {};
|
||||
|
||||
virtual ~AmsMqttHandler() {
|
||||
if(mqttClient != NULL) {
|
||||
@@ -54,6 +57,7 @@ public:
|
||||
protected:
|
||||
RemoteDebug* debugger;
|
||||
MqttConfig mqttConfig;
|
||||
bool mqttConfigChanged = true;
|
||||
MQTTClient mqtt = MQTTClient(256);
|
||||
unsigned long lastMqttRetry = -10000;
|
||||
bool caVerification = true;
|
||||
|
||||
@@ -13,6 +13,11 @@ void AmsMqttHandler::setCaVerification(bool caVerification) {
|
||||
this->caVerification = caVerification;
|
||||
}
|
||||
|
||||
void AmsMqttHandler::setConfig(MqttConfig& mqttConfig) {
|
||||
this->mqttConfig = mqttConfig;
|
||||
this->mqttConfigChanged = true;
|
||||
}
|
||||
|
||||
bool AmsMqttHandler::connect() {
|
||||
if(millis() - lastMqttRetry < 10000) {
|
||||
yield();
|
||||
@@ -21,6 +26,8 @@ bool AmsMqttHandler::connect() {
|
||||
lastMqttRetry = millis();
|
||||
|
||||
time_t epoch = time(nullptr);
|
||||
|
||||
WiFiClient *actualClient = NULL;
|
||||
|
||||
if(mqttConfig.ssl) {
|
||||
if(epoch < FirmwareVersion::BuildEpoch) {
|
||||
@@ -35,7 +42,9 @@ bool AmsMqttHandler::connect() {
|
||||
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("ESP8266 firmware does not have enough memory...\n"));
|
||||
return false;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
if(mqttConfigChanged) {
|
||||
if(caVerification && LittleFS.begin()) {
|
||||
File file;
|
||||
|
||||
@@ -50,62 +59,68 @@ bool AmsMqttHandler::connect() {
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("CA accepted\n"));
|
||||
} else {
|
||||
if(debugger->isActive(RemoteDebug::WARNING)) debugger->printf_P(PSTR("CA was rejected\n"));
|
||||
delete mqttSecureClient;
|
||||
mqttSecureClient = NULL;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
file.close();
|
||||
|
||||
if(LittleFS.exists(FILE_MQTT_CERT) && LittleFS.exists(FILE_MQTT_KEY)) {
|
||||
#if defined(ESP8266)
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Found MQTT certificate file (%dkb free heap)\n"), ESP.getFreeHeap());
|
||||
file = LittleFS.open(FILE_MQTT_CERT, (char*) "r");
|
||||
BearSSL::X509List *serverCertList = new BearSSL::X509List(file);
|
||||
file.close();
|
||||
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Found MQTT key file (%dkb free heap)\n"), ESP.getFreeHeap());
|
||||
file = LittleFS.open(FILE_MQTT_KEY, (char*) "r");
|
||||
BearSSL::PrivateKey *serverPrivKey = new BearSSL::PrivateKey(file);
|
||||
file.close();
|
||||
|
||||
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Setting client certificates (%dkb free heap)"), ESP.getFreeHeap());
|
||||
mqttSecureClient->setClientRSACert(serverCertList, serverPrivKey);
|
||||
#elif defined(ESP32)
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Found MQTT certificate file (%dkb free heap)\n"), ESP.getFreeHeap());
|
||||
file = LittleFS.open(FILE_MQTT_CERT, (char*) "r");
|
||||
mqttSecureClient->loadCertificate(file, file.size());
|
||||
file.close();
|
||||
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Found MQTT key file (%dkb free heap)\n"), ESP.getFreeHeap());
|
||||
file = LittleFS.open(FILE_MQTT_KEY, (char*) "r");
|
||||
mqttSecureClient->loadPrivateKey(file, file.size());
|
||||
file.close();
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("No CA, disabling validation\n"));
|
||||
mqttSecureClient->setInsecure();
|
||||
}
|
||||
|
||||
#if defined(ESP8266)
|
||||
if(LittleFS.exists(FILE_MQTT_CERT) && LittleFS.exists(FILE_MQTT_KEY)) {
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Found MQTT certificate file (%dkb free heap)\n"), ESP.getFreeHeap());
|
||||
file = LittleFS.open(FILE_MQTT_CERT, (char*) "r");
|
||||
BearSSL::X509List *serverCertList = new BearSSL::X509List(file);
|
||||
file.close();
|
||||
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Found MQTT key file (%dkb free heap)\n"), ESP.getFreeHeap());
|
||||
file = LittleFS.open(FILE_MQTT_KEY, (char*) "r");
|
||||
BearSSL::PrivateKey *serverPrivKey = new BearSSL::PrivateKey(file);
|
||||
file.close();
|
||||
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Loading cert and key (%dkb free heap)\n"), ESP.getFreeHeap());
|
||||
mqttSecureClient->setClientRSACert(serverCertList, serverPrivKey);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ESP32)
|
||||
if(LittleFS.exists(FILE_MQTT_CERT)) {
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Found MQTT certificate file (%dkb free heap)\n"), ESP.getFreeHeap());
|
||||
file = LittleFS.open(FILE_MQTT_CERT, (char*) "r");
|
||||
mqttSecureClient->loadCertificate(file, file.size());
|
||||
file.close();
|
||||
}
|
||||
|
||||
if(LittleFS.exists(FILE_MQTT_KEY)) {
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Found MQTT key file (%dkb free heap)\n"), ESP.getFreeHeap());
|
||||
file = LittleFS.open(FILE_MQTT_KEY, (char*) "r");
|
||||
mqttSecureClient->loadPrivateKey(file, file.size());
|
||||
file.close();
|
||||
}
|
||||
#endif
|
||||
|
||||
LittleFS.end();
|
||||
} else {
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("CA verification disabled\n"));
|
||||
mqttSecureClient->setInsecure();
|
||||
}
|
||||
mqttClient = mqttSecureClient;
|
||||
|
||||
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("MQTT SSL setup complete (%dkb free heap)\n"), ESP.getFreeHeap());
|
||||
}
|
||||
}
|
||||
|
||||
if(mqttClient == NULL) {
|
||||
actualClient = mqttSecureClient;
|
||||
|
||||
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("MQTT SSL setup complete (%dkb free heap)\n"), ESP.getFreeHeap());
|
||||
} else {
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("No SSL, using client without SSL support\n"));
|
||||
mqttClient = new WiFiClient();
|
||||
if(mqttClient == NULL) {
|
||||
mqttClient = new WiFiClient();
|
||||
}
|
||||
actualClient = mqttClient;
|
||||
}
|
||||
|
||||
mqttConfigChanged = false;
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Connecting to MQTT %s:%d\n"), mqttConfig.host, mqttConfig.port);
|
||||
|
||||
mqtt.begin(mqttConfig.host, mqttConfig.port, *mqttClient);
|
||||
mqtt.begin(mqttConfig.host, mqttConfig.port, *actualClient);
|
||||
|
||||
#if defined(ESP8266)
|
||||
if(mqttSecureClient) {
|
||||
@@ -118,7 +133,14 @@ bool AmsMqttHandler::connect() {
|
||||
// Connect to a unsecure or secure MQTT server
|
||||
if ((strlen(mqttConfig.username) == 0 && mqtt.connect(mqttConfig.clientId)) ||
|
||||
(strlen(mqttConfig.username) > 0 && mqtt.connect(mqttConfig.clientId, mqttConfig.username, mqttConfig.password))) {
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Successfully connected to MQTT!\n"));
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Successfully connected to MQTT\n"));
|
||||
mqtt.onMessage(std::bind(&AmsMqttHandler::onMessage, this, std::placeholders::_1, std::placeholders::_2));
|
||||
if(strlen(mqttConfig.subscribeTopic) > 0) {
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR(" Subscribing to [%s]\n"), mqttConfig.subscribeTopic);
|
||||
if(!mqtt.subscribe(mqttConfig.subscribeTopic)) {
|
||||
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf_P(PSTR(" Unable to subscribe to to [%s]\n"), mqttConfig.subscribeTopic);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
if (debugger->isActive(RemoteDebug::ERROR)) {
|
||||
@@ -139,15 +161,6 @@ void AmsMqttHandler::disconnect() {
|
||||
mqtt.loop();
|
||||
delay(10);
|
||||
yield();
|
||||
|
||||
if(mqttClient != NULL) {
|
||||
mqttClient->stop();
|
||||
delete mqttClient;
|
||||
mqttClient = NULL;
|
||||
if(mqttSecureClient != NULL) {
|
||||
mqttSecureClient = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lwmqtt_err_t AmsMqttHandler::lastError() {
|
||||
|
||||
@@ -21,6 +21,8 @@ public:
|
||||
bool publishSystem(HwTools* hw, PriceService* ps, EnergyAccounting* ea);
|
||||
bool publishRaw(String data);
|
||||
|
||||
void onMessage(String &topic, String &payload);
|
||||
|
||||
uint8_t getFormat();
|
||||
|
||||
private:
|
||||
|
||||
@@ -88,3 +88,6 @@ uint8_t DomoticzMqttHandler::getFormat() {
|
||||
bool DomoticzMqttHandler::publishRaw(String data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void DomoticzMqttHandler::onMessage(String &topic, String &payload) {
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ class HomeAssistantMqttHandler : public AmsMqttHandler {
|
||||
public:
|
||||
HomeAssistantMqttHandler(MqttConfig& mqttConfig, RemoteDebug* debugger, char* buf, uint8_t boardType, HomeAssistantConfig config, HwTools* hw) : AmsMqttHandler(mqttConfig, debugger, buf) {
|
||||
this->hw = hw;
|
||||
|
||||
l1Init = l2Init = l2eInit = l3Init = l3eInit = l4Init = l4eInit = rtInit = rteInit = pInit = sInit = false;
|
||||
|
||||
topic = String(mqttConfig.publishTopic);
|
||||
@@ -52,11 +53,16 @@ public:
|
||||
}
|
||||
|
||||
if(strlen(config.discoveryPrefix) > 0) {
|
||||
snprintf_P(json, 128, PSTR("%s/status"), config.discoveryPrefix);
|
||||
statusTopic = String(buf);
|
||||
|
||||
snprintf_P(buf, 128, PSTR("%s/sensor/"), config.discoveryPrefix);
|
||||
discoveryTopic = String(buf);
|
||||
} else {
|
||||
statusTopic = F("homeassistant/status");
|
||||
discoveryTopic = F("homeassistant/sensor/");
|
||||
}
|
||||
strcpy(this->mqttConfig.subscribeTopic, statusTopic.c_str());
|
||||
};
|
||||
bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea, PriceService* ps);
|
||||
bool publishTemperatures(AmsConfiguration*, HwTools*);
|
||||
@@ -64,6 +70,8 @@ public:
|
||||
bool publishSystem(HwTools* hw, PriceService* ps, EnergyAccounting* ea);
|
||||
bool publishRaw(String data);
|
||||
|
||||
void onMessage(String &topic, String &payload);
|
||||
|
||||
uint8_t getFormat();
|
||||
|
||||
private:
|
||||
@@ -75,6 +83,7 @@ private:
|
||||
String manufacturer;
|
||||
String deviceUrl;
|
||||
|
||||
String statusTopic;
|
||||
String discoveryTopic;
|
||||
String sensorNamePrefix;
|
||||
|
||||
@@ -89,8 +98,13 @@ private:
|
||||
bool publishList3(AmsData* data, EnergyAccounting* ea);
|
||||
bool publishList4(AmsData* data, EnergyAccounting* ea);
|
||||
String getMeterModel(AmsData* data);
|
||||
<<<<<<< HEAD
|
||||
bool publishRealtime(AmsData* data, EnergyAccounting* ea, PriceService* ps);
|
||||
void publishSensor(const HomeAssistantSensor& sensor);
|
||||
=======
|
||||
bool publishRealtime(AmsData* data, EnergyAccounting* ea, EntsoeApi* eapi);
|
||||
void publishSensor(const HomeAssistantSensor sensor);
|
||||
>>>>>>> main
|
||||
void publishList1Sensors();
|
||||
void publishList1ExportSensors();
|
||||
void publishList2Sensors();
|
||||
|
||||
@@ -13,6 +13,7 @@ struct HomeAssistantSensor {
|
||||
const char* name;
|
||||
const char* topic;
|
||||
const char* path;
|
||||
uint16_t ttl;
|
||||
const char* uom;
|
||||
const char* devcl;
|
||||
const char* stacl;
|
||||
@@ -21,98 +22,98 @@ struct HomeAssistantSensor {
|
||||
|
||||
const uint8_t List1SensorCount PROGMEM = 1;
|
||||
const HomeAssistantSensor List1Sensors[List1SensorCount] PROGMEM = {
|
||||
{"Active import", "/power", "P", "W", "power", "measurement"}
|
||||
{"Active import", "/power", "P", 30, "W", "power", "measurement"}
|
||||
};
|
||||
|
||||
const uint8_t List2SensorCount PROGMEM = 8;
|
||||
const HomeAssistantSensor List2Sensors[List2SensorCount] PROGMEM = {
|
||||
{"Reactive import", "/power", "Q", "var", "reactive_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"}
|
||||
{"Reactive import", "/power", "Q", 30, "var", "reactive_power", "measurement"},
|
||||
{"Reactive export", "/power", "QO", 30, "var", "reactive_power", "measurement"},
|
||||
{"L1 current", "/power", "I1", 30, "A", "current", "measurement"},
|
||||
{"L2 current", "/power", "I2", 30, "A", "current", "measurement"},
|
||||
{"L3 current", "/power", "I3", 30, "A", "current", "measurement"},
|
||||
{"L1 voltage", "/power", "U1", 30, "V", "voltage", "measurement"},
|
||||
{"L2 voltage", "/power", "U2", 30, "V", "voltage", "measurement"},
|
||||
{"L3 voltage", "/power", "U3", 30, "V", "voltage", "measurement"}
|
||||
};
|
||||
|
||||
const uint8_t List2ExportSensorCount PROGMEM = 1;
|
||||
const HomeAssistantSensor List2ExportSensors[List2ExportSensorCount] PROGMEM = {
|
||||
{"Active export", "/power", "PO", "W", "power", "measurement"}
|
||||
{"Active export", "/power", "PO", 30, "W", "power", "measurement"}
|
||||
};
|
||||
|
||||
const uint8_t List3SensorCount PROGMEM = 3;
|
||||
const HomeAssistantSensor List3Sensors[List3SensorCount] PROGMEM = {
|
||||
{"Accumulated active import", "/energy", "tPI", "kWh", "energy", "total_increasing"},
|
||||
{"Accumulated reactive import","/energy", "tQI", "kvarh","", "total_increasing"},
|
||||
{"Accumulated reactive export","/energy", "tQO", "kvarh","", "total_increasing"}
|
||||
{"Accumulated active import", "/energy", "tPI", 4000, "kWh", "energy", "total_increasing"},
|
||||
{"Accumulated reactive import","/energy", "tQI", 4000, "kvarh","", "total_increasing"},
|
||||
{"Accumulated reactive export","/energy", "tQO", 4000, "kvarh","", "total_increasing"}
|
||||
};
|
||||
|
||||
const uint8_t List3ExportSensorCount PROGMEM = 1;
|
||||
const HomeAssistantSensor List3ExportSensors[List3ExportSensorCount] PROGMEM = {
|
||||
{"Accumulated active export", "/energy", "tPO", "kWh", "energy", "total_increasing"}
|
||||
{"Accumulated active export", "/energy", "tPO", 4000, "kWh", "energy", "total_increasing"}
|
||||
};
|
||||
|
||||
const uint8_t List4SensorCount PROGMEM = 7;
|
||||
const HomeAssistantSensor List4Sensors[List4SensorCount] PROGMEM = {
|
||||
{"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"},
|
||||
{"L1 active import", "/power", "P1", "W", "power", "measurement"},
|
||||
{"L2 active import", "/power", "P2", "W", "power", "measurement"},
|
||||
{"L3 active import", "/power", "P3", "W", "power", "measurement"}
|
||||
{"Power factor", "/power", "PF", 30, "%", "power_factor", "measurement"},
|
||||
{"L1 power factor", "/power", "PF1", 30, "%", "power_factor", "measurement"},
|
||||
{"L2 power factor", "/power", "PF2", 30, "%", "power_factor", "measurement"},
|
||||
{"L3 power factor", "/power", "PF3", 30, "%", "power_factor", "measurement"},
|
||||
{"L1 active import", "/power", "P1", 30, "W", "power", "measurement"},
|
||||
{"L2 active import", "/power", "P2", 30, "W", "power", "measurement"},
|
||||
{"L3 active import", "/power", "P3", 30, "W", "power", "measurement"}
|
||||
};
|
||||
|
||||
const uint8_t List4ExportSensorCount PROGMEM = 3;
|
||||
const HomeAssistantSensor List4ExportSensors[List4ExportSensorCount] PROGMEM = {
|
||||
{"L1 active export", "/power", "PO1", "W", "power", "measurement"},
|
||||
{"L2 active export", "/power", "PO2", "W", "power", "measurement"},
|
||||
{"L3 active export", "/power", "PO3", "W", "power", "measurement"}
|
||||
{"L1 active export", "/power", "PO1", 30, "W", "power", "measurement"},
|
||||
{"L2 active export", "/power", "PO2", 30, "W", "power", "measurement"},
|
||||
{"L3 active export", "/power", "PO3", 30, "W", "power", "measurement"}
|
||||
};
|
||||
|
||||
const uint8_t RealtimeSensorCount PROGMEM = 8;
|
||||
const HomeAssistantSensor RealtimeSensors[RealtimeSensorCount] PROGMEM = {
|
||||
{"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", ""},
|
||||
{"Current day used", "/realtime","day.use", "kWh", "energy", "total_increasing"},
|
||||
{"Current day cost", "/realtime","day.cost", "", "monetary", ""},
|
||||
{"Current month used", "/realtime","month.use", "kWh", "energy", "total_increasing"},
|
||||
{"Current month cost", "/realtime","month.cost", "", "monetary", ""}
|
||||
{"Month max", "/realtime","max", 120, "kWh", "energy", "total_increasing"},
|
||||
{"Tariff threshold", "/realtime","threshold", 120, "kWh", "energy", "total_increasing"},
|
||||
{"Current hour used", "/realtime","hour.use", 120, "kWh", "energy", "total_increasing"},
|
||||
{"Current hour cost", "/realtime","hour.cost", 120, "", "monetary", ""},
|
||||
{"Current day used", "/realtime","day.use", 120, "kWh", "energy", "total_increasing"},
|
||||
{"Current day cost", "/realtime","day.cost", 120, "", "monetary", ""},
|
||||
{"Current month used", "/realtime","month.use", 120, "kWh", "energy", "total_increasing"},
|
||||
{"Current month cost", "/realtime","month.cost", 120, "", "monetary", ""}
|
||||
};
|
||||
|
||||
const uint8_t RealtimeExportSensorCount PROGMEM = 6;
|
||||
const HomeAssistantSensor RealtimeExportSensors[RealtimeExportSensorCount] PROGMEM = {
|
||||
{"Current hour produced", "/realtime","hour.produced", "kWh", "energy", "total_increasing"},
|
||||
{"Current hour income", "/realtime","hour.income", "", "monetary", ""},
|
||||
{"Current day produced", "/realtime","day.produced", "kWh", "energy", "total_increasing"},
|
||||
{"Current day income", "/realtime","day.income", "", "monetary", ""},
|
||||
{"Current month produced", "/realtime","month.produced", "kWh", "energy", "total_increasing"},
|
||||
{"Current month income", "/realtime","month.income", "", "monetary", ""}
|
||||
{"Current hour produced", "/realtime","hour.produced", 120, "kWh", "energy", "total_increasing"},
|
||||
{"Current hour income", "/realtime","hour.income", 120, "", "monetary", ""},
|
||||
{"Current day produced", "/realtime","day.produced", 120, "kWh", "energy", "total_increasing"},
|
||||
{"Current day income", "/realtime","day.income", 120, "", "monetary", ""},
|
||||
{"Current month produced", "/realtime","month.produced", 120, "kWh", "energy", "total_increasing"},
|
||||
{"Current month income", "/realtime","month.income", 120, "", "monetary", ""}
|
||||
};
|
||||
|
||||
const HomeAssistantSensor RealtimePeakSensor PROGMEM = {"Current month peak %d", "/realtime", "peaks[%d]", "kWh", "energy", ""};
|
||||
const HomeAssistantSensor RealtimePeakSensor PROGMEM = {"Current month peak %d", "/realtime", "peaks[%d]", 4000, "kWh", "energy", ""};
|
||||
|
||||
const uint8_t PriceSensorCount PROGMEM = 5;
|
||||
const HomeAssistantSensor PriceSensors[PriceSensorCount] PROGMEM = {
|
||||
{"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", ""}
|
||||
{"Minimum price ahead", "/prices", "prices.min", 4000, "", "monetary", ""},
|
||||
{"Maximum price ahead", "/prices", "prices.max", 4000, "", "monetary", ""},
|
||||
{"Cheapest 1hr period ahead", "/prices", "prices.cheapest1hr",4000, "", "timestamp", ""},
|
||||
{"Cheapest 3hr period ahead", "/prices", "prices.cheapest3hr",4000, "", "timestamp", ""},
|
||||
{"Cheapest 6hr period ahead", "/prices", "prices.cheapest6hr",4000, "", "timestamp", ""}
|
||||
};
|
||||
|
||||
const HomeAssistantSensor PriceSensor PROGMEM = {"Price in %02d %s", "/prices", "prices['%d']", "", "monetary", ""};
|
||||
const HomeAssistantSensor PriceSensor PROGMEM = {"Price in %02d %s", "/prices", "prices['%d']", 4000, "", "monetary", ""};
|
||||
|
||||
const uint8_t SystemSensorCount PROGMEM = 2;
|
||||
const HomeAssistantSensor SystemSensors[SystemSensorCount] PROGMEM = {
|
||||
{"Status", "/state", "rssi", "dBm", "signal_strength", "measurement"},
|
||||
{"Supply volt", "/state", "vcc", "V", "voltage", "measurement"}
|
||||
{"Status", "/state", "rssi", 180, "dBm", "signal_strength", "measurement"},
|
||||
{"Supply volt", "/state", "vcc", 180, "V", "voltage", "measurement"}
|
||||
};
|
||||
|
||||
const HomeAssistantSensor TemperatureSensor PROGMEM = {"Temperature sensor %s", "/temperatures", "temperatures['%s']", "°C", "temperature", "measurement"};
|
||||
const HomeAssistantSensor TemperatureSensor PROGMEM = {"Temperature sensor %s", "/temperatures", "temperatures['%s']", 900, "°C", "temperature", "measurement"};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"obj_id" : "%s_%s",
|
||||
"unit_of_meas" : "%s",
|
||||
"val_tpl" : "{{ value_json.%s | is_defined }}",
|
||||
"expire_after" : %d,
|
||||
"dev" : {
|
||||
"ids" : [ "%s" ],
|
||||
"name" : "%s",
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "json/jsonprices_json.h"
|
||||
#include "json/hadiscover_json.h"
|
||||
#include "json/realtime_json.h"
|
||||
#include "FirmwareVersion.h"
|
||||
|
||||
#if defined(ESP32)
|
||||
#include <esp_task_wdt.h>
|
||||
@@ -25,6 +26,9 @@ bool HomeAssistantMqttHandler::publish(AmsData* data, AmsData* previousState, En
|
||||
if(topic.isEmpty() || !mqtt.connected())
|
||||
return false;
|
||||
|
||||
if(time(nullptr) < FirmwareVersion::BuildEpoch)
|
||||
return false;
|
||||
|
||||
if(data->getListType() >= 3) { // publish energy counts
|
||||
publishList3(data, ea);
|
||||
loop();
|
||||
@@ -48,9 +52,7 @@ bool HomeAssistantMqttHandler::publish(AmsData* data, AmsData* previousState, En
|
||||
|
||||
bool HomeAssistantMqttHandler::publishList1(AmsData* data, EnergyAccounting* ea) {
|
||||
publishList1Sensors();
|
||||
snprintf_P(json, BufferSize, HA1_JSON,
|
||||
data->getActiveImportPower()
|
||||
);
|
||||
snprintf_P(json, BufferSize, HA1_JSON, data->getActiveImportPower());
|
||||
return mqtt.publish(topic + "/power", json);
|
||||
}
|
||||
|
||||
@@ -269,47 +271,46 @@ bool HomeAssistantMqttHandler::publishPrices(PriceService* ps) {
|
||||
breakTime(ts, tm);
|
||||
sprintf_P(ts6hr, PSTR("%04d-%02d-%02dT%02d:00:00Z"), tm.Year+1970, tm.Month, tm.Day, tm.Hour);
|
||||
}
|
||||
|
||||
snprintf_P(json, BufferSize, JSONPRICES_JSON,
|
||||
WiFi.macAddress().c_str(),
|
||||
values[0],
|
||||
values[1],
|
||||
values[2],
|
||||
values[3],
|
||||
values[4],
|
||||
values[5],
|
||||
values[6],
|
||||
values[7],
|
||||
values[8],
|
||||
values[9],
|
||||
values[10],
|
||||
values[11],
|
||||
values[12],
|
||||
values[13],
|
||||
values[14],
|
||||
values[15],
|
||||
values[16],
|
||||
values[17],
|
||||
values[18],
|
||||
values[19],
|
||||
values[20],
|
||||
values[21],
|
||||
values[22],
|
||||
values[23],
|
||||
values[24],
|
||||
values[25],
|
||||
values[26],
|
||||
values[27],
|
||||
values[28],
|
||||
values[29],
|
||||
values[30],
|
||||
values[31],
|
||||
values[32],
|
||||
values[33],
|
||||
values[34],
|
||||
values[35],
|
||||
values[36],
|
||||
values[37],
|
||||
values[0] == ENTSOE_NO_VALUE ? "null" : String(values[0], 4).c_str(),
|
||||
values[1] == ENTSOE_NO_VALUE ? "null" : String(values[1], 4).c_str(),
|
||||
values[2] == ENTSOE_NO_VALUE ? "null" : String(values[2], 4).c_str(),
|
||||
values[3] == ENTSOE_NO_VALUE ? "null" : String(values[3], 4).c_str(),
|
||||
values[4] == ENTSOE_NO_VALUE ? "null" : String(values[4], 4).c_str(),
|
||||
values[5] == ENTSOE_NO_VALUE ? "null" : String(values[5], 4).c_str(),
|
||||
values[6] == ENTSOE_NO_VALUE ? "null" : String(values[6], 4).c_str(),
|
||||
values[7] == ENTSOE_NO_VALUE ? "null" : String(values[7], 4).c_str(),
|
||||
values[8] == ENTSOE_NO_VALUE ? "null" : String(values[8], 4).c_str(),
|
||||
values[9] == ENTSOE_NO_VALUE ? "null" : String(values[9], 4).c_str(),
|
||||
values[10] == ENTSOE_NO_VALUE ? "null" : String(values[10], 4).c_str(),
|
||||
values[11] == ENTSOE_NO_VALUE ? "null" : String(values[11], 4).c_str(),
|
||||
values[12] == ENTSOE_NO_VALUE ? "null" : String(values[12], 4).c_str(),
|
||||
values[13] == ENTSOE_NO_VALUE ? "null" : String(values[13], 4).c_str(),
|
||||
values[14] == ENTSOE_NO_VALUE ? "null" : String(values[14], 4).c_str(),
|
||||
values[15] == ENTSOE_NO_VALUE ? "null" : String(values[15], 4).c_str(),
|
||||
values[16] == ENTSOE_NO_VALUE ? "null" : String(values[16], 4).c_str(),
|
||||
values[17] == ENTSOE_NO_VALUE ? "null" : String(values[17], 4).c_str(),
|
||||
values[18] == ENTSOE_NO_VALUE ? "null" : String(values[18], 4).c_str(),
|
||||
values[19] == ENTSOE_NO_VALUE ? "null" : String(values[19], 4).c_str(),
|
||||
values[20] == ENTSOE_NO_VALUE ? "null" : String(values[20], 4).c_str(),
|
||||
values[21] == ENTSOE_NO_VALUE ? "null" : String(values[21], 4).c_str(),
|
||||
values[22] == ENTSOE_NO_VALUE ? "null" : String(values[22], 4).c_str(),
|
||||
values[23] == ENTSOE_NO_VALUE ? "null" : String(values[23], 4).c_str(),
|
||||
values[24] == ENTSOE_NO_VALUE ? "null" : String(values[24], 4).c_str(),
|
||||
values[25] == ENTSOE_NO_VALUE ? "null" : String(values[25], 4).c_str(),
|
||||
values[26] == ENTSOE_NO_VALUE ? "null" : String(values[26], 4).c_str(),
|
||||
values[27] == ENTSOE_NO_VALUE ? "null" : String(values[27], 4).c_str(),
|
||||
values[28] == ENTSOE_NO_VALUE ? "null" : String(values[28], 4).c_str(),
|
||||
values[29] == ENTSOE_NO_VALUE ? "null" : String(values[29], 4).c_str(),
|
||||
values[30] == ENTSOE_NO_VALUE ? "null" : String(values[30], 4).c_str(),
|
||||
values[31] == ENTSOE_NO_VALUE ? "null" : String(values[31], 4).c_str(),
|
||||
values[32] == ENTSOE_NO_VALUE ? "null" : String(values[32], 4).c_str(),
|
||||
values[33] == ENTSOE_NO_VALUE ? "null" : String(values[33], 4).c_str(),
|
||||
values[34] == ENTSOE_NO_VALUE ? "null" : String(values[34], 4).c_str(),
|
||||
values[35] == ENTSOE_NO_VALUE ? "null" : String(values[35], 4).c_str(),
|
||||
values[36] == ENTSOE_NO_VALUE ? "null" : String(values[36], 4).c_str(),
|
||||
values[37] == ENTSOE_NO_VALUE ? "null" : String(values[37], 4).c_str(),
|
||||
min == INT16_MAX ? 0.0 : min,
|
||||
max == INT16_MIN ? 0.0 : max,
|
||||
ts1hr,
|
||||
@@ -342,7 +343,7 @@ bool HomeAssistantMqttHandler::publishSystem(HwTools* hw, PriceService* ps, Ener
|
||||
return ret;
|
||||
}
|
||||
|
||||
void HomeAssistantMqttHandler::publishSensor(const HomeAssistantSensor& sensor) {
|
||||
void HomeAssistantMqttHandler::publishSensor(const HomeAssistantSensor sensor) {
|
||||
String uid = String(sensor.path);
|
||||
uid.replace(".", "");
|
||||
uid.replace("[", "");
|
||||
@@ -356,6 +357,7 @@ void HomeAssistantMqttHandler::publishSensor(const HomeAssistantSensor& sensor)
|
||||
deviceUid.c_str(), uid.c_str(),
|
||||
sensor.uom,
|
||||
sensor.path,
|
||||
sensor.ttl,
|
||||
deviceUid.c_str(),
|
||||
deviceName.c_str(),
|
||||
deviceModel.c_str(),
|
||||
@@ -455,6 +457,7 @@ void HomeAssistantMqttHandler::publishRealtimeSensors(EnergyAccounting* ea, Pric
|
||||
name,
|
||||
RealtimePeakSensor.topic,
|
||||
path,
|
||||
RealtimePeakSensor.ttl,
|
||||
RealtimePeakSensor.uom,
|
||||
RealtimePeakSensor.devcl,
|
||||
RealtimePeakSensor.stacl
|
||||
@@ -493,6 +496,7 @@ void HomeAssistantMqttHandler::publishTemperatureSensor(uint8_t index, String id
|
||||
name,
|
||||
index == 0 ? SystemSensors[0].topic : TemperatureSensor.topic,
|
||||
path,
|
||||
TemperatureSensor.ttl,
|
||||
TemperatureSensor.uom,
|
||||
TemperatureSensor.devcl,
|
||||
TemperatureSensor.stacl
|
||||
@@ -528,6 +532,7 @@ void HomeAssistantMqttHandler::publishPriceSensors(PriceService* ps) {
|
||||
i == 0 ? "Price current hour" : name,
|
||||
PriceSensor.topic,
|
||||
path,
|
||||
PriceSensor.ttl,
|
||||
uom.c_str(),
|
||||
PriceSensor.devcl,
|
||||
i == 0 ? "total" : PriceSensor.stacl
|
||||
@@ -553,3 +558,14 @@ uint8_t HomeAssistantMqttHandler::getFormat() {
|
||||
bool HomeAssistantMqttHandler::publishRaw(String data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void HomeAssistantMqttHandler::onMessage(String &topic, String &payload) {
|
||||
if(topic.equals(statusTopic)) {
|
||||
if(payload.equals("online")) {
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Received online status from HA, resetting sensor status\n"));
|
||||
l1Init = l2Init = l2eInit = l3Init = l3eInit = l4Init = l4eInit = rtInit = rteInit = pInit = sInit = false;
|
||||
for(uint8_t i = 0; i < 32; i++) tInit[i] = false;
|
||||
for(uint8_t i = 0; i < 38; i++) prInit[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ public:
|
||||
bool publishSystem(HwTools* hw, PriceService* ps, EnergyAccounting* ea);
|
||||
bool publishRaw(String data);
|
||||
|
||||
void onMessage(String &topic, String &payload);
|
||||
|
||||
uint8_t getFormat();
|
||||
|
||||
private:
|
||||
|
||||
@@ -1,44 +1,44 @@
|
||||
{
|
||||
"id" : "%s",
|
||||
"prices" : {
|
||||
"0" : %.4f,
|
||||
"1" : %.4f,
|
||||
"2" : %.4f,
|
||||
"3" : %.4f,
|
||||
"4" : %.4f,
|
||||
"5" : %.4f,
|
||||
"6" : %.4f,
|
||||
"7" : %.4f,
|
||||
"8" : %.4f,
|
||||
"9" : %.4f,
|
||||
"10" : %.4f,
|
||||
"11" : %.4f,
|
||||
"12" : %.4f,
|
||||
"13" : %.4f,
|
||||
"14" : %.4f,
|
||||
"15" : %.4f,
|
||||
"16" : %.4f,
|
||||
"17" : %.4f,
|
||||
"18" : %.4f,
|
||||
"19" : %.4f,
|
||||
"20" : %.4f,
|
||||
"21" : %.4f,
|
||||
"22" : %.4f,
|
||||
"23" : %.4f,
|
||||
"24" : %.4f,
|
||||
"25" : %.4f,
|
||||
"26" : %.4f,
|
||||
"27" : %.4f,
|
||||
"28" : %.4f,
|
||||
"29" : %.4f,
|
||||
"30" : %.4f,
|
||||
"31" : %.4f,
|
||||
"32" : %.4f,
|
||||
"33" : %.4f,
|
||||
"34" : %.4f,
|
||||
"35" : %.4f,
|
||||
"36" : %.4f,
|
||||
"37" : %.4f,
|
||||
"0" : %s,
|
||||
"1" : %s,
|
||||
"2" : %s,
|
||||
"3" : %s,
|
||||
"4" : %s,
|
||||
"5" : %s,
|
||||
"6" : %s,
|
||||
"7" : %s,
|
||||
"8" : %s,
|
||||
"9" : %s,
|
||||
"10" : %s,
|
||||
"11" : %s,
|
||||
"12" : %s,
|
||||
"13" : %s,
|
||||
"14" : %s,
|
||||
"15" : %s,
|
||||
"16" : %s,
|
||||
"17" : %s,
|
||||
"18" : %s,
|
||||
"19" : %s,
|
||||
"20" : %s,
|
||||
"21" : %s,
|
||||
"22" : %s,
|
||||
"23" : %s,
|
||||
"24" : %s,
|
||||
"25" : %s,
|
||||
"26" : %s,
|
||||
"27" : %s,
|
||||
"28" : %s,
|
||||
"29" : %s,
|
||||
"30" : %s,
|
||||
"31" : %s,
|
||||
"32" : %s,
|
||||
"33" : %s,
|
||||
"34" : %s,
|
||||
"35" : %s,
|
||||
"36" : %s,
|
||||
"37" : %s,
|
||||
"min" : %.4f,
|
||||
"max" : %.4f,
|
||||
"cheapest1hr" : "%s",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name" : "%s",
|
||||
"up" : %d,
|
||||
"vcc" : %.3f,
|
||||
"rssi": %d,
|
||||
"temp": %.2f,
|
||||
"version": "%s"
|
||||
"rssi" : %d,
|
||||
"temp" : %.2f,
|
||||
"version" : "%s"
|
||||
}
|
||||
|
||||
@@ -294,44 +294,44 @@ bool JsonMqttHandler::publishPrices(PriceService* ps) {
|
||||
|
||||
snprintf_P(json, BufferSize, JSONPRICES_JSON,
|
||||
WiFi.macAddress().c_str(),
|
||||
values[0],
|
||||
values[1],
|
||||
values[2],
|
||||
values[3],
|
||||
values[4],
|
||||
values[5],
|
||||
values[6],
|
||||
values[7],
|
||||
values[8],
|
||||
values[9],
|
||||
values[10],
|
||||
values[11],
|
||||
values[12],
|
||||
values[13],
|
||||
values[14],
|
||||
values[15],
|
||||
values[16],
|
||||
values[17],
|
||||
values[18],
|
||||
values[19],
|
||||
values[20],
|
||||
values[21],
|
||||
values[22],
|
||||
values[23],
|
||||
values[24],
|
||||
values[25],
|
||||
values[26],
|
||||
values[27],
|
||||
values[28],
|
||||
values[29],
|
||||
values[30],
|
||||
values[31],
|
||||
values[32],
|
||||
values[33],
|
||||
values[34],
|
||||
values[35],
|
||||
values[36],
|
||||
values[37],
|
||||
values[0] == ENTSOE_NO_VALUE ? "null" : String(values[0], 4).c_str(),
|
||||
values[1] == ENTSOE_NO_VALUE ? "null" : String(values[1], 4).c_str(),
|
||||
values[2] == ENTSOE_NO_VALUE ? "null" : String(values[2], 4).c_str(),
|
||||
values[3] == ENTSOE_NO_VALUE ? "null" : String(values[3], 4).c_str(),
|
||||
values[4] == ENTSOE_NO_VALUE ? "null" : String(values[4], 4).c_str(),
|
||||
values[5] == ENTSOE_NO_VALUE ? "null" : String(values[5], 4).c_str(),
|
||||
values[6] == ENTSOE_NO_VALUE ? "null" : String(values[6], 4).c_str(),
|
||||
values[7] == ENTSOE_NO_VALUE ? "null" : String(values[7], 4).c_str(),
|
||||
values[8] == ENTSOE_NO_VALUE ? "null" : String(values[8], 4).c_str(),
|
||||
values[9] == ENTSOE_NO_VALUE ? "null" : String(values[9], 4).c_str(),
|
||||
values[10] == ENTSOE_NO_VALUE ? "null" : String(values[10], 4).c_str(),
|
||||
values[11] == ENTSOE_NO_VALUE ? "null" : String(values[11], 4).c_str(),
|
||||
values[12] == ENTSOE_NO_VALUE ? "null" : String(values[12], 4).c_str(),
|
||||
values[13] == ENTSOE_NO_VALUE ? "null" : String(values[13], 4).c_str(),
|
||||
values[14] == ENTSOE_NO_VALUE ? "null" : String(values[14], 4).c_str(),
|
||||
values[15] == ENTSOE_NO_VALUE ? "null" : String(values[15], 4).c_str(),
|
||||
values[16] == ENTSOE_NO_VALUE ? "null" : String(values[16], 4).c_str(),
|
||||
values[17] == ENTSOE_NO_VALUE ? "null" : String(values[17], 4).c_str(),
|
||||
values[18] == ENTSOE_NO_VALUE ? "null" : String(values[18], 4).c_str(),
|
||||
values[19] == ENTSOE_NO_VALUE ? "null" : String(values[19], 4).c_str(),
|
||||
values[20] == ENTSOE_NO_VALUE ? "null" : String(values[20], 4).c_str(),
|
||||
values[21] == ENTSOE_NO_VALUE ? "null" : String(values[21], 4).c_str(),
|
||||
values[22] == ENTSOE_NO_VALUE ? "null" : String(values[22], 4).c_str(),
|
||||
values[23] == ENTSOE_NO_VALUE ? "null" : String(values[23], 4).c_str(),
|
||||
values[24] == ENTSOE_NO_VALUE ? "null" : String(values[24], 4).c_str(),
|
||||
values[25] == ENTSOE_NO_VALUE ? "null" : String(values[25], 4).c_str(),
|
||||
values[26] == ENTSOE_NO_VALUE ? "null" : String(values[26], 4).c_str(),
|
||||
values[27] == ENTSOE_NO_VALUE ? "null" : String(values[27], 4).c_str(),
|
||||
values[28] == ENTSOE_NO_VALUE ? "null" : String(values[28], 4).c_str(),
|
||||
values[29] == ENTSOE_NO_VALUE ? "null" : String(values[29], 4).c_str(),
|
||||
values[30] == ENTSOE_NO_VALUE ? "null" : String(values[30], 4).c_str(),
|
||||
values[31] == ENTSOE_NO_VALUE ? "null" : String(values[31], 4).c_str(),
|
||||
values[32] == ENTSOE_NO_VALUE ? "null" : String(values[32], 4).c_str(),
|
||||
values[33] == ENTSOE_NO_VALUE ? "null" : String(values[33], 4).c_str(),
|
||||
values[34] == ENTSOE_NO_VALUE ? "null" : String(values[34], 4).c_str(),
|
||||
values[35] == ENTSOE_NO_VALUE ? "null" : String(values[35], 4).c_str(),
|
||||
values[36] == ENTSOE_NO_VALUE ? "null" : String(values[36], 4).c_str(),
|
||||
values[37] == ENTSOE_NO_VALUE ? "null" : String(values[37], 4).c_str(),
|
||||
min == INT16_MAX ? 0.0 : min,
|
||||
max == INT16_MIN ? 0.0 : max,
|
||||
ts1hr,
|
||||
@@ -368,3 +368,6 @@ uint8_t JsonMqttHandler::getFormat() {
|
||||
bool JsonMqttHandler::publishRaw(String data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void JsonMqttHandler::onMessage(String &topic, String &payload) {
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ public:
|
||||
bool publishSystem(HwTools* hw, PriceService* ps, EnergyAccounting* ea);
|
||||
bool publishRaw(String data);
|
||||
|
||||
void onMessage(String &topic, String &payload);
|
||||
|
||||
uint8_t getFormat();
|
||||
|
||||
private:
|
||||
|
||||
@@ -293,3 +293,6 @@ uint8_t RawMqttHandler::getFormat() {
|
||||
bool RawMqttHandler::publishRaw(String data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void RawMqttHandler::onMessage(String &topic, String &payload) {
|
||||
}
|
||||
|
||||
22
lib/SvelteUi/app/dist/index.js
vendored
22
lib/SvelteUi/app/dist/index.js
vendored
File diff suppressed because one or more lines are too long
@@ -13,12 +13,12 @@
|
||||
|
||||
let config = {};
|
||||
|
||||
function point(v,e) {
|
||||
function point(v) {
|
||||
return {
|
||||
label: fmtnum(v) + 'A',
|
||||
title: (e ? 'Estimated ' : '') + v.toFixed(1) + ' A',
|
||||
title: v.toFixed(1) + ' A',
|
||||
value: isNaN(v) ? 0 : v,
|
||||
color: ampcol(v ? (v)/(max)*100 : 0, e)
|
||||
color: ampcol(v ? (v)/(max)*100 : 0)
|
||||
};
|
||||
};
|
||||
|
||||
@@ -30,8 +30,19 @@
|
||||
points.push(point(i1));
|
||||
}
|
||||
if(u2 > 0) {
|
||||
xTicks.push({ label: 'L2' });
|
||||
points.push(point(i2, i2e));
|
||||
if(i2e) {
|
||||
xTicks.push({ label: 'L2' });
|
||||
points.push({
|
||||
label: 'Not available',
|
||||
labelAngle: -90,
|
||||
title: 'L2 current is not reported by your meter',
|
||||
value: 0,
|
||||
color: '#7c3aedcc'
|
||||
});
|
||||
} else {
|
||||
xTicks.push({ label: 'L2' });
|
||||
points.push(point(i2));
|
||||
}
|
||||
}
|
||||
if(u3 > 0) {
|
||||
xTicks.push({ label: 'L3' });
|
||||
|
||||
@@ -82,9 +82,9 @@
|
||||
<text
|
||||
width="{barWidth - 4}"
|
||||
dominant-baseline="middle"
|
||||
text-anchor="{barWidth < vertSwitch ? 'left' : 'middle'}"
|
||||
text-anchor="{barWidth < vertSwitch || point.labelAngle ? 'left' : 'middle'}"
|
||||
fill="{yScale(point.value) > yScale(0)-labelOffset ? point.color : 'white'}"
|
||||
transform="translate({xScale(i) + barWidth/2} {yScale(point.value) > yScale(0) - labelOffset ? yScale(point.value) - labelOffset : yScale(point.value) + 10}) rotate({barWidth < vertSwitch ? 90 : 0})"
|
||||
transform="translate({xScale(i) + barWidth/2} {yScale(point.value) > yScale(0) - labelOffset ? yScale(point.value) - labelOffset : yScale(point.value) + 10}) rotate({point.labelAngle ? point.labelAngle : barWidth < vertSwitch ? 90 : 0})"
|
||||
|
||||
>{point.label}</text>
|
||||
{#if point.title}
|
||||
|
||||
@@ -11,7 +11,7 @@ export function voltcol(volt) {
|
||||
return '#d90000';
|
||||
};
|
||||
|
||||
export function ampcol(pct, est) {
|
||||
export function ampcol(pct) {
|
||||
let col;
|
||||
if(pct > 90) col = '#d90000';
|
||||
else if(pct > 85) col = '#e32100';
|
||||
@@ -19,7 +19,7 @@ export function ampcol(pct, est) {
|
||||
else if(pct > 75) col = '#dcd800';
|
||||
else col = '#32d900';
|
||||
|
||||
return col+(est?'88':'');
|
||||
return col;
|
||||
};
|
||||
|
||||
export function exportcol(pct) {
|
||||
|
||||
@@ -35,9 +35,12 @@
|
||||
}
|
||||
|
||||
let cc = false;
|
||||
$: {
|
||||
sysinfoStore.subscribe(update => {
|
||||
sysinfo = update;
|
||||
if(update.fwconsent === 1) {
|
||||
cc = !sysinfo.usrcfg;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="grid xl:grid-cols-4 lg:grid-cols-3 md:grid-cols-2">
|
||||
|
||||
@@ -17,21 +17,21 @@ export default defineConfig({
|
||||
plugins: [svelte()],
|
||||
server: {
|
||||
proxy: {
|
||||
"/data.json": "http://192.168.233.244",
|
||||
"/energyprice.json": "http://192.168.233.244",
|
||||
"/dayplot.json": "http://192.168.233.244",
|
||||
"/monthplot.json": "http://192.168.233.244",
|
||||
"/temperature.json": "http://192.168.233.244",
|
||||
"/sysinfo.json": "http://192.168.233.244",
|
||||
"/configuration.json": "http://192.168.233.244",
|
||||
"/tariff.json": "http://192.168.233.244",
|
||||
"/save": "http://192.168.233.244",
|
||||
"/reboot": "http://192.168.233.244",
|
||||
"/configfile": "http://192.168.233.244",
|
||||
"/upgrade": "http://192.168.233.244",
|
||||
"/mqtt-ca": "http://192.168.233.244",
|
||||
"/mqtt-cert": "http://192.168.233.244",
|
||||
"/mqtt-key": "http://192.168.233.244",
|
||||
"/data.json": "http://192.168.233.49",
|
||||
"/energyprice.json": "http://192.168.233.49",
|
||||
"/dayplot.json": "http://192.168.233.49",
|
||||
"/monthplot.json": "http://192.168.233.49",
|
||||
"/temperature.json": "http://192.168.233.49",
|
||||
"/sysinfo.json": "http://192.168.233.49",
|
||||
"/configuration.json": "http://192.168.233.49",
|
||||
"/tariff.json": "http://192.168.233.49",
|
||||
"/save": "http://192.168.233.49",
|
||||
"/reboot": "http://192.168.233.49",
|
||||
"/configfile": "http://192.168.233.49",
|
||||
"/upgrade": "http://192.168.233.49",
|
||||
"/mqtt-ca": "http://192.168.233.49",
|
||||
"/mqtt-cert": "http://192.168.233.49",
|
||||
"/mqtt-key": "http://192.168.233.49",
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -501,7 +501,7 @@ void AmsWebServer::dataJson() {
|
||||
meterState->getL3Voltage(),
|
||||
meterState->getL1Current(),
|
||||
meterState->getL2Current(),
|
||||
meterState->isL2currentEstimated() ? "true" : "false",
|
||||
meterState->isL2currentMissing() ? "true" : "false",
|
||||
meterState->getL3Current(),
|
||||
meterState->getPowerFactor(),
|
||||
meterState->getL1PowerFactor(),
|
||||
|
||||
Reference in New Issue
Block a user