From e7c25fafda16fa1b4bab605bb3ff9fc35b3bd6e9 Mon Sep 17 00:00:00 2001 From: Gunnar Skjold Date: Wed, 28 Sep 2022 20:13:03 +0200 Subject: [PATCH] Prices and realtime HA sensors --- src/AmsToMqttBridge.ino | 4 +- src/mqtt/AmsMqttHandler.h | 2 +- src/mqtt/DomoticzMqttHandler.cpp | 2 +- src/mqtt/DomoticzMqttHandler.h | 2 +- src/mqtt/HomeAssistantMqttHandler.cpp | 71 +++++++++++++++++++++------ src/mqtt/HomeAssistantMqttHandler.h | 4 +- src/mqtt/HomeAssistantStatic.h | 71 ++++++++++++++++++++++++--- src/mqtt/JsonMqttHandler.cpp | 2 +- src/mqtt/JsonMqttHandler.h | 2 +- src/mqtt/RawMqttHandler.cpp | 2 +- src/mqtt/RawMqttHandler.h | 2 +- web/realtime.json | 20 ++++++++ 12 files changed, 149 insertions(+), 35 deletions(-) create mode 100644 web/realtime.json diff --git a/src/AmsToMqttBridge.ino b/src/AmsToMqttBridge.ino index 36b3919a..1ce6b331 100644 --- a/src/AmsToMqttBridge.ino +++ b/src/AmsToMqttBridge.ino @@ -594,7 +594,7 @@ void loop() { } if(now - lastSysupdate > 10000) { 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; } @@ -1379,7 +1379,7 @@ void MQTT_connect() { if (Debug.isActive(RemoteDebug::INFO)) debugI("Successfully connected to MQTT!"); if(mqttHandler != NULL) { - mqttHandler->publishSystem(&hw); + mqttHandler->publishSystem(&hw, eapi, &ea); } // Subscribe to the chosen MQTT topic, if set in configuration diff --git a/src/mqtt/AmsMqttHandler.h b/src/mqtt/AmsMqttHandler.h index e5364e4e..4ec71634 100644 --- a/src/mqtt/AmsMqttHandler.h +++ b/src/mqtt/AmsMqttHandler.h @@ -19,7 +19,7 @@ public: virtual bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea); virtual bool publishTemperatures(AmsConfiguration*, HwTools*); virtual bool publishPrices(EntsoeApi* eapi); - virtual bool publishSystem(HwTools*); + virtual bool publishSystem(HwTools*, EntsoeApi*, EnergyAccounting*); protected: MQTTClient* mqtt; diff --git a/src/mqtt/DomoticzMqttHandler.cpp b/src/mqtt/DomoticzMqttHandler.cpp index 11975d34..d173e0dd 100644 --- a/src/mqtt/DomoticzMqttHandler.cpp +++ b/src/mqtt/DomoticzMqttHandler.cpp @@ -71,6 +71,6 @@ bool DomoticzMqttHandler::publishPrices(EntsoeApi* eapi) { return false; } -bool DomoticzMqttHandler::publishSystem(HwTools* hw) { +bool DomoticzMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea) { return false; } diff --git a/src/mqtt/DomoticzMqttHandler.h b/src/mqtt/DomoticzMqttHandler.h index 145bffe5..2c1d68b0 100644 --- a/src/mqtt/DomoticzMqttHandler.h +++ b/src/mqtt/DomoticzMqttHandler.h @@ -12,7 +12,7 @@ public: bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea); bool publishTemperatures(AmsConfiguration*, HwTools*); bool publishPrices(EntsoeApi*); - bool publishSystem(HwTools*); + bool publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea); private: DomoticzConfig config; diff --git a/src/mqtt/HomeAssistantMqttHandler.cpp b/src/mqtt/HomeAssistantMqttHandler.cpp index f8678796..544ece33 100644 --- a/src/mqtt/HomeAssistantMqttHandler.cpp +++ b/src/mqtt/HomeAssistantMqttHandler.cpp @@ -9,6 +9,7 @@ #include "web/root/jsonsys_json.h" #include "web/root/jsonprices_json.h" #include "web/root/hadiscover_json.h" +#include "web/root/realtime_json.h" bool HomeAssistantMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea) { if(topic.isEmpty() || !mqtt->connected()) @@ -32,7 +33,7 @@ bool HomeAssistantMqttHandler::publish(AmsData* data, AmsData* previousState, En snprintf_P(json, BufferSize, HA1_JSON, data->getActiveImportPower() ); - return mqtt->publish(topic + "/power", json); + mqtt->publish(topic + "/power", json); } else if(data->getListType() >= 2) { // publish power counts and volts/amps snprintf_P(json, BufferSize, HA3_JSON, data->getListId().c_str(), @@ -53,9 +54,31 @@ bool HomeAssistantMqttHandler::publish(AmsData* data, AmsData* previousState, En data->getPowerFactor() == 0 ? 1 : data->getL2PowerFactor(), data->getPowerFactor() == 0 ? 1 : data->getL3PowerFactor() ); - return mqtt->publish(topic + "/power", json); + mqtt->publish(topic + "/power", json); } - return false; + + String peaks = ""; + for(uint8_t i = 1; i <= ea->getConfig()->hours; i++) { + if(!peaks.isEmpty()) peaks += ","; + peaks += String(ea->getPeak(i)); + } + 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) { @@ -187,10 +210,10 @@ bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) { ts3hr, 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()){ sequence = 0; return false; @@ -218,26 +241,42 @@ bool HomeAssistantMqttHandler::publishSystem(HwTools* hw) { String haUrl = "http://" + haUID + ".local/"; // Could this be necessary? haUID.replace("-", "_"); - for(int i=0;igetCurrency()); + } + if(strncmp(sensor.path, "peaks[", 6) == 0) { + if(peaks >= ea->getConfig()->hours) continue; + peaks++; + } snprintf_P(json, BufferSize, HADISCOVER_JSON, - FPSTR(HA_NAMES[i]), - topic.c_str(), FPSTR(HA_TOPICS[i]), - haUID.c_str(), FPSTR(HA_PARAMS[i]), - haUID.c_str(), FPSTR(HA_PARAMS[i]), - FPSTR(HA_UOM[i]), - FPSTR(HA_PARAMS[i]), - FPSTR(HA_DEVCL[i]), + FPSTR(sensor.name), + topic.c_str(), FPSTR(sensor.topic), + haUID.c_str(), uid.c_str(), + haUID.c_str(), uid.c_str(), + uom.c_str(), + FPSTR(sensor.path), + FPSTR(sensor.devcl), haUID.c_str(), haName.c_str(), haModel.c_str(), VERSION, haManuf.c_str(), haUrl.c_str(), - strlen_P(HA_STACL[i]) > 0 ? ", \"stat_cla\" :" : "", - strlen_P(HA_STACL[i]) > 0 ? (char *) FPSTR(HA_STACL[i]) : "" + strlen_P(sensor.stacl) > 0 ? ", \"stat_cla\" :" : "", + 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; } if(listType>0) sequence++; diff --git a/src/mqtt/HomeAssistantMqttHandler.h b/src/mqtt/HomeAssistantMqttHandler.h index d4b8f50e..05675eee 100644 --- a/src/mqtt/HomeAssistantMqttHandler.h +++ b/src/mqtt/HomeAssistantMqttHandler.h @@ -13,11 +13,9 @@ public: bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea); bool publishTemperatures(AmsConfiguration*, HwTools*); bool publishPrices(EntsoeApi*); - bool publishSystem(HwTools*); + bool publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea); private: - static const uint8_t sensors = 17; - String haTopic = "homeassistant/sensor/"; String haName = "AMS reader"; diff --git a/src/mqtt/HomeAssistantStatic.h b/src/mqtt/HomeAssistantStatic.h index 5f5b5b26..d60361ab 100644 --- a/src/mqtt/HomeAssistantStatic.h +++ b/src/mqtt/HomeAssistantStatic.h @@ -3,12 +3,69 @@ #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"}; -const char* HA_NAMES[17] PROGMEM = {"Status", "Supply volt", "Temperature", "Active import", "Reactive import", "Active export", "Reactive export", "L1 current", "L2 current", "L3 current", - "L1 voltage", "L2 voltage", "L3 voltage", "Accumulated active import", "Accumulated active export", "Accumulated reactive import", "Accumulated reactive export"}; -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* HA_UOM[17] PROGMEM = {"dBm", "V", "C", "W", "W", "W", "W", "A", "A", "A", "V", "V", "V", "kWh", "kWh", "kWh", "kWh"}; -const char* HA_DEVCL[17] PROGMEM = {"signal_strength", "voltage", "temperature", "power", "power", "power", "power", "current", "current", "current", "voltage", "voltage", "voltage", "energy", "energy", "energy", "energy"}; -const char* HA_STACL[17] PROGMEM = {"", "", "", "\"measurement\"", "\"measurement\"", "\"measurement\"", "\"measurement\"", "", "", "", "", "", "", "\"total_increasing\"", "\"total_increasing\"", "\"total_increasing\"", "\"total_increasing\""}; +struct HomeAssistantSensor { + char* name; + char* topic; + char* path; + char* uom; + char* devcl; + char* stacl; +}; + + +const uint8_t HA_SENSOR_COUNT PROGMEM = 50; +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\""}, + {"Reactive import", "/power", "Q", "VAr", "reactive_power", "\"measurement\""}, + {"Active export", "/power", "PO", "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\""}, + {"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 diff --git a/src/mqtt/JsonMqttHandler.cpp b/src/mqtt/JsonMqttHandler.cpp index 132d8e32..6329b389 100644 --- a/src/mqtt/JsonMqttHandler.cpp +++ b/src/mqtt/JsonMqttHandler.cpp @@ -271,7 +271,7 @@ bool JsonMqttHandler::publishPrices(EntsoeApi* eapi) { 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()) return false; diff --git a/src/mqtt/JsonMqttHandler.h b/src/mqtt/JsonMqttHandler.h index b799bbbe..db3e12bc 100644 --- a/src/mqtt/JsonMqttHandler.h +++ b/src/mqtt/JsonMqttHandler.h @@ -13,7 +13,7 @@ public: bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea); bool publishTemperatures(AmsConfiguration*, HwTools*); bool publishPrices(EntsoeApi*); - bool publishSystem(HwTools*); + bool publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea); private: String clientId; diff --git a/src/mqtt/RawMqttHandler.cpp b/src/mqtt/RawMqttHandler.cpp index 48f48a3f..c6763ed3 100644 --- a/src/mqtt/RawMqttHandler.cpp +++ b/src/mqtt/RawMqttHandler.cpp @@ -211,7 +211,7 @@ bool RawMqttHandler::publishPrices(EntsoeApi* eapi) { return true; } -bool RawMqttHandler::publishSystem(HwTools* hw) { +bool RawMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea) { if(topic.isEmpty() || !mqtt->connected()) return false; diff --git a/src/mqtt/RawMqttHandler.h b/src/mqtt/RawMqttHandler.h index 091103cb..737b72a3 100644 --- a/src/mqtt/RawMqttHandler.h +++ b/src/mqtt/RawMqttHandler.h @@ -12,7 +12,7 @@ public: bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea); bool publishTemperatures(AmsConfiguration*, HwTools*); bool publishPrices(EntsoeApi*); - bool publishSystem(HwTools*); + bool publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea); private: String topic; diff --git a/web/realtime.json b/web/realtime.json new file mode 100644 index 00000000..06ca2d73 --- /dev/null +++ b/web/realtime.json @@ -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 + } +} \ No newline at end of file