From a4944400d226c5ae49c40aa690e4b3ece39d01f2 Mon Sep 17 00:00:00 2001 From: IgorYbema Date: Tue, 3 Sep 2024 10:19:02 +0200 Subject: [PATCH] adding OPT topics to JSON output --- HeishaMon/HeishaMon.ino | 4 +-- HeishaMon/commands.h | 3 ++- HeishaMon/decode.cpp | 22 +++++++++++++--- HeishaMon/decode.h | 11 ++++++++ HeishaMon/htmlcode.h | 14 ++++++++++ HeishaMon/webfunctions.cpp | 53 ++++++++++++++++++++++++++++++++++++-- HeishaMon/webfunctions.h | 2 +- 7 files changed, 98 insertions(+), 11 deletions(-) diff --git a/HeishaMon/HeishaMon.ino b/HeishaMon/HeishaMon.ino index 5e3a08d3..cc508398 100644 --- a/HeishaMon/HeishaMon.ino +++ b/HeishaMon/HeishaMon.ino @@ -106,7 +106,6 @@ Adafruit_NeoPixel pixels(1, LEDPIN); // store actual data char actData[DATASIZE] = { '\0' }; char actDataExtra[DATASIZE] = { '\0' }; -#define OPTDATASIZE 20 char actOptData[OPTDATASIZE] = { '\0' }; // log message to sprintf to @@ -599,7 +598,6 @@ bool readSerial() else if (data_length == OPTDATASIZE ) { //optional pcb acknowledge answer log_message(_F("Received optional PCB ack answer. Decoding this in OPT topics.")); decode_optional_heatpump_data(data, actOptData, mqtt_client, log_message, heishamonSettings.mqtt_topic_base, heishamonSettings.updateAllTime); - memcpy(actOptData, data, OPTDATASIZE); data_length = 0; return true; } @@ -949,7 +947,7 @@ int8_t webserver_cb(struct webserver_t *client, void *dat) { return handleRoot(client, readpercentage, mqttReconnects, &heishamonSettings); } break; case 20: { - return handleJsonOutput(client, actData, actDataExtra, &heishamonSettings, extraDataBlockAvailable); + return handleJsonOutput(client, actData, actDataExtra, actOptData, &heishamonSettings, extraDataBlockAvailable); } break; case 30: { return handleReboot(client); diff --git a/HeishaMon/commands.h b/HeishaMon/commands.h index f6d040ad..6ed49d8d 100644 --- a/HeishaMon/commands.h +++ b/HeishaMon/commands.h @@ -3,13 +3,14 @@ #include #define DATASIZE 203 + #define INITIALQUERYSIZE 7 extern byte initialQuery[INITIALQUERYSIZE]; #define PANASONICQUERYSIZE 110 extern byte panasonicQuery[PANASONICQUERYSIZE]; - +#define OPTDATASIZE 20 #define OPTIONALPCBQUERYTIME 1000 //send optional pcb query each second #define OPTIONALPCBQUERYSIZE 19 #define OPTIONALPCBSAVETIME 300 //save each 5 minutes the current optional pcb state into flash to have valid values during reboot diff --git a/HeishaMon/decode.cpp b/HeishaMon/decode.cpp index 7ba20b53..6691adff 100644 --- a/HeishaMon/decode.cpp +++ b/HeishaMon/decode.cpp @@ -351,23 +351,29 @@ void decode_heatpump_data_extra(char* data, char* actDataExtra, PubSubClient &mq } void decode_optional_heatpump_data(char* data, char* actOptData, PubSubClient & mqtt_client, void (*log_message)(char*), char* mqtt_topic_base, unsigned int updateAllTime) { - bool updatenow = false; + bool updateTime = false; + bool updateTopic[NUMBER_OF_OPT_TOPICS] = { false }; + if ((lastalloptdatatime == 0) || ((unsigned long)(millis() - lastalloptdatatime) > (1000 * updateAllTime))) { - updatenow = true; + updateTime = true; lastalloptdatatime = millis(); } for (unsigned int Topic_Number = 0 ; Topic_Number < NUMBER_OF_OPT_TOPICS ; Topic_Number++) { String Topic_Value; Topic_Value = getOptDataValue(data, Topic_Number); - if ((updatenow) || ( getOptDataValue(actOptData, Topic_Number) != Topic_Value )) { + if(getOptDataValue(actOptData, Topic_Number) != Topic_Value) { + updateTopic[Topic_Number] = true; + } + + if (updateTime || updateTopic[Topic_Number]) { char log_msg[256]; char mqtt_topic[256]; sprintf_P(log_msg, PSTR("received OPT%d %s: %s"), Topic_Number, optTopics[Topic_Number], Topic_Value.c_str()); log_message(log_msg); sprintf_P(mqtt_topic, PSTR("%s/%s/%s"), mqtt_topic_base, mqtt_topic_pcbvalues, optTopics[Topic_Number]); mqtt_client.publish(mqtt_topic, Topic_Value.c_str(), MQTT_RETAIN_VALUES); - rules_event_cb(_F("@"), optTopics[Topic_Number]); + } } //response to heatpump should contain the data from heatpump on byte 4 and 5 @@ -375,4 +381,12 @@ void decode_optional_heatpump_data(char* data, char* actOptData, PubSubClient & optionalPCBQuery[4] = valueByte4; byte valueByte5 = data[5]; optionalPCBQuery[5] = valueByte5; + + memcpy(actOptData, data, OPTDATASIZE); + for (unsigned int Topic_Number = 0 ; Topic_Number < NUMBER_OF_OPT_TOPICS ; Topic_Number++) { + if(updateTopic[Topic_Number]) { + rules_event_cb(_F("@"), optTopics[Topic_Number]); + } + } + } diff --git a/HeishaMon/decode.h b/HeishaMon/decode.h index c7f08bc8..8d63b2d8 100644 --- a/HeishaMon/decode.h +++ b/HeishaMon/decode.h @@ -557,6 +557,7 @@ static const char *OpModeDesc[] PROGMEM = {"9", "Heat", "Cool", "Auto(heat)", "D static const char *Powerfulmode[] PROGMEM = {"4", "Off", "30min", "60min", "90min"}; static const char *Quietmode[] PROGMEM = {"4", "Off", "Level 1", "Level 2", "Level 3"}; static const char *Valve[] PROGMEM = {"2", "Room", "DHW"}; +static const char *MixingValve[] PROGMEM = {"4", "Off", "Increase","Nothing","Decrease"}; static const char *LitersPerMin[] PROGMEM = {"0", "l/min"}; static const char *RotationsPerMin[] PROGMEM = {"0", "r/min"}; static const char *Bar[] PROGMEM = {"0", "Bar"}; @@ -579,6 +580,16 @@ static const char *LiquidType[] PROGMEM = {"2", "Water", "Glycol"}; static const char *ExtPadHeaterType[] PROGMEM = {"3", "Disabled", "Type-A","Type-B"}; +static const char **opttopicDescription[] PROGMEM = { + OffOn, //OPT0 + MixingValve, //OPT1 + OffOn, //OPT2 + MixingValve, //OPT3 + OffOn, //OPT4 + OffOn, //OPT5 + OffOn, //OPT6 +}; + static const char **xtopicDescription[] PROGMEM = { Watt, //XTOP0 Watt, //XTOP1 diff --git a/HeishaMon/htmlcode.h b/HeishaMon/htmlcode.h index 116102ce..aefaf34f 100644 --- a/HeishaMon/htmlcode.h +++ b/HeishaMon/htmlcode.h @@ -116,6 +116,20 @@ static const char refreshJS[] PROGMEM = " tableBody.appendChild(row);" " });" " }" + " if (jsonData?.['heatpump optional'] && Array.isArray(jsonData['heatpump optional'])) {" + " const tableBody = document.getElementById('heishavalues');" + " jsonData['heatpump optional'].forEach(item => {" + " const row = document.createElement('tr');" + " for (const key in item) {" + " if (Object.hasOwn(item,key)) {" + " const cell = document.createElement('td');" + " cell.textContent = item[key];" + " row.appendChild(cell);" + " }" + " }" + " tableBody.appendChild(row);" + " });" + " }" " if (jsonData?.['1wire'] && Array.isArray(jsonData['1wire'])) {" " const tableBody = document.getElementById('dallasvalues');" " tableBody.innerHTML = '';" diff --git a/HeishaMon/webfunctions.cpp b/HeishaMon/webfunctions.cpp index 0ccb67b2..84c18929 100755 --- a/HeishaMon/webfunctions.cpp +++ b/HeishaMon/webfunctions.cpp @@ -1125,8 +1125,9 @@ int handleRoot(struct webserver_t *client, float readpercentage, int mqttReconne return 0; } -int handleJsonOutput(struct webserver_t *client, char* actData, char* actDataExtra, settingsStruct *heishamonSettings, bool extraDataBlockAvailable) { +int handleJsonOutput(struct webserver_t *client, char* actData, char* actDataExtra, char* actOptData, settingsStruct *heishamonSettings, bool extraDataBlockAvailable) { int extraTopics = extraDataBlockAvailable ? NUMBER_OF_TOPICS_EXTRA : 0; //set to 0 if there is no datablock so we don't run json data for it + int numOptTopics = heishamonSettings->optionalPCB ? NUMBER_OF_OPT_TOPICS : 0; //set to 0 if there is no optionalPCB emulation so we don't run json data for it if (client->content == 0) { webserver_send(client, 200, (char *)"application/json", 0); webserver_send_content_P(client, PSTR("{\"heatpump\":["), 13); @@ -1234,7 +1235,55 @@ int handleJsonOutput(struct webserver_t *client, char* actData, char* actDataExt client->content++; } client->content--; // The webserver also increases by 1 - } else if (client->content == (NUMBER_OF_TOPICS + extraTopics + 1)) { + } else if ((client->content - NUMBER_OF_TOPICS - extraTopics - 1) < numOptTopics) { + if (client->content == NUMBER_OF_TOPICS + extraTopics + 1) { + webserver_send_content_P(client, PSTR("],\"heatpump optional\":["), 23); + } + uint8_t maxTopics = client->content - NUMBER_OF_TOPICS + extraTopics + 4; //limit the amount of topic sent per webloop + for (uint8_t topic = (client->content - NUMBER_OF_TOPICS - extraTopics - 1); topic < numOptTopics && topic < maxTopics ; topic++) { + + webserver_send_content_P(client, PSTR("{\"Topic\":\"OPT"), 13); + + { + char str[12]; + itoa(topic, str, 10); + webserver_send_content(client, str, strlen(str)); + } + + webserver_send_content_P(client, PSTR("\",\"Name\":\""), 10); + webserver_send_content_P(client, optTopics[topic], strlen_P(optTopics[topic])); + + webserver_send_content_P(client, PSTR("\",\"Value\":\""), 11); + + { + String dataValue = getOptDataValue(actOptData, topic); + char* str = (char *)dataValue.c_str(); + webserver_send_content(client, str, strlen(str)); + } + + webserver_send_content_P(client, PSTR("\",\"Description\":\""), 17); + + int maxvalue = atoi(opttopicDescription[topic][0]); + int value = actOptData[0] == '\0' ? 0 : getOptDataValue(actDataExtra, topic).toInt(); + if (maxvalue == 0) { //this takes the special case where the description is a real value description instead of a mode, so value should take first index (= 0 + 1) + value = 0; + } + if ((value < 0) || (value > maxvalue)) { + webserver_send_content_P(client, _unknown, strlen_P(_unknown)); + } + else { + webserver_send_content_P(client, opttopicDescription[topic][value + 1], strlen_P(opttopicDescription[topic][value + 1])); + } + + webserver_send_content_P(client, PSTR("\"}"), 2); + + if (topic < (numOptTopics - 1)) { + webserver_send_content_P(client, PSTR(","), 1); + } + client->content++; + } + client->content--; // The webserver also increases by 1 + } else if (client->content == (NUMBER_OF_TOPICS + extraTopics + numOptTopics + 1)) { webserver_send_content_P(client, PSTR("]"), 1); if (heishamonSettings->use_1wire) { webserver_send_content_P(client, PSTR(",\"1wire\":"), 9); diff --git a/HeishaMon/webfunctions.h b/HeishaMon/webfunctions.h index 62854f89..79aa05e4 100644 --- a/HeishaMon/webfunctions.h +++ b/HeishaMon/webfunctions.h @@ -83,7 +83,7 @@ void log_message(char *string); int8_t webserver_cb(struct webserver_t *client, void *data); void getWifiScanResults(int numSsid); int handleRoot(struct webserver_t *client, float readpercentage, int mqttReconnects, settingsStruct *heishamonSettings); -int handleJsonOutput(struct webserver_t *client, char* actData, char* actDataExtra, settingsStruct *heishamonSettings, bool extraDataBlockAvailable); +int handleJsonOutput(struct webserver_t *client, char* actData, char* actDataExtra, char* actOptData, settingsStruct *heishamonSettings, bool extraDataBlockAvailable); int handleFactoryReset(struct webserver_t *client); int handleReboot(struct webserver_t *client); int handleDebug(struct webserver_t *client, char *hex, byte hex_len);