Browse Source

## 0.2.2
* Home Assistant direct support (setTemp and setMode topics, real name used in setMode, mode names are configurable)
* added MQTT connection message on LWT topic
* default MQTT_HEARTBEAT_MAXAGE 180000 (former 12000)
* updated PubSubClient to 2.7 (now included in src due to changed parameters)
* minor improvements at MQTT connecting
* added additional publish topic for PIR sensor (configurable)
* bugfixes

FloKra 4 years ago
parent
commit
c8ef723bb2

+ 9 - 0
CHANGELOG.md

@@ -1,4 +1,13 @@
 # WiFiThermostat - Changelog
+## 0.2.2
+* Home Assistant direct support (setTemp and setMode topics, real name used in setMode, mode names are configurable)
+* added MQTT connection message on LWT topic
+* default MQTT_HEARTBEAT_MAXAGE 180000 (former 12000)
+* updated PubSubClient to 2.7 (now included in src due to changed parameters)
+* minor improvements at MQTT connecting
+* added additional publish topic for PIR sensor (configurable)
+* bugfixes
+
 ## 0.2.1
 * added second reduction mode with different set temperature
 * MQTT reconnect now tries again every 5 min after 10 fails, finally restarts MCU on 20 unsuccessful retries

+ 66 - 0
Home Assistant integration.txt

@@ -0,0 +1,66 @@
+climate: 
+- platform: mqtt
+  name: Thermostat
+  current_temperature_topic: T5/Thermostat/status/temp
+  temperature_state_topic: T5/Thermostat/status/currSetTemp
+  #temperature_state_topic: T5/Thermostat/status/setTemp    # wenn die Absenktemperatur nicht ersichtlich sein soll
+  temperature_command_topic: T5/Thermostat/setTemp
+  availability_topic: T5/Thermostat/availability
+  payload_available: "online"
+  payload_not_available: "offline"
+  qos: 2
+  #target_temp_step: 0.5  ### leider nicht supported hier - muss via customize gemacht werden. 
+  initial: 22.0
+  min_temp: 16.0
+  max_temp: 26.0
+  #Top5/Thermostat/status/heating
+  #power_state_topic: "Top5/Thermostat/status/heating"
+  #payload_on: "on"
+  #payload_off: "off"
+  mode_command_topic: "T5/Thermostat/setMode"
+  #mode_state_topic: "T5/Thermostat/status/heatingMode"
+  mode_state_topic: "T5/Thermostat/status/heatingModeName"
+  modes: ["Heizung aus", "Heizen", "Absenkung 1", "Absenkung 2"]
+  #mode_state_template: >
+  #  {% if value == "0" -%}Heizung aus
+  #  {% elif value == "1" -%}Heizen
+  #  {% elif value == "2" -%}Absenkung 1
+  #  {% elif value == "3" -%}Absenkung 2
+  #  {%- endif %}
+  
+  
+  
+in customize.yaml:
+climate.thermostat:
+  target_temp_step: 0.5
+
+  
+  
+binary_sensor:
+- platform: mqtt
+  name: "Heizung aktiv"
+  state_topic: "T5/Thermostat/status/heating"
+  payload_on: "on"
+  payload_off: "off"
+  availability_topic: T5/Thermostat/availability
+  payload_available: "online"
+  payload_not_available: "offline"
+
+  
+sensor: 
+- platform: mqtt
+  name: Wohnzimmer Temp
+  unit_of_measurement: '°C'
+  device_class: temperature
+  state_topic: "T5/Thermostat/status/temp"
+  availability_topic: T5/Thermostat/availability
+  payload_available: "online"
+  payload_not_available: "offline"
+- platform: mqtt
+  name: Wohnzimmer Hum
+  unit_of_measurement: '%'
+  device_class: humidity
+  state_topic: "T5/Thermostat/status/hum"
+  availability_topic: T5/Thermostat/availability
+  payload_available: "online"
+  payload_not_available: "offline"

+ 63 - 9
MQTT-commands.txt

@@ -1,14 +1,68 @@
 
 Es werden jeweils nur gültige und plausible Befehle durchgeführt.
 
-Betriebsmodus umschalten: 
-set mode 0		Heizung aus
-set mode 1		Normalbetrieb
-set mode 2		Nachtabsenkung
-set mode 3		Absenkung 2 (vorgesehen)
+Topic [MQTT_TOPIC_IN]/cmd:
+    
+    Betriebsmodus umschalten: 
+    set mode 0		Heizung aus
+    set mode 1		Heizen
+    set mode 2		Absenkung 1
+    set mode 3		Absenkung 2
+    
+    Solltemperatur einstellen:
+    jeweils in 0.5er Schritten (andere Kommastellen werden auf den nächsten 0.5er gerundet)
+    
+    set temp 21.5	-> Solltemperatur für Normalbetrieb, min. 14.0, max. 27.0
+    set tlow 16.0	-> Solltemperatur für Nachtabschaltung, min. 10.0, max. 19.0
+    
+    
+Topic [MQTT_TOPIC_IN]/setTemp:
+    Solltemperatur in 0.5er Schritten (andere Kommastellen werden auf den nächsten 0.5er gerundet)
+    es werden nur Werte akzeptiert die innerhalb dem definierten min/max liegen
+	Für direkte Home Assistant Anbindung vorgesehen
+	
+Topic [MQTT_TOPIC_IN]/setMode:
+    Betriebsmodus umschalten
+	Akzeptiert entweder Zahlen oder die konfigurierten Klarnamen der einzelnen Modi (case insensitive). 
+	On/Ein und Off/Aus wird ebenfalls verstanden (hardcoded), wobei On/Ein zum Modus 1 wechselt. 
+        0    Heizung aus
+        1    Heizen
+        2    Absenkung 1
+        3    Absenkung 2
+        on, ein
+        off, aus
+	
+	Für direkte Home Assistant Anbindung vorgesehen
+		
+Ausgabe:
+Topic [MQTT_TOPIC_OUT]/setTemp:
+	aktuell eingestellte Solltemperatur
+	
+Topic [MQTT_TOPIC_OUT]/currSetTemp:
+	aktuelle Solltemperatur unter Berücksichtigung eines evtl. aktiven Absenkmodus
+	
+Topic [MQTT_TOPIC_OUT]/heatingMode:
+	aktueller Modus (numerisch, Bedeutung siehe oben)
 
-Solltemperatur einstellen:
-jeweils in 0.5er Schritten (andere Kommastellen werden auf den nächsten 0.5er gerundet)
+Topic [MQTT_TOPIC_OUT]/heatingModeName:
+	aktueller Modus (konfigurierter Klarname)
 
-set temp 21.5	-> Solltemperatur für Normalbetrieb, min. 14.0, max. 27.0
-set tlow 16.0	-> Solltemperatur für Nachtabschaltung, min. 10.0, max. 19.0
+Topic [MQTT_TOPIC_OUT]/heating:
+	Ansteuerung der Heizung - "on" oder "off"
+
+Topic [MQTT_TOPIC_OUT]/temp:
+	Ist-Temperatur
+	
+Topic [MQTT_TOPIC_OUT]/hum:
+	Ist-Luftfeuchte
+	
+Topic [MQTT_TOPIC_OUT]/temp_raw:
+	Ist-Temperatur (nicht gemittelt)
+	
+Topic [MQTT_TOPIC_OUT]/hum_raw:
+	Ist-Luftfeuchte (nicht gemittelt)
+
+Topic [Last_Will_Topic] (zb. Thermostat/availability):
+	online
+	offline (last will message)
+	für Einbindung in Home Assistant gedacht

BIN
bin/WiFiThermostat.ino.d1_mini.20190530_v0.2.2.bin


BIN
libraries/pubsubclient-2.7.zip


BIN
src/Releases/WiFiThermostat_0.2.1.7z


BIN
src/Releases/WiFiThermostat_0.2.2.7z


+ 654 - 0
src/WiFiThermostat/PubSubClient.cpp

