Skip to content

Commit

Permalink
Websocket data refresh (#151)
Browse files Browse the repository at this point in the history
Add realtime refresh of data in webgui using websocket
  • Loading branch information
IgorYbema authored Jan 14, 2025
1 parent 7ca46cc commit 38c7996
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 84 deletions.
23 changes: 10 additions & 13 deletions HeishaMon/HeishaMon.ino
Original file line number Diff line number Diff line change
Expand Up @@ -356,11 +356,10 @@ void mqtt_reconnect()
resetlastalldatatime(); //resend all heatpump values to mqtt
}
//use this to receive valid heishamon raw data from other heishamon to debug this OT code
#define OTDEBUG
#ifdef OTDEBUG
if ( heishamonSettings.listenonly && heishamonSettings.listenmqtt ) {
sprintf(topic, "%s/raw/data", heishamonSettings.mqtt_topic_listen);
mqtt_client.subscribe(topic); //subscribe to raw heatpump data over MQTT
//#define RAWDEBUG
#ifdef RAWDEBUG
if ( heishamonSettings.listenonly) {
mqtt_client.subscribe((char*)"panasonic_heat_pump/raw/data"); //subscribe to raw heatpump data over MQTT
}
#endif
}
Expand Down Expand Up @@ -389,7 +388,7 @@ void log_message(char* string)
struct tm *timeinfo = localtime(&rawtime);
char timestring[32];
strftime(timestring, 32, "%c", timeinfo);
size_t len = strlen(string) + strlen(timestring) + 20; //+20 long enough to contain millis()
size_t len = strlen(string) + strlen(timestring) + 32; //+32 long enough to contain millis() and the json part later for websocket mesg
char* log_line = (char *) malloc(len);
snprintf(log_line, len, "%s (%lu): %s", timestring, millis(), string);

