Skip to content
Open

sync #161

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
da1e8bd
Update HeatPumpType.md
valmir1989 Aug 17, 2025
86f7cfa
Add KIT-SDC07KE5 and missing trailing pipe
Sep 2, 2025
1421bc1
Merge pull request #706 from valmir1989/patch-1
Egyras Sep 17, 2025
b9c0157
Merge branch 'master' into master
Egyras Sep 17, 2025
cf5eaf3
Merge pull request #710 from muckingaround/master
Egyras Sep 17, 2025
b270f33
Fix wrong rounding of floats
CurlyMoo Nov 12, 2025
e29e4ce
Add Quiet Mode Level configuration
bassmaster187 Nov 17, 2025
c6a2c64
Update HeishaMon.ino - added TLS support-part1
confuzius0815 Nov 29, 2025
0c42e9d
Update htmlcode.h added TLS support, part 2
confuzius0815 Nov 29, 2025
e942cb1
Update webfunctions.cpp TLS support - part 3
confuzius0815 Nov 29, 2025
6cd9dc2
Update webfunctions.h - added TLS support
confuzius0815 Nov 29, 2025
bc261c7
added TLS support (ESP32 only)
confuzius0815 Nov 29, 2025
ee246c5
Added Communication Reliability section to Readme
henryfalconer Dec 11, 2025
9eba0fb
Merge pull request #752 from CurlyMoo/fix-round
Egyras Dec 15, 2025
616905d
Merge pull request #756 from bassmaster187/patch-1
Egyras Dec 15, 2025
d487506
Merge pull request #767 from henryfalconer/master
Egyras Dec 15, 2025
61d00fc
Add SET39 - Set Heating Control
ghecker1 Dec 22, 2025
bc0e74d
Add TOP139 Heating Control
ghecker1 Dec 22, 2025
27cb2e2
Add SET40 - SetSmartDHW
ghecker1 Dec 22, 2025
9c784eb
Add TOP140 - Smart DHW
ghecker1 Dec 22, 2025
c6dce7f
Add TOP141 - Quiet_Mode_Priority
ghecker1 Dec 22, 2025
062d8e5
Add SET41 - SetQuietModePriority
ghecker1 Dec 22, 2025
5d93469
Added TLS documentation
confuzius0815 Dec 27, 2025
64e8b46
Update HeatPumpType.md - added KIT-ADC05K3E5
Xploder Jan 14, 2026
fb6fd90
Merge pull request #768 from ghecker1/l_series_topics
Egyras Jan 14, 2026
b60828e
Merge pull request #771 from confuzius0815/master
Egyras Jan 14, 2026
f034cec
Merge pull request #774 from Xploder/patch-1
Egyras Jan 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion HeatPumpType.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Assuming that bytes from #129 to #138 are unique for each model of Aquarea heat
|29 | E2 CF 0B 83 05 12 D0 0D 92 05 | WH-SQC12H9E8 | WH-UQ12HE8 | KIT-WQC12H9E8 | 12 | 3ph | T-CAP - Super Quiet |
|30 | E2 CF 0C 78 09 12 D0 0B 06 11 | WH-SXC12H6E5 | WH-UX12HE5 | KIT-WXC12H6E5 | 12 | 1ph | T-CAP |
|31 | C2 D3 0C 35 65 B2 D3 0B 96 65 | Monoblock | WH-MDC09J3E5 | Monoblock | 9 | 1ph | HP (new version?) |
|32 | 32 D4 0B 99 77 62 90 0B 01 78 | Monoblock | WH-MXC09J3E5 | Monoblock | 9 | 1ph | T-CAP
|32 | 32 D4 0B 99 77 62 90 0B 01 78 | Monoblock | WH-MXC09J3E5 | Monoblock | 9 | 1ph | T-CAP |
|33 | 42 D4 0B 15 76 12 D0 0B 10 11 | WH-ADC1216H6E5C | WH-UD12HE5 | KIT-ADC12HE5C-CL | 12 | 1ph| HP - All-In-One Compact |
|34 | E2 D5 0C 29 99 83 92 0C 28 98 | WH-ADC0509L3E5 | WH-WDG07LE5 | KIT-ADC07L3E5 | 7 | 1ph | HP - All-In-One L-series |
|35 | E2 CF 0D 85 05 12 D0 0E 94 05 | WH-SXC09H3E8 | WH-UX09HE8 | KIT-WXC09H3E8 | 9 | 3ph | T-CAP - new version |
Expand All @@ -60,6 +60,8 @@ Assuming that bytes from #129 to #138 are unique for each model of Aquarea heat
|53 | 42 D4 0B 83 71 32 D2 0C 44 55 | WH-ADC0309J3E5C | WH-UD03JE5 | KIT-ADC03JE5C-S | 3.2 | 1ph | HP - All-In-One Compact |
|54 | E2 CF 0C 74 09 12 D0 0E 94 05 | WH-ADC0916H9E8 | WH-UX09HE8 | KIT-AXC9HE8 | 9 | 3ph | T-CAP - All-In-One |
|55 | 12 D7 0D 98 11 33 94 0C 83 10 | WH-ADC0316M9E8AN2 | WH-WXG09ME8 | Monoblock | 9 | 2ph | T-CAP - M-series DHW 185l |
|56 | E2 D5 0B 08 95 02 D6 0F 67 95 | WH-SDC0309K3E5 | WH-UDZ07KE5 | KIT-SDC07KE5 | 7 | 1ph | HP - split K-series (sold in Poland) |
|57 | E2 D5 0D 36 99 02 D6 10 66 95 | WH-ADC0309K3E5AN | WH-UDZ05KE5 | KIT-ADC05K3E5AN | 5 | 1ph | HP - All-In-One K-series - AN |

