diff --git a/README.md b/README.md index a0837846242d..1b37950e8f9d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ## Sonoff-Tasmota Provide ESP8266 based Sonoff by [iTead Studio](https://www.itead.cc/) and ElectroDragon IoT Relay with Serial, Web and MQTT control allowing 'Over the Air' or OTA firmware updates using Arduino IDE. -Current version is **4.0.1** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/master/sonoff/_releasenotes.ino) for change information. +Current version is **4.0.2** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/master/sonoff/_releasenotes.ino) for change information. - This version provides all (Sonoff) modules in one file and starts up with Sonoff Basic. - Once uploaded select module using the configuration webpage or the commands ```Modules``` and ```Module```. diff --git a/api/arduino/sonoff-minimal.ino.bin b/api/arduino/sonoff-minimal.ino.bin index 8bc4db84ed3a..323e05bc1816 100644 Binary files a/api/arduino/sonoff-minimal.ino.bin and b/api/arduino/sonoff-minimal.ino.bin differ diff --git a/api/arduino/sonoff.ino.bin b/api/arduino/sonoff.ino.bin index 3460234da68c..d88398cb1d8c 100644 Binary files a/api/arduino/sonoff.ino.bin and b/api/arduino/sonoff.ino.bin differ diff --git a/sonoff/_releasenotes.ino b/sonoff/_releasenotes.ino index 5080923b43ff..ca8e9e9b766f 100644 --- a/sonoff/_releasenotes.ino +++ b/sonoff/_releasenotes.ino @@ -1,4 +1,14 @@ -/* 4.0.1 20170305 +/* 4.0.2 20170308 + * Restore correct seriallog level after Serial logging was disabled + * Add simple dimmer slider to Sonoff Led web page + * Reduced root webpage size by 31% + * Expand Status 2 with Build date/time and core version + * Fix webserver redirection when not in WifiManager mode (#156) + * Add command ButtonRestrict On/Off to restrict access to button hold and button multi press options above 2 (#161) + * Fix DS18S20 negative temperature readings (#165) + * Fix crlf compilation error due to bad syntax (#144, #167) + * + * 4.0.1 20170305 * Fix char default sizes and set MESSZ to 360 (#143) * Fix SerialLog setting status * Disable syslog when emulation is active diff --git a/sonoff/settings.h b/sonoff/settings.h index 19fb12814eed..f8b25fed944b 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -77,10 +77,10 @@ struct SYSCFG { unsigned long saveFlag; unsigned long version; unsigned long bootcount; - byte migflg; // Not used since 3.9.1 + byte migflg; // Not used since 3.9.1 int16_t savedata; byte savestate; - byte model; // Not used since 3.9.1 + byte model; // Not used since 3.9.1 int8_t timezone; char otaUrl[101]; char ex_friendlyname[33]; // Not used since 3.2.5 - see below @@ -111,12 +111,12 @@ struct SYSCFG { byte mqtt_button_retain; byte mqtt_power_retain; byte value_units; - byte message_format; // Not used since 3.2.6a + byte button_restrict; // Was message_format until 3.2.6a uint16_t tele_period; uint8_t power; uint8_t ledstate; - uint8_t ex_switchmode; // Not used since 3.9.21 + uint8_t ex_switchmode; // Not used since 3.9.21 char domoticz_in_topic[33]; char domoticz_out_topic[33]; @@ -188,7 +188,6 @@ struct SYSCFG { char web_password[33]; uint8_t switchmode[4]; - } sysCfg; struct RTCMEM { diff --git a/sonoff/settings.ino b/sonoff/settings.ino index 8556544f993d..82ee1e0c49e8 100644 --- a/sonoff/settings.ino +++ b/sonoff/settings.ino @@ -414,7 +414,6 @@ void CFG_DefaultSet1() void CFG_DefaultSet2() { - sysCfg.migflg = 0xA5; sysCfg.savedata = SAVE_DATA; sysCfg.savestate = SAVE_STATE; sysCfg.module = MODULE; @@ -449,8 +448,9 @@ void CFG_DefaultSet2() strlcpy(sysCfg.mqtt_subtopic, MQTT_SUBTOPIC, sizeof(sysCfg.mqtt_subtopic)); sysCfg.mqtt_button_retain = MQTT_BUTTON_RETAIN; sysCfg.mqtt_power_retain = MQTT_POWER_RETAIN; - sysCfg.value_units = VALUE_UNITS; - sysCfg.message_format = 0; + sysCfg.value_units = 0; + sysCfg.button_restrict = 0; +// sysCfg.message_format = 0; sysCfg.tele_period = TELE_PERIOD; sysCfg.power = APP_POWER; @@ -614,7 +614,7 @@ void CFG_Migrate_Part2() sysCfg.hlw_imax = sysCfg2.hlw_imax; } if (sysCfg2.version >= 0x02000700) { // 2.0.7 - sysCfg.message_format = 0; +// sysCfg.message_format = 0; strlcpy(sysCfg.domoticz_in_topic, sysCfg2.domoticz_in_topic, sizeof(sysCfg.domoticz_in_topic)); strlcpy(sysCfg.domoticz_out_topic, sysCfg2.domoticz_out_topic, sizeof(sysCfg.domoticz_out_topic)); sysCfg.domoticz_update_timer = sysCfg2.domoticz_update_timer; @@ -670,7 +670,7 @@ void CFG_Delta() sysCfg.blinkcount = APP_BLINKCOUNT; } if (sysCfg.version < 0x03011000) { // 3.1.16 - Add parameter - getClient(sysCfg.ex_friendlyname, sysCfg.mqtt_client, sizeof(sysCfg.ex_friendlyname)); + getClient(sysCfg.friendlyname[0], sysCfg.mqtt_client, sizeof(sysCfg.friendlyname[0])); } if (sysCfg.version < 0x03020400) { // 3.2.4 - Add parameter sysCfg.ws_pixels = WS2812_LEDS; @@ -686,7 +686,7 @@ void CFG_Delta() sysCfg.ws_wakeup = 0; } if (sysCfg.version < 0x03020500) { // 3.2.5 - Add parameter - strlcpy(sysCfg.friendlyname[0], sysCfg.ex_friendlyname, sizeof(sysCfg.friendlyname[0])); + getClient(sysCfg.friendlyname[0], sysCfg.mqtt_client, sizeof(sysCfg.friendlyname[0])); strlcpy(sysCfg.friendlyname[1], FRIENDLY_NAME"2", sizeof(sysCfg.friendlyname[1])); strlcpy(sysCfg.friendlyname[2], FRIENDLY_NAME"3", sizeof(sysCfg.friendlyname[2])); strlcpy(sysCfg.friendlyname[3], FRIENDLY_NAME"4", sizeof(sysCfg.friendlyname[3])); @@ -725,8 +725,12 @@ void CFG_Delta() if (sysCfg.version < 0x03091500) { for (byte i = 0; i < 4; i++) sysCfg.switchmode[i] = sysCfg.ex_switchmode; } + if (sysCfg.version < 0x04000200) { + sysCfg.button_restrict = 0; + } sysCfg.version = VERSION; } } + diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index a71f7d4b408f..01c1edfb62d3 100644 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -12,9 +12,9 @@ //#define ALLOW_MIGRATE_TO_V3 #ifdef ALLOW_MIGRATE_TO_V3 - #define VERSION 0x03091800 // 3.9.24 + #define VERSION 0x03091900 // 3.9.25 #else - #define VERSION 0x04000100 // 4.0.1 + #define VERSION 0x04000200 // 4.0.2 #endif // ALLOW_MIGRATE_TO_V3 enum log_t {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE, LOG_LEVEL_ALL}; @@ -108,7 +108,6 @@ enum emul_t {EMUL_NONE, EMUL_WEMO, EMUL_HUE, EMUL_MAX}; #define HLW_UREF_PULSE 1950 // was 1666us = 600Hz = 220V #define HLW_IREF_PULSE 3500 // was 1666us = 600Hz = 4.545A -#define VALUE_UNITS 0 // Default do not show value units (Hr, Sec, V, A, W etc.) #define MQTT_SUBTOPIC "POWER" // Default MQTT subtopic (POWER or LIGHT) #define MQTT_RETRY_SECS 10 // Seconds to retry MQTT connection #define APP_POWER 0 // Default saved power state Off @@ -591,6 +590,147 @@ void mqtt_reconnect() /********************************************************************************************/ +boolean mqtt_command(boolean grpflg, char *type, uint16_t index, char *dataBuf, uint16_t data_len, int16_t payload, char *svalue, uint16_t ssvalue) +{ + boolean serviced = true; + char stemp1[TOPSZ], stemp2[10]; + uint16_t i; + + if (!strcmp(type,"MQTTHOST")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_host))) { + strlcpy(sysCfg.mqtt_host, (payload == 1) ? MQTT_HOST : dataBuf, sizeof(sysCfg.mqtt_host)); + restartflag = 2; + } + snprintf_P(svalue, ssvalue, PSTR("{\"MqttHost\",\"%s\"}"), sysCfg.mqtt_host); + } + else if (!strcmp(type,"MQTTPORT")) { + if ((data_len > 0) && (payload > 0) && (payload < 32766)) { + sysCfg.mqtt_port = (payload == 1) ? MQTT_PORT : payload; + restartflag = 2; + } + snprintf_P(svalue, ssvalue, PSTR("{\"MqttPort\":%d}"), sysCfg.mqtt_port); + } +#ifdef USE_MQTT_TLS + else if (!strcmp(type,"MQTTFINGERPRINT")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_fingerprint))) { + strlcpy(sysCfg.mqtt_fingerprint, (payload == 1) ? MQTT_FINGERPRINT : dataBuf, sizeof(sysCfg.mqtt_fingerprint)); + restartflag = 2; + } + snprintf_P(svalue, ssvalue, PSTR("{\"MqttFingerprint\":\"%s\"}"), sysCfg.mqtt_fingerprint); + } +#endif + else if (!grpflg && !strcmp(type,"MQTTCLIENT")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_client))) { + strlcpy(sysCfg.mqtt_client, (payload == 1) ? MQTT_CLIENT_ID : dataBuf, sizeof(sysCfg.mqtt_client)); + restartflag = 2; + } + snprintf_P(svalue, ssvalue, PSTR("{\"MqttClient\":\"%s\"}"), sysCfg.mqtt_client); + } + else if (!strcmp(type,"MQTTUSER")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_user))) { + strlcpy(sysCfg.mqtt_user, (payload == 1) ? MQTT_USER : dataBuf, sizeof(sysCfg.mqtt_user)); + restartflag = 2; + } + snprintf_P(svalue, ssvalue, PSTR("[\"MqttUser\":\"%s\"}"), sysCfg.mqtt_user); + } + else if (!strcmp(type,"MQTTPASSWORD")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_pwd))) { + strlcpy(sysCfg.mqtt_pwd, (payload == 1) ? MQTT_PASS : dataBuf, sizeof(sysCfg.mqtt_pwd)); + restartflag = 2; + } + snprintf_P(svalue, ssvalue, PSTR("{\"MqttPassword\":\"%s\"}"), sysCfg.mqtt_pwd); + } + else if (!strcmp(type,"GROUPTOPIC")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_grptopic))) { + for(i = 0; i <= data_len; i++) + if ((dataBuf[i] == '/') || (dataBuf[i] == '+') || (dataBuf[i] == '#')) dataBuf[i] = '_'; + if (!strcmp(dataBuf, MQTTClient)) payload = 1; + strlcpy(sysCfg.mqtt_grptopic, (payload == 1) ? MQTT_GRPTOPIC : dataBuf, sizeof(sysCfg.mqtt_grptopic)); + restartflag = 2; + } + snprintf_P(svalue, ssvalue, PSTR("{\"GroupTopic\":\"%s\"}"), sysCfg.mqtt_grptopic); + } + else if (!grpflg && !strcmp(type,"TOPIC")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_topic))) { + for(i = 0; i <= data_len; i++) + if ((dataBuf[i] == '/') || (dataBuf[i] == '+') || (dataBuf[i] == '#') || (dataBuf[i] == ' ')) dataBuf[i] = '_'; + if (!strcmp(dataBuf, MQTTClient)) payload = 1; + strlcpy(sysCfg.mqtt_topic, (payload == 1) ? MQTT_TOPIC : dataBuf, sizeof(sysCfg.mqtt_topic)); + restartflag = 2; + } + snprintf_P(svalue, ssvalue, PSTR("{\"Topic\":\"%s\"}"), sysCfg.mqtt_topic); + } + else if (!grpflg && !strcmp(type,"BUTTONTOPIC")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.button_topic))) { + for(i = 0; i <= data_len; i++) + if ((dataBuf[i] == '/') || (dataBuf[i] == '+') || (dataBuf[i] == '#') || (dataBuf[i] == ' ')) dataBuf[i] = '_'; + if (!strcmp(dataBuf, MQTTClient)) payload = 1; + strlcpy(sysCfg.button_topic, (payload == 1) ? sysCfg.mqtt_topic : dataBuf, sizeof(sysCfg.button_topic)); + } + snprintf_P(svalue, ssvalue, PSTR("{\"ButtonTopic\":\"%s\"}"), sysCfg.button_topic); + } + else if (!grpflg && !strcmp(type,"SWITCHTOPIC")) { + if ((data_len > 0) && (data_len < sizeof(sysCfg.switch_topic))) { + for(i = 0; i <= data_len; i++) + if ((dataBuf[i] == '/') || (dataBuf[i] == '+') || (dataBuf[i] == '#') || (dataBuf[i] == ' ')) dataBuf[i] = '_'; + if (!strcmp(dataBuf, MQTTClient)) payload = 1; + strlcpy(sysCfg.switch_topic, (payload == 1) ? sysCfg.mqtt_topic : dataBuf, sizeof(sysCfg.switch_topic)); + } + snprintf_P(svalue, ssvalue, PSTR("{\"SwitchTopic\":\"%s\"}"), sysCfg.switch_topic); + } + else if (!strcmp(type,"BUTTONRETAIN")) { + if ((data_len > 0) && (payload >= 0) && (payload <= 1)) { + strlcpy(sysCfg.button_topic, sysCfg.mqtt_topic, sizeof(sysCfg.button_topic)); + if (!payload) { + for(i = 1; i <= Maxdevice; i++) { + send_button_power(0, i, 3); // Clear MQTT retain in broker + } + } + sysCfg.mqtt_button_retain = payload; + } + snprintf_P(svalue, ssvalue, PSTR("{\"ButtonRetain\":\"%s\"}"), (sysCfg.mqtt_button_retain) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + } + else if (!strcmp(type,"SWITCHRETAIN")) { + if ((data_len > 0) && (payload >= 0) && (payload <= 1)) { + strlcpy(sysCfg.button_topic, sysCfg.mqtt_topic, sizeof(sysCfg.button_topic)); + if (!payload) { + for(i = 1; i <= 4; i++) { + send_button_power(1, i, 3); // Clear MQTT retain in broker + } + } + sysCfg.mqtt_switch_retain = payload; + } + snprintf_P(svalue, ssvalue, PSTR("{\"SwitchRetain\":\"%s\"}"), (sysCfg.mqtt_switch_retain) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + } + else if (!strcmp(type,"POWERRETAIN") || !strcmp(type,"LIGHTRETAIN")) { + if ((data_len > 0) && (payload >= 0) && (payload <= 1)) { + if (!payload) { + for(i = 1; i <= Maxdevice; i++) { // Clear MQTT retain in broker + snprintf_P(stemp2, sizeof(stemp2), PSTR("%d"), i); + snprintf_P(stemp1, sizeof(stemp1), PSTR("%s/%s/POWER%s"), PUB_PREFIX, sysCfg.mqtt_topic, (Maxdevice > 1) ? stemp2 : ""); + mqtt_publish(stemp1, "", sysCfg.mqtt_power_retain); + snprintf_P(stemp1, sizeof(stemp1), PSTR("%s/%s/LIGHT%s"), PUB_PREFIX, sysCfg.mqtt_topic, (Maxdevice > 1) ? stemp2 : ""); + mqtt_publish(stemp1, "", sysCfg.mqtt_power_retain); + } + } + sysCfg.mqtt_power_retain = payload; + } + snprintf_P(stemp1, sizeof(stemp1), PSTR("%s"), (!strcmp(sysCfg.mqtt_subtopic,"POWER")) ? "Power" : "Light"); + snprintf_P(svalue, ssvalue, PSTR("{\"%sRetain\":\"%s\"}"), stemp1, (sysCfg.mqtt_power_retain) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + } +#ifdef USE_DOMOTICZ + else if (domoticz_command(type, index, dataBuf, data_len, payload, svalue, ssvalue)) { + // Serviced + } +#endif // USE_DOMOTICZ + else { + serviced = false; + } + return serviced; +} + +/********************************************************************************************/ + void mqttDataCb(char* topic, byte* data, unsigned int data_len) { char *str; @@ -603,9 +743,8 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len) } } - char topicBuf[TOPSZ], dataBuf[data_len+1], dataBufUc[128], svalue[MESSZ]; + char topicBuf[TOPSZ], dataBuf[data_len+1], dataBufUc[128], svalue[MESSZ], stemp1[TOPSZ]; char *p, *mtopic = NULL, *type = NULL; - char stemp1[TOPSZ], stemp2[10]; uint16_t i = 0, grpflg = 0, index; strncpy(topicBuf, topic, sizeof(topicBuf)); @@ -721,6 +860,25 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len) } snprintf_P(svalue, sizeof(svalue), PSTR("{\"SaveState\":\"%s\"}"), (sysCfg.savestate) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); } + else if (!strcmp(type,"BUTTONRESTRICT")) { + if ((data_len > 0) && (payload >= 0) && (payload <= 1)) { + sysCfg.button_restrict = payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"ButtonRestrict\":\"%s\"}"), (sysCfg.button_restrict) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + } + else if (!strcmp(type,"UNITS")) { + if ((data_len > 0) && (payload >= 0) && (payload <= 1)) { + sysCfg.value_units = payload; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Units\":\"%s\"}"), (sysCfg.value_units) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + } + else if (!strcmp(type,"MQTT")) { + if ((data_len > 0) && (payload >= 0) && (payload <= 1)) { + sysCfg.mqtt_enabled = payload; + restartflag = 2; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Mqtt\":\"%s\"}"), (sysCfg.mqtt_enabled) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); + } else if (!strcmp(type,"MODULE")) { if ((data_len > 0) && (payload > 0) && (payload <= MAXMODULE)) { sysCfg.module = payload -1; @@ -951,19 +1109,6 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len) } #endif // USE_EMULATION #endif // USE_WEBSERVER - else if (!strcmp(type,"UNITS")) { - if ((data_len > 0) && (payload >= 0) && (payload <= 1)) { - sysCfg.value_units = payload; - } - snprintf_P(svalue, sizeof(svalue), PSTR("{\"Units\":\"%s\"}"), (sysCfg.value_units) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); - } - else if (!strcmp(type,"MQTT")) { - if ((data_len > 0) && (payload >= 0) && (payload <= 1)) { - sysCfg.mqtt_enabled = payload; - restartflag = 2; - } - snprintf_P(svalue, sizeof(svalue), PSTR("{\"Mqtt\":\"%s\"}"), (sysCfg.mqtt_enabled) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); - } else if (!strcmp(type,"TELEPERIOD")) { if ((data_len > 0) && (payload >= 0) && (payload < 3601)) { sysCfg.tele_period = (payload == 1) ? TELE_PERIOD : payload; @@ -1034,144 +1179,17 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len) CFG_Dump(); snprintf_P(svalue, sizeof(svalue), PSTR("{\"CfgDump\":\"Done\"}")); } -#ifdef USE_I2C - else if (i2c_flg && !strcmp(type,"I2CSCAN")) { - i2c_scan(svalue, sizeof(svalue)); - } -#endif // USE_I2C - -/*** MQTT Commands ***************************************************************************/ - - else if (sysCfg.mqtt_enabled && !strcmp(type,"MQTTHOST")) { - if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_host))) { - strlcpy(sysCfg.mqtt_host, (payload == 1) ? MQTT_HOST : dataBuf, sizeof(sysCfg.mqtt_host)); - restartflag = 2; - } - snprintf_P(svalue, sizeof(svalue), PSTR("{\"MqttHost\",\"%s\"}"), sysCfg.mqtt_host); - } - else if (sysCfg.mqtt_enabled && !strcmp(type,"MQTTPORT")) { - if ((data_len > 0) && (payload > 0) && (payload < 32766)) { - sysCfg.mqtt_port = (payload == 1) ? MQTT_PORT : payload; - restartflag = 2; - } - snprintf_P(svalue, sizeof(svalue), PSTR("{\"MqttPort\":%d}"), sysCfg.mqtt_port); - } -#ifdef USE_MQTT_TLS - else if (sysCfg.mqtt_enabled && !strcmp(type,"MQTTFINGERPRINT")) { - if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_fingerprint))) { - strlcpy(sysCfg.mqtt_fingerprint, (payload == 1) ? MQTT_FINGERPRINT : dataBuf, sizeof(sysCfg.mqtt_fingerprint)); - restartflag = 2; - } - snprintf_P(svalue, sizeof(svalue), PSTR("{\"MqttFingerprint\":\"%s\"}"), sysCfg.mqtt_fingerprint); - } -#endif - else if (sysCfg.mqtt_enabled && !grpflg && !strcmp(type,"MQTTCLIENT")) { - if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_client))) { - strlcpy(sysCfg.mqtt_client, (payload == 1) ? MQTT_CLIENT_ID : dataBuf, sizeof(sysCfg.mqtt_client)); - restartflag = 2; - } - snprintf_P(svalue, sizeof(svalue), PSTR("{\"MqttClient\":\"%s\"}"), sysCfg.mqtt_client); - } - else if (sysCfg.mqtt_enabled && !strcmp(type,"MQTTUSER")) { - if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_user))) { - strlcpy(sysCfg.mqtt_user, (payload == 1) ? MQTT_USER : dataBuf, sizeof(sysCfg.mqtt_user)); - restartflag = 2; - } - snprintf_P(svalue, sizeof(svalue), PSTR("[\"MqttUser\":\"%s\"}"), sysCfg.mqtt_user); - } - else if (sysCfg.mqtt_enabled && !strcmp(type,"MQTTPASSWORD")) { - if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_pwd))) { - strlcpy(sysCfg.mqtt_pwd, (payload == 1) ? MQTT_PASS : dataBuf, sizeof(sysCfg.mqtt_pwd)); - restartflag = 2; - } - snprintf_P(svalue, sizeof(svalue), PSTR("{\"MqttPassword\":\"%s\"}"), sysCfg.mqtt_pwd); - } - else if (sysCfg.mqtt_enabled && !strcmp(type,"GROUPTOPIC")) { - if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_grptopic))) { - for(i = 0; i <= data_len; i++) - if ((dataBuf[i] == '/') || (dataBuf[i] == '+') || (dataBuf[i] == '#')) dataBuf[i] = '_'; - if (!strcmp(dataBuf, MQTTClient)) payload = 1; - strlcpy(sysCfg.mqtt_grptopic, (payload == 1) ? MQTT_GRPTOPIC : dataBuf, sizeof(sysCfg.mqtt_grptopic)); - restartflag = 2; - } - snprintf_P(svalue, sizeof(svalue), PSTR("{\"GroupTopic\":\"%s\"}"), sysCfg.mqtt_grptopic); - } - else if (sysCfg.mqtt_enabled && !grpflg && !strcmp(type,"TOPIC")) { - if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_topic))) { - for(i = 0; i <= data_len; i++) - if ((dataBuf[i] == '/') || (dataBuf[i] == '+') || (dataBuf[i] == '#') || (dataBuf[i] == ' ')) dataBuf[i] = '_'; - if (!strcmp(dataBuf, MQTTClient)) payload = 1; - strlcpy(sysCfg.mqtt_topic, (payload == 1) ? MQTT_TOPIC : dataBuf, sizeof(sysCfg.mqtt_topic)); - restartflag = 2; - } - snprintf_P(svalue, sizeof(svalue), PSTR("{\"Topic\":\"%s\"}"), sysCfg.mqtt_topic); - } - else if (sysCfg.mqtt_enabled && !grpflg && !strcmp(type,"BUTTONTOPIC")) { - if ((data_len > 0) && (data_len < sizeof(sysCfg.button_topic))) { - for(i = 0; i <= data_len; i++) - if ((dataBuf[i] == '/') || (dataBuf[i] == '+') || (dataBuf[i] == '#') || (dataBuf[i] == ' ')) dataBuf[i] = '_'; - if (!strcmp(dataBuf, MQTTClient)) payload = 1; - strlcpy(sysCfg.button_topic, (payload == 1) ? sysCfg.mqtt_topic : dataBuf, sizeof(sysCfg.button_topic)); - } - snprintf_P(svalue, sizeof(svalue), PSTR("{\"ButtonTopic\":\"%s\"}"), sysCfg.button_topic); - } - else if (sysCfg.mqtt_enabled && !grpflg && !strcmp(type,"SWITCHTOPIC")) { - if ((data_len > 0) && (data_len < sizeof(sysCfg.switch_topic))) { - for(i = 0; i <= data_len; i++) - if ((dataBuf[i] == '/') || (dataBuf[i] == '+') || (dataBuf[i] == '#') || (dataBuf[i] == ' ')) dataBuf[i] = '_'; - if (!strcmp(dataBuf, MQTTClient)) payload = 1; - strlcpy(sysCfg.switch_topic, (payload == 1) ? sysCfg.mqtt_topic : dataBuf, sizeof(sysCfg.switch_topic)); - } - snprintf_P(svalue, sizeof(svalue), PSTR("{\"SwitchTopic\":\"%s\"}"), sysCfg.switch_topic); - } - else if (sysCfg.mqtt_enabled && !strcmp(type,"BUTTONRETAIN")) { - if ((data_len > 0) && (payload >= 0) && (payload <= 1)) { - strlcpy(sysCfg.button_topic, sysCfg.mqtt_topic, sizeof(sysCfg.button_topic)); - if (!payload) { - for(i = 1; i <= Maxdevice; i++) { - send_button_power(0, i, 3); // Clear MQTT retain in broker - } - } - sysCfg.mqtt_button_retain = payload; - } - snprintf_P(svalue, sizeof(svalue), PSTR("{\"ButtonRetain\":\"%s\"}"), (sysCfg.mqtt_button_retain) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); - } - else if (sysCfg.mqtt_enabled && !strcmp(type,"SWITCHRETAIN")) { - if ((data_len > 0) && (payload >= 0) && (payload <= 1)) { -// strlcpy(sysCfg.button_topic, sysCfg.mqtt_topic, sizeof(sysCfg.button_topic)); - if (!payload) { - for(i = 1; i <= 4; i++) { - send_button_power(1, i, 3); // Clear MQTT retain in broker - } - } - sysCfg.mqtt_switch_retain = payload; - } - snprintf_P(svalue, sizeof(svalue), PSTR("{\"SwitchRetain\":\"%s\"}"), (sysCfg.mqtt_switch_retain) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); - } - else if (sysCfg.mqtt_enabled && (!strcmp(type,"POWERRETAIN") || !strcmp(type,"LIGHTRETAIN"))) { - if ((data_len > 0) && (payload >= 0) && (payload <= 1)) { - if (!payload) { - for(i = 1; i <= Maxdevice; i++) { // Clear MQTT retain in broker - snprintf_P(stemp2, sizeof(stemp2), PSTR("%d"), i); - snprintf_P(stemp1, sizeof(stemp1), PSTR("%s/%s/POWER%s"), PUB_PREFIX, sysCfg.mqtt_topic, (Maxdevice > 1) ? stemp2 : ""); - mqtt_publish(stemp1, "", sysCfg.mqtt_power_retain); - snprintf_P(stemp1, sizeof(stemp1), PSTR("%s/%s/LIGHT%s"), PUB_PREFIX, sysCfg.mqtt_topic, (Maxdevice > 1) ? stemp2 : ""); - mqtt_publish(stemp1, "", sysCfg.mqtt_power_retain); - } - } - sysCfg.mqtt_power_retain = payload; - } - snprintf_P(stemp1, sizeof(stemp1), PSTR("%s"), (!strcmp(sysCfg.mqtt_subtopic,"POWER")) ? "Power" : "Light"); - snprintf_P(svalue, sizeof(svalue), PSTR("{\"%sRetain\":\"%s\"}"), stemp1, (sysCfg.mqtt_power_retain) ? MQTT_STATUS_ON : MQTT_STATUS_OFF); - } -#ifdef USE_DOMOTICZ - else if (sysCfg.mqtt_enabled && domoticz_command(type, index, dataBuf, data_len, payload, svalue, sizeof(svalue))) { + else if (sysCfg.mqtt_enabled && mqtt_command(grpflg, type, index, dataBuf, data_len, payload, svalue, sizeof(svalue))) { // Serviced } -#endif // USE_DOMOTICZ else if (hlw_flg && hlw_command(type, index, dataBuf, data_len, payload, svalue, sizeof(svalue))) { // Serviced } +#ifdef USE_I2C + else if (i2c_flg && !strcmp(type,"I2CSCAN")) { + i2c_scan(svalue, sizeof(svalue)); + } +#endif // USE_I2C #ifdef USE_WS2812 else if ((pin[GPIO_WS2812] < 99) && ws2812_command(type, index, dataBuf, data_len, payload, svalue, sizeof(svalue))) { // Serviced @@ -1204,7 +1222,7 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len) } mqtt_publish_topic_P(0, PSTR("COMMANDS2"), svalue); - snprintf_P(svalue, sizeof(svalue), PSTR("{\"Commands3\":\"%s%s, PulseTime, BlinkTime, BlinkCount"), (Maxdevice == 1) ? "Power, Light" : "Power1, Power2, Light1 Light2", (sysCfg.module != MOTOR) ? ", PowerOnState" : ""); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Commands3\":\"%s%s, PulseTime, BlinkTime, BlinkCount, ButtonRestrict"), (Maxdevice == 1) ? "Power, Light" : "Power1, Power2, Light1 Light2", (sysCfg.module != MOTOR) ? ", PowerOnState" : ""); #ifdef USE_WEBSERVER snprintf_P(svalue, sizeof(svalue), PSTR("%s, Weblog, Webserver, WebPassword, Emulation"), svalue); #endif @@ -1377,8 +1395,10 @@ void publish_status(uint8_t payload) } if ((payload == 0) || (payload == 2)) { - snprintf_P(svalue, sizeof(svalue), PSTR("{\"StatusFWR\":{\"Program\":\"%s\", \"Boot\":%d, \"SDK\":\"%s\"}}"), - Version, ESP.getBootVersion(), ESP.getSdkVersion()); +// snprintf_P(svalue, sizeof(svalue), PSTR("{\"StatusFWR\":{\"Program\":\"%s\", \"BuildDateTime\":\"%s/%s\", \"Boot\":%d, \"Core\":\"%s\", \"SDK\":\"%s\"}}"), +// Version, __DATE__, __TIME__, ESP.getBootVersion(), ESP.getCoreVersion().c_str(), ESP.getSdkVersion()); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"StatusFWR\":{\"Program\":\"%s\", \"BuildDateTime\":\"%s\", \"Boot\":%d, \"Core\":\"%s\", \"SDK\":\"%s\"}}"), + Version, getBuildDateTime().c_str(), ESP.getBootVersion(), ESP.getCoreVersion().c_str(), ESP.getSdkVersion()); mqtt_publish_topic_P(option, PSTR("STATUS2"), svalue); } @@ -1438,11 +1458,8 @@ void publish_status(uint8_t payload) void sensors_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson) { - char stime[21]; + snprintf_P(svalue, ssvalue, PSTR("%s{\"Time\":\"%s\""), svalue, getDateTime().c_str()); - snprintf_P(stime, sizeof(stime), PSTR("%04d-%02d-%02dT%02d:%02d:%02d"), - rtcTime.Year, rtcTime.Month, rtcTime.Day, rtcTime.Hour, rtcTime.Minute, rtcTime.Second); - snprintf_P(svalue, ssvalue, PSTR("%s{\"Time\":\"%s\""), svalue, stime); if (pin[GPIO_DSB] < 99) { #ifdef USE_DS18B20 dsb_mqttPresent(svalue, ssvalue, djson); @@ -1481,10 +1498,7 @@ void every_second_cb() void every_second() { - char svalue[MESSZ], stime[21]; - - snprintf_P(stime, sizeof(stime), PSTR("%04d-%02d-%02dT%02d:%02d:%02d"), - rtcTime.Year, rtcTime.Month, rtcTime.Day, rtcTime.Hour, rtcTime.Minute, rtcTime.Second); + char svalue[MESSZ]; if (pulse_timer > 111) pulse_timer--; @@ -1551,7 +1565,7 @@ void every_second() if (tele_period >= sysCfg.tele_period) { tele_period = 0; - snprintf_P(svalue, sizeof(svalue), PSTR("{\"Time\":\"%s\", \"Uptime\":%d"), stime, uptime); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Time\":\"%s\", \"Uptime\":%d"), getDateTime().c_str(), uptime); for (byte i = 0; i < Maxdevice; i++) { if (Maxdevice == 1) { // Legacy snprintf_P(svalue, sizeof(svalue), PSTR("%s, \"%s\":"), svalue, sysCfg.mqtt_subtopic); @@ -1577,7 +1591,7 @@ void every_second() if ((rtcTime.Minute == 2) && (rtcTime.Second == 30)) { uptime++; - snprintf_P(svalue, sizeof(svalue), PSTR("{\"Time\":\"%s\", \"Uptime\":%d}"), stime, uptime); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Time\":\"%s\", \"Uptime\":%d}"), getDateTime().c_str(), uptime); mqtt_publish_topic_P(1, PSTR("UPTIME"), svalue); } } @@ -1652,7 +1666,7 @@ void stateloop() holdcount = 0; } else { holdcount++; - if (holdcount == (STATES *4)) { // 4 seconds button hold + if (!sysCfg.button_restrict && (holdcount == (STATES *4))) { // 4 seconds button hold snprintf_P(scmnd, sizeof(scmnd), PSTR("reset 1")); multipress = 0; do_cmnd(scmnd); @@ -1669,8 +1683,7 @@ void stateloop() } if (flag && sysCfg.mqtt_enabled && mqttClient.connected() && strcmp(sysCfg.button_topic, "0")) { send_button_power(0, multipress, 2); // Execute command via MQTT using ButtonTopic to sync external clients - } else - { + } else { if ((multipress == 1) || (multipress == 2)) { if (WIFI_State()) { // WPSconfig, Smartconfig or Wifimanager active restartflag = 1; @@ -1678,8 +1691,10 @@ void stateloop() do_cmnd_power(multipress, 2); // Execute command internally } } else { - snprintf_P(scmnd, sizeof(scmnd), commands[multipress -3]); - do_cmnd(scmnd); + if (!sysCfg.button_restrict) { + snprintf_P(scmnd, sizeof(scmnd), commands[multipress -3]); + do_cmnd(scmnd); + } } } multipress = 0; @@ -1878,7 +1893,7 @@ void serial() } if (SerialInByte == '\n') { serialInBuf[SerialInByteCounter] = 0; // serial data completed - if (seriallog_level < LOG_LEVEL_INFO) seriallog_level = LOG_LEVEL_INFO; + seriallog_level = (sysCfg.seriallog_level < LOG_LEVEL_INFO) ? LOG_LEVEL_INFO : sysCfg.seriallog_level; snprintf_P(log, sizeof(log), PSTR("CMND: %s"), serialInBuf); addLog(LOG_LEVEL_INFO, log); do_cmnd(serialInBuf); diff --git a/sonoff/support.ino b/sonoff/support.ino index ae82ea15b63c..615ea0159891 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -587,12 +587,49 @@ extern "C" { Ticker tickerRTC; static const uint8_t monthDays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // API starts months from 1, this array starts from 0 -static const char monthNames[37] = { "JanFebMrtAprMayJunJulAugSepOctNovDec" }; +static const char monthNames[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; uint32_t utctime = 0, loctime = 0, dsttime = 0, stdtime = 0, ntptime = 0, midnight = 1451602800; rtcCallback rtcCb = NULL; +String getBuildDateTime() +{ + // "2017-03-07T11:08:02" + char bdt[21]; + char *str, *p, *smonth; + char mdate[] = __DATE__; // "Mar 7 2017" + int month, day, year; + +// sscanf(mdate, "%s %d %d", bdt, &day, &year); // Not implemented in 2.3.0 and probably too many code + byte i = 0; + for (str = strtok_r(mdate, " ", &p); str && i < 3; str = strtok_r(NULL, " ", &p)) { + switch (i++) { + case 0: // Month + smonth = str; + break; + case 1: // Day + day = atoi(str); + break; + case 2: // Year + year = atoi(str); + } + } + month = (strstr(monthNames, smonth) -monthNames) /3 +1; + snprintf_P(bdt, sizeof(bdt), PSTR("%d-%02d-%02dT%s"), year, month, day, __TIME__); + return String(bdt); +} + +String getDateTime() +{ + // "2017-03-07T11:08:02" + char dt[21]; + + snprintf_P(dt, sizeof(dt), PSTR("%04d-%02d-%02dT%02d:%02d:%02d"), + rtcTime.Year, rtcTime.Month, rtcTime.Day, rtcTime.Hour, rtcTime.Minute, rtcTime.Second); + return String(dt); +} + void breakTime(uint32_t timeInput, TIME_T &tm) { // break the given timeInput into time components diff --git a/sonoff/user_config.h b/sonoff/user_config.h index 31cdce711226..4949bfff92e9 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -141,7 +141,7 @@ // When USE_WS2812_DMA is enabled expect Exceptions on Pow /*********************************************************************************************\ - * Compile a minimal version if upgrade memory gets tight. + * Compile a minimal version if upgrade memory gets tight ONLY TO BE USED FOR UPGRADE STEP 1! * To be used as step 1 during upgrade. * Step 2 is re-compile with option BE_MINIMAL commented out. * !!! Needed for next release of Arduino/ESP8266 (+22k code, +2k mem) !!! diff --git a/sonoff/webserver.ino b/sonoff/webserver.ino index 64a51cb500c1..1b5f132ffd91 100644 --- a/sonoff/webserver.ino +++ b/sonoff/webserver.ino @@ -40,8 +40,11 @@ const char HTTP_HEAD[] PROGMEM = "" "" "
"), + snprintf_P(line, sizeof(line), PSTR(" | "), 100 / Maxdevice, idx, (Maxdevice > 1) ? stemp : ""); page += line; } @@ -416,7 +431,14 @@ void handleRoot() void handleAjax2() { + char svalue[16]; + if (strlen(webServer->arg("o").c_str())) do_cmnd_power(atoi(webServer->arg("o").c_str()), 2); + if (strlen(webServer->arg("d").c_str())) { + snprintf_P(svalue, sizeof(svalue), PSTR("dimmer %s"), webServer->arg("d").c_str()); + do_cmnd(svalue); + } + String tpage = ""; if (hlw_flg) tpage += hlw_webPresent(); #ifdef USE_DS18B20 @@ -445,18 +467,20 @@ void handleAjax2() if (Maxdevice) { page += F(" |
{3 | ");
- page.replace("{1", String(100 / Maxdevice));
- page.replace("{2", String(70 - (Maxdevice * 8)));
- page.replace("{3", (power & (0x01 << (idx -1))) ? "ON" : "OFF");
-*/
snprintf_P(line, sizeof(line), PSTR("%s | "),
100 / Maxdevice, 70 - (Maxdevice * 8), (power & (0x01 << (idx -1))) ? "ON" : "OFF");
page += line;
}
page += F("
Program version | "); page += Version; page += F(" |
---|---|
Build Date/Time | "); page += __DATE__; - page += F("/"); page += __TIME__ ; page += F(" |
Build Date & Time | "); page += getBuildDateTime(); page += F(" |
Core/SDK version | "); page += ESP.getCoreVersion(); page += F("/"); page += String(ESP.getSdkVersion()); page += F(" |
Boot version | "); page += String(ESP.getBootVersion()); page += F(" |
Uptime | "); page += String(uptime); page += F(" Hours |
MQTT Host | "); page += sysCfg.mqtt_host; page += F(" |
MQTT Port | "); page += String(sysCfg.mqtt_port); page += F(" |
MQTT Client and Fallback Topic | "); page += MQTTClient; page += F(" |
MQTT Client & Fallback Topic | "); page += MQTTClient; page += F(" |
MQTT User | "); page += sysCfg.mqtt_user; page += F(" |
MQTT Password | "); page += sysCfg.mqtt_pwd; page += F(" |
MQTT Topic | "); page += sysCfg.mqtt_topic; page += F(" |
mDNS Webserver Advertise | "); + page += F(" |
mDNS Advertise | "); #ifdef WEBSERVER_ADVERTISE - page += F("Enabled"); + page += F("Webserver"); #else page += F("Disabled"); #endif // WEBSERVER_ADVERTISE @@ -1355,7 +1380,7 @@ void handleNotFound() /* Redirect to captive portal if we got a request for another domain. Return true in that case so the page handler do not try to handle the request again. */ boolean captivePortal() { - if (!isIp(webServer->hostHeader())) { + if ((_httpflag == HTTP_MANAGER) && !isIp(webServer->hostHeader())) { addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Request redirected to captive portal")); webServer->sendHeader("Location", String("http://") + webServer->client().localIP().toString(), true); diff --git a/sonoff/xdrv_snfled.ino b/sonoff/xdrv_snfled.ino index 7d740a0f5a73..e77bb373c37e 100644 --- a/sonoff/xdrv_snfled.ino +++ b/sonoff/xdrv_snfled.ino @@ -99,12 +99,11 @@ void sl_init(void) } void sl_blank(byte state) -/* - * Called by interrupt disabling routines like OTA or web upload - * state = 0: No blank - * 1: Blank led to solve flicker - */ { +// Called by interrupt disabling routines like OTA or web upload +// state = 0: No blank +// 1: Blank led to solve flicker + if (sysCfg.module == SONOFF_LED) { sl_blankv = state; sl_wakeupActive = 0; diff --git a/sonoff/xdrv_wemohue.ino b/sonoff/xdrv_wemohue.ino index 0c3a03731fe9..f9f31ab9901f 100644 --- a/sonoff/xdrv_wemohue.ino +++ b/sonoff/xdrv_wemohue.ino @@ -394,16 +394,12 @@ void hue_todo(String *path) void hue_config_response(String *response) { - char buffer[21]; - *response += FPSTR(HUE_CONFIG_RESPONSE_JSON); response->replace("{mac}", WiFi.macAddress()); response->replace("{ip}", WiFi.localIP().toString()); response->replace("{mask}", WiFi.subnetMask().toString()); response->replace("{gw}", WiFi.gatewayIP().toString()); - snprintf_P(buffer, sizeof(buffer), PSTR("%04d-%02d-%02dT%02d:%02d:%02d"), - rtcTime.Year, rtcTime.Month, rtcTime.Day, rtcTime.Hour, rtcTime.Minute, rtcTime.Second); - response->replace("{dt}", buffer); + response->replace("{dt}", getDateTime()); } void hue_global_cfg(String *path) diff --git a/sonoff/xsns_ds18x20.ino b/sonoff/xsns_ds18x20.ino index 6088b33ceeab..ab264dc058a2 100644 --- a/sonoff/xsns_ds18x20.ino +++ b/sonoff/xsns_ds18x20.ino @@ -107,7 +107,7 @@ float ds18x20_convertCtoF(float c) boolean ds18x20_read(uint8_t sensor, bool S, float &t) { byte data[12]; - uint8_t sign = 1; + int8_t sign = 1; uint8_t i = 0; float temp9 = 0.0; uint8_t present = 0; @@ -122,7 +122,24 @@ boolean ds18x20_read(uint8_t sensor, bool S, float &t) if (OneWire::crc8(data, 8) == data[8]) { switch(ds18x20_addr[ds18x20_idx[sensor]][0]) { case 0x10: // DS18S20 - if (data[1] > 0x80) sign = -1; // App-Note fix possible sign error +/* +// App_note AN162.pdf page 9 + int temp_lsb, temp_msb; + temp_msb = data[1]; // Sign byte + lsbit + temp_lsb = data[0]; // Temp data plus lsb + if (temp_msb <= 0x80) temp_lsb = (temp_lsb/2); // Shift to get whole degree + temp_msb = temp_msb & 0x80; // Mask all but the sign bit + if (temp_msb >= 0x80) { // Negative temperature + temp_lsb = (~temp_lsb)+1; // Twos complement + temp_lsb = (temp_lsb/2); // Shift to get whole degree + temp_lsb = ((-1)*temp_lsb); // Add sign bit + } + t = (int)temp_lsb; // Temperature in whole degree +*/ + if (data[1] > 0x80) { + data[0] = (~data[0]) +1; + sign = -1; // App-Note fix possible sign error + } if (data[0] & 1) { temp9 = ((data[0] >> 1) + 0.5) * sign; } else { diff --git a/sonoff/xsns_hlw8012.ino b/sonoff/xsns_hlw8012.ino index 629e02a2796b..5deaada5f19d 100644 --- a/sonoff/xsns_hlw8012.ino +++ b/sonoff/xsns_hlw8012.ino @@ -546,8 +546,7 @@ void hlw_mqttPresent() // {"Time":"2017-03-04T13:37:24", "Yesterday":0.013, "Today":0.000, "Period":0, "Power":0, "Factor":0.00, "Voltage":0, "Current":0.000} char svalue[200]; // was MESSZ - snprintf_P(svalue, sizeof(svalue), PSTR("{\"Time\":\"%04d-%02d-%02dT%02d:%02d:%02d\", "), - rtcTime.Year, rtcTime.Month, rtcTime.Day, rtcTime.Hour, rtcTime.Minute, rtcTime.Second); + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Time\":\"%s\", "), getDateTime().c_str()); hlw_mqttStat(1, svalue, sizeof(svalue)); // snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/ENERGY"), PUB_PREFIX2, sysCfg.mqtt_topic); |