@@ -0,0 +1,654 @@
+/*
+  PubSubClient.cpp - A simple client for MQTT.
+  Nick O'Leary
+  http://knolleary.net
+*/
+
+#include "PubSubClient.h"
+#include "Arduino.h"
+
+PubSubClient::PubSubClient() {
+    this->_state = MQTT_DISCONNECTED;
+    this->_client = NULL;
+    this->stream = NULL;
+    setCallback(NULL);
+}
+
+PubSubClient::PubSubClient(Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setClient(client);
+    this->stream = NULL;
+}
+
+PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(addr, port);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(addr,port);
+    setClient(client);
+    setStream(stream);
+}
+PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(addr, port);
+    setCallback(callback);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(addr,port);
+    setCallback(callback);
+    setClient(client);
+    setStream(stream);
+}
+
+PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(ip, port);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(ip,port);
+    setClient(client);
+    setStream(stream);
+}
+PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(ip, port);
+    setCallback(callback);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(ip,port);
+    setCallback(callback);
+    setClient(client);
+    setStream(stream);
+}
+
+PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(domain,port);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(domain,port);
+    setClient(client);
+    setStream(stream);
+}
+PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(domain,port);
+    setCallback(callback);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(domain,port);
+    setCallback(callback);
+    setClient(client);
+    setStream(stream);
+}
+
+boolean PubSubClient::connect(const char *id) {
+    return connect(id,NULL,NULL,0,0,0,0,1);
+}
+
+boolean PubSubClient::connect(const char *id, const char *user, const char *pass) {
+    return connect(id,user,pass,0,0,0,0,1);
+}
+
+boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
+    return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage,1);
+}
+
+boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
+    return connect(id,user,pass,willTopic,willQos,willRetain,willMessage,1);
+}
+
+boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession) {
+    if (!connected()) {
+        int result = 0;
+
+        if (domain != NULL) {
+            result = _client->connect(this->domain, this->port);
+        } else {
+            result = _client->connect(this->ip, this->port);
+        }
+        if (result == 1) {
+            nextMsgId = 1;
+            // Leave room in the buffer for header and variable length field
+            uint16_t length = MQTT_MAX_HEADER_SIZE;
+            unsigned int j;
+
+#if MQTT_VERSION == MQTT_VERSION_3_1
+            uint8_t d[9] = {0x00,0x06,'M','Q','I','s','d','p', MQTT_VERSION};
+#define MQTT_HEADER_VERSION_LENGTH 9
+#elif MQTT_VERSION == MQTT_VERSION_3_1_1
+            uint8_t d[7] = {0x00,0x04,'M','Q','T','T',MQTT_VERSION};
+#define MQTT_HEADER_VERSION_LENGTH 7
+#endif
+            for (j = 0;j<MQTT_HEADER_VERSION_LENGTH;j++) {
+                buffer[length++] = d[j];
+            }
+
+            uint8_t v;
+            if (willTopic) {
+                v = 0x04|(willQos<<3)|(willRetain<<5);
+            } else {
+                v = 0x00;
+            }
+            if (cleanSession) {
+                v = v|0x02;
+            }
+
+            if(user != NULL) {
+                v = v|0x80;
+
+                if(pass != NULL) {
+                    v = v|(0x80>>1);
+                }
+            }
+
+            buffer[length++] = v;
+
+            buffer[length++] = ((MQTT_KEEPALIVE) >> 8);
+            buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF);
+
+            CHECK_STRING_LENGTH(length,id)
+            length = writeString(id,buffer,length);
+            if (willTopic) {
+                CHECK_STRING_LENGTH(length,willTopic)
+                length = writeString(willTopic,buffer,length);
+                CHECK_STRING_LENGTH(length,willMessage)
+                length = writeString(willMessage,buffer,length);
+            }
+
+            if(user != NULL) {
+                CHECK_STRING_LENGTH(length,user)
+                length = writeString(user,buffer,length);
+                if(pass != NULL) {
+                    CHECK_STRING_LENGTH(length,pass)
+                    length = writeString(pass,buffer,length);
+                }
+            }
+
+            write(MQTTCONNECT,buffer,length-MQTT_MAX_HEADER_SIZE);
+
+            lastInActivity = lastOutActivity = millis();
+
+            while (!_client->available()) {
+                unsigned long t = millis();
+                if (t-lastInActivity >= ((int32_t) MQTT_SOCKET_TIMEOUT*1000UL)) {
+                    _state = MQTT_CONNECTION_TIMEOUT;
+                    _client->stop();
+                    return false;
+                }
+            }
+            uint8_t llen;
+            uint16_t len = readPacket(&llen);
+
+            if (len == 4) {
+                if (buffer[3] == 0) {
+                    lastInActivity = millis();
+                    pingOutstanding = false;
+                    _state = MQTT_CONNECTED;
+                    return true;
+                } else {
+                    _state = buffer[3];
+                }
+            }
+            _client->stop();
+        } else {
+            _state = MQTT_CONNECT_FAILED;
+        }
+        return false;
+    }
+    return true;
+}
+
+// reads a byte into result
+boolean PubSubClient::readByte(uint8_t * result) {
+   uint32_t previousMillis = millis();
+   while(!_client->available()) {
+     yield();
+     uint32_t currentMillis = millis();
+     if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)){
+       return false;
+     }
+   }
+   *result = _client->read();
+   return true;
+}
+
+// reads a byte into result[*index] and increments index
+boolean PubSubClient::readByte(uint8_t * result, uint16_t * index){
+  uint16_t current_index = *index;
+  uint8_t * write_address = &(result[current_index]);
+  if(readByte(write_address)){
+    *index = current_index + 1;
+    return true;
+  }
+  return false;
+}
+
+uint16_t PubSubClient::readPacket(uint8_t* lengthLength) {
+    uint16_t len = 0;
+    if(!readByte(buffer, &len)) return 0;
+    bool isPublish = (buffer[0]&0xF0) == MQTTPUBLISH;
+    uint32_t multiplier = 1;
+    uint16_t length = 0;
+    uint8_t digit = 0;
+    uint16_t skip = 0;
+    uint8_t start = 0;
+
+    do {
+        if (len == 5) {
+            // Invalid remaining length encoding - kill the connection
+            _state = MQTT_DISCONNECTED;
+            _client->stop();
+            return 0;
+        }
+        if(!readByte(&digit)) return 0;
+        buffer[len++] = digit;
+        length += (digit & 127) * multiplier;
+        multiplier *= 128;
+    } while ((digit & 128) != 0);
+    *lengthLength = len-1;
+
+    if (isPublish) {
+        // Read in topic length to calculate bytes to skip over for Stream writing
+        if(!readByte(buffer, &len)) return 0;
+        if(!readByte(buffer, &len)) return 0;
+        skip = (buffer[*lengthLength+1]<<8)+buffer[*lengthLength+2];
+        start = 2;
+        if (buffer[0]&MQTTQOS1) {
+            // skip message id
+            skip += 2;
+        }
+    }
+
+    for (uint16_t i = start;i<length;i++) {
+        if(!readByte(&digit)) return 0;
+        if (this->stream) {
+            if (isPublish && len-*lengthLength-2>skip) {
+                this->stream->write(digit);
+            }
+        }
+        if (len < MQTT_MAX_PACKET_SIZE) {
+            buffer[len] = digit;
+        }
+        len++;
+    }
+
+    if (!this->stream && len > MQTT_MAX_PACKET_SIZE) {
+        len = 0; // This will cause the packet to be ignored.
+    }
+
+    return len;
+}
+
+boolean PubSubClient::loop() {
+    if (connected()) {
+        unsigned long t = millis();
+        if ((t - lastInActivity > MQTT_KEEPALIVE*1000UL) || (t - lastOutActivity > MQTT_KEEPALIVE*1000UL)) {
+            if (pingOutstanding) {
+                this->_state = MQTT_CONNECTION_TIMEOUT;
+                _client->stop();
+                return false;
+            } else {
+                buffer[0] = MQTTPINGREQ;
+                buffer[1] = 0;
+                _client->write(buffer,2);
+                lastOutActivity = t;
+                lastInActivity = t;
+                pingOutstanding = true;
+            }
+        }
+        if (_client->available()) {
+            uint8_t llen;
+            uint16_t len = readPacket(&llen);
+            uint16_t msgId = 0;
+            uint8_t *payload;
+            if (len > 0) {
+                lastInActivity = t;
+                uint8_t type = buffer[0]&0xF0;
+                if (type == MQTTPUBLISH) {
+                    if (callback) {
+                        uint16_t tl = (buffer[llen+1]<<8)+buffer[llen+2]; /* topic length in bytes */
+                        memmove(buffer+llen+2,buffer+llen+3,tl); /* move topic inside buffer 1 byte to front */
+                        buffer[llen+2+tl] = 0; /* end the topic as a 'C' string with \x00 */
+                        char *topic = (char*) buffer+llen+2;
+                        // msgId only present for QOS>0
+                        if ((buffer[0]&0x06) == MQTTQOS1) {
+                            msgId = (buffer[llen+3+tl]<<8)+buffer[llen+3+tl+1];
+                            payload = buffer+llen+3+tl+2;
+                            callback(topic,payload,len-llen-3-tl-2);
+
+                            buffer[0] = MQTTPUBACK;
+                            buffer[1] = 2;
+                            buffer[2] = (msgId >> 8);
+                            buffer[3] = (msgId & 0xFF);
+                            _client->write(buffer,4);
+                            lastOutActivity = t;
+
+                        } else {
+                            payload = buffer+llen+3+tl;
+                            callback(topic,payload,len-llen-3-tl);
+                        }
+                    }
+                } else if (type == MQTTPINGREQ) {
+                    buffer[0] = MQTTPINGRESP;
+                    buffer[1] = 0;
+                    _client->write(buffer,2);
+                } else if (type == MQTTPINGRESP) {
+                    pingOutstanding = false;
+                }
+            } else if (!connected()) {
+                // readPacket has closed the connection
+                return false;
+            }
+        }
+        return true;
+    }
+    return false;
+}
+
+boolean PubSubClient::publish(const char* topic, const char* payload) {
+    return publish(topic,(const uint8_t*)payload,strlen(payload),false);
+}
+
+boolean PubSubClient::publish(const char* topic, const char* payload, boolean retained) {
+    return publish(topic,(const uint8_t*)payload,strlen(payload),retained);
+}
+
+boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength) {
+    return publish(topic, payload, plength, false);
+}
+
+boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
+    if (connected()) {
+        if (MQTT_MAX_PACKET_SIZE < MQTT_MAX_HEADER_SIZE + 2+strlen(topic) + plength) {
+            // Too long
+            return false;
+        }
+        // Leave room in the buffer for header and variable length field
+        uint16_t length = MQTT_MAX_HEADER_SIZE;
+        length = writeString(topic,buffer,length);
+        uint16_t i;
+        for (i=0;i<plength;i++) {
+            buffer[length++] = payload[i];
+        }
+        uint8_t header = MQTTPUBLISH;
+        if (retained) {
+            header |= 1;
+        }
+        return write(header,buffer,length-MQTT_MAX_HEADER_SIZE);
+    }
+    return false;
+}
+
+boolean PubSubClient::publish_P(const char* topic, const char* payload, boolean retained) {
+    return publish_P(topic, (const uint8_t*)payload, strlen(payload), retained);
+}
+
+boolean PubSubClient::publish_P(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
+    uint8_t llen = 0;
+    uint8_t digit;
+    unsigned int rc = 0;
+    uint16_t tlen;
+    unsigned int pos = 0;
+    unsigned int i;
+    uint8_t header;
+    unsigned int len;
+
+    if (!connected()) {
+        return false;
+    }
+
+    tlen = strlen(topic);
+
+    header = MQTTPUBLISH;
+    if (retained) {
+        header |= 1;
+    }
+    buffer[pos++] = header;
+    len = plength + 2 + tlen;
+    do {
+        digit = len % 128;
+        len = len / 128;
+        if (len > 0) {
+            digit |= 0x80;
+        }
+        buffer[pos++] = digit;
+        llen++;
+    } while(len>0);
+
+    pos = writeString(topic,buffer,pos);
+
+    rc += _client->write(buffer,pos);
+
+    for (i=0;i<plength;i++) {
+        rc += _client->write((char)pgm_read_byte_near(payload + i));
+    }
+
+    lastOutActivity = millis();
+
+    return rc == tlen + 4 + plength;
+}
+
+boolean PubSubClient::beginPublish(const char* topic, unsigned int plength, boolean retained) {
+    if (connected()) {
+        // Send the header and variable length field
+        uint16_t length = MQTT_MAX_HEADER_SIZE;
+        length = writeString(topic,buffer,length);
+        uint16_t i;
+        uint8_t header = MQTTPUBLISH;
+        if (retained) {
+            header |= 1;
+        }
+        size_t hlen = buildHeader(header, buffer, plength+length-MQTT_MAX_HEADER_SIZE);
+        uint16_t rc = _client->write(buffer+(MQTT_MAX_HEADER_SIZE-hlen),length-(MQTT_MAX_HEADER_SIZE-hlen));
+        lastOutActivity = millis();
+        return (rc == (length-(MQTT_MAX_HEADER_SIZE-hlen)));
+    }
+    return false;
+}
+
+int PubSubClient::endPublish() {
+ return 1;
+}
+
+size_t PubSubClient::write(uint8_t data) {
+    lastOutActivity = millis();
+    return _client->write(data);
+}
+
+size_t PubSubClient::write(const uint8_t *buffer, size_t size) {
+    lastOutActivity = millis();
+    return _client->write(buffer,size);
+}
+
+size_t PubSubClient::buildHeader(uint8_t header, uint8_t* buf, uint16_t length) {
+    uint8_t lenBuf[4];
+    uint8_t llen = 0;
+    uint8_t digit;
+    uint8_t pos = 0;
+    uint16_t len = length;
+    do {
+        digit = len % 128;
+        len = len / 128;
+        if (len > 0) {
+            digit |= 0x80;
+        }
+        lenBuf[pos++] = digit;
+        llen++;
+    } while(len>0);
+
+    buf[4-llen] = header;
+    for (int i=0;i<llen;i++) {
+        buf[MQTT_MAX_HEADER_SIZE-llen+i] = lenBuf[i];
+    }
+    return llen+1; // Full header size is variable length bit plus the 1-byte fixed header
+}
+
+boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) {
+    uint16_t rc;
+    uint8_t hlen = buildHeader(header, buf, length);
+
+#ifdef MQTT_MAX_TRANSFER_SIZE
+    uint8_t* writeBuf = buf+(MQTT_MAX_HEADER_SIZE-hlen);
+    uint16_t bytesRemaining = length+hlen;  //Match the length type
+    uint8_t bytesToWrite;
+    boolean result = true;
+    while((bytesRemaining > 0) && result) {
+        bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining;
+        rc = _client->write(writeBuf,bytesToWrite);
+        result = (rc == bytesToWrite);
+        bytesRemaining -= rc;
+        writeBuf += rc;
+    }
+    return result;
+#else
+    rc = _client->write(buf+(MQTT_MAX_HEADER_SIZE-hlen),length+hlen);
+    lastOutActivity = millis();
+    return (rc == hlen+length);
+#endif
+}
+
+boolean PubSubClient::subscribe(const char* topic) {
+    return subscribe(topic, 0);
+}
+
+boolean PubSubClient::subscribe(const char* topic, uint8_t qos) {
+    if (qos > 1) {
+        return false;
+    }
+    if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
+        // Too long
+        return false;
+    }
+    if (connected()) {
+        // Leave room in the buffer for header and variable length field
+        uint16_t length = MQTT_MAX_HEADER_SIZE;
+        nextMsgId++;
+        if (nextMsgId == 0) {
+            nextMsgId = 1;
+        }
+        buffer[length++] = (nextMsgId >> 8);
+        buffer[length++] = (nextMsgId & 0xFF);
+        length = writeString((char*)topic, buffer,length);
+        buffer[length++] = qos;
+        return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE);
+    }
+    return false;
+}
+
+boolean PubSubClient::unsubscribe(const char* topic) {
+    if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
+        // Too long
+        return false;
+    }
+    if (connected()) {
+        uint16_t length = MQTT_MAX_HEADER_SIZE;
+        nextMsgId++;
+        if (nextMsgId == 0) {
+            nextMsgId = 1;
+        }
+        buffer[length++] = (nextMsgId >> 8);
+        buffer[length++] = (nextMsgId & 0xFF);
+        length = writeString(topic, buffer,length);
+        return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE);
+    }
+    return false;
+}
+
+void PubSubClient::disconnect() {
+    buffer[0] = MQTTDISCONNECT;
+    buffer[1] = 0;
+    _client->write(buffer,2);
+    _state = MQTT_DISCONNECTED;
+    _client->flush();
+    _client->stop();
+    lastInActivity = lastOutActivity = millis();
+}
+
+uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, uint16_t pos) {
+    const char* idp = string;
+    uint16_t i = 0;
+    pos += 2;
+    while (*idp) {
+        buf[pos++] = *idp++;
+        i++;
+    }
+    buf[pos-i-2] = (i >> 8);
+    buf[pos-i-1] = (i & 0xFF);
+    return pos;
+}
+
+
+boolean PubSubClient::connected() {
+    boolean rc;
+    if (_client == NULL ) {
+        rc = false;
+    } else {
+        rc = (int)_client->connected();
+        if (!rc) {
+            if (this->_state == MQTT_CONNECTED) {
+                this->_state = MQTT_CONNECTION_LOST;
+                _client->flush();
+                _client->stop();
+            }
+        }
+    }
+    return rc;
+}
+
+PubSubClient& PubSubClient::setServer(uint8_t * ip, uint16_t port) {
+    IPAddress addr(ip[0],ip[1],ip[2],ip[3]);
+    return setServer(addr,port);
+}
+
+PubSubClient& PubSubClient::setServer(IPAddress ip, uint16_t port) {
+    this->ip = ip;
+    this->port = port;
+    this->domain = NULL;
+    return *this;
+}
+
+PubSubClient& PubSubClient::setServer(const char * domain, uint16_t port) {
+    this->domain = domain;
+    this->port = port;
+    return *this;
+}
+
+PubSubClient& PubSubClient::setCallback(MQTT_CALLBACK_SIGNATURE) {
+    this->callback = callback;
+    return *this;
+}
+
+PubSubClient& PubSubClient::setClient(Client& client){
+    this->_client = &client;
+    return *this;
+}
+
+PubSubClient& PubSubClient::setStream(Stream& stream){
+    this->stream = &stream;
+    return *this;
+}
+
+int PubSubClient::state() {
+    return this->_state;
+}
+

