Merge branch 'dev-v1.3.0' of github.com:gskjold/AmsToMqttBridge into dev-v1.3.0

This commit is contained in:
Gunnar Skjold
2020-05-03 16:49:28 +02:00
14 changed files with 490 additions and 8 deletions

View File

@@ -177,6 +177,7 @@ void AmsConfiguration::clearMqtt() {
setMqttSubscribeTopic("");
setMqttUser("");
setMqttPassword("");
setMqttSsl(false);
}
void AmsConfiguration::setMqttChanged() {
@@ -191,7 +192,6 @@ void AmsConfiguration::ackMqttChange() {
mqttChanged = false;
}
byte AmsConfiguration::getAuthSecurity() {
return config.authSecurity;
}
@@ -278,6 +278,55 @@ void AmsConfiguration::setDebugLevel(uint8_t debugLevel) {
config.debugLevel = debugLevel;
}
int AmsConfiguration::getDomoELIDX() {
return config.domoELIDX;
}
int AmsConfiguration::getDomoVL1IDX() {
return config.domoVL1IDX;
}
int AmsConfiguration::getDomoVL2IDX() {
return config.domoVL2IDX;
}
int AmsConfiguration::getDomoVL3IDX() {
return config.domoVL3IDX;
}
int AmsConfiguration::getDomoCL1IDX() {
return config.domoCL1IDX;
}
void AmsConfiguration::setDomoELIDX(int domoELIDX) {
domoChanged |= config.domoELIDX != domoELIDX;
config.domoELIDX = domoELIDX;
}
void AmsConfiguration::setDomoVL1IDX(int domoVL1IDX) {
domoChanged |= config.domoVL1IDX != domoVL1IDX;
config.domoVL1IDX = domoVL1IDX;
}
void AmsConfiguration::setDomoVL2IDX(int domoVL2IDX) {
domoChanged |= config.domoVL2IDX != domoVL2IDX;
config.domoVL2IDX = domoVL2IDX;
}
void AmsConfiguration::setDomoVL3IDX(int domoVL3IDX) {
domoChanged |= config.domoVL3IDX != domoVL3IDX;
config.domoVL3IDX = domoVL3IDX;
}
void AmsConfiguration::setDomoCL1IDX(int domoCL1IDX) {
domoChanged |= config.domoCL1IDX != domoCL1IDX;
config.domoCL1IDX = domoCL1IDX;
}
void AmsConfiguration::clearDomo() {
setDomoELIDX(0);
setDomoVL1IDX(0);
setDomoVL2IDX(0);
setDomoVL3IDX(0);
setDomoCL1IDX(0);
}
bool AmsConfiguration::pinUsed(uint8_t pin) {
if(pin == 0xFF)
return false;
@@ -410,6 +459,13 @@ void AmsConfiguration::setVccBootLimit(double vccBootLimit) {
else
config.vccBootLimit = max(2.5, min(vccBootLimit, 3.5)) * 10;
}
bool AmsConfiguration::isDomoChanged() {
return domoChanged;
}
void AmsConfiguration::ackDomoChange() {
domoChanged = false;
}
bool AmsConfiguration::hasConfig() {
if(configVersion == 0) {
@@ -628,6 +684,29 @@ bool AmsConfiguration::loadConfig81(int address) {
address += readInt(address, &i);
setDebugLevel(i);
bool domo = false;
address += readBool(address, &domo);
if(domo) {
int domoELIDX;
address += readInt(address, &domoELIDX);
setDomoELIDX(domoELIDX);
int domoVL1IDX;
address += readInt(address, &domoVL1IDX);
setDomoVL1IDX(domoVL1IDX);
int domoVL2IDX;
address += readInt(address, &domoVL2IDX);
setDomoVL2IDX(domoVL2IDX);
int domoVL3IDX;
address += readInt(address, &domoVL3IDX);
setDomoVL3IDX(domoVL3IDX);
int domoCL1IDX;
address += readInt(address, &domoCL1IDX);
setDomoCL1IDX(domoCL1IDX);
} else {
clearDomo();
}
ackWifiChange();
return true;
@@ -730,5 +809,13 @@ void AmsConfiguration::print(Print* debugger)
debugger->printf("AP pin: %i\r\n", this->getApPin());
debugger->printf("Temperature pin: %i\r\n", this->getTempSensorPin());
if(this->getDomoELIDX() > 0) {
debugger->printf("Domoticz ELIDX: %i\r\n", this->getDomoELIDX());
debugger->printf("Domoticz VL1IDX: %i\r\n", this->getDomoVL1IDX());
debugger->printf("Domoticz VL2IDX: %i\r\n", this->getDomoVL2IDX());
debugger->printf("Domoticz VL3IDX: %i\r\n", this->getDomoVL3IDX());
debugger->printf("Domoticz CL1IDX: %i\r\n", this->getDomoCL1IDX());
}
debugger->println("-----------------------------------------------");
}

View File

@@ -45,6 +45,12 @@ struct ConfigObject {
uint8_t vccPin;
uint16_t vccMultiplier;
uint8_t vccBootLimit;
int domoELIDX;
int domoVL1IDX;
int domoVL2IDX;
int domoVL3IDX;
int domoCL1IDX;
};
class AmsConfiguration {
@@ -154,7 +160,23 @@ public:
void setVccBootLimit(double vccBootLimit);
void print(Print* debugger);
int getDomoELIDX();
int getDomoVL1IDX();
int getDomoVL2IDX();
int getDomoVL3IDX();
int getDomoCL1IDX();
double getDomoEnergy();
void setDomoELIDX(int domoELIDX);
void setDomoVL1IDX(int domoVL1IDX);
void setDomoVL2IDX(int domoVL2IDX);
void setDomoVL3IDX(int domoVL3IDX);
void setDomoCL1IDX(int domoCL1IDX);
void clearDomo();
bool isDomoChanged();
void ackDomoChange();
protected:
private:
@@ -198,9 +220,15 @@ private:
0xFF, // Temp sensor
0xFF, // Vcc
100, // Multiplier
0 // Boot limit
0, // Boot limit
//Domoticz
0, // ELIDX
0, // VL1IDX
0, // VL2IDX
0, // VL3IDX
0 // CL1IDX
};
bool wifiChanged, mqttChanged;
bool wifiChanged, mqttChanged, domoChanged;
const int EEPROM_SIZE = 2048;
const int EEPROM_CHECK_SUM = 82; // Used to check if config is stored. Change if structure changes

View File

@@ -258,6 +258,9 @@ unsigned long lastSuccessfulRead = 0;
unsigned long lastErrorBlink = 0;
int lastError = 0;
// domoticz energy init
double energy = -1.0;
void loop() {
Debug.handle();
unsigned long now = millis();
@@ -514,6 +517,156 @@ void readHanPort() {
String msg;
serializeJson(json, msg);
mqtt.publish(config.getMqttPublishTopic(), msg.c_str());
//
// Start DOMOTICZ
//
} else if(config.getMqttPayloadFormat() == 3) {
//
// This part is also publishing standard json message for now. May be removed.
//
StaticJsonDocument<512> json;
hanToJson(json, data, hw, temperature);
if (Debug.isActive(RemoteDebug::INFO)) {
debugI("Sending data to MQTT");
if (Debug.isActive(RemoteDebug::DEBUG)) {
serializeJsonPretty(json, Debug);
}
}
String msg;
serializeJson(json, msg);
mqtt.publish(config.getMqttPublishTopic(), msg.c_str()); // keep for now, this is identical to option 0.
//
// Special MQTT messages for DOMOTIZ (https://www.domoticz.com/wiki/MQTT)
// -All messages should be published to topic "domoticz/in"
//
// message msg_PE : send active power and and cumulative energy consuption to virtual meter "Electricity (instant and counter)"
//
// /json.htm?type=command&param=udevice&idx=IDX&nvalue=0&svalue=POWER;ENERGY
//
// MQTT sample message: {"command": "udevice", "idx" : IDX , "nvalue" : 0, "svalue" : "POWER;ENERGY"}
// IDX = id of your device (This number can be found in the devices tab in the column "IDX")
// POWER = current power (Watt)
// ENERGY = cumulative energy in Watt-hours (Wh) This is an incrementing counter.
// (if you choose as type "Energy read : Computed", this is just a "dummy" counter, not updatable because it's the result of DomoticZ calculs from POWER)
//
// message msg_V1 : send Voltage of L1 to virtual Voltage meter
//
// /json.htm?type=command&param=udevice&idx=IDX&nvalue=0&svalue=VOLTAGE
//
// MQTT sample message: {"command": "udevice", "idx" : IDX , "nvalue" : 0, "svalue" : "VOLTAGE"}
// IDX = id of your device (This number can be found in the devices tab in the column "IDX")
// VOLTAGE = Voltage (V)
//
int idx1 = config.getDomoELIDX();
if (idx1 > 0) {
String PowerEnergy;
int p;
// double energy = config.getDomoEnergy();
double tmp_energy;
StaticJsonDocument<200> json_PE;
p = data.getActiveImportPower();
// cumulative energy is given only once pr hour. check if value is different from 0 and store last valid value on global variable.
tmp_energy = data.getActiveImportCounter();
if (tmp_energy > 1.0) energy = tmp_energy;
// power_unit: watt, energy_unit: watt*h. Stored as kwh, need watth
PowerEnergy = String((double) p/1.0) + ";" + String((double) energy*1000.0) ;
json_PE["command"] = "udevice";
json_PE["idx"] = idx1;
json_PE["nvalue"] = 0;
json_PE["svalue"] = PowerEnergy;
// Stringify the json
String msg_PE;
serializeJson(json_PE, msg_PE);
// publish power data directly to domoticz/in, but only after first reading of total power, once an hour... . (otherwise total consumtion will be wrong.)
if (energy > 0.0 ) mqtt.publish("domoticz/in", msg_PE.c_str());
}
int idxu1 =config.getDomoVL1IDX();
if (idxu1 > 0){
StaticJsonDocument<200> json_u1;
double u1;
//
// prepare message msg_u1 for virtual Voltage meter"
//
u1 = data.getL1Voltage();
if (u1 > 0.1){
json_u1["command"] = "udevice";
json_u1["idx"] = idxu1;
json_u1["nvalue"] = 0;
json_u1["svalue"] = String(u1);
// Stringify the json
String msg_u1;
serializeJson(json_u1, msg_u1);
// publish power data directly to domoticz/in
mqtt.publish("domoticz/in", msg_u1.c_str());
}
}
int idxu2 =config.getDomoVL2IDX();
if (idxu2 > 0){
StaticJsonDocument<200> json_u2;
double u2;
//
// prepare message msg_u2 for virtual Voltage meter"
//
u2 = data.getL2Voltage();
if (u2 > 0.1){
json_u2["command"] = "udevice";
json_u2["idx"] = idxu2;
json_u2["nvalue"] = 0;
json_u2["svalue"] = String(u2);
// Stringify the json
String msg_u2;
serializeJson(json_u2, msg_u2);
// publish power data directly to domoticz/in
mqtt.publish("domoticz/in", msg_u2.c_str());
}
}
int idxu3 =config.getDomoVL3IDX();
if (idxu3 > 0){
StaticJsonDocument<200> json_u3;
double u3;
//
// prepare message msg_u3 for virtual Voltage meter"
//
u3 = data.getL3Voltage();
if (u3 > 0.1){
json_u3["command"] = "udevice";
json_u3["idx"] = idxu3;
json_u3["nvalue"] = 0;
json_u3["svalue"] = String(u3);
// Stringify the json
String msg_u3;
serializeJson(json_u3, msg_u3);
// publish power data directly to domoticz/in
mqtt.publish("domoticz/in", msg_u3.c_str());
}
}
int idxi1 =config.getDomoCL1IDX();
if (idxi1 > 0){
StaticJsonDocument<200> json_i1;
double i1, i2, i3;
String Ampere3;
//
// prepare message msg_i1 for virtual Current/Ampere 3phase mater"
//
i1 = data.getL1Current();
i2 = data.getL2Current();
i3 = data.getL3Current();
Ampere3 = String(i1) + ";" + String(i2) + ";" + String(i3) ;
json_i1["command"] = "udevice";
json_i1["idx"] = idxi1;
json_i1["nvalue"] = 0;
json_i1["svalue"] = Ampere3;
// Stringify the json
String msg_i1;
serializeJson(json_i1, msg_i1);
// publish power data directly to domoticz/in
if (i1 > 0.0) mqtt.publish("domoticz/in", msg_i1.c_str());
}
//
// End DOMOTICZ
//
} else if(config.getMqttPayloadFormat() == 1 || config.getMqttPayloadFormat() == 2) {
mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/dlms/timestamp", String(data.getPackageTimestamp()));
switch(data.getListType()) {

View File

@@ -9,6 +9,7 @@
#include "root/configwifi_html.h"
#include "root/configmqtt_html.h"
#include "root/configweb_html.h"
#include "root/configdomoticz_html.h"
#include "root/configsystem_html.h"
#include "root/restartwait_html.h"
#include "root/boot_css.h"
@@ -34,6 +35,7 @@ void AmsWebServer::setup(AmsConfiguration* config, MQTTClient* mqtt) {
server.on("/config-wifi", HTTP_GET, std::bind(&AmsWebServer::configWifiHtml, this));
server.on("/config-mqtt", HTTP_GET, std::bind(&AmsWebServer::configMqttHtml, this));
server.on("/config-web", HTTP_GET, std::bind(&AmsWebServer::configWebHtml, this));
server.on("/config-domoticz",HTTP_GET, std::bind(&AmsWebServer::configDomoticzHtml, this));
server.on("/boot.css", HTTP_GET, std::bind(&AmsWebServer::bootCss, this));
server.on("/gaugemeter.js", HTTP_GET, std::bind(&AmsWebServer::gaugemeterJs, this));
server.on("/data.json", HTTP_GET, std::bind(&AmsWebServer::dataJson, this));
@@ -268,7 +270,7 @@ void AmsWebServer::configMqttHtml() {
html.replace("${config.mqttUser}", config->getMqttUser());
html.replace("${config.mqttPassword}", config->getMqttPassword());
html.replace("${config.mqttPayloadFormat}", String(config->getMqttPayloadFormat()));
for(int i = 0; i<3; i++) {
for(int i = 0; i<4; i++) {
html.replace("${config.mqttPayloadFormat" + String(i) + "}", config->getMqttPayloadFormat() == i ? "selected" : "");
}
@@ -296,6 +298,38 @@ void AmsWebServer::configMqttHtml() {
server.send(200, "text/html", html);
}
void AmsWebServer::configDomoticzHtml() {
printD("Serving /config/domoticz.html over http...");
if(!checkSecurity(1))
return;
String html = String((const __FlashStringHelper*) CONFIGDOMOTICZ_HTML);
html.replace("${version}", VERSION);
if(WiFi.getMode() != WIFI_AP) {
html.replace("boot.css", BOOTSTRAP_URL);
}
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
html.replace("${config.domo}", config->getDomoELIDX() <= 0 ? "" : "checked");
if(config->getDomoELIDX() > 0){ html.replace("${config.domoELIDX}", String(config->getDomoELIDX()));
} else { html.replace("${config.domoELIDX}", ""); }
if(config->getDomoVL1IDX() > 0){ html.replace("${config.domoVL1IDX}", String(config->getDomoVL1IDX()));
} else { html.replace("${config.domoVL1IDX}", ""); }
if(config->getDomoVL2IDX() > 0){ html.replace("${config.domoVL2IDX}", String(config->getDomoVL2IDX()));
} else { html.replace("${config.domoVL2IDX}", ""); }
if(config->getDomoVL3IDX() > 0){ html.replace("${config.domoVL3IDX}", String(config->getDomoVL3IDX()));
} else { html.replace("${config.domoVL3IDX}", ""); }
if(config->getDomoCL1IDX() > 0){ html.replace("${config.domoCL1IDX}", String(config->getDomoCL1IDX()));
} else { html.replace("${config.domoCL1IDX}", ""); }
server.setContentLength(html.length());
server.send(200, "text/html", html);
}
void AmsWebServer::configWebHtml() {
printD("Serving /config-web.html over http...");
@@ -471,6 +505,18 @@ void AmsWebServer::dataJson() {
json.createNestedObject("mqtt");
json["mqtt"]["lastError"] = (int) mqtt->lastError();
String domoStatus;
if(String(config->getDomoELIDX()).isEmpty()) {
domoStatus = "secondary";
} else if(mqtt->connected() && config->getMqttPayloadFormat() == 3 && config->getDomoELIDX() > 0) {
domoStatus = "success";
} else if(mqtt->lastError() == 0) {
domoStatus = "warning";
} else {
domoStatus = "danger";
}
json["status"]["domo"] = domoStatus;
serializeJson(json, jsonStr);
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
@@ -617,6 +663,20 @@ void AmsWebServer::handleSave() {
}
}
if(server.hasArg("domoConfig") && server.arg("domoConfig") == "true") {
if(server.hasArg("domo") && server.arg("domo") == "true") {
config->setDomoELIDX(server.arg("domoELIDX").toInt());
config->setDomoVL1IDX(server.arg("domoVL1IDX").toInt());
config->setDomoVL2IDX(server.arg("domoVL2IDX").toInt());
config->setDomoVL3IDX(server.arg("domoVL3IDX").toInt());
config->setDomoCL1IDX(server.arg("domoCL1IDX").toInt());
} else {
config->clearDomo();
}
}
if(server.hasArg("authConfig") && server.arg("authConfig") == "true") {
config->setAuthSecurity((byte)server.arg("authSecurity").toInt());
if(config->getAuthSecurity() > 0) {

View File

@@ -61,6 +61,7 @@ private:
void configWifiHtml();
void configMqttHtml();
void configWebHtml();
void configDomoticzHtml();
void bootCss();
void gaugemeterJs();
void dataJson();