Browse Source

refactor app, mqtt

pull/421/head
lumapu 2 years ago
parent
commit
0a0d90dc92
  1. 114
      src/app.cpp
  2. 236
      src/web/mqtt.h

114
src/app.cpp

@ -170,7 +170,7 @@ void app::loop(void) {
if ((++mMqttTicker >= mMqttInterval) && (mMqttInterval != 0xffff) && mMqttActive) {
mMqttTicker = 0;
sendMqtt();
mMqtt.sendIvData(mSys, mUtcTimestamp, mMqttSendList);
}
if (mConfig.serialShowIv) {
@ -525,118 +525,6 @@ void app::getAvailNetworks(JsonObject obj) {
}
//-----------------------------------------------------------------------------
void app::sendMqtt(void) {
mMqtt.isConnected(true); // really needed? See comment from HorstG-57 #176
char topic[32 + MAX_NAME_LENGTH], val[32];
float total[4];
bool sendTotal = false;
bool totalIncomplete = false;
snprintf(val, 32, "%ld", millis() / 1000);
mMqtt.sendMsg("uptime", val);
if(mMqttSendList.empty())
return;
while(!mMqttSendList.empty()) {
memset(total, 0, sizeof(float) * 4);
for (uint8_t id = 0; id < mSys->getNumInverters(); id++) {
Inverter<> *iv = mSys->getInverterByPos(id);
if (NULL == iv)
continue; // skip to next inverter
record_t<> *rec = iv->getRecordStruct(mMqttSendList.front());
if(mMqttSendList.front() == RealTimeRunData_Debug) {
// inverter status
uint8_t status = MQTT_STATUS_AVAIL_PROD;
if (!iv->isAvailable(mUtcTimestamp, rec)) {
status = MQTT_STATUS_NOT_AVAIL_NOT_PROD;
totalIncomplete = true;
}
else if (!iv->isProducing(mUtcTimestamp, rec)) {
if (MQTT_STATUS_AVAIL_PROD == status)
status = MQTT_STATUS_AVAIL_NOT_PROD;
}
snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/available_text", iv->name);
snprintf(val, 32, "%s%s%s%s",
(MQTT_STATUS_NOT_AVAIL_NOT_PROD) ? "not yet " : "",
"available and ",
(MQTT_STATUS_AVAIL_NOT_PROD) ? "not " : "",
(MQTT_STATUS_NOT_AVAIL_NOT_PROD) ? "" : "producing"
);
mMqtt.sendMsg(topic, val);
snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/available", iv->name);
snprintf(val, 32, "%d", status);
mMqtt.sendMsg(topic, val);
snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/last_success", iv->name);
snprintf(val, 48, "%i", iv->getLastTs(rec) * 1000);
mMqtt.sendMsg(topic, val);
}
// data
if(iv->isAvailable(mUtcTimestamp, rec)) {
for (uint8_t i = 0; i < rec->length; i++) {
snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/ch%d/%s", iv->name, rec->assign[i].ch, fields[rec->assign[i].fieldId]);
snprintf(val, 10, "%.3f", iv->getValue(i, rec));
mMqtt.sendMsg(topic, val);
// calculate total values for RealTimeRunData_Debug
if (mMqttSendList.front() == RealTimeRunData_Debug) {
if (CH0 == rec->assign[i].ch) {
switch (rec->assign[i].fieldId) {
case FLD_PAC:
total[0] += iv->getValue(i, rec);
break;
case FLD_YT:
total[1] += iv->getValue(i, rec);
break;
case FLD_YD:
total[2] += iv->getValue(i, rec);
break;
case FLD_PDC:
total[3] += iv->getValue(i, rec);
break;
}
}
sendTotal = true;
}
yield();
}
}
}
mMqttSendList.pop(); // remove from list once all inverters were processed
if ((true == sendTotal) && (false == totalIncomplete)) {
uint8_t fieldId;
for (uint8_t i = 0; i < 4; i++) {
switch (i) {
default:
case 0:
fieldId = FLD_PAC;
break;
case 1:
fieldId = FLD_YT;
break;
case 2:
fieldId = FLD_YD;
break;
case 3:
fieldId = FLD_PDC;
break;
}
snprintf(topic, 32 + MAX_NAME_LENGTH, "total/%s", fields[fieldId]);
snprintf(val, 10, "%.3f", total[i]);
mMqtt.sendMsg(topic, val);
}
}
}
}
//-----------------------------------------------------------------------------
void app::resetSystem(void) {
mUptimeSecs = 0;

236
src/web/mqtt.h

@ -95,76 +95,184 @@ class mqtt {
}
void sendMqttDiscoveryConfig(HMSYSTEM *sys, const char *topic, uint32_t invertval) {
DPRINTLN(DBG_VERBOSE, F("app::sendMqttDiscoveryConfig"));
char stateTopic[64], discoveryTopic[64], buffer[512], name[32], uniq_id[32];
for (uint8_t id = 0; id < sys->getNumInverters(); id++) {
Inverter<> *iv = sys->getInverterByPos(id);
if (NULL != iv) {
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
DynamicJsonDocument deviceDoc(128);
deviceDoc["name"] = iv->name;
deviceDoc["ids"] = String(iv->serial.u64, HEX);
deviceDoc["cu"] = F("http://") + String(WiFi.localIP().toString());
deviceDoc["mf"] = "Hoymiles";
deviceDoc["mdl"] = iv->name;
JsonObject deviceObj = deviceDoc.as<JsonObject>();
DynamicJsonDocument doc(384);
for (uint8_t i = 0; i < rec->length; i++) {
if (rec->assign[i].ch == CH0) {
snprintf(name, 32, "%s %s", iv->name, iv->getFieldName(i, rec));
} else {
snprintf(name, 32, "%s CH%d %s", iv->name, rec->assign[i].ch, iv->getFieldName(i, rec));
DPRINTLN(DBG_VERBOSE, F("app::sendMqttDiscoveryConfig"));
char stateTopic[64], discoveryTopic[64], buffer[512], name[32], uniq_id[32];
for (uint8_t id = 0; id < sys->getNumInverters(); id++) {
Inverter<> *iv = sys->getInverterByPos(id);
if (NULL != iv) {
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
DynamicJsonDocument deviceDoc(128);
deviceDoc["name"] = iv->name;
deviceDoc["ids"] = String(iv->serial.u64, HEX);
deviceDoc["cu"] = F("http://") + String(WiFi.localIP().toString());
deviceDoc["mf"] = "Hoymiles";
deviceDoc["mdl"] = iv->name;
JsonObject deviceObj = deviceDoc.as<JsonObject>();
DynamicJsonDocument doc(384);
for (uint8_t i = 0; i < rec->length; i++) {
if (rec->assign[i].ch == CH0) {
snprintf(name, 32, "%s %s", iv->name, iv->getFieldName(i, rec));
} else {
snprintf(name, 32, "%s CH%d %s", iv->name, rec->assign[i].ch, iv->getFieldName(i, rec));
}
snprintf(stateTopic, 64, "%s/%s/ch%d/%s", topic, iv->name, rec->assign[i].ch, iv->getFieldName(i, rec));
snprintf(discoveryTopic, 64, "%s/sensor/%s/ch%d_%s/config", MQTT_DISCOVERY_PREFIX, iv->name, rec->assign[i].ch, iv->getFieldName(i, rec));
snprintf(uniq_id, 32, "ch%d_%s", rec->assign[i].ch, iv->getFieldName(i, rec));
const char *devCls = getFieldDeviceClass(rec->assign[i].fieldId);
const char *stateCls = getFieldStateClass(rec->assign[i].fieldId);
doc["name"] = name;
doc["stat_t"] = stateTopic;
doc["unit_of_meas"] = iv->getUnit(i, rec);
doc["uniq_id"] = String(iv->serial.u64, HEX) + "_" + uniq_id;
doc["dev"] = deviceObj;
doc["exp_aft"] = invertval + 5; // add 5 sec if connection is bad or ESP too slow @TODO: stimmt das wirklich als expire!?
if (devCls != NULL)
doc["dev_cla"] = devCls;
if (stateCls != NULL)
doc["stat_cla"] = stateCls;
serializeJson(doc, buffer);
sendMsg2(discoveryTopic, buffer, true);
// DPRINTLN(DBG_INFO, F("mqtt sent"));
doc.clear();
}
yield();
}
snprintf(stateTopic, 64, "%s/%s/ch%d/%s", topic, iv->name, rec->assign[i].ch, iv->getFieldName(i, rec));
snprintf(discoveryTopic, 64, "%s/sensor/%s/ch%d_%s/config", MQTT_DISCOVERY_PREFIX, iv->name, rec->assign[i].ch, iv->getFieldName(i, rec));
snprintf(uniq_id, 32, "ch%d_%s", rec->assign[i].ch, iv->getFieldName(i, rec));
const char *devCls = getFieldDeviceClass(rec->assign[i].fieldId);
const char *stateCls = getFieldStateClass(rec->assign[i].fieldId);
doc["name"] = name;
doc["stat_t"] = stateTopic;
doc["unit_of_meas"] = iv->getUnit(i, rec);
doc["uniq_id"] = String(iv->serial.u64, HEX) + "_" + uniq_id;
doc["dev"] = deviceObj;
doc["exp_aft"] = invertval + 5; // add 5 sec if connection is bad or ESP too slow @TODO: stimmt das wirklich als expire!?
if (devCls != NULL)
doc["dev_cla"] = devCls;
if (stateCls != NULL)
doc["stat_cla"] = stateCls;
serializeJson(doc, buffer);
sendMsg2(discoveryTopic, buffer, true);
// DPRINTLN(DBG_INFO, F("mqtt sent"));
doc.clear();
}
yield();
}
}
}
void sendIvData(HMSYSTEM *sys, uint32_t mUtcTs, std::queue<uint8_t> list) {
isConnected(true); // really needed? See comment from HorstG-57 #176
char topic[32 + MAX_NAME_LENGTH], val[32];
float total[4];
bool sendTotal = false;
bool totalIncomplete = false;
snprintf(val, 32, "%ld", millis() / 1000);
sendMsg("uptime", val);
if(list.empty())
return;
while(!list.empty()) {
memset(total, 0, sizeof(float) * 4);
for (uint8_t id = 0; id < sys->getNumInverters(); id++) {
Inverter<> *iv = sys->getInverterByPos(id);
if (NULL == iv)
continue; // skip to next inverter
record_t<> *rec = iv->getRecordStruct(list.front());
if(list.front() == RealTimeRunData_Debug) {
// inverter status
uint8_t status = MQTT_STATUS_AVAIL_PROD;
if (!iv->isAvailable(mUtcTs, rec)) {
status = MQTT_STATUS_NOT_AVAIL_NOT_PROD;
totalIncomplete = true;
}
else if (!iv->isProducing(mUtcTs, rec)) {
if (MQTT_STATUS_AVAIL_PROD == status)
status = MQTT_STATUS_AVAIL_NOT_PROD;
}
snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/available_text", iv->name);
snprintf(val, 32, "%s%s%s%s",
(MQTT_STATUS_NOT_AVAIL_NOT_PROD) ? "not yet " : "",
"available and ",
(MQTT_STATUS_AVAIL_NOT_PROD) ? "not " : "",
(MQTT_STATUS_NOT_AVAIL_NOT_PROD) ? "" : "producing"
);
sendMsg(topic, val);
snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/available", iv->name);
snprintf(val, 32, "%d", status);
sendMsg(topic, val);
snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/last_success", iv->name);
snprintf(val, 48, "%i", iv->getLastTs(rec) * 1000);
sendMsg(topic, val);
}
//-----------------------------------------------------------------------------
const char *getFieldDeviceClass(uint8_t fieldId) {
uint8_t pos = 0;
for (; pos < DEVICE_CLS_ASSIGN_LIST_LEN; pos++) {
if (deviceFieldAssignment[pos].fieldId == fieldId)
break;
}
return (pos >= DEVICE_CLS_ASSIGN_LIST_LEN) ? NULL : deviceClasses[deviceFieldAssignment[pos].deviceClsId];
}
// data
if(iv->isAvailable(mUtcTs, rec)) {
for (uint8_t i = 0; i < rec->length; i++) {
snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/ch%d/%s", iv->name, rec->assign[i].ch, fields[rec->assign[i].fieldId]);
snprintf(val, 10, "%.3f", iv->getValue(i, rec));
sendMsg(topic, val);
// calculate total values for RealTimeRunData_Debug
if (list.front() == RealTimeRunData_Debug) {
if (CH0 == rec->assign[i].ch) {
switch (rec->assign[i].fieldId) {
case FLD_PAC:
total[0] += iv->getValue(i, rec);
break;
case FLD_YT:
total[1] += iv->getValue(i, rec);
break;
case FLD_YD:
total[2] += iv->getValue(i, rec);
break;
case FLD_PDC:
total[3] += iv->getValue(i, rec);
break;
}
}
sendTotal = true;
}
yield();
}
}
}
//-----------------------------------------------------------------------------
const char *getFieldStateClass(uint8_t fieldId) {
uint8_t pos = 0;
for (; pos < DEVICE_CLS_ASSIGN_LIST_LEN; pos++) {
if (deviceFieldAssignment[pos].fieldId == fieldId)
break;
}
return (pos >= DEVICE_CLS_ASSIGN_LIST_LEN) ? NULL : stateClasses[deviceFieldAssignment[pos].stateClsId];
}
list.pop(); // remove from list once all inverters were processed
if ((true == sendTotal) && (false == totalIncomplete)) {
uint8_t fieldId;
for (uint8_t i = 0; i < 4; i++) {
switch (i) {
default:
case 0:
fieldId = FLD_PAC;
break;
case 1:
fieldId = FLD_YT;
break;
case 2:
fieldId = FLD_YD;
break;
case 3:
fieldId = FLD_PDC;
break;
}
snprintf(topic, 32 + MAX_NAME_LENGTH, "total/%s", fields[fieldId]);
snprintf(val, 10, "%.3f", total[i]);
sendMsg(topic, val);
}
}
}
}
const char *getFieldDeviceClass(uint8_t fieldId) {
uint8_t pos = 0;
for (; pos < DEVICE_CLS_ASSIGN_LIST_LEN; pos++) {
if (deviceFieldAssignment[pos].fieldId == fieldId)
break;
}
return (pos >= DEVICE_CLS_ASSIGN_LIST_LEN) ? NULL : deviceClasses[deviceFieldAssignment[pos].deviceClsId];
}
const char *getFieldStateClass(uint8_t fieldId) {
uint8_t pos = 0;
for (; pos < DEVICE_CLS_ASSIGN_LIST_LEN; pos++) {
if (deviceFieldAssignment[pos].fieldId == fieldId)
break;
}
return (pos >= DEVICE_CLS_ASSIGN_LIST_LEN) ? NULL : stateClasses[deviceFieldAssignment[pos].stateClsId];
}
private:
void reconnect(void) {

Loading…
Cancel
Save