+ 174 - 0
src/WiFiThermostat/PubSubClient.h

@@ -0,0 +1,174 @@
+/*
+ PubSubClient.h - A simple client for MQTT.
+  Nick O'Leary
+  http://knolleary.net
+*/
+
+#ifndef PubSubClient_h
+#define PubSubClient_h
+
+#include <Arduino.h>
+#include "IPAddress.h"
+#include "Client.h"
+#include "Stream.h"
+
+#define MQTT_VERSION_3_1      3
+#define MQTT_VERSION_3_1_1    4
+
+// MQTT_VERSION : Pick the version
+//#define MQTT_VERSION MQTT_VERSION_3_1
+#ifndef MQTT_VERSION
+#define MQTT_VERSION MQTT_VERSION_3_1_1
+#endif
+
+// MQTT_MAX_PACKET_SIZE : Maximum packet size
+#ifndef MQTT_MAX_PACKET_SIZE
+#define MQTT_MAX_PACKET_SIZE 512
+#endif
+
+// MQTT_KEEPALIVE : keepAlive interval in Seconds
+#ifndef MQTT_KEEPALIVE
+#define MQTT_KEEPALIVE 10
+#endif
+
+// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds
+#ifndef MQTT_SOCKET_TIMEOUT
+#define MQTT_SOCKET_TIMEOUT 20
+#endif
+
+// MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client
+//  in each write call. Needed for the Arduino Wifi Shield. Leave undefined to
+//  pass the entire MQTT packet in each write call.
+//#define MQTT_MAX_TRANSFER_SIZE 80
+
+// Possible values for client.state()
+#define MQTT_CONNECTION_TIMEOUT     -4
+#define MQTT_CONNECTION_LOST        -3
+#define MQTT_CONNECT_FAILED         -2
+#define MQTT_DISCONNECTED           -1
+#define MQTT_CONNECTED               0
+#define MQTT_CONNECT_BAD_PROTOCOL    1
+#define MQTT_CONNECT_BAD_CLIENT_ID   2
+#define MQTT_CONNECT_UNAVAILABLE     3
+#define MQTT_CONNECT_BAD_CREDENTIALS 4
+#define MQTT_CONNECT_UNAUTHORIZED    5
+
+#define MQTTCONNECT     1 << 4  // Client request to connect to Server
+#define MQTTCONNACK     2 << 4  // Connect Acknowledgment
+#define MQTTPUBLISH     3 << 4  // Publish message
+#define MQTTPUBACK      4 << 4  // Publish Acknowledgment
+#define MQTTPUBREC      5 << 4  // Publish Received (assured delivery part 1)
+#define MQTTPUBREL      6 << 4  // Publish Release (assured delivery part 2)
+#define MQTTPUBCOMP     7 << 4  // Publish Complete (assured delivery part 3)
+#define MQTTSUBSCRIBE   8 << 4  // Client Subscribe request
+#define MQTTSUBACK      9 << 4  // Subscribe Acknowledgment
+#define MQTTUNSUBSCRIBE 10 << 4 // Client Unsubscribe request
+#define MQTTUNSUBACK    11 << 4 // Unsubscribe Acknowledgment
+#define MQTTPINGREQ     12 << 4 // PING Request
+#define MQTTPINGRESP    13 << 4 // PING Response
+#define MQTTDISCONNECT  14 << 4 // Client is Disconnecting
+#define MQTTReserved    15 << 4 // Reserved
+
+#define MQTTQOS0        (0 << 1)
+#define MQTTQOS1        (1 << 1)
+#define MQTTQOS2        (2 << 1)
+
+// Maximum size of fixed header and variable length size header
+#define MQTT_MAX_HEADER_SIZE 5
+
+#if defined(ESP8266) || defined(ESP32)
+#include <functional>
+#define MQTT_CALLBACK_SIGNATURE std::function<void(char*, uint8_t*, unsigned int)> callback
+#else
+#define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int)
+#endif
+
+#define CHECK_STRING_LENGTH(l,s) if (l+2+strlen(s) > MQTT_MAX_PACKET_SIZE) {_client->stop();return false;}
+
+class PubSubClient : public Print {
+private:
+   Client* _client;
+   uint8_t buffer[MQTT_MAX_PACKET_SIZE];
+   uint16_t nextMsgId;
+   unsigned long lastOutActivity;
+   unsigned long lastInActivity;
+   bool pingOutstanding;
+   MQTT_CALLBACK_SIGNATURE;
+   uint16_t readPacket(uint8_t*);
+   boolean readByte(uint8_t * result);
+   boolean readByte(uint8_t * result, uint16_t * index);
+   boolean write(uint8_t header, uint8_t* buf, uint16_t length);
+   uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos);
+   // Build up the header ready to send
+   // Returns the size of the header
+   // Note: the header is built at the end of the first MQTT_MAX_HEADER_SIZE bytes, so will start
+   //       (MQTT_MAX_HEADER_SIZE - <returned size>) bytes into the buffer
+   size_t buildHeader(uint8_t header, uint8_t* buf, uint16_t length);
+   IPAddress ip;
+   const char* domain;
+   uint16_t port;
+   Stream* stream;
+   int _state;
+public:
+   PubSubClient();
+   PubSubClient(Client& client);
+   PubSubClient(IPAddress, uint16_t, Client& client);
+   PubSubClient(IPAddress, uint16_t, Client& client, Stream&);
+   PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
+   PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
+   PubSubClient(uint8_t *, uint16_t, Client& client);
+   PubSubClient(uint8_t *, uint16_t, Client& client, Stream&);
+   PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
+   PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
+   PubSubClient(const char*, uint16_t, Client& client);
+   PubSubClient(const char*, uint16_t, Client& client, Stream&);
+   PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
+   PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
+
+   PubSubClient& setServer(IPAddress ip, uint16_t port);
+   PubSubClient& setServer(uint8_t * ip, uint16_t port);
+   PubSubClient& setServer(const char * domain, uint16_t port);
+   PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE);
+   PubSubClient& setClient(Client& client);
+   PubSubClient& setStream(Stream& stream);
+
+   boolean connect(const char* id);
+   boolean connect(const char* id, const char* user, const char* pass);
+   boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
+   boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
+   boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession);
+   void disconnect();
+   boolean publish(const char* topic, const char* payload);
+   boolean publish(const char* topic, const char* payload, boolean retained);
+   boolean publish(const char* topic, const uint8_t * payload, unsigned int plength);
+   boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
+   boolean publish_P(const char* topic, const char* payload, boolean retained);
+   boolean publish_P(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
+   // Start to publish a message.
+   // This API:
+   //   beginPublish(...)
+   //   one or more calls to write(...)
+   //   endPublish()
+   // Allows for arbitrarily large payloads to be sent without them having to be copied into
+   // a new buffer and held in memory at one time
+   // Returns 1 if the message was started successfully, 0 if there was an error
+   boolean beginPublish(const char* topic, unsigned int plength, boolean retained);
+   // Finish off this publish message (started with beginPublish)
+   // Returns 1 if the packet was sent successfully, 0 if there was an error
+   int endPublish();
+   // Write a single byte of payload (only to be used with beginPublish/endPublish)
+   virtual size_t write(uint8_t);
+   // Write size bytes from buffer into the payload (only to be used with beginPublish/endPublish)
+   // Returns the number of bytes written
+   virtual size_t write(const uint8_t *buffer, size_t size);
+   boolean subscribe(const char* topic);
+   boolean subscribe(const char* topic, uint8_t qos);
+   boolean unsubscribe(const char* topic);
+   boolean loop();
+   boolean connected();
+   int state();
+};
+
+
+#endif
+