All bytes are used for Heat Pump model identification in the code.

Expand Down
148 changes: 144 additions & 4 deletions HeishaMon/HeishaMon.ino
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,19 @@ static uint8_t cmdnrel = 0;


// mqtt
#ifdef TLS_SUPPORT
#include <WiFiClientSecure.h>
WiFiClientSecure mqtt_tls_client;
WiFiClient mqtt_wifi_client;
bool loadTlsCaFromFS(WiFiClientSecure &client);
static bool last_tls_enabled = false;
static bool new_ca_stored = false;
static std::unique_ptr<char[]> persistent_ca_pem;
PubSubClient mqtt_client;

#else
WiFiClient mqtt_wifi_client;
PubSubClient mqtt_client(mqtt_wifi_client);
#endif


bool firstConnectSinceBoot = true; //if this is true there is no first connection made yet
Expand Down Expand Up @@ -307,6 +317,34 @@ void check_wifi() {
}


#ifdef TLS_SUPPORT
bool loadTlsCaFromFS(WiFiClientSecure &client) {
if (!LittleFS.exists("/ca.pem")) {
log_message(_F("[TLS] /ca.pem not found"));
return false;
}
File certFile = LittleFS.open("/ca.pem", "r");
if (!certFile) {
log_message(_F("[TLS] open(/ca.pem) failed"));
return false;
}
size_t certSize = certFile.size();
if (certSize == 0) {
log_message(_F("[TLS] /ca.pem is empty"));
certFile.close();
return false;
}
persistent_ca_pem.reset(new char[certSize + 1]);
size_t n = certFile.readBytes(persistent_ca_pem.get(), certSize);
persistent_ca_pem[n] = '\0';
certFile.close();
client.setCACert(persistent_ca_pem.get());
log_message(_F("[TLS] CA loaded into client"));
return true;
}
#endif


void mqtt_reconnect()
{
unsigned long now = millis();
Expand All @@ -319,6 +357,36 @@ void mqtt_reconnect()
}
char topic[256];
sprintf(topic, "%s/%s", heishamonSettings.mqtt_topic_base, mqtt_willtopic);
#ifdef TLS_SUPPORT
if (heishamonSettings.mqtt_tls_enabled != last_tls_enabled) {
mqtt_client.disconnect();
if (last_tls_enabled) {
mqtt_tls_client.stop();
} else {
mqtt_wifi_client.stop();
if (!loadTlsCaFromFS(mqtt_tls_client)) {
log_message(_F("[TLS] Proceeding without valid CA (expect failure)"));
}
}
last_tls_enabled = heishamonSettings.mqtt_tls_enabled;
}

