diff --git a/src/AmsToMqttBridge.ino b/src/AmsToMqttBridge.ino
index c84d9635..6f00b570 100644
--- a/src/AmsToMqttBridge.ino
+++ b/src/AmsToMqttBridge.ino
@@ -32,6 +32,7 @@ ADC_MODE(ADC_VCC);
#include "mqtt/JsonMqttHandler.h"
#include "mqtt/RawMqttHandler.h"
#include "mqtt/DomoticzMqttHandler.h"
+#include "mqtt/HomeAssistantMqttHandler.h"
#include "Uptime.h"
@@ -947,6 +948,9 @@ void MQTT_connect() {
config.getDomoticzConfig(domo);
mqttHandler = new DomoticzMqttHandler(mqtt, domo);
break;
+ case 4:
+ mqttHandler = new HomeAssistantMqttHandler(mqtt, mqttConfig.clientId, mqttConfig.publishTopic, &hw);
+ break;
}
if(mqttConfig.ssl) {
diff --git a/src/mqtt/HomeAssistantMqttHandler.cpp b/src/mqtt/HomeAssistantMqttHandler.cpp
new file mode 100644
index 00000000..fc8a1c8f
--- /dev/null
+++ b/src/mqtt/HomeAssistantMqttHandler.cpp
@@ -0,0 +1,272 @@
+#include "HomeAssistantMqttHandler.h"
+#include "hexutils.h"
+#include "Uptime.h"
+#include "web/root/json1_json.h"
+#include "web/root/json2_json.h"
+#include "web/root/json3_json.h"
+#include "web/root/json3pf_json.h"
+#include "web/root/jsonsys_json.h"
+#include "web/root/jsonprices_json.h"
+
+bool HomeAssistantMqttHandler::publish(AmsData* data, AmsData* previousState) {
+ if(topic.isEmpty() || !mqtt->connected())
+ return false;
+
+ if(data->getListType() == 1) {
+ char json[192];
+ snprintf_P(json, sizeof(json), JSON1_JSON,
+ WiFi.macAddress().c_str(),
+ clientId.c_str(),
+ (uint32_t) (millis64()/1000),
+ data->getPackageTimestamp(),
+ hw->getVcc(),
+ hw->getWifiRssi(),
+ hw->getTemperature(),
+ data->getActiveImportPower()
+ );
+ return mqtt->publish(topic, json);
+ } else if(data->getListType() == 2) {
+ char json[384];
+ snprintf_P(json, sizeof(json), JSON2_JSON,
+ WiFi.macAddress().c_str(),
+ clientId.c_str(),
+ (uint32_t) (millis64()/1000),
+ data->getPackageTimestamp(),
+ hw->getVcc(),
+ hw->getWifiRssi(),
+ hw->getTemperature(),
+ data->getListId().c_str(),
+ data->getMeterId().c_str(),
+ data->getMeterModel().c_str(),
+ data->getActiveImportPower(),
+ data->getReactiveImportPower(),
+ data->getActiveExportPower(),
+ data->getReactiveExportPower(),
+ data->getL1Current(),
+ data->getL2Current(),
+ data->getL3Current(),
+ data->getL1Voltage(),
+ data->getL2Voltage(),
+ data->getL3Voltage()
+ );
+ return mqtt->publish(topic, json);
+ } else if(data->getListType() == 3) {
+ if(data->getPowerFactor() == 0) {
+ char json[512];
+ snprintf_P(json, sizeof(json), JSON3_JSON,
+ WiFi.macAddress().c_str(),
+ clientId.c_str(),
+ (uint32_t) (millis64()/1000),
+ data->getPackageTimestamp(),
+ hw->getVcc(),
+ hw->getWifiRssi(),
+ hw->getTemperature(),
+ data->getListId().c_str(),
+ data->getMeterId().c_str(),
+ data->getMeterModel().c_str(),
+ data->getActiveImportPower(),
+ data->getReactiveImportPower(),
+ data->getActiveExportPower(),
+ data->getReactiveExportPower(),
+ data->getL1Current(),
+ data->getL2Current(),
+ data->getL3Current(),
+ data->getL1Voltage(),
+ data->getL2Voltage(),
+ data->getL3Voltage(),
+ data->getActiveImportCounter(),
+ data->getActiveExportCounter(),
+ data->getReactiveImportCounter(),
+ data->getReactiveExportCounter(),
+ data->getMeterTimestamp()
+ );
+ return mqtt->publish(topic, json);
+ } else {
+ char json[768];
+ snprintf_P(json, sizeof(json), JSON3PF_JSON,
+ WiFi.macAddress().c_str(),
+ clientId.c_str(),
+ (uint32_t) (millis64()/1000),
+ data->getPackageTimestamp(),
+ hw->getVcc(),
+ hw->getWifiRssi(),
+ hw->getTemperature(),
+ data->getListId().c_str(),
+ data->getMeterId().c_str(),
+ data->getMeterModel().c_str(),
+ data->getActiveImportPower(),
+ data->getReactiveImportPower(),
+ data->getActiveExportPower(),
+ data->getReactiveExportPower(),
+ data->getL1Current(),
+ data->getL2Current(),
+ data->getL3Current(),
+ data->getL1Voltage(),
+ data->getL2Voltage(),
+ data->getL3Voltage(),
+ data->getPowerFactor(),
+ data->getL1PowerFactor(),
+ data->getL2PowerFactor(),
+ data->getL3PowerFactor(),
+ data->getActiveImportCounter(),
+ data->getActiveExportCounter(),
+ data->getReactiveImportCounter(),
+ data->getReactiveExportCounter(),
+ data->getMeterTimestamp()
+ );
+ return mqtt->publish(topic, json);
+ }
+ }
+ return false;
+}
+
+bool HomeAssistantMqttHandler::publishTemperatures(AmsConfiguration* config, HwTools* hw) {
+ int count = hw->getTempSensorCount();
+ if(count == 0)
+ return false;
+
+ int size = 32 + (count * 26);
+
+ char buf[size];
+ snprintf(buf, 24, "{\"temperatures\":{");
+
+ for(int i = 0; i < count; i++) {
+ TempSensorData* data = hw->getTempSensorData(i);
+ if(data != NULL) {
+ char* pos = buf+strlen(buf);
+ snprintf(pos, 26, "\"%s\":%.2f,",
+ toHex(data->address, 8).c_str(),
+ data->lastRead
+ );
+ data->changed = false;
+ delay(1);
+ }
+ }
+ char* pos = buf+strlen(buf);
+ snprintf(count == 0 ? pos : pos-1, 8, "}}");
+ return mqtt->publish(topic, buf);
+}
+
+bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
+ if(topic.isEmpty() || !mqtt->connected())
+ return false;
+ if(strlen(eapi->getToken()) == 0)
+ return false;
+
+ time_t now = time(nullptr);
+
+ float min1hr, min3hr, min6hr;
+ int8_t min1hrIdx = -1, min3hrIdx = -1, min6hrIdx = -1;
+ float min = INT16_MAX, max = INT16_MIN;
+ float values[24] = {0};
+ for(uint8_t i = 0; i < 24; i++) {
+ float val = eapi->getValueForHour(now, i);
+ values[i] = val;
+
+ if(val == ENTSOE_NO_VALUE) break;
+
+ if(val < min) min = val;
+ if(val > max) max = val;
+
+ if(min1hrIdx == -1 || min1hr > val) {
+ min1hr = val;
+ min1hrIdx = i;
+ }
+
+ if(i >= 2) {
+ i -= 2;
+ float val1 = values[i++];
+ float val2 = values[i++];
+ float val3 = val;
+ if(val1 == ENTSOE_NO_VALUE || val2 == ENTSOE_NO_VALUE || val3 == ENTSOE_NO_VALUE) continue;
+ float val3hr = val1+val2+val3;
+ if(min3hrIdx == -1 || min3hr > val3hr) {
+ min3hr = val3hr;
+ min3hrIdx = i-2;
+ }
+ }
+
+ if(i >= 5) {
+ i -= 5;
+ float val1 = values[i++];
+ float val2 = values[i++];
+ float val3 = values[i++];
+ float val4 = values[i++];
+ float val5 = values[i++];
+ float val6 = val;
+ if(val1 == ENTSOE_NO_VALUE || val2 == ENTSOE_NO_VALUE || val3 == ENTSOE_NO_VALUE || val4 == ENTSOE_NO_VALUE || val5 == ENTSOE_NO_VALUE || val6 == ENTSOE_NO_VALUE) continue;
+ float val6hr = val1+val2+val3+val4+val5+val6;
+ if(min6hrIdx == -1 || min6hr > val6hr) {
+ min6hr = val6hr;
+ min6hrIdx = i-5;
+ }
+ }
+
+ }
+
+ char ts1hr[21];
+ if(min1hrIdx > -1) {
+ time_t ts = now + (SECS_PER_HOUR * min1hrIdx);
+ //Serial.printf("1hr: %d %lu\n", min1hrIdx, ts);
+ tmElements_t tm;
+ breakTime(ts, tm);
+ sprintf(ts1hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
+ }
+ char ts3hr[21];
+ if(min3hrIdx > -1) {
+ time_t ts = now + (SECS_PER_HOUR * min3hrIdx);
+ //Serial.printf("3hr: %d %lu\n", min3hrIdx, ts);
+ tmElements_t tm;
+ breakTime(ts, tm);
+ sprintf(ts3hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
+ }
+ char ts6hr[21];
+ if(min6hrIdx > -1) {
+ time_t ts = now + (SECS_PER_HOUR * min6hrIdx);
+ //Serial.printf("6hr: %d %lu\n", min6hrIdx, ts);
+ tmElements_t tm;
+ breakTime(ts, tm);
+ sprintf(ts6hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
+ }
+
+ char json[384];
+ snprintf_P(json, sizeof(json), 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],
+ min == INT16_MAX ? 0.0 : min,
+ max == INT16_MIN ? 0.0 : max,
+ ts1hr,
+ ts3hr,
+ ts6hr
+ );
+ return mqtt->publish(topic, json);
+}
+
+bool HomeAssistantMqttHandler::publishSystem(HwTools* hw) {
+ if(init || topic.isEmpty() || !mqtt->connected())
+ return false;
+ if(!topic.endsWith("/")) topic += "/";
+
+ char json[192];
+ snprintf_P(json, sizeof(json), JSONSYS_JSON,
+ WiFi.macAddress().c_str(),
+ clientId.c_str(),
+ (uint32_t) (millis64()/1000),
+ hw->getVcc(),
+ hw->getWifiRssi(),
+ hw->getTemperature()
+ );
+ init = mqtt->publish(topic, json);
+ return init;
+}
diff --git a/src/mqtt/HomeAssistantMqttHandler.h b/src/mqtt/HomeAssistantMqttHandler.h
new file mode 100644
index 00000000..14c652b2
--- /dev/null
+++ b/src/mqtt/HomeAssistantMqttHandler.h
@@ -0,0 +1,24 @@
+#ifndef _HOMEASSISTANTMQTTHANDLER_H
+#define _HOMEASSISTANTMQTTHANDLER_H
+
+#include "AmsMqttHandler.h"
+
+class HomeAssistantMqttHandler : public AmsMqttHandler {
+public:
+ HomeAssistantMqttHandler(MQTTClient* mqtt, const char* clientId, const char* topic, HwTools* hw) : AmsMqttHandler(mqtt) {
+ this->clientId = clientId;
+ this->topic = String(topic);
+ this->hw = hw;
+ };
+ bool publish(AmsData* data, AmsData* previousState);
+ bool publishTemperatures(AmsConfiguration*, HwTools*);
+ bool publishPrices(EntsoeApi*);
+ bool publishSystem(HwTools*);
+
+private:
+ String clientId;
+ String topic;
+ HwTools* hw;
+ bool init = false;
+};
+#endif
diff --git a/web/mqtt.html b/web/mqtt.html
index 9fefb1d7..ce128e01 100644
--- a/web/mqtt.html
+++ b/web/mqtt.html
@@ -62,6 +62,7 @@
+