+ 42 - 6
src/WiFiThermostat/WiFiThermostat.ino

@@ -7,7 +7,7 @@
 #define SPIFFS_USE_MAGIC
 
 #define FIRMWARE_NAME "WiFiThermostat"
-#define VERSION "0.2.1"
+#define VERSION "0.2.2"
 
 
 
@@ -20,13 +20,14 @@
 #define MQTT_PORT 1883
 #define MQTT_USER ""
 #define MQTT_PASS ""
-#define MQTT_TOPIC_IN "Test/Thermostat/cmd"
+#define MQTT_TOPIC_IN "Test/Thermostat"
 #define MQTT_TOPIC_OUT "Test/Thermostat/status"
 #define MQTT_OUT_RETAIN false
-#define MQTT_WILLTOPIC ""
+#define MQTT_WILLTOPIC "Test/Thermostat/availability"
 #define MQTT_WILLQOS 2
 #define MQTT_WILLRETAIN false
-#define MQTT_WILLMSG ""
+#define MQTT_WILLMSG "offline"
+#define MQTT_CONNMSG "online"
 #define DOMOTICZ_OUT_TOPIC "domoticz/out"
 
 // default values, can later be overridden via configuration (conf2)
@@ -37,6 +38,7 @@
 #define DOMOTICZ_IDX_PIR 0
 #define OUTTEMP_TOPIC_IN ""
 #define OUTHUM_TOPIC_IN ""
+#define MQTT_TOPIC_PIR ""                // extra publish topic for PIR sensor
 #define AUTOSAVE_SETTEMP true
 #define AUTOSAVE_SETMODE true
 #define DEFAULT_HEATING_MIN_OFFTIME 120  // minimal time the heating keeps turned off in s
@@ -53,6 +55,11 @@
 #define DEFAULT_DISPLAY_TIMEOUT 30       // display timeout after keypress (illumination)
 #define DEFAULT_PIR_ENABLES_DISPLAY false
 
+#define MODE_NAME_0 "Heizung aus"
+#define MODE_NAME_1 "Heizen"
+#define MODE_NAME_2 "Absenkung 1"
+#define MODE_NAME_3 "Absenkung 2"
+
 // default initial values
 #define DEFAULT_SETTEMP 21.5
 #define DEFAULT_HEATINGMODE 1
@@ -67,7 +74,7 @@
 #define SETTEMP_LOW_MAX 20.0 // maximal configurable temperature for reduction mode
 #define DOMOTICZ_DISMISSUPDATE_TIMEOUT 2500 // after a value was changed by data from domoticz/out, domoticz/out parsing for this device will be turned off for this time to prevent infinite loops
 #define DOMOTICZ_FORCEUPDATE_INTERVAL 15 // interval in min to force update of domoticz devices
-#define MQTT_HEARTBEAT_MAXAGE 120000 // interval for MQTT heartbeat message. only applicable if MQTT IN-topic is defined. after this timeout MQTT reconnect is forced
+#define MQTT_HEARTBEAT_MAXAGE 180000 // interval for MQTT heartbeat message. only applicable if MQTT IN-topic is defined. after this timeout MQTT reconnect is forced
 
 // pin assignments and I2C addresses
 #define PIN_DHTSENSOR 13
