added mqtt_nestedjson

This commit is contained in:
Paul
2020-02-22 11:37:55 +01:00
parent 97aaff07ef
commit 9eda49b46a
5 changed files with 434 additions and 394 deletions

View File

@@ -582,13 +582,34 @@ void publishSensorValues() {
bool hasdata = false;
char buffer[128] = {0}; // temp string buffer
// if we're not using nested JSON, send each sensor out seperately
if (!myESP.mqttUseNestedJson()) {
for (uint8_t i = 0; i < EMSESP_Settings.dallas_sensors; i++) {
float sensorValue = ds18.getValue(i);
if (sensorValue != DS18_DISCONNECTED) {
hasdata = true;
char topic[30]; // sensors{1-n}
strlcpy(topic, TOPIC_EXTERNAL_SENSORS, sizeof(topic)); // create topic
strlcat(topic, _int_to_char(buffer, i + 1), sizeof(topic));
sensors[PAYLOAD_EXTERNAL_SENSOR_ID] = ds18.getDeviceID(buffer, i); // add ID
sensors[PAYLOAD_EXTERNAL_SENSOR_TEMP] = sensorValue; // add temp value
char data[100] = {0};
serializeJson(doc, data, sizeof(data)); // convert to string
myESP.mqttPublish(topic, data); // and publish
}
}
if (hasdata) {
myDebugLog("Publishing external sensor data via MQTT");
}
return; // exit
}
// see if the sensor values have changed, if so send it on
for (uint8_t i = 0; i < EMSESP_Settings.dallas_sensors; i++) {
float sensorValue = ds18.getValue(i);
if (sensorValue != DS18_DISCONNECTED) {
hasdata = true;
// create a nested object
// https://github.com/proddy/EMS-ESP/issues/327
// create a nested object - https://github.com/proddy/EMS-ESP/issues/327
char sensorID[10]; // sensor{1-n}
strlcpy(sensorID, PAYLOAD_EXTERNAL_SENSOR_NUM, sizeof(sensorID));
strlcat(sensorID, _int_to_char(buffer, i + 1), sizeof(sensorID));
@@ -598,343 +619,313 @@ void publishSensorValues() {
}
}
/* test code - https://github.com/proddy/EMS-ESP/issues/326
float sensorValue = 23.43;
hasdata = true;
char sensorID[10]; // sensor{1-n}
for (uint8_t i = 0; i < 10; i++) {
strlcpy(sensorID, PAYLOAD_EXTERNAL_SENSOR_NUM, sizeof(sensorID));
strlcat(sensorID, _int_to_char(buffer, i + 1), sizeof(sensorID));
JsonObject dataSensor = sensors.createNestedObject(sensorID);
dataSensor[PAYLOAD_EXTERNAL_SENSOR_ID] = "28D45A79A2190310";
dataSensor[PAYLOAD_EXTERNAL_SENSOR_TEMP] = sensorValue;
}
*/
if (!hasdata) {
return; // nothing to send
}
char data[DS18_MQTT_PAYLOAD_MAXSIZE] = {0};
serializeJson(doc, data, sizeof(data));
myDebugLog("Publishing external sensor data via MQTT");
myESP.mqttPublish(TOPIC_EXTERNAL_SENSORS, data);
if (hasdata) {
myDebugLog("Publishing external sensor data via MQTT");
}
}
// send values via MQTT
// a json object is created for each device type
void publishEMSValues(bool force) {
// don't send if MQTT is not connected or EMS bus is not connected
if (!myESP.isMQTTConnected() || (!ems_getBusConnected()) || (EMSESP_Settings.publish_time == -1)) {
return;
}
// publish Boiler data via MQTT
void publishEMSValues_boiler() {
char s[20] = {0}; // for formatting strings
StaticJsonDocument<MQTT_MAX_PAYLOAD_SIZE> doc;
char data[MQTT_MAX_PAYLOAD_SIZE] = {0};
JsonObject rootBoiler = doc.to<JsonObject>();
// do we have boiler changes?
if (ems_getBoilerEnabled() && (ems_Device_has_flags(EMS_DEVICE_UPDATE_FLAG_BOILER) || force)) {
JsonObject rootBoiler = doc.to<JsonObject>();
if (EMS_Boiler.wWComfort == EMS_VALUE_UBAParameterWW_wwComfort_Hot) {
rootBoiler["wWComfort"] = "Hot";
} else if (EMS_Boiler.wWComfort == EMS_VALUE_UBAParameterWW_wwComfort_Eco) {
rootBoiler["wWComfort"] = "Eco";
} else if (EMS_Boiler.wWComfort == EMS_VALUE_UBAParameterWW_wwComfort_Intelligent) {
rootBoiler["wWComfort"] = "Intelligent";
}
if (EMS_Boiler.wWSelTemp != EMS_VALUE_INT_NOTSET)
rootBoiler["wWSelTemp"] = EMS_Boiler.wWSelTemp;
if (EMS_Boiler.wWDesinfectTemp != EMS_VALUE_INT_NOTSET)
rootBoiler["wWDesinfectionTemp"] = EMS_Boiler.wWDesinfectTemp;
if (EMS_Boiler.selFlowTemp != EMS_VALUE_INT_NOTSET)
rootBoiler["selFlowTemp"] = EMS_Boiler.selFlowTemp;
if (EMS_Boiler.selBurnPow != EMS_VALUE_INT_NOTSET)
rootBoiler["selBurnPow"] = EMS_Boiler.selBurnPow;
if (EMS_Boiler.curBurnPow != EMS_VALUE_INT_NOTSET)
rootBoiler["curBurnPow"] = EMS_Boiler.curBurnPow;
if (EMS_Boiler.pumpMod != EMS_VALUE_INT_NOTSET)
rootBoiler["pumpMod"] = EMS_Boiler.pumpMod;
if (EMS_Boiler.wWCircPump != EMS_VALUE_BOOL_NOTSET)
rootBoiler["wWCircPump"] = EMS_Boiler.wWCircPump;
if (EMS_Boiler.extTemp > EMS_VALUE_SHORT_NOTSET)
rootBoiler["outdoorTemp"] = (float)EMS_Boiler.extTemp / 10;
if (EMS_Boiler.wWCurTmp < EMS_VALUE_USHORT_NOTSET)
rootBoiler["wWCurTmp"] = (float)EMS_Boiler.wWCurTmp / 10;
if (EMS_Boiler.wWCurFlow != EMS_VALUE_INT_NOTSET)
rootBoiler["wWCurFlow"] = (float)EMS_Boiler.wWCurFlow / 10;
if (EMS_Boiler.curFlowTemp < EMS_VALUE_USHORT_NOTSET)
rootBoiler["curFlowTemp"] = (float)EMS_Boiler.curFlowTemp / 10;
if (EMS_Boiler.retTemp < EMS_VALUE_USHORT_NOTSET)
rootBoiler["retTemp"] = (float)EMS_Boiler.retTemp / 10;
if (EMS_Boiler.switchTemp < EMS_VALUE_USHORT_NOTSET)
rootBoiler["switchTemp"] = (float)EMS_Boiler.switchTemp / 10;
if (EMS_Boiler.sysPress != EMS_VALUE_INT_NOTSET)
rootBoiler["sysPress"] = (float)EMS_Boiler.sysPress / 10;
if (EMS_Boiler.boilTemp < EMS_VALUE_USHORT_NOTSET)
rootBoiler["boilTemp"] = (float)EMS_Boiler.boilTemp / 10;
if (EMS_Boiler.exhaustTemp < EMS_VALUE_USHORT_NOTSET)
rootBoiler["exhaustTemp"] = (float)EMS_Boiler.exhaustTemp / 10;
if (EMS_Boiler.wWActivated != EMS_VALUE_BOOL_NOTSET)
rootBoiler["wWActivated"] = _bool_to_char(s, EMS_Boiler.wWActivated);
if (EMS_Boiler.wWActivated != EMS_VALUE_BOOL_NOTSET)
rootBoiler["wWOnetime"] = _bool_to_char(s, EMS_Boiler.wWOneTime);
if (EMS_Boiler.wWCirc != EMS_VALUE_BOOL_NOTSET)
rootBoiler["wWCirc"] = _bool_to_char(s, EMS_Boiler.wWCirc);
if (EMS_Boiler.burnGas != EMS_VALUE_BOOL_NOTSET)
rootBoiler["burnGas"] = _bool_to_char(s, EMS_Boiler.burnGas);
if (EMS_Boiler.flameCurr < EMS_VALUE_USHORT_NOTSET)
rootBoiler["flameCurr"] = (float)(int16_t)EMS_Boiler.flameCurr / 10;
if (EMS_Boiler.heatPmp != EMS_VALUE_BOOL_NOTSET)
rootBoiler["heatPmp"] = _bool_to_char(s, EMS_Boiler.heatPmp);
if (EMS_Boiler.fanWork != EMS_VALUE_BOOL_NOTSET)
rootBoiler["fanWork"] = _bool_to_char(s, EMS_Boiler.fanWork);
if (EMS_Boiler.ignWork != EMS_VALUE_BOOL_NOTSET)
rootBoiler["ignWork"] = _bool_to_char(s, EMS_Boiler.ignWork);
if (EMS_Boiler.heating_temp != EMS_VALUE_INT_NOTSET)
rootBoiler["heating_temp"] = EMS_Boiler.heating_temp;
if (EMS_Boiler.pump_mod_max != EMS_VALUE_INT_NOTSET)
rootBoiler["pump_mod_max"] = EMS_Boiler.pump_mod_max;
if (EMS_Boiler.pump_mod_min != EMS_VALUE_INT_NOTSET)
rootBoiler["pump_mod_min"] = EMS_Boiler.pump_mod_min;
if (EMS_Boiler.wWHeat != EMS_VALUE_BOOL_NOTSET)
rootBoiler["wWHeat"] = _bool_to_char(s, EMS_Boiler.wWHeat);
if (abs(EMS_Boiler.wWStarts) != EMS_VALUE_LONG_NOTSET)
rootBoiler["wWStarts"] = (float)EMS_Boiler.wWStarts;
if (abs(EMS_Boiler.wWWorkM) != EMS_VALUE_LONG_NOTSET)
rootBoiler["wWWorkM"] = (float)EMS_Boiler.wWWorkM;
if (abs(EMS_Boiler.UBAuptime) != EMS_VALUE_LONG_NOTSET)
rootBoiler["UBAuptime"] = (float)EMS_Boiler.UBAuptime;
if (abs(EMS_Boiler.burnStarts) != EMS_VALUE_LONG_NOTSET)
rootBoiler["burnStarts"] = (float)EMS_Boiler.burnStarts;
if (abs(EMS_Boiler.burnWorkMin) != EMS_VALUE_LONG_NOTSET)
rootBoiler["burnWorkMin"] = (float)EMS_Boiler.burnWorkMin;
if (abs(EMS_Boiler.heatWorkMin) != EMS_VALUE_LONG_NOTSET)
rootBoiler["heatWorkMin"] = (float)EMS_Boiler.heatWorkMin;
if (EMS_Boiler.serviceCode != EMS_VALUE_USHORT_NOTSET) {
rootBoiler["ServiceCode"] = EMS_Boiler.serviceCodeChar;
rootBoiler["ServiceCodeNumber"] = EMS_Boiler.serviceCode;
}
serializeJson(doc, data, sizeof(data));
myDebugLog("Publishing boiler data via MQTT");
myESP.mqttPublish(TOPIC_BOILER_DATA, data);
// see if the heating or hot tap water has changed, if so send
// last_boilerActive stores heating in bit 1 and tap water in bit 2
static uint8_t last_boilerActive = 0xFF; // for remembering last setting of the tap water or heating on/off
if ((last_boilerActive != ((EMS_Boiler.tapwaterActive << 1) + EMS_Boiler.heatingActive)) || force) {
myDebugLog("Publishing hot water and heating states via MQTT");
myESP.mqttPublish(TOPIC_BOILER_TAPWATER_ACTIVE, EMS_Boiler.tapwaterActive == 1 ? "1" : "0");
myESP.mqttPublish(TOPIC_BOILER_HEATING_ACTIVE, EMS_Boiler.heatingActive == 1 ? "1" : "0");
last_boilerActive = ((EMS_Boiler.tapwaterActive << 1) + EMS_Boiler.heatingActive); // remember last state
}
ems_Device_remove_flags(EMS_DEVICE_UPDATE_FLAG_BOILER); // unset flag
if (EMS_Boiler.wWComfort == EMS_VALUE_UBAParameterWW_wwComfort_Hot) {
rootBoiler["wWComfort"] = "Hot";
} else if (EMS_Boiler.wWComfort == EMS_VALUE_UBAParameterWW_wwComfort_Eco) {
rootBoiler["wWComfort"] = "Eco";
} else if (EMS_Boiler.wWComfort == EMS_VALUE_UBAParameterWW_wwComfort_Intelligent) {
rootBoiler["wWComfort"] = "Intelligent";
}
// handle the thermostat values
if (ems_getThermostatEnabled() && (ems_Device_has_flags(EMS_DEVICE_UPDATE_FLAG_THERMOSTAT) || force)) {
doc.clear();
JsonObject rootThermostat = doc.to<JsonObject>();
if (EMS_Boiler.wWSelTemp != EMS_VALUE_INT_NOTSET)
rootBoiler["wWSelTemp"] = EMS_Boiler.wWSelTemp;
if (EMS_Boiler.wWDesinfectTemp != EMS_VALUE_INT_NOTSET)
rootBoiler["wWDesinfectionTemp"] = EMS_Boiler.wWDesinfectTemp;
if (EMS_Boiler.selFlowTemp != EMS_VALUE_INT_NOTSET)
rootBoiler["selFlowTemp"] = EMS_Boiler.selFlowTemp;
if (EMS_Boiler.selBurnPow != EMS_VALUE_INT_NOTSET)
rootBoiler["selBurnPow"] = EMS_Boiler.selBurnPow;
if (EMS_Boiler.curBurnPow != EMS_VALUE_INT_NOTSET)
rootBoiler["curBurnPow"] = EMS_Boiler.curBurnPow;
if (EMS_Boiler.pumpMod != EMS_VALUE_INT_NOTSET)
rootBoiler["pumpMod"] = EMS_Boiler.pumpMod;
if (EMS_Boiler.wWCircPump != EMS_VALUE_BOOL_NOTSET)
rootBoiler["wWCircPump"] = EMS_Boiler.wWCircPump;
for (uint8_t hc_v = 1; hc_v <= EMS_THERMOSTAT_MAXHC; hc_v++) {
_EMS_Thermostat_HC * thermostat = &EMS_Thermostat.hc[hc_v - 1];
if (EMS_Boiler.extTemp > EMS_VALUE_SHORT_NOTSET)
rootBoiler["outdoorTemp"] = (float)EMS_Boiler.extTemp / 10;
if (EMS_Boiler.wWCurTmp < EMS_VALUE_USHORT_NOTSET)
rootBoiler["wWCurTmp"] = (float)EMS_Boiler.wWCurTmp / 10;
if (EMS_Boiler.wWCurFlow != EMS_VALUE_INT_NOTSET)
rootBoiler["wWCurFlow"] = (float)EMS_Boiler.wWCurFlow / 10;
if (EMS_Boiler.curFlowTemp < EMS_VALUE_USHORT_NOTSET)
rootBoiler["curFlowTemp"] = (float)EMS_Boiler.curFlowTemp / 10;
if (EMS_Boiler.retTemp < EMS_VALUE_USHORT_NOTSET)
rootBoiler["retTemp"] = (float)EMS_Boiler.retTemp / 10;
if (EMS_Boiler.switchTemp < EMS_VALUE_USHORT_NOTSET)
rootBoiler["switchTemp"] = (float)EMS_Boiler.switchTemp / 10;
if (EMS_Boiler.sysPress != EMS_VALUE_INT_NOTSET)
rootBoiler["sysPress"] = (float)EMS_Boiler.sysPress / 10;
if (EMS_Boiler.boilTemp < EMS_VALUE_USHORT_NOTSET)
rootBoiler["boilTemp"] = (float)EMS_Boiler.boilTemp / 10;
if (EMS_Boiler.exhaustTemp < EMS_VALUE_USHORT_NOTSET)
rootBoiler["exhaustTemp"] = (float)EMS_Boiler.exhaustTemp / 10;
if (EMS_Boiler.wWActivated != EMS_VALUE_BOOL_NOTSET)
rootBoiler["wWActivated"] = _bool_to_char(s, EMS_Boiler.wWActivated);
// only send if we have an active Heating Circuit with an actual setpoint temp temperature values
if ((thermostat->active) && (thermostat->setpoint_roomTemp > EMS_VALUE_SHORT_NOTSET)) {
// build new json object
char hc[10]; // hc{1-4}
strlcpy(hc, THERMOSTAT_HC, sizeof(hc));
strlcat(hc, _int_to_char(s, thermostat->hc), sizeof(hc));
JsonObject dataThermostat = rootThermostat.createNestedObject(hc);
uint8_t model = ems_getThermostatModel();
if (EMS_Boiler.wWActivated != EMS_VALUE_BOOL_NOTSET)
rootBoiler["wWOnetime"] = _bool_to_char(s, EMS_Boiler.wWOneTime);
// different logic depending on thermostat types
if (model == EMS_DEVICE_FLAG_EASY) {
if (thermostat->setpoint_roomTemp > EMS_VALUE_SHORT_NOTSET)
dataThermostat[THERMOSTAT_SELTEMP] = (float)thermostat->setpoint_roomTemp / 100;
if (thermostat->curr_roomTemp > EMS_VALUE_SHORT_NOTSET)
dataThermostat[THERMOSTAT_CURRTEMP] = (float)thermostat->curr_roomTemp / 100;
} else if (model == EMS_DEVICE_FLAG_JUNKERS) {
if (thermostat->setpoint_roomTemp > EMS_VALUE_SHORT_NOTSET)
dataThermostat[THERMOSTAT_SELTEMP] = (float)thermostat->setpoint_roomTemp / 10;
if (thermostat->curr_roomTemp > EMS_VALUE_SHORT_NOTSET)
dataThermostat[THERMOSTAT_CURRTEMP] = (float)thermostat->curr_roomTemp / 10;
} else {
if (thermostat->setpoint_roomTemp > EMS_VALUE_SHORT_NOTSET)
dataThermostat[THERMOSTAT_SELTEMP] = (float)thermostat->setpoint_roomTemp / 2;
if (thermostat->curr_roomTemp > EMS_VALUE_SHORT_NOTSET)
dataThermostat[THERMOSTAT_CURRTEMP] = (float)thermostat->curr_roomTemp / 10;
if (EMS_Boiler.wWCirc != EMS_VALUE_BOOL_NOTSET)
rootBoiler["wWCirc"] = _bool_to_char(s, EMS_Boiler.wWCirc);
if (thermostat->daytemp != EMS_VALUE_INT_NOTSET)
dataThermostat[THERMOSTAT_DAYTEMP] = (float)thermostat->daytemp / 2;
if (thermostat->nighttemp != EMS_VALUE_INT_NOTSET)
dataThermostat[THERMOSTAT_NIGHTTEMP] = (float)thermostat->nighttemp / 2;
if (thermostat->holidaytemp != EMS_VALUE_INT_NOTSET)
dataThermostat[THERMOSTAT_HOLIDAYTEMP] = (float)thermostat->holidaytemp / 2;
if (EMS_Boiler.burnGas != EMS_VALUE_BOOL_NOTSET)
rootBoiler["burnGas"] = _bool_to_char(s, EMS_Boiler.burnGas);
if (thermostat->heatingtype != EMS_VALUE_INT_NOTSET)
dataThermostat[THERMOSTAT_HEATINGTYPE] = thermostat->heatingtype;
if (EMS_Boiler.flameCurr < EMS_VALUE_USHORT_NOTSET)
rootBoiler["flameCurr"] = (float)(int16_t)EMS_Boiler.flameCurr / 10;
if (thermostat->circuitcalctemp != EMS_VALUE_INT_NOTSET)
dataThermostat[THERMOSTAT_CIRCUITCALCTEMP] = thermostat->circuitcalctemp;
}
if (EMS_Boiler.heatPmp != EMS_VALUE_BOOL_NOTSET)
rootBoiler["heatPmp"] = _bool_to_char(s, EMS_Boiler.heatPmp);
// Thermostat Mode
_EMS_THERMOSTAT_MODE thermoMode = _getThermostatMode(hc_v);
if (thermoMode == EMS_THERMOSTAT_MODE_OFF) {
dataThermostat[THERMOSTAT_MODE] = "off";
} else if (thermoMode == EMS_THERMOSTAT_MODE_MANUAL) {
dataThermostat[THERMOSTAT_MODE] = "manual";
} else if (thermoMode == EMS_THERMOSTAT_MODE_AUTO) {
dataThermostat[THERMOSTAT_MODE] = "auto";
} else if (thermoMode == EMS_THERMOSTAT_MODE_DAY) {
dataThermostat[THERMOSTAT_MODE] = "day";
} else if (thermoMode == EMS_THERMOSTAT_MODE_NIGHT) {
dataThermostat[THERMOSTAT_MODE] = "night";
}
if (EMS_Boiler.fanWork != EMS_VALUE_BOOL_NOTSET)
rootBoiler["fanWork"] = _bool_to_char(s, EMS_Boiler.fanWork);
if (EMS_Boiler.ignWork != EMS_VALUE_BOOL_NOTSET)
rootBoiler["ignWork"] = _bool_to_char(s, EMS_Boiler.ignWork);
if (EMS_Boiler.heating_temp != EMS_VALUE_INT_NOTSET)
rootBoiler["heating_temp"] = EMS_Boiler.heating_temp;
if (EMS_Boiler.pump_mod_max != EMS_VALUE_INT_NOTSET)
rootBoiler["pump_mod_max"] = EMS_Boiler.pump_mod_max;
if (EMS_Boiler.pump_mod_min != EMS_VALUE_INT_NOTSET)
rootBoiler["pump_mod_min"] = EMS_Boiler.pump_mod_min;
if (EMS_Boiler.wWHeat != EMS_VALUE_BOOL_NOTSET)
rootBoiler["wWHeat"] = _bool_to_char(s, EMS_Boiler.wWHeat);
if (abs(EMS_Boiler.wWStarts) != EMS_VALUE_LONG_NOTSET)
rootBoiler["wWStarts"] = (float)EMS_Boiler.wWStarts;
if (abs(EMS_Boiler.wWWorkM) != EMS_VALUE_LONG_NOTSET)
rootBoiler["wWWorkM"] = (float)EMS_Boiler.wWWorkM;
if (abs(EMS_Boiler.UBAuptime) != EMS_VALUE_LONG_NOTSET)
rootBoiler["UBAuptime"] = (float)EMS_Boiler.UBAuptime;
if (abs(EMS_Boiler.burnStarts) != EMS_VALUE_LONG_NOTSET)
rootBoiler["burnStarts"] = (float)EMS_Boiler.burnStarts;
if (abs(EMS_Boiler.burnWorkMin) != EMS_VALUE_LONG_NOTSET)
rootBoiler["burnWorkMin"] = (float)EMS_Boiler.burnWorkMin;
if (abs(EMS_Boiler.heatWorkMin) != EMS_VALUE_LONG_NOTSET)
rootBoiler["heatWorkMin"] = (float)EMS_Boiler.heatWorkMin;
if (EMS_Boiler.serviceCode != EMS_VALUE_USHORT_NOTSET) {
rootBoiler["ServiceCode"] = EMS_Boiler.serviceCodeChar;
rootBoiler["ServiceCodeNumber"] = EMS_Boiler.serviceCode;
}
serializeJson(doc, data, sizeof(data));
myDebugLog("Publishing boiler data via MQTT");
myESP.mqttPublish(TOPIC_BOILER_DATA, data);
// see if the heating or hot tap water has changed, if so send
// last_boilerActive stores heating in bit 1 and tap water in bit 2
static uint8_t last_boilerActive = 0xFF; // for remembering last setting of the tap water or heating on/off
if (last_boilerActive != ((EMS_Boiler.tapwaterActive << 1) + EMS_Boiler.heatingActive)) {
myDebugLog("Publishing hot water and heating states via MQTT");
myESP.mqttPublish(TOPIC_BOILER_TAPWATER_ACTIVE, EMS_Boiler.tapwaterActive == 1 ? "1" : "0");
myESP.mqttPublish(TOPIC_BOILER_HEATING_ACTIVE, EMS_Boiler.heatingActive == 1 ? "1" : "0");
last_boilerActive = ((EMS_Boiler.tapwaterActive << 1) + EMS_Boiler.heatingActive); // remember last state
}
}
// handle the thermostat values
void publishEMSValues_thermostat() {
char s[20] = {0}; // for formatting strings
StaticJsonDocument<MQTT_MAX_PAYLOAD_SIZE> doc;
char data[MQTT_MAX_PAYLOAD_SIZE] = {0};
JsonObject rootThermostat = doc.to<JsonObject>();
for (uint8_t hc_v = 1; hc_v <= EMS_THERMOSTAT_MAXHC; hc_v++) {
_EMS_Thermostat_HC * thermostat = &EMS_Thermostat.hc[hc_v - 1];
// only send if we have an active Heating Circuit with an actual setpoint temp temperature values
if ((thermostat->active) && (thermostat->setpoint_roomTemp > EMS_VALUE_SHORT_NOTSET)) {
// build new json object
char hc[10]; // hc{1-4}
strlcpy(hc, THERMOSTAT_HC, sizeof(hc));
strlcat(hc, _int_to_char(s, thermostat->hc), sizeof(hc));
JsonObject dataThermostat = rootThermostat.createNestedObject(hc);
uint8_t model = ems_getThermostatModel();
// different logic depending on thermostat types
if (model == EMS_DEVICE_FLAG_EASY) {
if (thermostat->setpoint_roomTemp > EMS_VALUE_SHORT_NOTSET)
dataThermostat[THERMOSTAT_SELTEMP] = (float)thermostat->setpoint_roomTemp / 100;
if (thermostat->curr_roomTemp > EMS_VALUE_SHORT_NOTSET)
dataThermostat[THERMOSTAT_CURRTEMP] = (float)thermostat->curr_roomTemp / 100;
} else if (model == EMS_DEVICE_FLAG_JUNKERS) {
if (thermostat->setpoint_roomTemp > EMS_VALUE_SHORT_NOTSET)
dataThermostat[THERMOSTAT_SELTEMP] = (float)thermostat->setpoint_roomTemp / 10;
if (thermostat->curr_roomTemp > EMS_VALUE_SHORT_NOTSET)
dataThermostat[THERMOSTAT_CURRTEMP] = (float)thermostat->curr_roomTemp / 10;
} else {
if (thermostat->setpoint_roomTemp > EMS_VALUE_SHORT_NOTSET)
dataThermostat[THERMOSTAT_SELTEMP] = (float)thermostat->setpoint_roomTemp / 2;
if (thermostat->curr_roomTemp > EMS_VALUE_SHORT_NOTSET)
dataThermostat[THERMOSTAT_CURRTEMP] = (float)thermostat->curr_roomTemp / 10;
if (thermostat->daytemp != EMS_VALUE_INT_NOTSET)
dataThermostat[THERMOSTAT_DAYTEMP] = (float)thermostat->daytemp / 2;
if (thermostat->nighttemp != EMS_VALUE_INT_NOTSET)
dataThermostat[THERMOSTAT_NIGHTTEMP] = (float)thermostat->nighttemp / 2;
if (thermostat->holidaytemp != EMS_VALUE_INT_NOTSET)
dataThermostat[THERMOSTAT_HOLIDAYTEMP] = (float)thermostat->holidaytemp / 2;
if (thermostat->heatingtype != EMS_VALUE_INT_NOTSET)
dataThermostat[THERMOSTAT_HEATINGTYPE] = thermostat->heatingtype;
if (thermostat->circuitcalctemp != EMS_VALUE_INT_NOTSET)
dataThermostat[THERMOSTAT_CIRCUITCALCTEMP] = thermostat->circuitcalctemp;
}
// Thermostat Mode
_EMS_THERMOSTAT_MODE thermoMode = _getThermostatMode(hc_v);
if (thermoMode == EMS_THERMOSTAT_MODE_OFF) {
dataThermostat[THERMOSTAT_MODE] = "off";
} else if (thermoMode == EMS_THERMOSTAT_MODE_MANUAL) {
dataThermostat[THERMOSTAT_MODE] = "manual";
} else if (thermoMode == EMS_THERMOSTAT_MODE_AUTO) {
dataThermostat[THERMOSTAT_MODE] = "auto";
} else if (thermoMode == EMS_THERMOSTAT_MODE_DAY) {
dataThermostat[THERMOSTAT_MODE] = "day";
} else if (thermoMode == EMS_THERMOSTAT_MODE_NIGHT) {
dataThermostat[THERMOSTAT_MODE] = "night";
}
}
data[0] = '\0'; // reset data for next package
serializeJson(doc, data, sizeof(data));
myDebugLog("Publishing thermostat data via MQTT");
myESP.mqttPublish(TOPIC_THERMOSTAT_DATA, data);
ems_Device_remove_flags(EMS_DEVICE_UPDATE_FLAG_THERMOSTAT); // unset flag
}
// handle the mixing values
if (ems_getMixingModuleEnabled() && (ems_Device_has_flags(EMS_DEVICE_UPDATE_FLAG_MIXING) || force)) {
doc.clear();
JsonObject rootMixing = doc.to<JsonObject>();
serializeJson(doc, data, sizeof(data));
myDebugLog("Publishing thermostat data via MQTT");
myESP.mqttPublish(TOPIC_THERMOSTAT_DATA, data);
}
for (uint8_t hc_v = 1; hc_v <= EMS_MIXING_MAXHC; hc_v++) {
_EMS_MixingModule_HC * mixingHC = &EMS_MixingModule.hc[hc_v - 1];
// publish mixing data
void publishEMSValues_mixing() {
char s[20] = {0}; // for formatting strings
StaticJsonDocument<MQTT_MAX_PAYLOAD_SIZE> doc;
char data[MQTT_MAX_PAYLOAD_SIZE] = {0};
JsonObject rootMixing = doc.to<JsonObject>();
// only send if we have an active Heating Circuit with real data
if (mixingHC->active) {
char hc[10]; // hc{1-4}
strlcpy(hc, MIXING_HC, sizeof(hc));
strlcat(hc, _int_to_char(s, mixingHC->hc), sizeof(hc));
JsonObject dataMixingHC = rootMixing.createNestedObject(hc);
if (mixingHC->flowTemp < EMS_VALUE_USHORT_NOTSET)
dataMixingHC["flowTemp"] = (float)mixingHC->flowTemp / 10;
if (mixingHC->flowSetTemp != EMS_VALUE_INT_NOTSET)
dataMixingHC["setflowTemp"] = mixingHC->flowSetTemp;
if (mixingHC->pumpMod != EMS_VALUE_INT_NOTSET)
dataMixingHC["pumpMod"] = mixingHC->pumpMod;
if (mixingHC->valveStatus != EMS_VALUE_INT_NOTSET)
dataMixingHC["valveStatus"] = mixingHC->valveStatus;
}
for (uint8_t hc_v = 1; hc_v <= EMS_MIXING_MAXHC; hc_v++) {
_EMS_MixingModule_HC * mixingHC = &EMS_MixingModule.hc[hc_v - 1];
// only send if we have an active Heating Circuit with real data
if (mixingHC->active) {
char hc[10]; // hc{1-4}
strlcpy(hc, MIXING_HC, sizeof(hc));
strlcat(hc, _int_to_char(s, mixingHC->hc), sizeof(hc));
JsonObject dataMixingHC = rootMixing.createNestedObject(hc);
if (mixingHC->flowTemp < EMS_VALUE_USHORT_NOTSET)
dataMixingHC["flowTemp"] = (float)mixingHC->flowTemp / 10;
if (mixingHC->flowSetTemp != EMS_VALUE_INT_NOTSET)
dataMixingHC["setflowTemp"] = mixingHC->flowSetTemp;
if (mixingHC->pumpMod != EMS_VALUE_INT_NOTSET)
dataMixingHC["pumpMod"] = mixingHC->pumpMod;
if (mixingHC->valveStatus != EMS_VALUE_INT_NOTSET)
dataMixingHC["valveStatus"] = mixingHC->valveStatus;
}
for (uint8_t wwc_v = 1; wwc_v <= EMS_MIXING_MAXWWC; wwc_v++) {
_EMS_MixingModule_WWC * mixingWWC = &EMS_MixingModule.wwc[wwc_v - 1];
// only send if we have an active Warm water Circuit with real data
if (mixingWWC->active) {
char wwc[10]; // wwc{1-2}
strlcpy(wwc, MIXING_WWC, sizeof(wwc));
strlcat(wwc, _int_to_char(s, mixingWWC->wwc), sizeof(wwc));
JsonObject dataMixing = rootMixing.createNestedObject(wwc);
if (mixingWWC->flowTemp < EMS_VALUE_USHORT_NOTSET)
dataMixing["wwTemp"] = (float)mixingWWC->flowTemp / 10;
if (mixingWWC->pumpMod != EMS_VALUE_INT_NOTSET)
dataMixing["pumpStatus"] = mixingWWC->pumpMod;
if (mixingWWC->tempStatus != EMS_VALUE_INT_NOTSET)
dataMixing["tempStatus"] = mixingWWC->tempStatus;
}
}
data[0] = '\0'; // reset data for next package
serializeJson(doc, data, sizeof(data));
myDebugLog("Publishing mixing data via MQTT");
myESP.mqttPublish(TOPIC_MIXING_DATA, data);
ems_Device_remove_flags(EMS_DEVICE_UPDATE_FLAG_MIXING); // unset flag
}
// For SM10 and SM100/SM200 Solar Modules
if (ems_getSolarModuleEnabled() && (ems_Device_has_flags(EMS_DEVICE_UPDATE_FLAG_SOLAR) || force)) {
// build new json object
doc.clear();
JsonObject rootSM = doc.to<JsonObject>();
if (EMS_SolarModule.collectorTemp > EMS_VALUE_SHORT_NOTSET)
rootSM[SM_COLLECTORTEMP] = (float)EMS_SolarModule.collectorTemp / 10;
if (EMS_SolarModule.bottomTemp > EMS_VALUE_SHORT_NOTSET)
rootSM[SM_BOTTOMTEMP] = (float)EMS_SolarModule.bottomTemp / 10;
if (EMS_SolarModule.pumpModulation != EMS_VALUE_INT_NOTSET)
rootSM[SM_PUMPMODULATION] = EMS_SolarModule.pumpModulation;
if (EMS_SolarModule.pump != EMS_VALUE_BOOL_NOTSET) {
rootSM[SM_PUMP] = _bool_to_char(s, EMS_SolarModule.pump);
for (uint8_t wwc_v = 1; wwc_v <= EMS_MIXING_MAXWWC; wwc_v++) {
_EMS_MixingModule_WWC * mixingWWC = &EMS_MixingModule.wwc[wwc_v - 1];
// only send if we have an active Warm water Circuit with real data
if (mixingWWC->active) {
char wwc[10]; // wwc{1-2}
strlcpy(wwc, MIXING_WWC, sizeof(wwc));
strlcat(wwc, _int_to_char(s, mixingWWC->wwc), sizeof(wwc));
JsonObject dataMixing = rootMixing.createNestedObject(wwc);
if (mixingWWC->flowTemp < EMS_VALUE_USHORT_NOTSET)
dataMixing["wwTemp"] = (float)mixingWWC->flowTemp / 10;
if (mixingWWC->pumpMod != EMS_VALUE_INT_NOTSET)
dataMixing["pumpStatus"] = mixingWWC->pumpMod;
if (mixingWWC->tempStatus != EMS_VALUE_INT_NOTSET)
dataMixing["tempStatus"] = mixingWWC->tempStatus;
}
if (EMS_SolarModule.pumpWorkMin != EMS_VALUE_LONG_NOTSET) {
rootSM[SM_PUMPWORKMIN] = (float)EMS_SolarModule.pumpWorkMin;
}
if (EMS_SolarModule.EnergyLastHour < EMS_VALUE_USHORT_NOTSET)
rootSM[SM_ENERGYLASTHOUR] = (float)EMS_SolarModule.EnergyLastHour / 10;
if (EMS_SolarModule.EnergyToday < EMS_VALUE_USHORT_NOTSET)
rootSM[SM_ENERGYTODAY] = EMS_SolarModule.EnergyToday;
if (EMS_SolarModule.EnergyTotal < EMS_VALUE_USHORT_NOTSET)
rootSM[SM_ENERGYTOTAL] = (float)EMS_SolarModule.EnergyTotal / 10;
data[0] = '\0'; // reset data for next package
serializeJson(doc, data, sizeof(data));
myDebugLog("Publishing SM data via MQTT");
myESP.mqttPublish(TOPIC_SM_DATA, data);
ems_Device_remove_flags(EMS_DEVICE_UPDATE_FLAG_SOLAR); // unset flag
}
// handle HeatPump
if (ems_getHeatPumpEnabled() && (ems_Device_has_flags(EMS_DEVICE_UPDATE_FLAG_HEATPUMP) || force)) {
// build new json object
doc.clear();
JsonObject rootSM = doc.to<JsonObject>();
serializeJson(doc, data, sizeof(data));
myDebugLog("Publishing mixing data via MQTT");
myESP.mqttPublish(TOPIC_MIXING_DATA, data);
}
if (EMS_HeatPump.HPModulation != EMS_VALUE_INT_NOTSET)
rootSM[HP_PUMPMODULATION] = EMS_HeatPump.HPModulation;
// For SM10 and SM100/SM200 Solar Modules
void publishEMSValues_solar() {
char s[20] = {0}; // for formatting strings
StaticJsonDocument<MQTT_MAX_PAYLOAD_SIZE> doc;
char data[MQTT_MAX_PAYLOAD_SIZE] = {0};
JsonObject rootSM = doc.to<JsonObject>();
if (EMS_HeatPump.HPSpeed != EMS_VALUE_INT_NOTSET)
rootSM[HP_PUMPSPEED] = EMS_HeatPump.HPSpeed;
if (EMS_SolarModule.collectorTemp > EMS_VALUE_SHORT_NOTSET)
rootSM[SM_COLLECTORTEMP] = (float)EMS_SolarModule.collectorTemp / 10;
data[0] = '\0'; // reset data for next package
serializeJson(doc, data, sizeof(data));
myDebugLog("Publishing HeatPump data via MQTT");
myESP.mqttPublish(TOPIC_HP_DATA, data);
ems_Device_remove_flags(EMS_DEVICE_UPDATE_FLAG_HEATPUMP); // unset flag
if (EMS_SolarModule.bottomTemp > EMS_VALUE_SHORT_NOTSET)
rootSM[SM_BOTTOMTEMP] = (float)EMS_SolarModule.bottomTemp / 10;
if (EMS_SolarModule.pumpModulation != EMS_VALUE_INT_NOTSET)
rootSM[SM_PUMPMODULATION] = EMS_SolarModule.pumpModulation;
if (EMS_SolarModule.pump != EMS_VALUE_BOOL_NOTSET) {
rootSM[SM_PUMP] = _bool_to_char(s, EMS_SolarModule.pump);
}
if (EMS_SolarModule.pumpWorkMin != EMS_VALUE_LONG_NOTSET) {
rootSM[SM_PUMPWORKMIN] = (float)EMS_SolarModule.pumpWorkMin;
}
if (EMS_SolarModule.EnergyLastHour < EMS_VALUE_USHORT_NOTSET)
rootSM[SM_ENERGYLASTHOUR] = (float)EMS_SolarModule.EnergyLastHour / 10;
if (EMS_SolarModule.EnergyToday < EMS_VALUE_USHORT_NOTSET)
rootSM[SM_ENERGYTODAY] = EMS_SolarModule.EnergyToday;
if (EMS_SolarModule.EnergyTotal < EMS_VALUE_USHORT_NOTSET)
rootSM[SM_ENERGYTOTAL] = (float)EMS_SolarModule.EnergyTotal / 10;
serializeJson(doc, data, sizeof(data));
myDebugLog("Publishing SM data via MQTT");
myESP.mqttPublish(TOPIC_SM_DATA, data);
}
// handle HeatPump
void publishEMSValues_heatpump() {
StaticJsonDocument<MQTT_MAX_PAYLOAD_SIZE> doc;
char data[MQTT_MAX_PAYLOAD_SIZE] = {0};
JsonObject rootHP = doc.to<JsonObject>();
if (EMS_HeatPump.HPModulation != EMS_VALUE_INT_NOTSET)
rootHP[HP_PUMPMODULATION] = EMS_HeatPump.HPModulation;
if (EMS_HeatPump.HPSpeed != EMS_VALUE_INT_NOTSET)
rootHP[HP_PUMPSPEED] = EMS_HeatPump.HPSpeed;
serializeJson(doc, data, sizeof(data));
myDebugLog("Publishing HeatPump data via MQTT");
myESP.mqttPublish(TOPIC_HP_DATA, data);
}
// Publish shower data
void do_publishShowerData() {
StaticJsonDocument<200> doc;
JsonObject rootShower = doc.to<JsonObject>();
rootShower[TOPIC_SHOWER_TIMER] = EMSESP_Settings.shower_timer ? "1" : "0";
rootShower[TOPIC_SHOWER_ALERT] = EMSESP_Settings.shower_alert ? "1" : "0";
StaticJsonDocument<MQTT_MAX_PAYLOAD_SIZE_SMALL> doc;
JsonObject rootShower = doc.to<JsonObject>();
rootShower[TOPIC_SHOWER_TIMER] = EMSESP_Settings.shower_timer ? "1" : "0";
rootShower[TOPIC_SHOWER_ALERT] = EMSESP_Settings.shower_alert ? "1" : "0";
// only publish shower duration if there is a value
char s[50] = {0};
@@ -956,17 +947,58 @@ void do_publishShowerData() {
myESP.mqttPublish(TOPIC_SHOWER_DATA, data, false);
}
// call PublishValues with forcing forcing
void do_publishValues() {
// send values via MQTT
// a json object is created for each device type
void publishEMSValues(bool force) {
// don't send if MQTT is not connected or EMS bus is not connected
if (!myESP.isMQTTConnected() || (!ems_getBusConnected()) || (EMSESP_Settings.publish_time == -1)) {
return;
}
if (ems_getBoilerEnabled() && (ems_Device_has_flags(EMS_DEVICE_UPDATE_FLAG_BOILER) || force)) {
publishEMSValues_boiler();
ems_Device_remove_flags(EMS_DEVICE_UPDATE_FLAG_BOILER); // unset flag
}
if (ems_getThermostatEnabled() && (ems_Device_has_flags(EMS_DEVICE_UPDATE_FLAG_THERMOSTAT) || force)) {
publishEMSValues_thermostat();
ems_Device_remove_flags(EMS_DEVICE_UPDATE_FLAG_THERMOSTAT); // unset flag
}
if (ems_getMixingModuleEnabled() && (ems_Device_has_flags(EMS_DEVICE_UPDATE_FLAG_MIXING) || force)) {
publishEMSValues_mixing();
ems_Device_remove_flags(EMS_DEVICE_UPDATE_FLAG_MIXING); // unset flag
}
if (ems_getSolarModuleEnabled() && (ems_Device_has_flags(EMS_DEVICE_UPDATE_FLAG_SOLAR) || force)) {
publishEMSValues_solar();
ems_Device_remove_flags(EMS_DEVICE_UPDATE_FLAG_SOLAR); // unset flag
}
if (ems_getHeatPumpEnabled() && (ems_Device_has_flags(EMS_DEVICE_UPDATE_FLAG_HEATPUMP) || force)) {
publishEMSValues_heatpump();
ems_Device_remove_flags(EMS_DEVICE_UPDATE_FLAG_HEATPUMP); // unset flag
}
}
// publishes value via MQTT
void publishValues(bool force, bool send_sensor) {
if (EMSESP_Settings.publish_time == -1) {
myDebugLog("Publishing is disabled.");
return;
}
myDebugLog("Starting scheduled MQTT publish...");
publishEMSValues(false);
publishSensorValues();
myESP.heartbeatCheck(true);
publishEMSValues(force);
if (send_sensor) {
publishSensorValues();
}
// myESP.heartbeatCheck(true);
}
// calls publishValues fron the Ticker loop, also sending sensor data
// but not using false for force so only data that has changed will be sent
void do_publishValues() {
publishValues(false, true);
}
// callback to light up the LED, called via Ticker every second
@@ -2096,6 +2128,10 @@ void initEMSESP() {
* Shower Logic
*/
void showerCheck() {
if (!EMSESP_Settings.shower_timer) {
return;
}
uint32_t time_now = millis();
// if already in cold mode, ignore all this logic until we're out of the cold blast
if (!EMSESP_Shower.doingColdShot) {
@@ -2231,7 +2267,7 @@ void setup() {
if (EMSESP_Settings.publish_time > 0) {
publishValuesTimer.attach(EMSESP_Settings.publish_time, do_publishValues); // post MQTT EMS values
} else if (EMSESP_Settings.publish_time == 0) {
// automatic mode. use this Ticker to send out sensor values
// automatic mode. use this Ticker to send out sensor values only. the EMS ones are done in the loop.
publishValuesTimer.attach(DEFAULT_SENSOR_PUBLISHTIME, publishSensorValues);
}
@@ -2256,33 +2292,30 @@ void setup() {
void loop() {
myESP.loop(); // handle telnet, mqtt, wifi etc
// to prevent load, only run checks every second
// get Dallas Sensor readings every 2 seconds
static uint32_t last_check = 0;
if (millis() - last_check < 1000) {
return;
}
last_check = millis();
// get Dallas Sensor readings
if (EMSESP_Settings.dallas_sensors) {
ds18.loop();
}
// if we have an EMS bus connection go and fetch some data and MQTT publish it
if (_need_first_publish) {
publishEMSValues(false);
publishSensorValues();
_need_first_publish = false; // reset flag
} else {
// check if we're on auto mode for publishing
// then send EMS values, only if its been flagged to update
if (EMSESP_Settings.publish_time == 0) {
publishEMSValues(false);
uint32_t time_now = millis();
if (time_now - last_check > 2000) {
last_check = time_now;
if (EMSESP_Settings.dallas_sensors) {
ds18.loop();
}
}
// do shower logic, if enabled
if (EMSESP_Settings.shower_timer) {
showerCheck();
// if we just have an EMS bus connection go and fetch the data and MQTT publish it to get started
if (_need_first_publish) {
publishValues(true, true);
_need_first_publish = false;
return;
}
// check if we're on auto mode for publishing
// then send EMS values, only if its been flagged to update.
// Don't send sensor data as this is done by the Ticker
if (EMSESP_Settings.publish_time == 0) {
publishValues(false, false);
}
// do shower logic
showerCheck();
}