if (new_ca_stored) {
log_message(_F("[TLS] Trying to load new CA ertificate"));
if (!loadTlsCaFromFS(mqtt_tls_client)) {
log_message(_F("[TLS] Proceeding without valid CA (expect failure)"));
}
new_ca_stored = false;
}
if (heishamonSettings.mqtt_tls_enabled) {
mqtt_client.setClient(mqtt_tls_client);
} else {
mqtt_client.setClient(mqtt_wifi_client);
}
mqtt_client.setSocketTimeout(10);
mqtt_client.setKeepAlive(30);
mqtt_client.setServer(heishamonSettings.mqtt_server, atoi(heishamonSettings.mqtt_port));
#endif
if (mqtt_client.connect(heishamonSettings.wifi_hostname, heishamonSettings.mqtt_username, heishamonSettings.mqtt_password, topic, 1, true, "Offline"))
{
mqttReconnects++;
Expand Down Expand Up @@ -363,6 +431,20 @@ void mqtt_reconnect()
}
#endif
}
//#ifdef TLS_SUPPORT // error state is useful in any case
else {
int8_t err = mqtt_client.state();
log_message(_F("MQTT connect failed, state:"));
switch (err) {
case -1: log_message(_F(" -1 → TLS handshake or network error")); break;
case -2: log_message(_F(" -2 → Connection timeout – cannot reach broker or CA/time error")); break;
case -3: log_message(_F(" -3 → Server not found or rejected")); break;
case -4: log_message(_F(" -4 → Connection lost")); break;
case -5: log_message(_F(" -5 → Check username/password")); break;
default: log_message(_F(" → Unknown error")); break;
}
}
//#endif
}
}