@@ -102,7 +109,7 @@ PushButton pirSensor = PushButton(PIN_PIRSENSOR, PRESSED_WHEN_HIGH);
 #include <ESP8266WebServer.h>
 #include <ESP8266mDNS.h>
 #include <ESP8266HTTPUpdateServer.h>
-#include <PubSubClient.h>
+#include "PubSubClient.h"
 #include <DNSServer.h>
 #include <FS.h>
 #include <Wire.h>
@@ -120,6 +127,8 @@ PushButton pirSensor = PushButton(PIN_PIRSENSOR, PRESSED_WHEN_HIGH);
 #endif
 
 
+bool serialdebug = true;
+bool mqttdebug = true;
 
 // config variables - do not change here!
 //conf
@@ -138,8 +147,14 @@ char mqtt_willTopic[51];  // MQTT Last Will topic
 int mqtt_willQos = MQTT_WILLQOS;     // MQTT Last Will topic QOS
 boolean mqtt_willRetain = MQTT_WILLRETAIN;  // MQTT Last Will retain
 char mqtt_willMsg[31];    // MQTT Last Will payload
+char mqtt_connMsg[31];
 char domoticz_out_topic[55];  // domoticz out topic to subscribe to (only applicable if domoticzIdx_Thermostat and/or domoticzIdx_ThermostatMode is set to >0)
 
+char mqtt_topic_in_cmd[61];
+char mqtt_topic_in_setTemp[61];
+char mqtt_topic_in_setMode[61];
+
+
 //conf2
 int domoticzIdx_Thermostat = DOMOTICZ_IDX_THERMOSTAT;
 int domoticzIdx_ThermostatMode = DOMOTICZ_IDX_THERMOSTATMODE;
@@ -148,6 +163,7 @@ int domoticzIdx_Heating = DOMOTICZ_IDX_HEATING;
 int domoticzIdx_PIR = DOMOTICZ_IDX_PIR;
 char outTemp_topic_in[51];
 char outHum_topic_in[51];
+char mqtt_topic_pir[51];
 boolean autoSaveSetTemp = AUTOSAVE_SETTEMP;
 boolean autoSaveHeatingMode = AUTOSAVE_SETMODE;
 int heatingMinOffTime = DEFAULT_HEATING_MIN_OFFTIME; // minimal time the heating keeps turned off in s
@@ -163,6 +179,10 @@ int measureInterval = DEFAULT_MEASURE_INTERVAL;   // interval for temp/hum measu
 int displayInterval = DEFAULT_DISPLAY_INTERVAL;   // interval for display updates (if out-temp is active, display will toggle in this interval)
 int displayTimeout = DEFAULT_DISPLAY_TIMEOUT;     // display timeout after keypress (illumination)
 boolean PIR_enablesDisplay = DEFAULT_PIR_ENABLES_DISPLAY; // PIR sensor enables display illumination
+char modename0[15];
+char modename1[15];
+char modename2[15];
+char modename3[15];
 
 //set values
 float setTemp = DEFAULT_SETTEMP;
@@ -193,6 +213,7 @@ char outTemp_newValue[6];
 bool outTemp_parseNewValue;
 char outHum_newValue[4];
 bool outHum_parseNewValue;
+char currentModeName[15];
 
 byte whichTempToDisplay; // 1=temp inside (from DHT sensor), 2= temp outside (via MQTT) - if out temp/hum available this value and the displayed value pair toggles with every displayInterval
 unsigned long lastMeasure = 0; // millis of last temp/hum measurement
@@ -233,6 +254,8 @@ int mqttReconnects = 0;
 unsigned long mqttLastHeartbeat;
 bool mqttInTopicSubscribed = false;
 
+
+
 DHT dht(PIN_DHTSENSOR, DHTTYPE);
 LiquidCrystal_I2C lcd(LCDADDR, LCDCOLS, LCDLINES); // set the LCD address to 0x27 for a 16 chars and 2 line display
 