Expand All @@ -410,11 +409,10 @@ void log_message(char* string)
mqtt_client.disconnect();
}
}
char* websocketMsg = (char *) malloc(len+12);
snprintf(websocketMsg, len+12, "{\"logMsg\":\"%s\"}", log_line);
//send log message to websocket
snprintf(log_line, len+12, "{\"logMsg\":\"%s (%lu): %s\"}", timestring, millis(), string);
websocket_write_all(log_line, strlen(log_line));
free(log_line);
websocket_write_all(websocketMsg, strlen(websocketMsg));
free(websocketMsg);
#ifdef ESP32
if (!inSetup) blinkNeoPixel(false);
#endif
Expand Down Expand Up @@ -709,10 +707,9 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
{
char* topic_sendcommand = topic_command + strlen(mqtt_topic_commands) + 1; //strip the first 9 "commands/" from the topic to get what we need
send_heatpump_command(topic_sendcommand, msg, send_command, log_message, heishamonSettings.optionalPCB);
}
//use this to receive valid heishamon raw data from other heishamon to debug this OT code
#ifdef OTDEBUG
else if (strcmp((char*)"panasonic_heat_pump/data", topic) == 0) { // check for raw heatpump input
#ifdef RAWDEBUG
} else if (strcmp((char*)"panasonic_heat_pump/raw/data", topic) == 0) { // check for raw heatpump input
sprintf_P(log_msg, PSTR("Received raw heatpump data from MQTT"));
log_message(log_msg);
decode_heatpump_data(msg, actData, mqtt_client, log_message, heishamonSettings.mqtt_topic_base, heishamonSettings.updateAllTime);
Expand Down
22 changes: 21 additions & 1 deletion HeishaMon/HeishaOT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,20 @@ void processOTRequest(unsigned long request, OpenThermResponseStatus status) {
if ((bool)CHEnable != getOTStructMember(_F("chEnable"))->value.b) { //only publish if changed
getOTStructMember(_F("chEnable"))->value.b = (bool)CHEnable;
CHEnable ? mqttPublish((char*)mqtt_topic_opentherm_write, _F("chEnable"), _F("true")) : mqttPublish((char*)mqtt_topic_opentherm_write, _F("chEnable"), _F("false")) ;
sprintf_P(log_msg, PSTR("{\"data\": {\"opentherm\": {\"name\": \"%s\", \"value\": %s}}}"), _F("chEnable"), CHEnable ? _F("true") : _F("false"));
websocket_write_all(log_msg, strlen(log_msg));
}
if ((bool)DHWEnable != getOTStructMember(_F("dhwEnable"))->value.b) { //only publish if changed
getOTStructMember(_F("dhwEnable"))->value.b = (bool)DHWEnable;
DHWEnable ? mqttPublish((char*)mqtt_topic_opentherm_write, _F("dhwEnable"), _F("true")) : mqttPublish((char*)mqtt_topic_opentherm_write, _F("dhwEnable"), _F("false")) ;
sprintf_P(log_msg, PSTR("{\"data\": {\"opentherm\": {\"name\": \"%s\", \"value\": %s}}}"), _F("dhwEnable"), DHWEnable ? _F("true") : _F("false"));
websocket_write_all(log_msg, strlen(log_msg));
}
if ((bool)Cooling != getOTStructMember(_F("coolingEnable"))->value.b) { //only publish if changed
getOTStructMember(_F("coolingEnable"))->value.b = (bool)Cooling;
Cooling ? mqttPublish((char*)mqtt_topic_opentherm_write, _F("coolingEnable"), _F("true")) : mqttPublish((char*)mqtt_topic_opentherm_write, _F("coolingEnable"), _F("false")) ;
sprintf_P(log_msg, PSTR("{\"data\": {\"opentherm\": {\"name\": \"%s\", \"value\": %s}}}"), _F("coolingEnable"), Cooling ? _F("true") : _F("false"));
websocket_write_all(log_msg, strlen(log_msg));
}

sprintf_P(log_msg, PSTR(
Expand Down Expand Up @@ -129,6 +135,8 @@ void processOTRequest(unsigned long request, OpenThermResponseStatus status) {
if (getOTStructMember(_F("chSetpoint"))->value.f != ot.getFloat(request)) { //only publish if changed
getOTStructMember(_F("chSetpoint"))->value.f = ot.getFloat(request);
mqttPublish((char*)mqtt_topic_opentherm_write, _F("chSetpoint"), str);
sprintf_P(log_msg, PSTR("{\"data\": {\"opentherm\": {\"name\": \"%s\", \"value\": %.2f}}}"), _F("chSetpoint"), getOTStructMember(_F("chSetpoint"))->value.f);
websocket_write_all(log_msg, strlen(log_msg));
}
otResponse = ot.buildResponse(OpenThermMessageType::WRITE_ACK, OpenThermMessageID::TSet, request & 0xffff);
rules_event_cb(_F("?"), _F("chsetpoint"));
Expand Down Expand Up @@ -170,6 +178,8 @@ void processOTRequest(unsigned long request, OpenThermResponseStatus status) {
getOTStructMember(_F("relativeModulation"))->value.f = getOTStructMember(_F("maxRelativeModulation"))->value.f;
}
mqttPublish((char*)mqtt_topic_opentherm_write, _F("maxRelativeModulation"), str);
sprintf_P(log_msg, PSTR("{\"data\": {\"opentherm\": {\"name\": \"%s\", \"value\": %.2f}}}"), _F("maxRelativeModulation"), getOTStructMember(_F("maxRelativeModulation"))->value.f);
websocket_write_all(log_msg, strlen(log_msg));
}
otResponse = ot.buildResponse(OpenThermMessageType::WRITE_ACK, OpenThermMessageID::MaxRelModLevelSetting, request & 0xffff); //ACK for mandatory fields
} break;
Expand Down Expand Up @@ -212,6 +222,8 @@ void processOTRequest(unsigned long request, OpenThermResponseStatus status) {
if (getOTStructMember(_F("coolingControl"))->value.f != ot.getFloat(request)) {
getOTStructMember(_F("coolingControl"))->value.f = ot.getFloat(request);
mqttPublish((char*)mqtt_topic_opentherm_write, _F("coolingControl"), str);
sprintf_P(log_msg, PSTR("{\"data\": {\"opentherm\": {\"name\": \"%s\", \"value\": %.2f}}}"), _F("coolingControl"), getOTStructMember(_F("coolingControl"))->value.f);
websocket_write_all(log_msg, strlen(log_msg));
}
otResponse = ot.buildResponse(OpenThermMessageType::WRITE_ACK, OpenThermMessageID::CoolingControl, request & 0xffff);
rules_event_cb(_F("?"), _F("coolingControl"));
Expand All @@ -238,6 +250,8 @@ void processOTRequest(unsigned long request, OpenThermResponseStatus status) {
if (getOTStructMember(_F("roomTemp"))->value.f != ot.getFloat(request)) {
mqttPublish((char*)mqtt_topic_opentherm_write, _F("roomTemp"), str);
getOTStructMember(_F("roomTemp"))->value.f = ot.getFloat(request);
sprintf_P(log_msg, PSTR("{\"data\": {\"opentherm\": {\"name\": \"%s\", \"value\": %.2f}}}"), _F("roomTemp"), getOTStructMember(_F("roomTemp"))->value.f);
websocket_write_all(log_msg, strlen(log_msg));
}
otResponse = ot.buildResponse(OpenThermMessageType::WRITE_ACK, OpenThermMessageID::Tr, request & 0xffff);
rules_event_cb(_F("?"), _F("roomtemp"));
Expand All @@ -250,6 +264,8 @@ void processOTRequest(unsigned long request, OpenThermResponseStatus status) {
if (getOTStructMember(_F("roomTempSet"))->value.f != ot.getFloat(request)) {
getOTStructMember(_F("roomTempSet"))->value.f = ot.getFloat(request);
mqttPublish((char*)mqtt_topic_opentherm_write, _F("roomTempSet"), str);
sprintf_P(log_msg, PSTR("{\"data\": {\"opentherm\": {\"name\": \"%s\", \"value\": %.2f}}}"), _F("roomTempSet"), getOTStructMember(_F("roomTempSet"))->value.f);
websocket_write_all(log_msg, strlen(log_msg));
}
otResponse = ot.buildResponse(OpenThermMessageType::WRITE_ACK, OpenThermMessageID::TrSet, request & 0xffff);
rules_event_cb(_F("?"), _F("roomtempset"));
Expand All @@ -262,7 +278,9 @@ void processOTRequest(unsigned long request, OpenThermResponseStatus status) {
log_message(log_msg);
if (getOTStructMember(_F("dhwSetpoint"))->value.f != ot.getFloat(request)) {
getOTStructMember(_F("dhwSetpoint"))->value.f = ot.getFloat(request);
mqttPublish((char*)mqtt_topic_opentherm_write, _F("dhwSetpoint"), str);
mqttPublish((char*)mqtt_topic_opentherm_write, _F("dhwSetpoint"), str);
sprintf_P(log_msg, PSTR("{\"data\": {\"opentherm\": {\"name\": \"%s\", \"value\": %.2f}}}"), _F("dhwSetpoint"), getOTStructMember(_F("dhwSetpoint"))->value.f);
websocket_write_all(log_msg, strlen(log_msg));
}
otResponse = ot.buildResponse(OpenThermMessageType::WRITE_ACK, OpenThermMessageID::TdhwSet, ot.temperatureToData(getOTStructMember(_F("dhwSetpoint"))->value.f));
} else { //READ_DATA
Expand All @@ -281,6 +299,8 @@ void processOTRequest(unsigned long request, OpenThermResponseStatus status) {
if (getOTStructMember(_F("maxTSet"))->value.f != ot.getFloat(request)) {
getOTStructMember(_F("maxTSet"))->value.f = ot.getFloat(request);
mqttPublish((char*)mqtt_topic_opentherm_write, _F("maxTSet"), str);
sprintf_P(log_msg, PSTR("{\"data\": {\"opentherm\": {\"name\": \"%s\", \"value\": %.2f}}}"), _F("maxTSet"), getOTStructMember(_F("maxTSet"))->value.f);
websocket_write_all(log_msg, strlen(log_msg));
}
otResponse = ot.buildResponse(OpenThermMessageType::WRITE_ACK, OpenThermMessageID::MaxTSet, ot.temperatureToData(getOTStructMember(_F("maxTSet"))->value.f));
} else { //READ_DATA
Expand Down
2 changes: 2 additions & 0 deletions HeishaMon/dallas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ void readNewDallasTemp(PubSubClient &mqtt_client, void (*log_message)(char*), ch
sprintf_P(valueStr, PSTR("{\"Temperature\":%.2f,\"Alias\":\"%s\"}"), actDallasData[i].temperature, actDallasData[i].alias);
sprintf_P(mqtt_topic, PSTR("%s/%s/%s"), mqtt_topic_base, mqtt_topic_1wire, actDallasData[i].address); mqtt_client.publish(mqtt_topic, valueStr, MQTT_RETAIN_VALUES);
}
sprintf_P(log_msg, PSTR("{\"data\": {\"dallasvalues\": {\"sensorID\": \"%s\", \"value\": %.2f}}}"), actDallasData[i].address, actDallasData[i].temperature);
websocket_write_all(log_msg, strlen(log_msg));
rules_event_cb(_F("ds18b20#"), actDallasData[i].address);
}
}
Expand Down
32 changes: 32 additions & 0 deletions HeishaMon/decode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include "rules.h"
#include "src/common/progmem.h"

void websocket_write_all(char *data, uint16_t data_len);

unsigned long lastalldatatime = 0;
unsigned long lastallextradatatime = 0;
unsigned long lastalloptdatatime = 0;
Expand Down Expand Up @@ -311,6 +313,19 @@ void decode_heatpump_data(char* data, char* actData, PubSubClient &mqtt_client,
memcpy(actData, data, DATASIZE);
for (unsigned int Topic_Number = 0 ; Topic_Number < NUMBER_OF_TOPICS ; Topic_Number++) {
if(updateTopic[Topic_Number]) {
char log_msg[256];
int maxvalue = atoi(topicDescription[Topic_Number][0]);
String dataValue = getDataValue(actData, Topic_Number);
if (maxvalue == 0) { //this takes the special case where the description is a real value description instead of a mode, so get description index 1
if ((Topic_Number != 44) && (Topic_Number != 92)) {
sprintf_P(log_msg, PSTR("{\"data\": {\"heishavalues\": {\"topic\": \"TOP%u\", \"value\": %s, \"description\": \"%s\"}}}"), Topic_Number, dataValue.c_str(),topicDescription[Topic_Number][1]);
} else {
sprintf_P(log_msg, PSTR("{\"data\": {\"heishavalues\": {\"topic\": \"TOP%u\", \"value\": \"%s\", \"description\": \"%s\"}}}"), Topic_Number, dataValue.c_str(),topicDescription[Topic_Number][1]);
}
} else {
sprintf_P(log_msg, PSTR("{\"data\": {\"heishavalues\": {\"topic\": \"TOP%u\", \"value\": %s, \"description\": \"%s\"}}}"), Topic_Number, dataValue.c_str(),topicDescription[Topic_Number][dataValue.toInt() + 1]);
}
websocket_write_all(log_msg, strlen(log_msg));
rules_event_cb(_F("@"), topics[Topic_Number]);
}
}
Expand Down Expand Up @@ -344,6 +359,15 @@ void decode_heatpump_data_extra(char* data, char* actDataExtra, PubSubClient &mq
memcpy(actDataExtra, data, DATASIZE);
for (unsigned int Topic_Number = 0 ; Topic_Number < NUMBER_OF_TOPICS_EXTRA ; Topic_Number++) {
if(updateTopic[Topic_Number]) {
char log_msg[256];
int maxvalue = atoi(xtopicDescription[Topic_Number][0]);
String dataValue = getDataValueExtra(actDataExtra, Topic_Number);
if (maxvalue == 0) { //this takes the special case where the description is a real value description instead of a mode, so get description index 1
sprintf_P(log_msg, PSTR("{\"data\": {\"heishavalues\": {\"topic\": \"XTOP%u\", \"value\": %s, \"description\": \"%s\"}}}"), Topic_Number, dataValue.c_str(),xtopicDescription[Topic_Number][1]);
} else {
sprintf_P(log_msg, PSTR("{\"data\": {\"heishavalues\": {\"topic\": \"XTOP%u\", \"value\": %s, \"description\": \"%s\"}}}"), Topic_Number, dataValue.c_str(),xtopicDescription[Topic_Number][dataValue.toInt() + 1]);
}
websocket_write_all(log_msg, strlen(log_msg));
rules_event_cb(_F("@"), xtopics[Topic_Number]);
}
}
Expand Down Expand Up @@ -384,6 +408,14 @@ void decode_optional_heatpump_data(char* data, char* actOptData, PubSubClient &
memcpy(actOptData, data, OPTDATASIZE);
for (unsigned int Topic_Number = 0 ; Topic_Number < NUMBER_OF_OPT_TOPICS ; Topic_Number++) {
if(updateTopic[Topic_Number]) {
char log_msg[256];
int maxvalue = atoi(opttopicDescription[Topic_Number][0]);
String dataValue = getOptDataValue(actOptData, Topic_Number);
if (maxvalue == 0) { //this takes the special case where the description is a real value description instead of a mode, so get description index 1
sprintf_P(log_msg, PSTR("{\"data\": {\"heishavalues\": {\"topic\": \"OPTTOP%u\", \"value\": %s, \"description\": \"%s\"}}}"), Topic_Number, dataValue.c_str(),opttopicDescription[Topic_Number][1]);
} else {
sprintf_P(log_msg, PSTR("{\"data\": {\"heishavalues\": {\"topic\": \"OPTTOP%u\", \"value\": %s, \"description\": \"%s\"}}}"), Topic_Number, dataValue.c_str(),opttopicDescription[Topic_Number][dataValue.toInt() + 1]);
}
rules_event_cb(_F("@"), optTopics[Topic_Number]);
}
}
Expand Down
2 changes: 2 additions & 0 deletions HeishaMon/decode.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#define MQTT_RETAIN_VALUES 1

void resetlastalldatatime();
void websocket_write_all(char *data, uint16_t data_len);


String getDataValue(char* data, unsigned int Topic_Number);
String getDataValueExtra(char* data, unsigned int Topic_Number);
Expand Down
Loading

0 comments on commit 38c7996

Please sign in to comment.