Expand Down Expand Up @@ -809,6 +891,18 @@ int8_t webserver_cb(struct webserver_t *client, void *dat) {
LittleFS.remove("/rules.new");
client->userdata = new File(LittleFS.open("/rules.new", "a+"));
}
#ifdef TLS_SUPPORT
} else if (strcmp_P((char *)dat, PSTR("/cacert")) == 0) {
client->route = 165;
if (LittleFS.begin()) {
LittleFS.remove("/ca.tmp");
File cf = LittleFS.open("/ca.tmp", "w");
if (cf) {
client->userdata = new File(cf);
}
new_ca_stored = true;
}
#endif
} else if (strcmp_P((char *)dat, PSTR("/firmware")) == 0) {
if (!Update.isRunning()) {
#ifdef ESP8266
Expand Down Expand Up @@ -836,6 +930,10 @@ int8_t webserver_cb(struct webserver_t *client, void *dat) {
client->route = 140;
} else if (strcmp_P((char *)dat, PSTR("/rules")) == 0) {
client->route = 160;
#ifdef TLS_SUPPORT
} else if (strcmp_P((char *)dat, PSTR("/cacert")) == 0) {
client->route = 166;
#endif
} else if (strcmp_P((char *)dat, PSTR("/scandallas")) == 0) {
client->route = 180;
} else {
Expand Down Expand Up @@ -940,6 +1038,15 @@ int8_t webserver_cb(struct webserver_t *client, void *dat) {
f->write(args->value, args->len);
}
} break;
#ifdef TLS_SUPPORT
case 165: {
File *f = (File *)client->userdata;
if (f && *f && args->len > 0) {
f->write((const uint8_t*)args->value, (size_t)args->len);
}
return 0;
} break;
#endif
}
} break;
case WEBSERVER_CLIENT_HEADER: {
Expand Down Expand Up @@ -1065,6 +1172,20 @@ int8_t webserver_cb(struct webserver_t *client, void *dat) {
case 160: {
return showRules(client);
} break;
#ifdef TLS_SUPPORT
case 165: {
if (client->userdata) {
File *pf = (File *)client->userdata;
pf->close();
delete pf;
client->userdata = NULL;
}
return handleCACert(client);
} break;
case 166: {
return showCACert(client);
} break;
#endif
case 170: {
File *f = (File *)client->userdata;
if (f) {
Expand Down Expand Up @@ -1127,6 +1248,9 @@ int8_t webserver_cb(struct webserver_t *client, void *dat) {
}
} break;
case 160:
#ifdef TLS_SUPPORT
case 165:
#endif
case 170: {
if (client->userdata != NULL) {
File *f = (File *)client->userdata;
Expand Down Expand Up @@ -1270,9 +1394,21 @@ void switchSerial() {
}

void setupMqtt() {
mqtt_client.setClient(mqtt_wifi_client);
mqtt_client.setBufferSize(1024);
#ifdef TLS_SUPPORT
mqtt_client.setSocketTimeout(8); mqtt_client.setKeepAlive(30); //fast timeout, any slower than 10s will block the main loop too long (8s might be even safer to avoid reboots on bad wifi); short keepalive may lead to problems with TLS
if (heishamonSettings.mqtt_tls_enabled) {
if (!loadTlsCaFromFS(mqtt_tls_client)) {
log_message(_F("[TLS] Proceeding without valid CA (expect failure)"));
}
mqtt_client.setClient(mqtt_tls_client);
} else {
mqtt_client.setClient(mqtt_wifi_client);
}
last_tls_enabled = heishamonSettings.mqtt_tls_enabled;
#else
mqtt_client.setSocketTimeout(10); mqtt_client.setKeepAlive(5); //fast timeout, any slower will block the main loop too long
#endif
mqtt_client.setServer(heishamonSettings.mqtt_server, atoi(heishamonSettings.mqtt_port));
mqtt_client.setCallback(mqtt_callback);
}
Expand Down Expand Up @@ -1447,8 +1583,9 @@ void setup() {
setupETH();
#endif

loggingSerial.println(F("Setup MQTT..."));
setupMqtt();
// loggingSerial.println(F("Setup MQTT..."));
// setupMqtt();
//shifted - TLS requires correct time

loggingSerial.println(F("Setup HTTP..."));
setupHttp();
Expand All @@ -1458,6 +1595,9 @@ void setup() {
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_init();

loggingSerial.println(F("Setup MQTT..."));
setupMqtt();

loggingSerial.println(F("Switch serial..."));
switchSerial(); //switch serial to gpio13/gpio15

Expand Down
69 changes: 69 additions & 0 deletions HeishaMon/commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,75 @@ unsigned int set_external_error(char *msg, unsigned char *cmd, char *log_msg){
return sizeof(panasonicSendQuery);
}

unsigned int set_heatingcontrol(char *msg, unsigned char *cmd, char *log_msg) {

const byte address=30;
byte value = 0b01;

if ( String(msg).toInt() == 1 ) {
value = 0b10;
}

{
char tmp[256] = { 0 };
snprintf_P(tmp, 255, PSTR("set heating control %d"), value - 1);
memcpy(log_msg, tmp, sizeof(tmp));
}

{
memcpy_P(cmd, panasonicSendQuery, sizeof(panasonicSendQuery));
cmd[address] = value << 2;
}

return sizeof(panasonicSendQuery);
}

unsigned int set_smart_dhw(char *msg, unsigned char *cmd, char *log_msg) {

const byte address=24;
byte value = 0b01;

if ( String(msg).toInt() == 1 ) {
value = 0b10;
}

{
char tmp[256] = { 0 };
snprintf_P(tmp, 255, PSTR("set smart dhw %d"), value - 1);
memcpy(log_msg, tmp, sizeof(tmp));
}

{
memcpy_P(cmd, panasonicSendQuery, sizeof(panasonicSendQuery));
cmd[address] = value << 6;
}

return sizeof(panasonicSendQuery);
}

unsigned int set_quiet_mode_priority(char *msg, unsigned char *cmd, char *log_msg) {

const byte address=11;
byte value = 0b01;

if ( String(msg).toInt() == 1 ) {
value = 0b10;
}

{
char tmp[256] = { 0 };
snprintf_P(tmp, 255, PSTR("set quiet mode priority %d"), value - 1);
memcpy(log_msg, tmp, sizeof(tmp));
}

{
memcpy_P(cmd, panasonicSendQuery, sizeof(panasonicSendQuery));
cmd[address] = value << 4;
}

return sizeof(panasonicSendQuery);
}

unsigned int set_external_compressor_control(char *msg, unsigned char *cmd, char *log_msg){
const byte off_state=64;
const byte address=23;
Expand Down
6 changes: 6 additions & 0 deletions HeishaMon/commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ unsigned int set_bivalent_ap_start_temp(char *msg, unsigned char *cmd, char *log
unsigned int set_bivalent_ap_stop_temp(char *msg, unsigned char *cmd, char *log_msg);
unsigned int set_external_control(char *msg, unsigned char *cmd, char *log_msg);
unsigned int set_external_error(char *msg, unsigned char *cmd, char *log_msg);
unsigned int set_heatingcontrol(char *msg, unsigned char *cmd, char *log_msg);
unsigned int set_smart_dhw(char *msg, unsigned char *cmd, char *log_msg);
unsigned int set_quiet_mode_priority(char *msg, unsigned char *cmd, char *log_msg);

//optional pcb commands
unsigned int set_heat_cool_mode(char *msg, char *log_msg);
Expand Down Expand Up @@ -153,6 +156,9 @@ const cmdStruct commands[] PROGMEM = {
{ "SetBivalentAPStartTemp", set_bivalent_ap_start_temp },
// bivalent AP stop temp - set from -15C to 35C
{ "SetBivalentAPStopTemp", set_bivalent_ap_stop_temp },
{ "SetHeatingControl", set_heatingcontrol },
{ "SetSmartDHW", set_smart_dhw },
{ "SetQuietModePriority", set_quiet_mode_priority },
};

struct optCmdStruct{
Expand Down
Loading