@@ -292,11 +315,18 @@ void setup() {
   strlcpy(mqtt_topic_out, MQTT_TOPIC_OUT, 51);
   strlcpy(mqtt_willTopic, MQTT_WILLTOPIC, 51);
   strlcpy(mqtt_willMsg, MQTT_WILLMSG, 31);
+  strlcpy(mqtt_connMsg, MQTT_CONNMSG, 31);
   strlcpy(domoticz_out_topic, DOMOTICZ_OUT_TOPIC, 51); // changeable subscription topic, as domoticz supports different flat/hierarchical out-topics
 
   //set conf2 default values (bool, int and float variables are set at declaration)
   strlcpy(outTemp_topic_in, OUTTEMP_TOPIC_IN, 51);
   strlcpy(outHum_topic_in, OUTHUM_TOPIC_IN, 51);
+  strlcpy(mqtt_topic_pir, MQTT_TOPIC_PIR, 51);
+
+  strlcpy(modename0, MODE_NAME_0, 14);
+  strlcpy(modename1, MODE_NAME_1, 14);
+  strlcpy(modename2, MODE_NAME_2, 14);
+  strlcpy(modename3, MODE_NAME_3, 14);
 
   Serial.println("default config values loaded..");
 
@@ -355,9 +385,15 @@ void setup() {
   setTempSaved = setTemp;
   heatingModeSaved = heatingMode;
 
+  updateCurrentHeatingModeName();
+
   // initialize DHT11/22 temp/hum sensor
   dht.begin();
 
+  
+  
+  mqttPrepareSubscribeTopics();
+
   checkUseDomoticz();
   delay(500);
 

+ 67 - 0
src/WiFiThermostat/config.ino

@@ -90,6 +90,9 @@ bool setConfig(char* param, char* value) {
   else if ( strcmp(param, "willMsg") == 0 ) {
     strlcpy(mqtt_willMsg, value, 31);
   }
+  else if ( strcmp(param, "connMsg") == 0 ) {
+    strlcpy(mqtt_connMsg, value, 31);
+  }
   else if ( strcmp(param, "domOutTop") == 0 ) {
     strlcpy(domoticz_out_topic, value, 51);
   }
@@ -116,6 +119,9 @@ bool setConfig(char* param, char* value) {
   else if ( strcmp(param, "outHumTop") == 0 ) {
     strlcpy(outHum_topic_in, value, 51);
   }
+  else if ( strcmp(param, "PIRTop") == 0 ) {
+    strlcpy(mqtt_topic_pir, value, 51);
+  }
   else if ( strcmp(param, "autoSaveTemp") == 0 ) {
     if (atoi(value) == 1) autoSaveSetTemp = true;
     else autoSaveSetTemp = false;
@@ -193,6 +199,18 @@ bool setConfig(char* param, char* value) {
       displayTimeout = valueInt;
     }
   }
+  else if ( strcmp(param, "modename0") == 0 ) {
+    strlcpy(modename0, value, 14);
+  }
+  else if ( strcmp(param, "modename1") == 0 ) {
+    strlcpy(modename1, value, 14);
+  }
+  else if ( strcmp(param, "modename2") == 0 ) {
+    strlcpy(modename2, value, 14);
+  }
+  else if ( strcmp(param, "modename3") == 0 ) {
+    strlcpy(modename3, value, 14);
+  }
   else if ( strcmp(param, "PIRenDisp") == 0 ) {
     int valueInt = atoi(value);
     if (valueInt == 1) PIR_enablesDisplay = true;
@@ -302,6 +320,10 @@ void getConfig(char* param) {
     sprintf(buf, "willMsg: '%s'", mqtt_willMsg);
     sendStatus(buf);
   }
+  else if ( strcmp(param, "connMsg") == 0 ) {
+    sprintf(buf, "connMsg: '%s'", mqtt_connMsg);
+    sendStatus(buf);
+  }
   else if ( strcmp(param, "domOutTop") == 0 ) {
     sprintf(buf, "domOutTop: '%s'", domoticz_out_topic);
     sendStatus(buf);
@@ -336,6 +358,10 @@ void getConfig(char* param) {
     sprintf(buf, "outHumTop: '%s'", outHum_topic_in);
     sendStatus(buf);
   }
+  else if ( strcmp(param, "PIRTop") == 0 ) {
+    sprintf(buf, "PIRTop: '%s'", mqtt_topic_pir);
+    sendStatus(buf);
+  }
   else if ( strcmp(param, "autoSaveTemp") == 0 ) {
     char buf2[11];
     if (autoSaveSetTemp) strcpy(buf2, "1");
@@ -400,6 +426,22 @@ void getConfig(char* param) {
     sprintf(buf, "dispTout: '%d'", displayTimeout);
     sendStatus(buf);
   }
+  else if ( strcmp(param, "modename0") == 0 ) {
+    sprintf(buf, "modename0: '%s'", modename0);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "modename1") == 0 ) {
+    sprintf(buf, "modename1: '%s'", modename1);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "modename2") == 0 ) {
+    sprintf(buf, "modename2: '%s'", modename2);
+    sendStatus(buf);
+  }
+  else if ( strcmp(param, "modename3") == 0 ) {
+    sprintf(buf, "modename3: '%s'", modename3);
+    sendStatus(buf);
+  }
   else if ( strcmp(param, "PIRenDisp") == 0 ) {
     char buf2[11];
     if (PIR_enablesDisplay) strcpy(buf2, "1");
@@ -442,6 +484,8 @@ void printConfig() {
   Serial.println(mqtt_willRetain);
   Serial.print("willMsg: ");
   Serial.println(mqtt_willMsg);
+  Serial.print("connMsg: ");
+  Serial.println(mqtt_connMsg);
   Serial.print("domOutTop: ");
   Serial.println(domoticz_out_topic);
   Serial.println();
@@ -462,6 +506,8 @@ void printConfig2() {
   Serial.println(outTemp_topic_in);
   Serial.print("outHumTop: ");
   Serial.println(outHum_topic_in);
+  Serial.print("PIRTop: ");
+  Serial.println(mqtt_topic_pir);
   Serial.print("autoSaveTemp: ");
   Serial.println(autoSaveSetTemp);
   Serial.print("autoSaveMode: ");
@@ -490,6 +536,14 @@ void printConfig2() {
   Serial.println(displayInterval);
   Serial.print("dispTout: ");
   Serial.println(displayTimeout);
+  Serial.print("modename0: ");
+  Serial.println(modename0);
+  Serial.print("modename1: ");
+  Serial.println(modename1);
+  Serial.print("modename2: ");
+  Serial.println(modename2);
+  Serial.print("modename3: ");
+  Serial.println(modename3);
   Serial.print("PIRenDisp: ");
   Serial.println(PIR_enablesDisplay);
   Serial.println();
@@ -563,6 +617,7 @@ bool loadConfig() { // loadConfig 1
       else mqtt_willRetain = false;
 
       strlcpy(mqtt_willMsg, json["willMsg"] | "", 31);
+      strlcpy(mqtt_connMsg, json["connMsg"] | "", 31);
       strlcpy(domoticz_out_topic, json["domOutTop"] | "", 51);
 
       Serial.println("Loaded config values:");
@@ -620,6 +675,7 @@ bool loadConfig2() {
       domoticzIdx_PIR = atoi(json["domIdxPIR"] | "");
       strlcpy(outTemp_topic_in, json["outTempTop"] | "", 51);
       strlcpy(outHum_topic_in, json["outHumTop"] | "", 51);
+      strlcpy(mqtt_topic_pir, json["PIRTop"] | "", 51);
 
       if (atoi(json["autoSaveTemp"] | "") == 1) autoSaveSetTemp = true;
       else autoSaveSetTemp = false;
@@ -639,6 +695,10 @@ bool loadConfig2() {
       measureInterval = atoi(json["measInt"] | "");
       displayInterval = atoi(json["dispInt"] | "");
       displayTimeout = atoi(json["dispTout"] | "");
+      strlcpy(modename0, json["modename0"] | "", 14);
+      strlcpy(modename1, json["modename1"] | "", 14);
+      strlcpy(modename2, json["modename2"] | "", 14);
+      strlcpy(modename3, json["modename3"] | "", 14);
 
       if (atoi(json["PIRenDisp"] | "") == 1) PIR_enablesDisplay = true;
       else PIR_enablesDisplay = false;
@@ -714,6 +774,7 @@ bool saveConfig() { // safeConfig
   else json["willRet"] = 0;
 
   json["willMsg"] = mqtt_willMsg;
+  json["connMsg"] = mqtt_connMsg;
   json["domOutTop"] = domoticz_out_topic;
 
   yield();
@@ -741,6 +802,7 @@ bool saveConfig2() { // safeConfig2
   json["domIdxPIR"] = domoticzIdx_PIR;
   json["outTempTop"] = outTemp_topic_in;
   json["outHumTop"] = outHum_topic_in;
+  json["PIRTop"] = mqtt_topic_pir;
 
   if (autoSaveSetTemp) json["autoSaveTemp"] = 1;
   else json["autoSaveTemp"] = 0;
@@ -761,6 +823,11 @@ bool saveConfig2() { // safeConfig2
   json["dispInt"] = displayInterval;
   json["dispTout"] = displayTimeout;
 
+  json["modename0"] = modename0;
+  json["modename1"] = modename1;
+  json["modename2"] = modename2;
+  json["modename3"] = modename3;
+
   if (PIR_enablesDisplay) json["PIRenDisp"] = 1;
   else json["PIRenDisp"] = 0;
 

+ 26 - 12
src/WiFiThermostat/httpServer.ino

@@ -81,22 +81,13 @@ static const char httpRoot[] PROGMEM =
           g('hum').innerHTML = data.hum;
           g('setTemp').innerHTML = data.setTemp.toFixed(1);
           g('ssid').innerHTML = data.ssid;
-            
+          g('mode').innerHTML = data.modeName + ' (' + data.mode + ')';
+          
           if(data.mode == '0') {
         g('btn_onoff').value = textA;
-            g('mode').innerHTML = 'Heating OFF';
-          }
-          else if(data.mode == '1') {
-        g('btn_onoff').value = textE;
-            g('mode').innerHTML = 'Normal';
           }
-          else if(data.mode == '2') {
+          else {
         g('btn_onoff').value = textE;
-            g('mode').innerHTML = 'Reduction 1';
-          }
-          else if(data.mode == '3') {
-        g('btn_onoff').value = textE;
-            g('mode').innerHTML = 'Reduction 2';
           }
           
           if(data.heating == '1') g('heating').innerHTML = 'active';
@@ -145,6 +136,7 @@ LastWill Topic *: <input type='text' name='willTop' id='willTop'/><br>
 LastWill Qos *: <select name='willQos' id='willQos'><option>0</option><option>1</option><option>2</option></select><br>
 LastWill Retain *: <input type='checkbox' name='willRet' id='willRet'/><br>
 LastWill Message *: <input type='text' name='willMsg' id='willMsg'/><br><br>
+Connect Message *: <input type='text' name='connMsg' id='connMsg'/><br><br>
 Domoticz Out Topic *: <input type='text' name='domOutTop' id='domOutTop'/><br>
 <br>
 <input type='submit' value='Save'/>
@@ -213,6 +205,7 @@ Domoticz Out Topic *: <input type='text' name='domOutTop' id='domOutTop'/><br>
         setCheckbox(g('outRet'), data.outRet);
         setCheckbox(g('willRet'), data.willRet);
         g('willMsg').value = data.willMsg;
+        g('connMsg').value = data.connMsg;
         g('domOutTop').value = data.domOutTop;
         xhttp = null;
         reqFin = true;
@@ -252,6 +245,9 @@ Idx PIR: <input type='number' name='domIdxPIR' id='domIdxPIR'/><br>
 Temp In-Topic: <input type='text' name='outTempTop' id='outTempTop'/><br>
 Hum In-Topic: <input type='text' name='outHumTop' id='outHumTop'/><br>
 
+<h4>Additional MQTT publish topics</h4>
+PIR sensor: <input type='text' name='PIRTop' id='PIRTop'/><br>
+
 <h4>Auto-Save</h4>
 setTemp: <input type='checkbox' name='autoSaveTemp' id='autoSaveTemp'/><br>
 Mode: <input type='checkbox' name='autoSaveMode' id='autoSaveMode'/><br>
@@ -272,6 +268,12 @@ Measure Interval: <input type='number' name='measInt' id='measInt'/><br>
 Display Interval: <input type='number' name='dispInt' id='dispInt'/><br>
 Display Timeout: <input type='number' name='dispTout' id='dispTout'/><br>
 
+<h4>Names</h4>
+Mode Off: <input type='text' name='modename0' id='modename0'/><br>
+Mode Normal: <input type='text' name='modename1' id='modename1'/><br>
+Mode Reduction 1: <input type='text' name='modename2' id='modename2'/><br>
+Mode Reduction 2: <input type='text' name='modename3' id='modename3'/><br>
+
 <h4>Misc</h4>
 PIR enables Display: <input type='checkbox' name='PIRenDisp' id='PIRenDisp'/>
 
@@ -335,6 +337,7 @@ PIR enables Display: <input type='checkbox' name='PIRenDisp' id='PIRenDisp'/>
           g('domIdxPIR').value = data.domIdxPIR;
           g('outTempTop').value = data.outTempTop;
           g('outHumTop').value = data.outHumTop;
+          g('PIRTop').value = data.PIRTop;
           g('minOffTime').value = data.minOffTime;
           g('tempMin').value = data.tempMin;
           g('tempMax').value = data.tempMax;
@@ -347,6 +350,10 @@ PIR enables Display: <input type='checkbox' name='PIRenDisp' id='PIRenDisp'/>
           g('measInt').value = data.measInt;
           g('dispInt').value = data.dispInt;
           g('dispTout').value = data.dispTout;
+          g('modename0').value = data.modename0;
+          g('modename1').value = data.modename1;
+          g('modename2').value = data.modename2;
+          g('modename3').value = data.modename3;
           
           xhttp = null;
           reqFin = false;
@@ -445,6 +452,7 @@ void httpServerInit() {
     json["temp"] = currTemp;
     json["hum"] = int(currHum);
     json["mode"] = heatingMode;
+    json["modeName"] = currentModeName;
     json["heating"] = turnHeatingOn;
 
     char jsonchar[200];
@@ -551,6 +559,7 @@ void httpServerInit() {
       json["willQos"] = mqtt_willQos;
       json["willRet"] = mqtt_willRetain;
       json["willMsg"] = mqtt_willMsg;
+      json["connMsg"] = mqtt_connMsg;
       json["domOutTop"] = domoticz_out_topic;
 
       yield();
@@ -600,6 +609,7 @@ void httpServerInit() {
       json["domIdxPIR"] = domoticzIdx_PIR;
       json["outTempTop"] = outTemp_topic_in;
       json["outHumTop"] = outHum_topic_in;
+      json["PIRTop"] = mqtt_topic_pir;
       json["autoSaveTemp"] = autoSaveSetTemp;
       json["autoSaveMode"] = autoSaveHeatingMode;
       json["minOffTime"] = heatingMinOffTime;
@@ -614,6 +624,10 @@ void httpServerInit() {
       json["measInt"] = measureInterval;
       json["dispInt"] = displayInterval;
       json["dispTout"] = displayTimeout;
+      json["modename0"] = modename0;
+      json["modename1"] = modename1;
+      json["modename2"] = modename2;
+      json["modename3"] = modename3;
       json["PIRenDisp"] = PIR_enablesDisplay;
 
       yield();

+ 191 - 33
src/WiFiThermostat/mqtt.ino

@@ -9,10 +9,10 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) {
   //  }
   //  Serial.println();
 
-  if (strcmp(topic, mqtt_topic_in) == 0) { //if topic = mqtt_topic_in
-    Serial.print("received MQTT ");
-    Serial.println(mqtt_topic_in);
+  char tmp_topic_pub[51];
+  char tmp_payload_pub[51];
 
+  if (strcmp(topic, mqtt_topic_in_cmd) == 0) { //if topic = mqtt_topic_in_cmd
     int len;
     if (length < 101) len = length; // if input is bigger than dest buffer, cut
     else len = 100;
@@ -20,11 +20,130 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) {
     for (int i = 0; i < len; i++) {
       cmdPayload[i] = (char)payload[i];
     }
-    cmdPayload[len + 1] = '\0';
+    //cmdPayload[len + 1] = '\0';
+    cmdPayload[len] = '\0';
     //    Serial.print("cmdPayload:");
     //    Serial.println(cmdPayload);
+
+    if (serialdebug) {
+      Serial.print("received MQTT ");
+      Serial.print(mqtt_topic_in_cmd);
+      Serial.print(" - \"");
+      Serial.print(cmdPayload);
+      Serial.println("\"");
+    }
+    if (mqttdebug) {
+      sprintf(tmp_topic_pub, "%s/%s", mqtt_topic_out, "mqtt_received");
+      sprintf(tmp_payload_pub, "%s: %s", mqtt_topic_in_cmd, cmdPayload);
+      mqttclient.publish(tmp_topic_pub, tmp_payload_pub);
+    }
+
     cmdInQueue = true; // payload is processed in "commands"
-  }//if topic = mqtt_topic_in
+  }//if topic = mqtt_topic_in_cmd
+
+  if (strcmp(topic, mqtt_topic_in_setTemp) == 0) { //if topic = mqtt_topic_in_setTemp
+    int len;
+    if (length < 101) len = length; // if input is bigger than dest buffer, cut
+    else len = 100;
+
+    for (int i = 0; i < len; i++) {
+      cmdPayload[i] = (char)payload[i];
+    }
+    //cmdPayload[len + 1] = '\0';
+    cmdPayload[len] = '\0';
+    //    Serial.print("cmdPayload:");
+    //    Serial.println(cmdPayload);
+
+    char tmpPayload[11];
+    strlcpy(tmpPayload, cmdPayload, 10);
+
+    float valueFloat;
+    valueFloat = round(atof(tmpPayload) * 2.0) / 2.0;
+    setTempTo(valueFloat);
+
+    if (serialdebug) {
+      Serial.print("received MQTT ");
+      Serial.print(mqtt_topic_in_setTemp);
+      Serial.print(" - \"");
+      Serial.print(cmdPayload);
+      Serial.println("\"");
+    }
+    if (mqttdebug) {
+      sprintf(tmp_topic_pub, "%s/%s", mqtt_topic_out, "mqtt_received");
+      sprintf(tmp_payload_pub, "%s: %s", mqtt_topic_in_setTemp, cmdPayload);
+      mqttclient.publish(tmp_topic_pub, tmp_payload_pub);
+    }
+
+    cmdInQueue = false; // payload is processed in "commands"
+  }//if topic = mqtt_topic_in_setTemp
+
+  if (strcmp(topic, mqtt_topic_in_setMode) == 0) { //if topic = mqtt_topic_in_setMode
+    int len;
+    if (length < 101) len = length; // if input is bigger than dest buffer, cut
+    else len = 100;
+
+    for (int i = 0; i < len; i++) {
+      cmdPayload[i] = (char)payload[i];
+    }
+    //cmdPayload[len + 1] = '\0';
+    cmdPayload[len] = '\0';
+    //    Serial.print("cmdPayload:");
+    //    Serial.println(cmdPayload);
+
+    char tmpPayload[21];
+    strlcpy(tmpPayload, cmdPayload, 20);
+    strlwr(tmpPayload);
+
+    byte tmpHeatMode;
+
+    char tmpModename0[15];
+    char tmpModename1[15];
+    char tmpModename2[15];
+    char tmpModename3[15];
+    strlcpy(tmpModename0, modename0, 14);
+    strlcpy(tmpModename1, modename1, 14);
+    strlcpy(tmpModename2, modename2, 14);
+    strlcpy(tmpModename3, modename3, 14);
+    strlwr(tmpModename0);
+    strlwr(tmpModename1);
+    strlwr(tmpModename2);
+    strlwr(tmpModename3);
+
+    if (strcmp(tmpPayload, tmpModename0) == 0) tmpHeatMode = 0;
+    else if (strcmp(tmpPayload, tmpModename1) == 0) tmpHeatMode = 1;
+    else if (strcmp(tmpPayload, tmpModename2) == 0) tmpHeatMode = 2;
+    else if (strcmp(tmpPayload, tmpModename3) == 0) tmpHeatMode = 3;
+    else if (strcmp(tmpPayload, "off") == 0) tmpHeatMode = 0;
+    else if (strcmp(tmpPayload, "aus") == 0) tmpHeatMode = 0;
+    else if (strcmp(tmpPayload, "on") == 0) tmpHeatMode = 1;
+    else if (strcmp(tmpPayload, "ein") == 0) tmpHeatMode = 1;
+    else if (atoi(tmpPayload) == 0) tmpHeatMode = 0;
+    else if (atoi(tmpPayload) == 1) tmpHeatMode = 1;
+    else if (atoi(tmpPayload) == 2) tmpHeatMode = 2;
+    else if (atoi(tmpPayload) == 3) tmpHeatMode = 3;
+
+    if (serialdebug) {
+      Serial.print("set heatmode to: ");
+      Serial.println(tmpHeatMode);
+    }
+
+    setHeatingmodeTo(tmpHeatMode);
+
+    if (serialdebug) {
+      Serial.print("received MQTT ");
+      Serial.print(mqtt_topic_in_setMode);
+      Serial.print(" - \"");
+      Serial.print(cmdPayload);
+      Serial.println("\"");
+    }
+    if (mqttdebug) {
+      sprintf(tmp_topic_pub, "%s/%s", mqtt_topic_out, "mqtt_received");
+      sprintf(tmp_payload_pub, "%s: %s", mqtt_topic_in_setMode, cmdPayload);
+      mqttclient.publish(tmp_topic_pub, tmp_payload_pub);
+    }
+
+    cmdInQueue = false; // payload is processed in "commands"
+  }//if topic = mqtt_topic_in_setMode
 
   if (strcmp(topic, outTemp_topic_in) == 0) { //if topic = outTemp_topic_in
     int len;
@@ -33,7 +152,8 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) {
     for (int i = 0; i < len; i++) {
       outTemp_newValue[i] = (char)payload[i];
     }
-    outTemp_newValue[len + 1] = '\0';
+    //outTemp_newValue[len + 1] = '\0';
+    outTemp_newValue[len] = '\0';
     outTemp_parseNewValue = true;
   }//if topic = outTemp_topic_in
 
@@ -44,7 +164,8 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) {
     for (int i = 0; i < len; i++) {
       outHum_newValue[i] = (char)payload[i];
     }
-    outHum_newValue[len + 1] = '\0';
+    //outHum_newValue[len + 1] = '\0';
+    outHum_newValue[len] = '\0';
     outHum_parseNewValue = true;
   }//if topic = outHum_topic_in
 
@@ -58,7 +179,8 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) {
       for (int i = 0; i < len; i++) {
         domoticzOutPayload[i] = (char)payload[i];
       }
-      domoticzOutPayload[len + 1] = '\0';
+      //domoticzOutPayload[len + 1] = '\0';
+      domoticzOutPayload[len] = '\0';
       domoticzOutParseData = true; // parse domoticz data in the next loop()
     }
   }//if topic = domoticz_out_topic
@@ -90,80 +212,106 @@ void mqttPrepareConnection() {
   }
 }
 
+void mqttPrepareSubscribeTopics() {
+  char tmp_topic_out[50];
+  sprintf(mqtt_topic_in_cmd, "%s/%s", mqtt_topic_in, "cmd");
+  sprintf(mqtt_topic_in_setTemp, "%s/%s", mqtt_topic_in, "setTemp");
+  sprintf(mqtt_topic_in_setMode, "%s/%s", mqtt_topic_in, "setMode");
+}
+
 bool mqttReconnect() {
   mqttclient.disconnect();
   delay(10);
-  // Create a random MQTT client ID
+  // Create MQTT client ID from device name
   String mqttClientId = "ESP8266Client-";
-  mqttClientId += String(random(0xffff), HEX);
+  //mqttClientId += String(random(0xffff), HEX);   //or random
+  mqttClientId += String(deviceName);
 
-  Serial.print("connecting to MQTT broker with ");
+  if (serialdebug) Serial.print("connecting to MQTT broker with ");
 
   bool connRes;
 
   switch (mqttMode) {
     case 1:
-      Serial.print("no user, password and no Last Will");
+      if (serialdebug) Serial.println("no user, password and no Last Will");
       connRes = mqttclient.connect(mqttClientId.c_str());
       break;
     case 2:
-      Serial.print("user and password, no Last Will");
+      if (serialdebug) Serial.println("user and password, no Last Will");
       connRes = mqttclient.connect(mqttClientId.c_str(), mqtt_user, mqtt_pass);
       break;
     case 3:
-      Serial.print("user, password and Last Will");
+      if (serialdebug) Serial.println("user, password and Last Will");
       connRes = mqttclient.connect(mqttClientId.c_str(), mqtt_user, mqtt_pass, mqtt_willTopic, mqtt_willQos, mqtt_willRetain, mqtt_willMsg);
       break;
     case 4:
-      Serial.println("Last Will, no user and password");
+      if (serialdebug) Serial.println("Last Will, no user and password");
       connRes = mqttclient.connect(mqttClientId.c_str(), mqtt_willTopic, mqtt_willQos, mqtt_willRetain, mqtt_willMsg);
       break;
   }
 
-  Serial.print("... attempt: ");
-  Serial.print(mqttReconnectAttempts);
-  Serial.println();
+  if (serialdebug) {
+    Serial.print("... attempt: ");
+    Serial.print(mqttReconnectAttempts);
+    Serial.println();
+  }
 
   if (connRes) {
-    Serial.print("MQTT connected. Reconnects: ");
-    Serial.println(mqttReconnects);
+    if (serialdebug) {
+      Serial.print("MQTT connected. Reconnects: ");
+      Serial.println(mqttReconnects);
+    }
     mqttReconnects++;
 
     // Once connected, publish an announcement...
     //    char outMsg[30];
     //    sprintf(outMsg, "connected, %d reconnects", mqttReconnects);
     //    mqttclient.publish(mqtt_topic_out, outMsg, mqtt_outRetain);
+
+    mqttclient.publish(mqtt_willTopic, mqtt_connMsg, mqtt_willRetain);
     publishStatus();
     //mqttclient.publish(mqtt_topic_out, "connected");
     // ... and resubscribe
-    Serial.println("Subscribed to:");
-    if (strlen(mqtt_topic_in) > 0) {
-      if (mqttclient.subscribe(mqtt_topic_in)) {
-        Serial.println(mqtt_topic_in);
+    if (serialdebug) Serial.println("Subscribed to:");
+    if (strlen(mqtt_topic_in_cmd) > 0) {
+      if (mqttclient.subscribe(mqtt_topic_in_cmd)) {
+        if (serialdebug) Serial.println(mqtt_topic_in_cmd);
         mqttInTopicSubscribed = true;
       }
     }
+    if (strlen(mqtt_topic_in_setTemp) > 0) {
+      if (mqttclient.subscribe(mqtt_topic_in_setTemp)) {
+        if (serialdebug) Serial.println(mqtt_topic_in_setTemp);
+      }
+    }
+    if (strlen(mqtt_topic_in_setMode) > 0) {
+      if (mqttclient.subscribe(mqtt_topic_in_setMode)) {
+        if (serialdebug) Serial.println(mqtt_topic_in_setMode);
+      }
+    }
     if (strlen(outTemp_topic_in) > 0) {
       if (mqttclient.subscribe(outTemp_topic_in)) {
-        Serial.println(outTemp_topic_in);
+        if (serialdebug) Serial.println(outTemp_topic_in);
       }
     }
     if (strlen(outHum_topic_in) > 0) {
       if (mqttclient.subscribe(outHum_topic_in)) {
-        Serial.println(outHum_topic_in);
+        if (serialdebug) Serial.println(outHum_topic_in);
       }
     }
     if (useDomoticz && strlen(domoticz_out_topic) > 0) {
       if (mqttclient.subscribe(domoticz_out_topic)) {
-        Serial.println(domoticz_out_topic);
+        if (serialdebug) Serial.println(domoticz_out_topic);
       }
     }
+    return mqttclient.connected();
   }
   else {
-    Serial.print("MQTT connect FAILED, rc=");
-    Serial.println(mqttclient.state());
+    if (serialdebug) {
+      Serial.print("MQTT connect FAILED, rc=");
+      Serial.println(mqttclient.state());
+    }
   }
-  return mqttclient.connected();
 } //mqttReconnect
 
 
@@ -203,8 +351,8 @@ void mqttHandleConnection() {
       if (mqttReconnectAttempts < 3) mqttReconnectAttemptDelay = 15000; // if this is the 1-3rd attempt, try again in 15s
       else if (mqttReconnectAttempts < 10) mqttReconnectAttemptDelay = 60000; // if more than 3 attempts failed, try again every min
       else if (mqttReconnectAttempts < 20) mqttReconnectAttemptDelay = 300000; // if more than 10 attempts failed, try again every 5 min
-      else if (mqttReconnectAttempts >= 20) {
-        // if more than 20 attempts failed, restart the ESP
+      else if (mqttReconnectAttempts >= 6) {
+        // if more than 6 attempts (= > 30 min) failed, restart the ESP
         delay(100);
         ESP.restart();
       }
@@ -230,6 +378,8 @@ void publishStatus() {
   long upTime = millis() / 1000;
   sprintf(outMsg, "connected, reconnects: %d, uptime: %d, free heap: %d", mqttReconnects - 1, upTime, ESP.getFreeHeap());
   mqttclient.publish(mqtt_topic_out, outMsg, mqtt_outRetain);
+
+  mqttclient.publish(mqtt_willTopic, mqtt_connMsg, mqtt_willRetain);
   yield();
 }
 
@@ -277,6 +427,10 @@ void publishCurrentThermostatValues() {
   char heatingMode_chararr[3];
   sprintf(heatingMode_chararr, "%d", heatingMode);
   mqttclient.publish(tmp_topic_out, heatingMode_chararr);
+
+  sprintf(tmp_topic_out, "%s/%s", mqtt_topic_out, "heatingModeName");
+  mqttclient.publish(tmp_topic_out, currentModeName);
+
   yield();
 
   char turnHeatingOn_char[5];
@@ -353,10 +507,14 @@ void publishCurrentPIRValue() {
     strcpy(PIRStatus, "off");
   }
   mqttclient.publish(tmp_topic_out, PIRStatus);
+
+  if (strlen(mqtt_topic_pir) > 0) {
+    mqttclient.publish(mqtt_topic_pir, PIRStatus);
+  }
 }
 
 void mqttPublishHeartbeat() {
-  mqttclient.publish(mqtt_topic_in, "HEARTBEAT"); // publishes to IN-topic. MQTT will force-reconnect if it does not get a HEARTBEAT for 2 min
+  mqttclient.publish(mqtt_topic_in_cmd, "HEARTBEAT"); // publishes to IN-topic. MQTT will force-reconnect if it does not get a HEARTBEAT for 2 min
 }
 
 

+ 15 - 1
src/WiFiThermostat/thermostat.ino

@@ -155,11 +155,22 @@ void toggleHeatingMode() {
       lastValueChange = millis();
       heatingModeAlreadySaved = false;
     }
-    Serial.println(heatingMode);
+    Serial.print(heatingMode);
+    Serial.print(" - \"");
+    Serial.print(currentModeName);
+    Serial.println("\"");
     updateDisplay();
+    updateCurrentHeatingModeName();
   }
 }
 
+void updateCurrentHeatingModeName() {
+  if(heatingMode == 0) strlcpy(currentModeName, modename0, 14);
+  else if(heatingMode == 1) strlcpy(currentModeName, modename1, 14);
+  else if(heatingMode == 2) strlcpy(currentModeName, modename2, 14);
+  else if(heatingMode == 3) strlcpy(currentModeName, modename3, 14);
+}
+
 void toggleThermostatOnOff() {
   if (heatingMode > 0) {
     heatingMode = 0;
@@ -172,6 +183,7 @@ void toggleThermostatOnOff() {
     heatingModeAlreadySaved = false;
   }
   updateDisplay();
+  updateCurrentHeatingModeName();
 }
 
 void setTempStepUp() {
@@ -283,6 +295,7 @@ void setHeatingmodeTo(byte setTo) {
   if (changes) {
     lastValueChange = millis();
     heatingModeAlreadySaved = false;
+    updateCurrentHeatingModeName();
     updateDisplay();
     publishCurrentThermostatValues();
   }
@@ -291,6 +304,7 @@ void setHeatingmodeTo(byte setTo) {
 void checkValuesChanged() { // called every second by everySecond() / misc.ino
   if ( !setTempAlreadySaved || !heatingModeAlreadySaved ) {
     if ( (millis() - lastValueChange) > saveValuesTimeout ) { // value was changed 5s ago. now save if auto-save enabled
+      publishCurrentThermostatValues();
       if (!setTempAlreadySaved) {
         lastUpdate_setTemp = millis();
         sendToDomoticz_thermostat();