diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0f13bcf7..3f52781d 100755 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,19 +2,24 @@ name: Build binary on: [push, pull_request] +env: + ARDUINO_BOARD_MANAGER_ADDITIONAL_URLS: http://arduino.esp8266.com/stable/package_esp8266com_index.json + jobs: build: runs-on: windows-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup Arduino CLI - uses: arduino/setup-arduino-cli@v1 + uses: arduino/setup-arduino-cli@v1.1.1 - name: Install platform - run: arduino-cli core install esp8266:esp8266 --additional-urls https://arduino.esp8266.com/stable/package_esp8266com_index.json -v + run: | + arduino-cli core update-index + arduino-cli core install esp8266:esp8266 - name: Install dependencies run: arduino-cli lib install ringbuffer pubsubclient doubleresetdetect arduinojson dallastemperature onewire WebSockets diff --git a/HeatPumpType.md b/HeatPumpType.md index 16dea466..b678b539 100644 --- a/HeatPumpType.md +++ b/HeatPumpType.md @@ -27,6 +27,12 @@ Assuming that bytes from #129 to #138 are unique for each model of Aquarea heat |20 | C2 D3 0B 34 65 B2 D3 0B 95 65 | Monoblock | WH-MDC07J3E5 | Monoblock | 7 | 1ph | HP | |21 | C2 D3 0B 35 65 B2 D3 0B 96 65 | Monoblock | WH-MDC09J3E5 | Monoblock | 9 | 1ph | HP | |22 | 62 D2 0B 41 54 32 D2 0C 45 55 | WH-SDC0305J3E5 | WH-UD05JE5 | KIT-WC05J3E5 | 5 | 1ph | HP | +|23 | 32 D4 0B 87 84 73 90 0C 84 84 | Monoblock | WH-MXC09J3E8 | Monoblock | 9 | 3ph | T-CAP | +|24 | 32 D4 0B 88 84 73 90 0C 85 84 | MonoBlock | WH-MXC12J9E8 | Monoblock | 12 | 3ph | T-CAP | +|25 | E2 CF 0B 75 09 12 D0 0C 06 11 | WH-ADC1216H6E5 | WH-UD12HE5 | KIT-ADC12HE5 | 12 | 1ph | T-CAP | +|26 | 42 D4 0B 83 71 42 D2 0C 46 55 | WH-ADC0309J3E5C | WH-UD07JE5 | KIT-ADC07JE5C | 7 | 1ph | HP - All-In-One Compact | +|27 | C2 D3 0C 34 65 B2 D3 0B 95 65 | Monoblock | WH-MDC07J3E5 | Monoblock | 7 | 1ph | HP (new version?) | +|28 | C2 D3 0C 33 65 B2 D3 0B 94 65 | Monoblcok | WH-MDC05J3E5 | Monoblock | 5 | 1ph | HP (new version? | All bytes are used for Heat Pump model identification in the code. diff --git a/HeishaMon/HeishaMon.ino b/HeishaMon/HeishaMon.ino index 2dbd58a3..d076e068 100755 --- a/HeishaMon/HeishaMon.ino +++ b/HeishaMon/HeishaMon.ino @@ -69,7 +69,7 @@ static int uploadpercentage = 0; char data[MAXDATASIZE] = { '\0' }; byte data_length = 0; -// store actual data +// store actual data String openTherm[2]; char actData[DATASIZE] = { '\0' }; #define OPTDATASIZE 20 @@ -117,7 +117,7 @@ void check_wifi() if ((WiFi.status() != WL_CONNECTED) && (WiFi.localIP())) { // special case where it seems that we are not connect but we do have working IP (causing the -1% wifi signal), do a reset. log_message((char *)"Weird case, WiFi seems disconnected but is not. Resetting WiFi!"); - setupWifi(&heishamonSettings); + setupWifi(&heishamonSettings); } else if ((WiFi.status() != WL_CONNECTED) || (!WiFi.localIP())) { /* if we are not connected to an AP @@ -231,8 +231,8 @@ void mqtt_reconnect() void log_message(const __FlashStringHelper *msg) { PGM_P p = (PGM_P)msg; int len = strlen_P((const char *)p); - char *str = (char *)MALLOC(len+1); - if(str == NULL) { + char *str = (char *)MALLOC(len + 1); + if (str == NULL) { OUT_OF_MEMORY } strcpy_P(str, p); @@ -307,7 +307,7 @@ bool readSerial() { int len = 0; while ((Serial.available()) && (len < MAXDATASIZE)) { - data[data_length+len] = Serial.read(); //read available data and place it after the last received data + data[data_length + len] = Serial.read(); //read available data and place it after the last received data len++; if (data[0] != 113) { //wrong header received! log_message(F("Received bad header. Ignoring this data!")); @@ -320,7 +320,7 @@ bool readSerial() if ((len > 0) && (data_length == 0 )) totalreads++; //this is the start of a new read data_length += len; - + if (data_length > 1) { //should have received length part of header now if ((data_length > (data[1] + 3)) || (data_length >= MAXDATASIZE) ) { @@ -534,7 +534,7 @@ int8_t webserver_cb(struct webserver_t *client, void *dat) { } else if (strcmp_P((char *)dat, PSTR("/factoryreset")) == 0) { client->route = 90; } else if (strcmp_P((char *)dat, PSTR("/command")) == 0) { - if((client->userdata = malloc(1)) == NULL) { + if ((client->userdata = malloc(1)) == NULL) { Serial1.printf(PSTR("Out of memory %s:#%d\n"), __FUNCTION__, __LINE__); ESP.restart(); exit(-1); @@ -701,7 +701,7 @@ int8_t webserver_cb(struct webserver_t *client, void *dat) { case WEBSERVER_CLIENT_WRITE: { switch (client->route) { case 0: { - if(client->content == 0) { + if (client->content == 0) { webserver_send(client, 404, (char *)"text/plain", 13); webserver_send_content_P(client, PSTR("404 Not found"), 13); } @@ -745,6 +745,12 @@ int8_t webserver_cb(struct webserver_t *client, void *dat) { } break; case 110: { int ret = saveSettings(client, &heishamonSettings); + if (heishamonSettings.listenonly) { + //make sure we disable TX to heatpump-RX using the mosfet so this line is floating and will not disturb cz-taw1 + digitalWrite(5, LOW); + } else { + digitalWrite(5, HIGH); + } switch (client->route) { case 111: { return settingsNewPassword(client, &heishamonSettings); @@ -825,7 +831,7 @@ int8_t webserver_cb(struct webserver_t *client, void *dat) { return -1; } break; default: { - if(client->route != 0) { + if (client->route != 0) { header->ptr += sprintf_P((char *)header->buffer, PSTR("Access-Control-Allow-Origin: *")); } } break; @@ -835,33 +841,33 @@ int8_t webserver_cb(struct webserver_t *client, void *dat) { case WEBSERVER_CLIENT_CLOSE: { switch (client->route) { case 100: { - if (client->userdata != NULL) { - free(client->userdata); - } - } break; + if (client->userdata != NULL) { + free(client->userdata); + } + } break; case 110: { - struct websettings_t *tmp = NULL; - while (client->userdata) { - tmp = (struct websettings_t *)client->userdata; - client->userdata = ((struct websettings_t *)(client->userdata))->next; - free(tmp); - } - } break; + struct websettings_t *tmp = NULL; + while (client->userdata) { + tmp = (struct websettings_t *)client->userdata; + client->userdata = ((struct websettings_t *)(client->userdata))->next; + free(tmp); + } + } break; case 160: case 170: { - if (client->userdata != NULL) { - File *f = (File *)client->userdata; - if (f) { - if (*f) { - f->close(); + if (client->userdata != NULL) { + File *f = (File *)client->userdata; + if (f) { + if (*f) { + f->close(); + } + delete f; } - delete f; } - } - } break; + } break; } client->userdata = NULL; - } break; + } break; default: { return 0; } break; @@ -928,9 +934,21 @@ void switchSerial() { setupGPIO(heishamonSettings.gpioSettings); //switch extra GPIOs to configured mode - //enable gpio15 after boot using gpio5 (D1) which enables the level shifter for the tx to panasonic + //mosfet output enable pinMode(5, OUTPUT); - digitalWrite(5, HIGH); + + //try to detect if cz-taw1 is connected in parallel + if (!heishamonSettings.listenonly) { + if (Serial.available() > 0) { + log_message(F("There is data on the line without asking for it. Switching to listen only mode.")); + heishamonSettings.listenonly = true; + } + else { + //enable gpio15 after boot using gpio5 (D1) which enables the level shifter for the tx to panasonic + //do not enable if listen only to keep the line floating + digitalWrite(5, HIGH); + } + } } void setupMqtt() { @@ -941,6 +959,8 @@ void setupMqtt() { } void setupConditionals() { + //send_initial_query(); //maybe necessary but for now disable. CZ-TAW1 sends this query on boot + //load optional PCB data from flash if (heishamonSettings.optionalPCB) { if (loadOptionalPCB(optionalPCBQuery, OPTIONALPCBQUERYSIZE)) { @@ -957,10 +977,12 @@ void setupConditionals() { //these two after optional pcb because it needs to send a datagram fast after boot if (heishamonSettings.use_1wire) initDallasSensors(log_message, heishamonSettings.updataAllDallasTime, heishamonSettings.waitDallasTime, heishamonSettings.dallasResolution); if (heishamonSettings.use_s0) initS0Sensors(heishamonSettings.s0Settings); + + } void timer_cb(int nr) { - if(nr > 0) { + if (nr > 0) { rules_timer_cb(nr); } else { switch (nr) { @@ -977,12 +999,12 @@ void timer_cb(int nr) { setupWifi(&heishamonSettings); } break; case -4: { - if(rules_parse("/rules.new") == -1) { + if (rules_parse("/rules.new") == -1) { logprintln_P(F("new ruleset failed to parse, using previous ruleset")); rules_parse("/rules.txt"); } else { logprintln_P(F("new ruleset successfully parsed")); - if(LittleFS.begin()) { + if (LittleFS.begin()) { LittleFS.rename("/rules.new", "/rules.txt"); } } @@ -1034,18 +1056,15 @@ void setup() { dnsServer.setErrorReplyCode(DNSReplyCode::NoError); dnsServer.start(DNS_PORT, "*", apIP); - //maybe necessary but for now disable. CZ-TAW1 sends this query on boot - //if (!heishamonSettings.listenonly) send_initial_query(); - rst_info *resetInfo = ESP.getResetInfoPtr(); Serial1.printf(PSTR("Reset reason: %d, exception cause: %d\n"), resetInfo->reason, resetInfo->exccause); - if(resetInfo->reason > 0 && resetInfo->reason < 4) { - if(LittleFS.begin()) { + if (resetInfo->reason > 0 && resetInfo->reason < 4) { + if (LittleFS.begin()) { LittleFS.rename("/rules.txt", "/rules.old"); } rules_setup(); - if(LittleFS.begin()) { + if (LittleFS.begin()) { LittleFS.rename("/rules.old", "/rules.txt"); } } else { diff --git a/HeishaMon/commands.cpp b/HeishaMon/commands.cpp index 068b5693..6a106312 100644 --- a/HeishaMon/commands.cpp +++ b/HeishaMon/commands.cpp @@ -595,6 +595,74 @@ unsigned int set_main_schedule(char *msg, unsigned char *cmd, char *log_msg) { return sizeof(panasonicSendQuery); } +unsigned int set_alt_external_sensor(char *msg, unsigned char *cmd, char *log_msg) { + + String set_alt_string(msg); + + byte set_alt = 16; + if ( set_alt_string.toInt() == 1 ) { + set_alt = 32; + } + + { + char tmp[256] = { 0 }; + snprintf_P(tmp, 255, PSTR("set alternative external sensor to %d"), ((set_alt / 16) - 1) ); + memcpy(log_msg, tmp, sizeof(tmp)); + } + + { + memcpy_P(cmd, panasonicSendQuery, sizeof(panasonicSendQuery)); + cmd[20] = set_alt; + } + + return sizeof(panasonicSendQuery); +} + +unsigned int set_external_pad_heater(char *msg, unsigned char *cmd, char *log_msg) { + + String set_pad_string(msg); + + byte set_pad = 16; + if ( set_pad_string.toInt() == 1 ) { + set_pad = 32; + } + if ( set_pad_string.toInt() == 2 ) { + set_pad = 48; + } + { + char tmp[256] = { 0 }; + snprintf_P(tmp, 255, PSTR("set external pad heater to %d"), ((set_pad / 16) - 1) ); + memcpy(log_msg, tmp, sizeof(tmp)); + } + + { + memcpy_P(cmd, panasonicSendQuery, sizeof(panasonicSendQuery)); + cmd[25] = set_pad; + } + + return sizeof(panasonicSendQuery); +} + +unsigned int set_buffer_delta(char *msg, unsigned char *cmd, char *log_msg) { + + String set_temperature_string(msg); + + byte request_temp = set_temperature_string.toInt() + 128; + + { + char tmp[256] = { 0 }; + snprintf_P(tmp, 255, PSTR("set buffer tank delta to %d"), request_temp - 128 ); + memcpy(log_msg, tmp, sizeof(tmp)); + } + + { + memcpy_P(cmd, panasonicSendQuery, sizeof(panasonicSendQuery)); + cmd[59] = request_temp; + } + + return sizeof(panasonicSendQuery); +} + //start of optional pcb commands unsigned int set_byte_6(int val, int base, int bit, char *log_msg, const char *func) { unsigned char hex = (optionalPCBQuery[6] & ~(base << bit)) | (val << bit); diff --git a/HeishaMon/commands.h b/HeishaMon/commands.h index 853a5e58..a4ef3daf 100644 --- a/HeishaMon/commands.h +++ b/HeishaMon/commands.h @@ -52,6 +52,9 @@ unsigned int set_heater_delay_time(char *msg, unsigned char *cmd, char *log_msg) unsigned int set_heater_start_delta(char *msg, unsigned char *cmd, char *log_msg); unsigned int set_heater_stop_delta(char *msg, unsigned char *cmd, char *log_msg); unsigned int set_main_schedule(char *msg, unsigned char *cmd, char *log_msg); +unsigned int set_alt_external_sensor(char *msg, unsigned char *cmd, char *log_msg); +unsigned int set_external_pad_heater(char *msg, unsigned char *cmd, char *log_msg); +unsigned int set_buffer_delta(char *msg, unsigned char *cmd, char *log_msg); //optional pcb commands unsigned int set_heat_cool_mode(char *msg, char *log_msg); @@ -117,6 +120,9 @@ const cmdStruct commands[] PROGMEM = { { "SetHeaterStartDelta", set_heater_start_delta }, { "SetHeaterStopDelta", set_heater_stop_delta }, { "SetMainSchedule", set_main_schedule }, + { "SetAltExternalSensor", set_alt_external_sensor }, + { "SetExternalPadHeater", set_external_pad_heater }, + { "SetBufferDelta", set_buffer_delta }, }; struct optCmdStruct{ diff --git a/HeishaMon/decode.cpp b/HeishaMon/decode.cpp index a45a1c73..a3763688 100644 --- a/HeishaMon/decode.cpp +++ b/HeishaMon/decode.cpp @@ -5,6 +5,10 @@ unsigned long lastalldatatime = 0; unsigned long lastalloptdatatime = 0; +String getBit1(byte input) { + return String(input >> 7); +} + String getBit1and2(byte input) { return String((input >> 6) - 1); } @@ -142,6 +146,52 @@ String getDataValue(char* data, unsigned int Topic_Number) { case 1: Topic_Value = getPumpFlow(data); break; + case 5: { + byte cpy; + memcpy_P(&cpy, &topicBytes[Topic_Number], sizeof(byte)); + Input_Byte = data[cpy]; + Topic_Value = topicFunctions[Topic_Number](Input_Byte); + int fractional = (int)(data[118] & 0b111); + switch (fractional) { + case 1: // fractional .00 + break; + case 2: // fractional .25 + Topic_Value = Topic_Value + ".25"; + break; + case 3: // fractional .50 + Topic_Value = Topic_Value + ".50"; + break; + case 4: // fractional .75 + Topic_Value = Topic_Value + ".75"; + break; + default: + break; + } + } + break; + case 6: { + byte cpy; + memcpy_P(&cpy, &topicBytes[Topic_Number], sizeof(byte)); + Input_Byte = data[cpy]; + Topic_Value = topicFunctions[Topic_Number](Input_Byte); + int fractional = (int)((data[118] >> 3) & 0b111) ; + switch (fractional) { + case 1: // fractional .00 + break; + case 2: // fractional .25 + Topic_Value = Topic_Value + ".25"; + break; + case 3: // fractional .50 + Topic_Value = Topic_Value + ".50"; + break; + case 4: // fractional .75 + Topic_Value = Topic_Value + ".75"; + break; + default: + break; + } + } + break; case 11: Topic_Value = String(word(data[183], data[182]) - 1); break; @@ -200,6 +250,16 @@ String getOptDataValue(char* data, unsigned int Topic_Number) { return Topic_Value; } +String getFirstByte(byte input) { + return String((input >> 4) - 1); +} + +String getSecondByte(byte input) { + return String((input & 0b1111) - 1); +} + + + // Decode //////////////////////////////////////////////////////////////////////////// void decode_heatpump_data(char* data, char* actData, PubSubClient &mqtt_client, void (*log_message)(char*), char* mqtt_topic_base, unsigned int updateAllTime) { bool updatenow = false; diff --git a/HeishaMon/decode.h b/HeishaMon/decode.h index 18d83df5..608e659e 100644 --- a/HeishaMon/decode.h +++ b/HeishaMon/decode.h @@ -12,6 +12,7 @@ void decode_heatpump_data(char* data, char* actData, PubSubClient &mqtt_client, void decode_optional_heatpump_data(char* data, char* actOptDat, PubSubClient &mqtt_client, void (*log_message)(char*), char* mqtt_topic_base, unsigned int updateAllTime); String unknown(byte input); +String getBit1(byte input); String getBit1and2(byte input); String getBit3and4(byte input); String getBit5and6(byte input); @@ -28,63 +29,77 @@ String getOpMode(byte input); String getEnergy(byte input); String getHeatMode(byte input); String getModel(byte input); +String getFirstByte(byte input); +String getSecondByte(byte input); static const char _unknown[] PROGMEM = "unknown"; static const char *Model[] PROGMEM = { - "23", //string representation of number of known models - "WH-MDC05H3E5", - "WH-MDC07H3E5", - "IDU:WH-SXC09H3E5, ODU:WH-UX09HE5", - "IDU:WH-SDC09H3E8, ODU:WH-UD09HE8", - "IDU:WH-SXC09H3E8, ODU:WH-UX09HE8", - "IDU:WH-SXC12H9E8, ODU:WH-UX12HE8", - "IDU:WH-SXC16H9E8, ODU:WH-UX16HE8", - "IDU:WH-SDC05H3E5, ODU:WH-UD05HE5", - "IDU:WH-SDC0709J3E5, ODU:WH-UD09JE5", - "WH-MDC05J3E5", - "WH-MDC09H3E5", - "WH-MXC09H3E5", - "IDU:WH-ADC0309J3E5, ODU:WH-UD09JE5", - "IDU:WH-ADC0916H9E8, ODU:WH-UX12HE8", - "IDU:WH-SQC09H3E8, ODU:WH-UQ09HE8", - "IDU:WH-SDC09H3E5, ODU:WH-UD09HE5", - "IDU:WH-ADC0309H3E5, ODU:WH-UD09HE5", - "IDU:WH-ADC0309J3E5, ODU: WH-UD05JE5", - "IDU: WH-SDC0709J3E5, ODU: WH-UD07JE5", - "IDU: WH-SDC07H3E5-1 ODU: WH-UD07HE5-1", - "WH-MDC07J3E5", - "WH-MDC09J3E5", - "IDU: WH-SDC0305J3E5 ODU: WH-UD05JE5", + "29", //string representation of number of known models (last model number + 1) + "WH-MDC05H3E5", //0 + "WH-MDC07H3E5", //1 + "IDU:WH-SXC09H3E5, ODU:WH-UX09HE5", //2 + "IDU:WH-SDC09H3E8, ODU:WH-UD09HE8", //3 + "IDU:WH-SXC09H3E8, ODU:WH-UX09HE8", //4 + "IDU:WH-SXC12H9E8, ODU:WH-UX12HE8", //5 + "IDU:WH-SXC16H9E8, ODU:WH-UX16HE8", //6 + "IDU:WH-SDC05H3E5, ODU:WH-UD05HE5", //7 + "IDU:WH-SDC0709J3E5, ODU:WH-UD09JE5", //8 + "WH-MDC05J3E5", //9 + "WH-MDC09H3E5", //10 + "WH-MXC09H3E5", //11 + "IDU:WH-ADC0309J3E5, ODU:WH-UD09JE5", //12 + "IDU:WH-ADC0916H9E8, ODU:WH-UX12HE8", //13 + "IDU:WH-SQC09H3E8, ODU:WH-UQ09HE8", //14 + "IDU:WH-SDC09H3E5, ODU:WH-UD09HE5", //15 + "IDU:WH-ADC0309H3E5, ODU:WH-UD09HE5", //16 + "IDU:WH-ADC0309J3E5, ODU: WH-UD05JE5", //17 + "IDU: WH-SDC0709J3E5, ODU: WH-UD07JE5", //18 + "IDU: WH-SDC07H3E5-1 ODU: WH-UD07HE5-1", //19 + "WH-MDC07J3E5", //20 + "WH-MDC09J3E5", //21 + "IDU: WH-SDC0305J3E5 ODU: WH-UD05JE5", //22 + "WH-MXC09J3E8", //23 + "WH-MXC12J9E8", //24 + "IDU: WH-ADC1216H6E5 ODU: WH-UD12HE5", //25 + "IDU: WH-ADC0309J3E5C ODU: WH-UD07JE5", //26 + "WH-MDC07J3E5", //27 + "WH-MDC05J3E5", //28 }; static const byte knownModels[sizeof(Model) / sizeof(Model[0])][10] PROGMEM = { //stores the bytes #129 to #138 of known models in the same order as the const above - 0xE2, 0xCF, 0x0B, 0x13, 0x33, 0x32, 0xD1, 0x0C, 0x16, 0x33, - 0xE2, 0xCF, 0x0B, 0x14, 0x33, 0x42, 0xD1, 0x0B, 0x17, 0x33, - 0xE2, 0xCF, 0x0D, 0x77, 0x09, 0x12, 0xD0, 0x0B, 0x05, 0x11, - 0xE2, 0xCF, 0x0C, 0x88, 0x05, 0x12, 0xD0, 0x0B, 0x97, 0x05, - 0xE2, 0xCF, 0x0D, 0x85, 0x05, 0x12, 0xD0, 0x0C, 0x94, 0x05, - 0xE2, 0xCF, 0x0D, 0x86, 0x05, 0x12, 0xD0, 0x0C, 0x95, 0x05, - 0xE2, 0xCF, 0x0D, 0x87, 0x05, 0x12, 0xD0, 0x0C, 0x96, 0x05, - 0xE2, 0xCE, 0x0D, 0x71, 0x81, 0x72, 0xCE, 0x0C, 0x92, 0x81, - 0x62, 0xD2, 0x0B, 0x43, 0x54, 0x42, 0xD2, 0x0B, 0x72, 0x66, - 0xC2, 0xD3, 0x0B, 0x33, 0x65, 0xB2, 0xD3, 0x0B, 0x94, 0x65, - 0xE2, 0xCF, 0x0B, 0x15, 0x33, 0x42, 0xD1, 0x0B, 0x18, 0x33, - 0xE2, 0xCF, 0x0B, 0x41, 0x34, 0x82, 0xD1, 0x0B, 0x31, 0x35, - 0x62, 0xD2, 0x0B, 0x45, 0x54, 0x42, 0xD2, 0x0B, 0x47, 0x55, - 0xE2, 0xCF, 0x0C, 0x74, 0x09, 0x12, 0xD0, 0x0D, 0x95, 0x05, - 0xE2, 0xCF, 0x0B, 0x82, 0x05, 0x12, 0xD0, 0x0C, 0x91, 0x05, - 0xE2, 0xCF, 0x0C, 0x55, 0x14, 0x12, 0xD0, 0x0B, 0x15, 0x08, - 0xE2, 0xCF, 0x0C, 0x43, 0x00, 0x12, 0xD0, 0x0B, 0x15, 0x08, - 0x62, 0xD2, 0x0B, 0x45, 0x54, 0x32, 0xD2, 0x0C, 0x45, 0x55, - 0x62, 0xD2, 0x0B, 0x43, 0x54, 0x42, 0xD2, 0x0C, 0x46, 0x55, - 0xE2, 0xCF, 0x0C, 0x54, 0x14, 0x12, 0xD0, 0x0B, 0x14, 0x08, - 0xC2, 0xD3, 0x0B, 0x34, 0x65, 0xB2, 0xD3, 0x0B, 0x95, 0x65, - 0xC2, 0xD3, 0x0B, 0x35, 0x65, 0xB2, 0xD3, 0x0B, 0x96, 0x65, - 0x62, 0xD2, 0x0B, 0x41, 0x54, 0x32, 0xD2, 0x0C, 0x45, 0x55, + 0xE2, 0xCF, 0x0B, 0x13, 0x33, 0x32, 0xD1, 0x0C, 0x16, 0x33, //0 + 0xE2, 0xCF, 0x0B, 0x14, 0x33, 0x42, 0xD1, 0x0B, 0x17, 0x33, //1 + 0xE2, 0xCF, 0x0D, 0x77, 0x09, 0x12, 0xD0, 0x0B, 0x05, 0x11, //2 + 0xE2, 0xCF, 0x0C, 0x88, 0x05, 0x12, 0xD0, 0x0B, 0x97, 0x05, //3 + 0xE2, 0xCF, 0x0D, 0x85, 0x05, 0x12, 0xD0, 0x0C, 0x94, 0x05, //4 + 0xE2, 0xCF, 0x0D, 0x86, 0x05, 0x12, 0xD0, 0x0C, 0x95, 0x05, //5 + 0xE2, 0xCF, 0x0D, 0x87, 0x05, 0x12, 0xD0, 0x0C, 0x96, 0x05, //6 + 0xE2, 0xCE, 0x0D, 0x71, 0x81, 0x72, 0xCE, 0x0C, 0x92, 0x81, //7 + 0x62, 0xD2, 0x0B, 0x43, 0x54, 0x42, 0xD2, 0x0B, 0x72, 0x66, //8 + 0xC2, 0xD3, 0x0B, 0x33, 0x65, 0xB2, 0xD3, 0x0B, 0x94, 0x65, //9 + 0xE2, 0xCF, 0x0B, 0x15, 0x33, 0x42, 0xD1, 0x0B, 0x18, 0x33, //10 + 0xE2, 0xCF, 0x0B, 0x41, 0x34, 0x82, 0xD1, 0x0B, 0x31, 0x35, //11 + 0x62, 0xD2, 0x0B, 0x45, 0x54, 0x42, 0xD2, 0x0B, 0x47, 0x55, //12 + 0xE2, 0xCF, 0x0C, 0x74, 0x09, 0x12, 0xD0, 0x0D, 0x95, 0x05, //13 + 0xE2, 0xCF, 0x0B, 0x82, 0x05, 0x12, 0xD0, 0x0C, 0x91, 0x05, //14 + 0xE2, 0xCF, 0x0C, 0x55, 0x14, 0x12, 0xD0, 0x0B, 0x15, 0x08, //15 + 0xE2, 0xCF, 0x0C, 0x43, 0x00, 0x12, 0xD0, 0x0B, 0x15, 0x08, //16 + 0x62, 0xD2, 0x0B, 0x45, 0x54, 0x32, 0xD2, 0x0C, 0x45, 0x55, //17 + 0x62, 0xD2, 0x0B, 0x43, 0x54, 0x42, 0xD2, 0x0C, 0x46, 0x55, //18 + 0xE2, 0xCF, 0x0C, 0x54, 0x14, 0x12, 0xD0, 0x0B, 0x14, 0x08, //19 + 0xC2, 0xD3, 0x0B, 0x34, 0x65, 0xB2, 0xD3, 0x0B, 0x95, 0x65, //20 + 0xC2, 0xD3, 0x0B, 0x35, 0x65, 0xB2, 0xD3, 0x0B, 0x96, 0x65, //21 + 0x62, 0xD2, 0x0B, 0x41, 0x54, 0x32, 0xD2, 0x0C, 0x45, 0x55, //22 + 0x32, 0xD4, 0x0B, 0x87, 0x84, 0x73, 0x90, 0x0C, 0x84, 0x84, //23 + 0x32, 0xD4, 0x0B, 0x88, 0x84, 0x73, 0x90, 0x0C, 0x85, 0x84, //24 + 0xE2, 0xCF, 0x0B, 0x75, 0x09, 0x12, 0xD0, 0x0C, 0x06, 0x11, //25 + 0x42, 0xD4, 0x0B, 0x83, 0x71, 0x42, 0xD2, 0x0C, 0x46, 0x55, //26 + 0xC2, 0xD3, 0x0C, 0x34, 0x65, 0xB2, 0xD3, 0x0B, 0x95, 0x65, //27 + 0xC2, 0xD3, 0x0C, 0x33, 0x65, 0xB2, 0xD3, 0x0B, 0x94, 0x65, //28 }; -#define NUMBER_OF_TOPICS 107 //last topic number + 1 +#define NUMBER_OF_TOPICS 115 //last topic number + 1 #define NUMBER_OF_OPT_TOPICS 7 //last topic number + 1 #define MAX_TOPIC_LEN 41 // max length + 1 @@ -189,23 +204,31 @@ static const char topics[][MAX_TOPIC_LEN] PROGMEM = { "Z2_Cool_Curve_Target_Low_Temp", //TOP87 "Z2_Cool_Curve_Outside_High_Temp", //TOP88 "Z2_Cool_Curve_Outside_Low_Temp", //TOP89 - "Room_Heater_Operations_Hours", //TOP90 - "DHW_Heater_Operations_Hours", //TOP91 - "Heat_Pump_Model", //TOP92, - "Pump_Duty", //TOP93 - "Zones_State", //TOP94 - "Max_Pump_Duty", //TOP95 - "Heater_Delay_Time", //TOP96 - "Heater_Start_Delta", //TOP97 - "Heater_Stop_Delta", //TOP98 - "Buffer_Installed", //TOP99 - "DHW_Installed", //TOP100 - "Solar_Mode", //TOP101 - "Solar_On_Delta", //TOP102 - "Solar_Off_Delta", //TOP103 - "Solar_Frost_Protection", //TOP104 - "Solar_High_Limit", //TOP105 - "Pump_Flowrate_Mode", //TOP106 + "Room_Heater_Operations_Hours", //TOP90 + "DHW_Heater_Operations_Hours", //TOP91 + "Heat_Pump_Model", //TOP92 + "Pump_Duty", //TOP93 + "Zones_State", //TOP94 + "Max_Pump_Duty", //TOP95 + "Heater_Delay_Time", //TOP96 + "Heater_Start_Delta", //TOP97 + "Heater_Stop_Delta", //TOP98 + "Buffer_Installed", //TOP99 + "DHW_Installed", //TOP100 + "Solar_Mode", //TOP101 + "Solar_On_Delta", //TOP102 + "Solar_Off_Delta", //TOP103 + "Solar_Frost_Protection", //TOP104 + "Solar_High_Limit", //TOP105 + "Pump_Flowrate_Mode", //TOP106 + "Liquid_Type", //TOP107 + "Alt_External_Sensor", //TOP108 + "Anti_Freeze_Mode", //TOP109 + "Optional_PCB", //TOP110 + "Z1_Sensor_Settings", //TOP111 + "Z2_Sensor_Settings", //TOP112 + "Buffer_Tank_Delta", //TOP113 + "External_Pad_Heater", //TOP114 }; static const byte topicBytes[] PROGMEM = { //can store the index as byte (8-bit unsigned humber) as there aren't more then 255 bytes (actually only 203 bytes) to decode @@ -316,6 +339,14 @@ static const byte topicBytes[] PROGMEM = { //can store the index as byte (8-bit 63, //TOP104 64, //TOP105 29, //TOP106 + 20, //TOP107 + 20, //TOP108 + 20, //TOP109 + 20, //TOP110 + 22, //TOP111 + 22, //TOP112 + 59, //TOP113 + 25, //TOP114 }; typedef String (*topicFP)(byte); @@ -428,6 +459,14 @@ static const topicFP topicFunctions[] PROGMEM = { getIntMinus128, //TOP104 getIntMinus128, //TOP105 getBit3and4, //TOP106 + getBit1, //TOP107 + getBit3and4, //TOP108 + getBit5and6, //TOP109 + getBit7and8, //TOP110 + getSecondByte, //TOP111 + getFirstByte, //TOP112 + getIntMinus128, //TOP113 + getBit3and4, //TOP114 }; static const char *DisabledEnabled[] PROGMEM = {"2", "Disabled", "Enabled"}; @@ -456,6 +495,9 @@ static const char *Duty[] PROGMEM = {"0", "Duty"}; static const char *ZonesState[] PROGMEM = {"3", "Zone1 active", "Zone2 active", "Zone1 and zone2 active"}; static const char *HeatCoolModeDesc[] PROGMEM = {"2", "Comp. Curve", "Direct"}; static const char *SolarModeDesc[] PROGMEM = {"3", "Disabled", "Buffer", "DHW"}; +static const char *ZonesSensorType[] PROGMEM = {"4", "Water Temperature", "External Thermostat", "Internal Thermostat", "Thermistor"}; +static const char *LiquidType[] PROGMEM = {"2", "Water", "Glycol"}; +static const char *ExtPadHeaterType[] PROGMEM = {"3", "Disabled", "Type-A","Type-B"}; static const char **topicDescription[] PROGMEM = { OffOn, //TOP0 @@ -565,4 +607,12 @@ static const char **topicDescription[] PROGMEM = { Celsius, //TOP104 Celsius, //TOP105 PumpFlowRateMode,//TOP106 + LiquidType, //TOP107 + DisabledEnabled, //TOP108 + DisabledEnabled, //TOP109 + DisabledEnabled, //TOP110 + ZonesSensorType, //TOP111 + ZonesSensorType, //TOP112 + Kelvin, //TOP113 + ExtPadHeaterType,//TOP114 }; diff --git a/HeishaMon/htmlcode.h b/HeishaMon/htmlcode.h index 9c5d9454..70610ae7 100644 --- a/HeishaMon/htmlcode.h +++ b/HeishaMon/htmlcode.h @@ -203,6 +203,7 @@ static const char webBodyRootStatusMemory[] PROGMEM = "%
Memory free: "; static const char webBodyRootStatusReceived[] PROGMEM = "%
Correct received data: "; static const char webBodyRootStatusReconnects[] PROGMEM = "%
MQTT reconnects: "; static const char webBodyRootStatusUptime[] PROGMEM = "
Uptime: "; +static const char webBodyRootStatusListenOnly[] PROGMEM = "
Listen only mode active"; static const char webBodyRootHeatpumpValues[] PROGMEM = "
" @@ -691,13 +692,20 @@ static const char settingsForm2[] PROGMEM = " " " " " " - " Listen only mode:" + " Listen only (parallel CZ-TAW1) mode:" " " " " " " " " " " " " + " Emulate optional PCB (does not work in listen only mode):" + " " + " " + " " + " " + " " + " " " Debug log to MQTT topic from start:" " " " " @@ -717,13 +725,6 @@ static const char settingsForm2[] PROGMEM = " " " " " " - " " - " " - " Emulate optional PCB:" - " " - " " - " " - " " " " " " " " diff --git a/HeishaMon/rules.cpp b/HeishaMon/rules.cpp index e5fdbc32..293e8c7d 100755 --- a/HeishaMon/rules.cpp +++ b/HeishaMon/rules.cpp @@ -133,10 +133,10 @@ static int is_variable(char *text, unsigned int *pos, unsigned int size) { } if(text[*pos] == '%') { - if(strnicmp(&text[(*pos)+1], "hour", 4) == 0) { + if(size == 5 && strnicmp(&text[(*pos)+1], "hour", 4) == 0) { return 5; } - if(strnicmp(&text[(*pos)+1], "month", 4) == 0) { + if(size == 6 && strnicmp(&text[(*pos)+1], "month", 5) == 0) { return 6; } } @@ -147,7 +147,7 @@ static int is_variable(char *text, unsigned int *pos, unsigned int size) { cmdStruct cmd; memcpy_P(&cmd, &commands[x], sizeof(cmd)); size_t len = strlen(cmd.name); - if(strnicmp(&text[(*pos)+1], cmd.name, len) == 0) { + if(size-1 == len && strnicmp(&text[(*pos)+1], cmd.name, len) == 0) { i = len+1; match = 1; break; @@ -159,7 +159,7 @@ static int is_variable(char *text, unsigned int *pos, unsigned int size) { optCmdStruct cmd; memcpy_P(&cmd, &optionalCommands[x], sizeof(cmd)); size_t len = strlen(cmd.name); - if(strnicmp(&text[(*pos)+1], cmd.name, len) == 0) { + if(size-1 == len && strnicmp(&text[(*pos)+1], cmd.name, len) == 0) { i = len+1; match = 1; break; @@ -172,7 +172,7 @@ static int is_variable(char *text, unsigned int *pos, unsigned int size) { char cpy[MAX_TOPIC_LEN]; memcpy_P(&cpy, topics[x], MAX_TOPIC_LEN); size_t len = strlen(cpy); - if(strnicmp(&text[(*pos)+1], cpy, len) == 0) { + if(size-1 == len && strnicmp(&text[(*pos)+1], cpy, len) == 0) { i = len+1; match = 1; break; @@ -211,7 +211,7 @@ static int is_event(char *text, unsigned int *pos, unsigned int size) { cmdStruct cmd; memcpy_P(&cmd, &commands[x], sizeof(cmd)); size_t len = strlen(cmd.name); - if(strnicmp(&text[(*pos)+1], cmd.name, len) == 0) { + if(size-1 == len && strnicmp(&text[(*pos)+1], cmd.name, len) == 0) { i = len+1; match = 1; break; @@ -223,7 +223,7 @@ static int is_event(char *text, unsigned int *pos, unsigned int size) { optCmdStruct cmd; memcpy_P(&cmd, &optionalCommands[x], sizeof(cmd)); size_t len = strlen(cmd.name); - if(strnicmp(&text[(*pos)+1], cmd.name, len) == 0) { + if(size-1 == len && strnicmp(&text[(*pos)+1], cmd.name, len) == 0) { i = len+1; match = 1; break; @@ -235,7 +235,7 @@ static int is_event(char *text, unsigned int *pos, unsigned int size) { size_t len = strlen_P(topics[x]); char cpy[len]; memcpy_P(&cpy, &topics[x], len); - if(strnicmp(&text[(*pos)+1], cpy, len) == 0) { + if(size-1 == len && strnicmp(&text[(*pos)+1], cpy, len) == 0) { i = len+1; match = 1; break; diff --git a/HeishaMon/src/common/timerqueue.cpp b/HeishaMon/src/common/timerqueue.cpp index 0ba39352..56b57bdb 100644 --- a/HeishaMon/src/common/timerqueue.cpp +++ b/HeishaMon/src/common/timerqueue.cpp @@ -67,7 +67,7 @@ struct timerqueue_t *timerqueue_pop() { } else { if((timerqueue = (struct timerqueue_t **)realloc(timerqueue, sizeof(struct timerqueue_t *)*timerqueue_size)) == NULL) { #ifdef ESP8266 - Serial.printf("Out of memory %s:#%d\n", __FUNCTION__, __LINE__); + Serial1.printf("Out of memory %s:#%d\n", __FUNCTION__, __LINE__); ESP.restart(); exit(-1); #else @@ -144,7 +144,7 @@ void timerqueue_insert(int sec, int usec, int nr) { if((timerqueue = (struct timerqueue_t **)realloc(timerqueue, sizeof(struct timerqueue_t *)*(timerqueue_size+1))) == NULL) { #ifdef ESP8266 - Serial.printf("Out of memory %s:#%d\n", __FUNCTION__, __LINE__); + Serial1.printf("Out of memory %s:#%d\n", __FUNCTION__, __LINE__); ESP.restart(); exit(-1); #else @@ -156,7 +156,7 @@ void timerqueue_insert(int sec, int usec, int nr) { node = (struct timerqueue_t *)malloc(sizeof(struct timerqueue_t)); if(node == NULL) { #ifdef ESP8266 - Serial.printf("Out of memory %s:#%d\n", __FUNCTION__, __LINE__); + Serial1.printf("Out of memory %s:#%d\n", __FUNCTION__, __LINE__); ESP.restart(); exit(-1); #else @@ -198,7 +198,7 @@ void timerqueue_update(void) { int nr = timerqueue[a]->nr; if((calls = (unsigned int *)realloc(calls, (nrcalls+1)*sizeof(int))) == NULL) { #ifdef ESP8266 - Serial.printf("Out of memory %s:#%d\n", __FUNCTION__, __LINE__); + Serial1.printf("Out of memory %s:#%d\n", __FUNCTION__, __LINE__); ESP.restart(); exit(-1); #else diff --git a/HeishaMon/src/rules/function.cpp b/HeishaMon/src/rules/function.cpp index 20a1a979..6f98ff53 100755 --- a/HeishaMon/src/rules/function.cpp +++ b/HeishaMon/src/rules/function.cpp @@ -29,6 +29,8 @@ #include "functions/max.h" #include "functions/min.h" #include "functions/coalesce.h" +#include "functions/ceil.h" +#include "functions/floor.h" #include "functions/settimer.h" #include "functions/isset.h" #include "functions/round.h" @@ -36,6 +38,8 @@ struct rule_function_t rule_functions[] = { { "max", rule_function_max_callback }, { "min", rule_function_min_callback }, + { "ceil", rule_function_ceil_callback }, + { "floor", rule_function_floor_callback }, { "coalesce", rule_function_coalesce_callback }, { "settimer", rule_function_set_timer_callback }, { "isset", rule_function_isset_callback }, diff --git a/HeishaMon/src/rules/functions/ceil.cpp b/HeishaMon/src/rules/functions/ceil.cpp new file mode 100755 index 00000000..202ad3d3 --- /dev/null +++ b/HeishaMon/src/rules/functions/ceil.cpp @@ -0,0 +1,58 @@ +/* + Copyright (C) CurlyMo + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifdef ESP8266 + #pragma GCC diagnostic warning "-fpermissive" +#endif + +#include +#include +#include +#include + +#include "../function.h" +#include "../../common/mem.h" +#include "../rules.h" + +int rule_function_ceil_callback(struct rules_t *obj, uint16_t argc, uint16_t *argv, int *ret) { +/* LCOV_EXCL_START*/ +#ifdef DEBUG + printf("%s\n", __FUNCTION__); +#endif +/* LCOV_EXCL_STOP*/ + + if(argc != 1) { + return -1; + } + + *ret = obj->varstack.nrbytes; + + unsigned int size = alignedbytes(obj->varstack.nrbytes+sizeof(struct vm_vinteger_t)); + if((obj->varstack.buffer = (unsigned char *)REALLOC(obj->varstack.buffer, alignedbuffer(size))) == NULL) { + OUT_OF_MEMORY /*LCOV_EXCL_LINE*/ + } + struct vm_vinteger_t *out = (struct vm_vinteger_t *)&obj->varstack.buffer[obj->varstack.nrbytes]; + out->ret = 0; + out->type = VINTEGER; + + switch(obj->varstack.buffer[argv[0]]) { + case VINTEGER: { + struct vm_vinteger_t *val = (struct vm_vinteger_t *)&obj->varstack.buffer[argv[0]]; + out->value = val->value; + } break; + case VFLOAT: { + struct vm_vfloat_t *val = (struct vm_vfloat_t *)&obj->varstack.buffer[argv[0]]; + out->value = (int)ceil(val->value); + } break; + } + + obj->varstack.nrbytes = size; + obj->varstack.bufsize = alignedbuffer(size); + + return 0; +} diff --git a/HeishaMon/src/rules/functions/ceil.h b/HeishaMon/src/rules/functions/ceil.h new file mode 100755 index 00000000..ddce66bf --- /dev/null +++ b/HeishaMon/src/rules/functions/ceil.h @@ -0,0 +1,17 @@ +/* + Copyright (C) CurlyMo + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _RULES_CEIL_H_ +#define _RULES_CEIL_H_ + +#include +#include "../rules.h" + +int rule_function_ceil_callback(struct rules_t *obj, uint16_t argc, uint16_t *argv, int *ret); + +#endif \ No newline at end of file diff --git a/HeishaMon/src/rules/functions/floor.cpp b/HeishaMon/src/rules/functions/floor.cpp new file mode 100755 index 00000000..1adff285 --- /dev/null +++ b/HeishaMon/src/rules/functions/floor.cpp @@ -0,0 +1,58 @@ +/* + Copyright (C) CurlyMo + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifdef ESP8266 + #pragma GCC diagnostic warning "-fpermissive" +#endif + +#include +#include +#include +#include + +#include "../function.h" +#include "../../common/mem.h" +#include "../rules.h" + +int rule_function_floor_callback(struct rules_t *obj, uint16_t argc, uint16_t *argv, int *ret) { +/* LCOV_EXCL_START*/ +#ifdef DEBUG + printf("%s\n", __FUNCTION__); +#endif +/* LCOV_EXCL_STOP*/ + + if(argc != 1) { + return -1; + } + + *ret = obj->varstack.nrbytes; + + unsigned int size = alignedbytes(obj->varstack.nrbytes+sizeof(struct vm_vinteger_t)); + if((obj->varstack.buffer = (unsigned char *)REALLOC(obj->varstack.buffer, alignedbuffer(size))) == NULL) { + OUT_OF_MEMORY /*LCOV_EXCL_LINE*/ + } + struct vm_vinteger_t *out = (struct vm_vinteger_t *)&obj->varstack.buffer[obj->varstack.nrbytes]; + out->ret = 0; + out->type = VINTEGER; + + switch(obj->varstack.buffer[argv[0]]) { + case VINTEGER: { + struct vm_vinteger_t *val = (struct vm_vinteger_t *)&obj->varstack.buffer[argv[0]]; + out->value = val->value; + } break; + case VFLOAT: { + struct vm_vfloat_t *val = (struct vm_vfloat_t *)&obj->varstack.buffer[argv[0]]; + out->value = (int)floor(val->value); + } break; + } + + obj->varstack.nrbytes = size; + obj->varstack.bufsize = alignedbuffer(size); + + return 0; +} diff --git a/HeishaMon/src/rules/functions/floor.h b/HeishaMon/src/rules/functions/floor.h new file mode 100755 index 00000000..8f2cb287 --- /dev/null +++ b/HeishaMon/src/rules/functions/floor.h @@ -0,0 +1,17 @@ +/* + Copyright (C) CurlyMo + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef _RULES_FLOOR_H_ +#define _RULES_FLOOR_H_ + +#include +#include "../rules.h" + +int rule_function_floor_callback(struct rules_t *obj, uint16_t argc, uint16_t *argv, int *ret); + +#endif \ No newline at end of file diff --git a/HeishaMon/version.h b/HeishaMon/version.h index c894d221..e8f51d1a 100644 --- a/HeishaMon/version.h +++ b/HeishaMon/version.h @@ -1 +1 @@ -static const char* heishamon_version = "3.0"; +static const char* heishamon_version = "3.1"; \ No newline at end of file diff --git a/HeishaMon/webfunctions.cpp b/HeishaMon/webfunctions.cpp index 18563d33..d036d7a2 100755 --- a/HeishaMon/webfunctions.cpp +++ b/HeishaMon/webfunctions.cpp @@ -527,7 +527,7 @@ int saveSettings(struct webserver_t *client, settingsStruct *heishamonSettings) client->route = 111; return 0; } - + if (reconnectWiFi) { client->route = 112; return 0; @@ -541,11 +541,11 @@ int cacheSettings(struct webserver_t *client, struct arguments_t * args) { struct websettings_t *tmp = (struct websettings_t *)client->userdata; while (tmp) { /* - * this part is useless as websettings is always NULL at start of a new POST - * it will only interrate over already POSTed args which are pushed on the list below - * we only need to find the tail of the list - * / - * + this part is useless as websettings is always NULL at start of a new POST + it will only interrate over already POSTed args which are pushed on the list below + we only need to find the tail of the list + / + if (strcmp(tmp->name.c_str(), (char *)args->name) == 0) { char *cpy = (char *)malloc(args->len + 1); memset(cpy, 0, args->len + 1); @@ -936,6 +936,9 @@ int handleRoot(struct webserver_t *client, float readpercentage, int mqttReconne char *up = getUptime(); webserver_send_content(client, up, strlen(up)); free(up); + if (heishamonSettings->listenonly) { + webserver_send_content_P(client, webBodyRootStatusListenOnly, strlen_P(webBodyRootStatusListenOnly)); + } } break; case 5: { webserver_send_content_P(client, webBodyEndDiv, strlen_P(webBodyEndDiv)); diff --git a/Integrations/Home Assistant/README.md b/Integrations/Home Assistant/README.md index c6c6520d..89c42831 100644 --- a/Integrations/Home Assistant/README.md +++ b/Integrations/Home Assistant/README.md @@ -1,3 +1,9 @@ +## Integration + +See https://github.com/kamaradclimber/heishamon-homeassistant/ + + +## or using manual declaration of entities 1. Create or copy file heishamon.yaml in directory /config/packages ( if directory not exist , create it) 2. Open file configuration.yaml in directory /config ,and check for lines: diff --git a/MQTT-Topics.md b/MQTT-Topics.md index 265bdd76..b4b3d029 100644 --- a/MQTT-Topics.md +++ b/MQTT-Topics.md @@ -123,10 +123,19 @@ TOP103 | main/Solar_Off_Delta | solar heating delta off TOP104 | main/Solar_Frost_Protection | Solar frost protection temp TOP105 | main/Solar_High_Limit | Solar max temp limit TOP106 | main/Pump_Flowrate_mode | Settings for pump flow rate (0=DeltaT, 1=Maximum flow, J-series only) +TOP107 | main/Liquid_Type | Type of liquid in settings (Water / Glycol) +TOP108 | main/Alt_External_Sensor | If external outdoor sensor is selected +TOP109 | main/Anti_Freeze_Mode | Is anti freeze mode enabled or disabled +TOP110 | main/Optional_PCB | If the optional PCB is enabled (if installed) +TOP111 | main/Z2_Sensor_Settings | Setting of the sensor for zone 2 (water, ext thermostat, int. thermostat or thermistor) +TOP112 | main/Z1_Sensor_Settings | Setting of the sensor for zone 1 (water, ext thermostat, int. thermostat or thermistor) +TOP113 | main/Buffer_Tank_Delta | Delta of buffer tank setting in Kelvin +TOP114 | main/External_Pad_Heater | If the external pad heater is enabled (if installed) -All Topics realated with state can have also value -1 - unknown - but only in ubnormal situations. + +All Topics realated with state can have also value -1 - unknown - but only in abnormal situations. ## Option PCB Topics: The following topics are actions from the heatpump to the optional pcb (for example, start pump on zone 2). This is only available if you have enable optional pcb emulation. @@ -176,7 +185,9 @@ SET21 | SetHeaterDelayTime | Set heater start delay time (only J-series) | in mi SET22 | SetHeaterStartDelta | Set heater start delta T (only J-series) | in kelvin SET23 | SetHeaterStopDelta | Set heater stop delta T (only J-series) | in kelvin SET24 | SetMainSchedule | Set weekly schedule | 0=off, 1=on - +SET25 | SetAltExternalSensor | Set the alternative external outdoor sensor | 0=disabled, 1=enabled +SET26 | SetExternalPadHeater | Set the external pad heater | 0=disabled, 1=type-A, 2=type-B +SET27 | SetBufferDelta | Set buffer tank delta | 0 - 10 *If you operate your heatpump with direct temperature setup: topics ending xxxRequestTemperature will set the absolute target temperature* diff --git a/ProtocolByteDecrypt.md b/ProtocolByteDecrypt.md index 81d2219d..c3931f22 100644 --- a/ProtocolByteDecrypt.md +++ b/ProtocolByteDecrypt.md @@ -22,12 +22,13 @@ | TOP | 17 | 00 | | 0 byte | | TOP | 18 | 00 | | 0 byte | | TOP | 19 | 00 | | 0 byte | -| TOP | 20 | 19 | 1st Bit = b0 Water , b1 Glycol
3rd & 4th bit = b01 Alternative Sensor Off ,b10 Alternative Sensor On
5rd & 6th bit = b01 Antifreezing Off ,b10 Antifreezing on
7rd & 8th bit = b01 Optional PCB Off ,b10 Optional PCB on
| Circulation liquid
,Alternative outdoor temp sensor
Anti freezing
Optional PCB | +| TOP107/108/109/110 | 20 | 19 | 1st Bit = b0 Water , b1 Glycol
3rd & 4th bit = b01 Alternative Sensor Off ,b10 Alternative Sensor On
5rd & 6th bit = b01 Antifreezing Off ,b10 Antifreezing on
7rd & 8th bit = b01 Optional PCB Off ,b10 Optional PCB on
| Circulation liquid
,Alternative outdoor temp sensor
Anti freezing
Optional PCB | +| TOP107/108/109/110 | 20 | 19 | 1st Bit = b0 Water , b1 Glycol
3rd & 4th bit = b01 Alternative Sensor Off ,b10 Alternative Sensor On
5rd & 6th bit = b01 Antifreezing Off ,b10 Antifreezing on
7rd & 8th bit = b01 Optional PCB Off ,b10 Optional PCB on
| Circulation liquid
,Alternative outdoor temp sensor
Anti freezing
Optional PCB | | TOP | 21 | 15 | (hex) 15 - One Zone and Z1 as room , 19 - One Zone and Z1 as pool, 16 - Two Zones and Z2 as room, 26 - Two Zones ,Z2 as pool| No. of Zones and Zone Destination | -| TOP | 22 | 11 |First digit -Z2 ,Second digit Z1 (hex) 1 - water temperature,2 - External Thermostat, 3 - Internal Thermostat, , 4 - Thermistor | Zone & sensor settings ( system setup - Installer ) | +| TOP111+TOP112 | 22 | 11 |First digit -Z2 ,Second digit Z1 (hex) 1 - water temperature,2 - External Thermostat, 3 - Internal Thermostat, , 4 - Thermistor | Zone & sensor settings ( system setup - Installer ) | | TOP | 23 | 55 | (hex) Off (same for compressor )=55, On=56, External compressor On=95 | External and External compressor Switch | | TOP99+TOP100+TOP101 | 24 | 16 | 1st & 2nd bit Smart DHW -All-In-One only, 3rd & 4th bit = solar buffer (0b01=no solar, 0b10=solar buffer, 0b11=solar dhw), 5th & 6th bit = buffer installed, 7th & 8th bit = DHW installed| -| TOP | 25 | 5e | 1st & 2nd bit = 10
3rd & 4th bit = b01 no Pad Heater, b10 - Type A, b11 Type B
5th & 6th bit = b01 - Internal Heater 3kW, b10 - 6kW, b11 - 9kW
7th & 8th bit = b01 DHW Internal Heater , b10 - DHW External Heater | External Pad Heater
Power of internal heater
DHW heater Internal/External | +| TOP114 | 25 | 5e | 1st & 2nd bit = 10
3rd & 4th bit = b01 no Pad Heater, b10 - Type A, b11 Type B
5th & 6th bit = b01 - Internal Heater 3kW, b10 - 6kW, b11 - 9kW
7th & 8th bit = b01 DHW Internal Heater , b10 - DHW External Heater | External Pad Heater
Power of internal heater
DHW heater Internal/External | | TOP | 26 | 55 | (hex) Biwalent Off=55, Biwalent alternative =56, Biwalent parallel=5A | Biwalent settings | | TOP | 27 | 05 | SG Ready Control on/off (bit5and6) ,Demand Control on/off (bit7and8) | SG Ready Control, Demand Control | | TOP76+TOP81 | 28 | 09 | (hex) 09 - Compensation curve heat and direct cool, 05 - both compensation curves , 0a - direct heat and direct cool, 06 - heat direct, cool compensation curve | Operation Setup -Installer -water temperature heating on status and cooling | @@ -61,7 +62,7 @@ | TOP | 56 | 00 | | 0 byte | | TOP | 57 | 00 | | 0 byte | | TOP | 58 | 80 | Convert to DEC-128 | Delta T for Pool [°K] | -| TOP | 59 | 85 | Convert to DEC-128 | Delta T for buffer tank [°K]| +| TOP113 | 59 | 85 | Convert to DEC-128 | Delta T for buffer tank [°K]| | TOP | 60 | 15 | Convert to DEC X-1 | Time set for external heaters 20min-3h, step 5min. | | TOP102 | 61 | 8a | Convert to DEC-128 | Solar Connection Set delta T for tank ON (DHW or Buffer) | | TOP103 | 62 | 85 | Convert to DEC-128 | Solar Connection Set delta T for tank OFF (DHW or Buffer)| @@ -120,9 +121,9 @@ | TOP | 115 | 15 | | ? | | TOP | 116 | 5a | | ? | | TOP69 | 117 | 05 | Sterilization on/off (bit5and6) , Z2 active (bit7) ,Z1 active (bit8)| Sterilization status Zone active information (look byte #6) | -| TOP | 118 | 12 | | ? | +| TOP | 118 | 12 | | Believed to provide fractional info for TOP5 and TOP6 values | | TOP | 119 | 12 | | ? | -| TOP | 120 | 19 | | ? | +| TOP | 120 | 19 | | Possible 3e/4e bit for backup heater custom menu disable/enable | | TOP | 121 | 00 | | 0 byte | | TOP | 122 | 00 | | 0 byte | | TOP | 123 | 00 | | 0 byte | @@ -131,16 +132,16 @@ | TOP | 126 | 00 | | 0 byte | | TOP | 127 | 00 | | 0 byte | | TOP | 128 | 00 | | 0 byte | -| TOP | 129 | e2 | | ? | -| TOP | 130 | ce | | ? | -| TOP | 131 | 0d | | ? | +| TOP92 | 129 | e2 | look in HeatPumpType.md | Heat pump model | +| TOP92 | 130 | ce | look in HeatPumpType.md | Heat pump model | +| TOP92 | 131 | 0d | look in HeatPumpType.md | Heat pump model | | TOP92 | 132 | 71 | look in HeatPumpType.md | Heat pump model | -| TOP | 133 | 81 | | ? | -| TOP | 134 | 72 | | ? | -| TOP | 135 | ce | | ? | -| TOP | 136 | 0c | | ? | -| TOP | 137 | 92 | | ? | -| TOP | 138 | 81 | | ? | +| TOP92 | 133 | 81 | look in HeatPumpType.md | Heat pump model | +| TOP92 | 134 | 72 | look in HeatPumpType.md | Heat pump model | +| TOP92 | 135 | ce | look in HeatPumpType.md | Heat pump model | +| TOP92 | 136 | 0c | look in HeatPumpType.md | Heat pump model | +| TOP92 | 137 | 92 | look in HeatPumpType.md | Heat pump model | +| TOP92 | 138 | 81 | look in HeatPumpType.md | Heat pump model | | TOP56 | 139 | b0 | Convert to DEC-128 | Zone1: Actual (Zone 1) Temperature [°C] | | TOP57 | 140 | 00 | Convert to DEC-128 | Zone2: Actual (Zone 2) Temperature [°C] | | TOP10 | 141 | aa | Convert to DEC-128 | Actual DHW Temperature [°C] | diff --git a/README.md b/README.md index 0a6646f6..14acb6d0 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ All received data will be sent to different MQTT topics (see below for topic des You can connect a 1wire network on GPIO4 which will report in seperate MQTT topics (panasonic_heat_pump/1wire/sensorid). -The software is also able to measure Watt on a S0 port of two kWh meters. You only need to connect GPIO12 and GND to the S0 of one kWh meter and if you need a second kWh meter use GPIO14 and GND. It will report on MQTT topic panasonic_heat_pump/s0/Watt/1 and panasonic_heat_pump/s0/Watt/2 and also in the JSON output. You can replace 'Watt' in the previous topic with 'Watthour' to get consumption counter in WattHour (per mqtt message) or to 'WatthourTotal' to get the total consumption measured in WattHour. +The software is also able to measure Watt on a S0 port of two kWh meters. You only need to connect GPIO12 and GND to the S0 of one kWh meter and if you need a second kWh meter use GPIO14 and GND. It will report on MQTT topic panasonic_heat_pump/s0/Watt/1 and panasonic_heat_pump/s0/Watt/2 and also in the JSON output. You can replace 'Watt' in the previous topic with 'Watthour' to get consumption counter in WattHour (per mqtt message) or to 'WatthourTotal' to get the total consumption measured in WattHour. To sync the WatthourTotal with your kWh-meter, publish the correct value to MQTT to the panasonic_heat_pump/s0/WattHourTotal/1 or panasonic_heat_pump/s0/WattHourTotal/2 topic with the 'retain' option while heishamon is rebooting. Upon reboot, heishamon reads this value as the last known value to you can sync using this method. Updating the firmware is as easy as going to the firmware menu and, after authentication with username 'admin' and password 'heisha' (or other provided during setup), uploading the binary there. @@ -152,6 +152,12 @@ Return boolean true when the input variable is still `NULL` in any other cases i - `round` Rounds the input float to the nearest integer. +- `floor` +The largest integer value less than or equal to the input float. + +- `ceil` +The smallest integer value greater than or equal to the input float. + - `setTimer` Sets a timer to trigger in X seconds. The first parameter is the timer number and the second parameters the number of seconds before it fires. A timer only fires once so it has to be re-set for recurring events. When a timer triggers it will can the timer event as described above. diff --git a/binaries/HeishaMon.ino.d1-v1.0.md5 b/binaries/HeishaMon.ino.d1-v1.0.md5 new file mode 100644 index 00000000..03be2c00 --- /dev/null +++ b/binaries/HeishaMon.ino.d1-v1.0.md5 @@ -0,0 +1 @@ +36ebefae1ba10b3a9630ce178401c0d4 \ No newline at end of file diff --git a/binaries/HeishaMon.ino.d1-v2.0.md5 b/binaries/HeishaMon.ino.d1-v2.0.md5 new file mode 100644 index 00000000..86f96f55 --- /dev/null +++ b/binaries/HeishaMon.ino.d1-v2.0.md5 @@ -0,0 +1 @@ +60538140e4d93c29971954feef067ffc \ No newline at end of file diff --git a/binaries/HeishaMon.ino.d1-v3.0.md5 b/binaries/HeishaMon.ino.d1-v3.0.md5 new file mode 100644 index 00000000..1e989067 --- /dev/null +++ b/binaries/HeishaMon.ino.d1-v3.0.md5 @@ -0,0 +1 @@ +872192efa66c91946e0295d5f8140fc5 \ No newline at end of file diff --git a/binaries/HeishaMon.ino.d1-v3.1.bin b/binaries/HeishaMon.ino.d1-v3.1.bin new file mode 100644 index 00000000..f58e246c Binary files /dev/null and b/binaries/HeishaMon.ino.d1-v3.1.bin differ diff --git a/binaries/HeishaMon.ino.d1-v3.1.md5 b/binaries/HeishaMon.ino.d1-v3.1.md5 new file mode 100644 index 00000000..13e9fb13 --- /dev/null +++ b/binaries/HeishaMon.ino.d1-v3.1.md5 @@ -0,0 +1 @@ +407c4c45cea30193b450ddfaf4f44ed4 \ No newline at end of file diff --git a/binaries/README.md b/binaries/README.md index 4dd867d7..11ab7ea4 100644 --- a/binaries/README.md +++ b/binaries/README.md @@ -6,5 +6,5 @@ The LittleFS versions will, after updating to this version, reset your HeishaMon From version 1.0 some topics are changed so you need to update your automation for this. The sensors are now in /main/ (before /sdc/) and the commands are expected in /commands/ (before in root topic). Check MQTT-Topics.md for the overview of all topics -The latest production release is v3.0. If you decide to try out a later development version, you should be able to restore the firmware using a USB-TTL cable as sometimes upgrading to a development versions seems to fail and brick the heishamon pcb. +The latest production release is v3.1. If you decide to try out a later development version, you should be able to restore the firmware using a USB-TTL cable as sometimes upgrading to a development versions seems to fail and brick the heishamon pcb.