From c4b3d4269371cfc9a203b8ee4787dd8970c91b0c Mon Sep 17 00:00:00 2001 From: Theo Arends Date: Wed, 28 Mar 2018 18:01:38 +0200 Subject: [PATCH 1/7] v5.12.0i - Add Timers webpage 5.12.0i * Add commands Timer 0 to clear timer and Timer 1..16 to copy timer * Add optional Timer configuration webpage to be enabled in user_config.h with define USE_TIMERS_WEB * Change webpage parameter communication --- sonoff/_releasenotes.ino | 3 + sonoff/language/cs-CZ.h | 10 ++ sonoff/language/de-DE.h | 10 ++ sonoff/language/en-GB.h | 10 ++ sonoff/language/es-AR.h | 10 ++ sonoff/language/fr-FR.h | 10 ++ sonoff/language/hu-HU.h | 10 ++ sonoff/language/it-IT.h | 10 ++ sonoff/language/nl-NL.h | 12 +- sonoff/language/pl-PL.h | 10 ++ sonoff/language/pt-PT.h | 10 ++ sonoff/language/ru-RU.h | 10 ++ sonoff/language/zh-CN.h | 10 ++ sonoff/language/zh-TW.h | 10 ++ sonoff/settings.h | 4 +- sonoff/user_config.h | 1 + sonoff/webserver.ino | 61 ++++++--- sonoff/xdrv_05_domoticz.ino | 2 +- sonoff/xdrv_09_timers.ino | 258 ++++++++++++++++++++++++++++-------- 19 files changed, 382 insertions(+), 79 deletions(-) diff --git a/sonoff/_releasenotes.ino b/sonoff/_releasenotes.ino index d92495343..5583d95fc 100644 --- a/sonoff/_releasenotes.ino +++ b/sonoff/_releasenotes.ino @@ -1,5 +1,8 @@ /* 5.12.0i * Add 16 timers using commands Timer and Timers (#1091) + * Add commands Timer 0 to clear timer and Timer 1..16 to copy timer + * Add optional Timer configuration webpage to be enabled in user_config.h with define USE_TIMERS_WEB + * Change webpage parameter communication * * 5.12.0h * Add optional Arduino OTA support to be enabled in user_config.h (#1998) diff --git a/sonoff/language/cs-CZ.h b/sonoff/language/cs-CZ.h index e78d0a8e0..617524e20 100644 --- a/sonoff/language/cs-CZ.h +++ b/sonoff/language/cs-CZ.h @@ -358,6 +358,16 @@ #define D_DOMOTICZ_AIRQUALITY "AirQuality" #define D_DOMOTICZ_UPDATE_TIMER "Aktualizace stopek" +// xdrv_09_timers.ino +#define D_CONFIGURE_TIMER "Configure Timer" +#define D_TIMER_PARAMETERS "Timer parameters" +#define D_TIMER_ARM "Arm" +#define D_TIMER_TIME "Time" +#define D_TIMER_DAYS "Days" +#define D_TIMER_REPEAT "Repeat" +#define D_TIMER_DEVICE "Device" +#define D_TIMER_POWER "Power" + // xdrv_03_energy.ino #define D_ENERGY_TODAY "Energie Dnes" #define D_ENERGY_YESTERDAY "Energie Včera" diff --git a/sonoff/language/de-DE.h b/sonoff/language/de-DE.h index b0ea8033a..f6f5c78a0 100644 --- a/sonoff/language/de-DE.h +++ b/sonoff/language/de-DE.h @@ -358,6 +358,16 @@ #define D_DOMOTICZ_AIRQUALITY "AirQuality" #define D_DOMOTICZ_UPDATE_TIMER "Update timer" +// xdrv_09_timers.ino +#define D_CONFIGURE_TIMER "Configure Timer" +#define D_TIMER_PARAMETERS "Timer parameters" +#define D_TIMER_ARM "Arm" +#define D_TIMER_TIME "Time" +#define D_TIMER_DAYS "Days" +#define D_TIMER_REPEAT "Repeat" +#define D_TIMER_DEVICE "Device" +#define D_TIMER_POWER "Power" + // xdrv_03_energy.ino #define D_ENERGY_TODAY "Energie heute" #define D_ENERGY_YESTERDAY "Energie gestern" diff --git a/sonoff/language/en-GB.h b/sonoff/language/en-GB.h index 5fb862b5e..c7db040db 100644 --- a/sonoff/language/en-GB.h +++ b/sonoff/language/en-GB.h @@ -358,6 +358,16 @@ #define D_DOMOTICZ_AIRQUALITY "AirQuality" #define D_DOMOTICZ_UPDATE_TIMER "Update timer" +// xdrv_09_timers.ino +#define D_CONFIGURE_TIMER "Configure Timer" +#define D_TIMER_PARAMETERS "Timer parameters" +#define D_TIMER_ARM "Arm" +#define D_TIMER_TIME "Time" +#define D_TIMER_DAYS "Days" +#define D_TIMER_REPEAT "Repeat" +#define D_TIMER_DEVICE "Device" +#define D_TIMER_POWER "Power" + // xdrv_03_energy.ino #define D_ENERGY_TODAY "Energy Today" #define D_ENERGY_YESTERDAY "Energy Yesterday" diff --git a/sonoff/language/es-AR.h b/sonoff/language/es-AR.h index dbe241731..5ad7d344c 100644 --- a/sonoff/language/es-AR.h +++ b/sonoff/language/es-AR.h @@ -358,6 +358,16 @@ #define D_DOMOTICZ_AIRQUALITY "Calidad del Aire" #define D_DOMOTICZ_UPDATE_TIMER "Intervalo de refresco" +// xdrv_09_timers.ino +#define D_CONFIGURE_TIMER "Configure Timer" +#define D_TIMER_PARAMETERS "Timer parameters" +#define D_TIMER_ARM "Arm" +#define D_TIMER_TIME "Time" +#define D_TIMER_DAYS "Days" +#define D_TIMER_REPEAT "Repeat" +#define D_TIMER_DEVICE "Device" +#define D_TIMER_POWER "Power" + // xdrv_03_energy.ino #define D_ENERGY_TODAY "Energía Hoy" #define D_ENERGY_YESTERDAY "Energía Ayer" diff --git a/sonoff/language/fr-FR.h b/sonoff/language/fr-FR.h index 7b9c0137f..64953f3b3 100644 --- a/sonoff/language/fr-FR.h +++ b/sonoff/language/fr-FR.h @@ -358,6 +358,16 @@ #define D_DOMOTICZ_AIRQUALITY "AirQuality" #define D_DOMOTICZ_UPDATE_TIMER "Update timer" +// xdrv_09_timers.ino +#define D_CONFIGURE_TIMER "Configure Timer" +#define D_TIMER_PARAMETERS "Timer parameters" +#define D_TIMER_ARM "Arm" +#define D_TIMER_TIME "Time" +#define D_TIMER_DAYS "Days" +#define D_TIMER_REPEAT "Repeat" +#define D_TIMER_DEVICE "Device" +#define D_TIMER_POWER "Power" + // xsns_03_energy.ino #define D_ENERGY_TODAY "Energie aujourd'hui" #define D_ENERGY_YESTERDAY "Energie hier" diff --git a/sonoff/language/hu-HU.h b/sonoff/language/hu-HU.h index 82dde4c38..dd6ec3292 100644 --- a/sonoff/language/hu-HU.h +++ b/sonoff/language/hu-HU.h @@ -358,6 +358,16 @@ #define D_DOMOTICZ_AIRQUALITY "Légminőség" #define D_DOMOTICZ_UPDATE_TIMER "Update időzítő" +// xdrv_09_timers.ino +#define D_CONFIGURE_TIMER "Configure Timer" +#define D_TIMER_PARAMETERS "Timer parameters" +#define D_TIMER_ARM "Arm" +#define D_TIMER_TIME "Time" +#define D_TIMER_DAYS "Days" +#define D_TIMER_REPEAT "Repeat" +#define D_TIMER_DEVICE "Device" +#define D_TIMER_POWER "Power" + // xdrv_03_energy.ino #define D_ENERGY_TODAY "Mai Energia" #define D_ENERGY_YESTERDAY "Tegnapi Energia" diff --git a/sonoff/language/it-IT.h b/sonoff/language/it-IT.h index 4e16869a4..c9134c7dd 100644 --- a/sonoff/language/it-IT.h +++ b/sonoff/language/it-IT.h @@ -358,6 +358,16 @@ #define D_DOMOTICZ_AIRQUALITY "AirQuality" #define D_DOMOTICZ_UPDATE_TIMER "Intervallo di aggiornamento" +// xdrv_09_timers.ino +#define D_CONFIGURE_TIMER "Configure Timer" +#define D_TIMER_PARAMETERS "Timer parameters" +#define D_TIMER_ARM "Arm" +#define D_TIMER_TIME "Time" +#define D_TIMER_DAYS "Days" +#define D_TIMER_REPEAT "Repeat" +#define D_TIMER_DEVICE "Device" +#define D_TIMER_POWER "Power" + // xdrv_03_energy.ino #define D_ENERGY_TODAY "Energia Oggi" #define D_ENERGY_YESTERDAY "Energia Ieri" diff --git a/sonoff/language/nl-NL.h b/sonoff/language/nl-NL.h index 53bdb7f67..3b91d3d84 100644 --- a/sonoff/language/nl-NL.h +++ b/sonoff/language/nl-NL.h @@ -110,7 +110,7 @@ #define D_OFF "Uit" #define D_OFFLINE "Offline" #define D_OK "Ok" -#define D_ON "Ann" +#define D_ON "Aan" #define D_ONLINE "Online" #define D_PASSWORD "Wachtwoord" #define D_PORT "Poort" @@ -358,6 +358,16 @@ #define D_DOMOTICZ_AIRQUALITY "AirQuality" #define D_DOMOTICZ_UPDATE_TIMER "Bijwerk timer" +// xdrv_09_timers.ino +#define D_CONFIGURE_TIMER "Configureer Tijdschakelaar" +#define D_TIMER_PARAMETERS "Tijdschakelaar parameters" +#define D_TIMER_ARM "Actief" +#define D_TIMER_TIME "Tijd" +#define D_TIMER_DAYS "Dagen" +#define D_TIMER_REPEAT "Herhaal" +#define D_TIMER_DEVICE "Uitgang" +#define D_TIMER_POWER "Actie" + // xdrv_03_energy.ino #define D_ENERGY_TODAY "Verbruik vandaag" #define D_ENERGY_YESTERDAY "Verbruik gisteren" diff --git a/sonoff/language/pl-PL.h b/sonoff/language/pl-PL.h index 90a69afb5..f1023e5e4 100644 --- a/sonoff/language/pl-PL.h +++ b/sonoff/language/pl-PL.h @@ -358,6 +358,16 @@ #define D_DOMOTICZ_AIRQUALITY "AirQuality" #define D_DOMOTICZ_UPDATE_TIMER "Zaktualizuj czasomierz" +// xdrv_09_timers.ino +#define D_CONFIGURE_TIMER "Configure Timer" +#define D_TIMER_PARAMETERS "Timer parameters" +#define D_TIMER_ARM "Arm" +#define D_TIMER_TIME "Time" +#define D_TIMER_DAYS "Days" +#define D_TIMER_REPEAT "Repeat" +#define D_TIMER_DEVICE "Device" +#define D_TIMER_POWER "Power" + // xdrv_03_energy.ino #define D_ENERGY_TODAY "Energia Dzisiaj" #define D_ENERGY_YESTERDAY "Energia Wczoraj" diff --git a/sonoff/language/pt-PT.h b/sonoff/language/pt-PT.h index 7d8f0512c..73a8b53ea 100644 --- a/sonoff/language/pt-PT.h +++ b/sonoff/language/pt-PT.h @@ -358,6 +358,16 @@ #define D_DOMOTICZ_AIRQUALITY "Qualidade do Ar" #define D_DOMOTICZ_UPDATE_TIMER "Tempo de atualização" +// xdrv_09_timers.ino +#define D_CONFIGURE_TIMER "Configure Timer" +#define D_TIMER_PARAMETERS "Timer parameters" +#define D_TIMER_ARM "Arm" +#define D_TIMER_TIME "Time" +#define D_TIMER_DAYS "Days" +#define D_TIMER_REPEAT "Repeat" +#define D_TIMER_DEVICE "Device" +#define D_TIMER_POWER "Power" + // xdrv_03_energy.ino #define D_ENERGY_TODAY "Consumo energético de hoje" #define D_ENERGY_YESTERDAY "Consumo energético de ontem" diff --git a/sonoff/language/ru-RU.h b/sonoff/language/ru-RU.h index 640b8bef7..5c5b964e6 100644 --- a/sonoff/language/ru-RU.h +++ b/sonoff/language/ru-RU.h @@ -358,6 +358,16 @@ #define D_DOMOTICZ_AIRQUALITY "AirQuality" #define D_DOMOTICZ_UPDATE_TIMER "Update timer" +// xdrv_09_timers.ino +#define D_CONFIGURE_TIMER "Configure Timer" +#define D_TIMER_PARAMETERS "Timer parameters" +#define D_TIMER_ARM "Arm" +#define D_TIMER_TIME "Time" +#define D_TIMER_DAYS "Days" +#define D_TIMER_REPEAT "Repeat" +#define D_TIMER_DEVICE "Device" +#define D_TIMER_POWER "Power" + // xdrv_03_energy.ino #define D_ENERGY_TODAY "Энергия Сегодня" #define D_ENERGY_YESTERDAY "Энергия Вчера" diff --git a/sonoff/language/zh-CN.h b/sonoff/language/zh-CN.h index 0cd86bcc8..86c352dbf 100644 --- a/sonoff/language/zh-CN.h +++ b/sonoff/language/zh-CN.h @@ -358,6 +358,16 @@ #define D_DOMOTICZ_AIRQUALITY "空气质量" #define D_DOMOTICZ_UPDATE_TIMER "更新计时器" +// xdrv_09_timers.ino +#define D_CONFIGURE_TIMER "Configure Timer" +#define D_TIMER_PARAMETERS "Timer parameters" +#define D_TIMER_ARM "Arm" +#define D_TIMER_TIME "Time" +#define D_TIMER_DAYS "Days" +#define D_TIMER_REPEAT "Repeat" +#define D_TIMER_DEVICE "Device" +#define D_TIMER_POWER "Power" + // xdrv_03_energy.ino #define D_ENERGY_TODAY "今日用电量" #define D_ENERGY_YESTERDAY "昨日用电量" diff --git a/sonoff/language/zh-TW.h b/sonoff/language/zh-TW.h index c3efc118b..f015232e3 100644 --- a/sonoff/language/zh-TW.h +++ b/sonoff/language/zh-TW.h @@ -358,6 +358,16 @@ #define D_DOMOTICZ_AIRQUALITY "空氣品質" #define D_DOMOTICZ_UPDATE_TIMER "更新計時器" +// xdrv_09_timers.ino +#define D_CONFIGURE_TIMER "Configure Timer" +#define D_TIMER_PARAMETERS "Timer parameters" +#define D_TIMER_ARM "Arm" +#define D_TIMER_TIME "Time" +#define D_TIMER_DAYS "Days" +#define D_TIMER_REPEAT "Repeat" +#define D_TIMER_DEVICE "Device" +#define D_TIMER_POWER "Power" + // xdrv_03_energy.ino #define D_ENERGY_TODAY "今日用電量" #define D_ENERGY_YESTERDAY "昨日用電量" diff --git a/sonoff/settings.h b/sonoff/settings.h index 173958b86..5c18c9ca2 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -93,10 +93,10 @@ typedef union { uint32_t data; struct { uint32_t time : 11; // bits 0 - 10 = minutes in a day - uint32_t mday : 5; // bits 11 - 15 = optional day in a month + uint32_t mday : 5; // bits 11 - 15 = 32 days in a month uint32_t days : 7; // bits 16 - 22 = week day mask uint32_t device : 4; // bits 23 - 26 = 16 devices - uint32_t power : 2; // bits 27 - 28 = 4 power states - Off, On, Toggle + uint32_t power : 2; // bits 27 - 28 = 4 power states - Off, On, Toggle, Blink uint32_t repeat : 1; // bit 29 uint32_t arm : 1; // bit 30 uint32_t spare : 1; // bit 31 diff --git a/sonoff/user_config.h b/sonoff/user_config.h index 5de54309a..d49373f67 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -163,6 +163,7 @@ #define NTP_SERVER3 "0.nl.pool.ntp.org" // [NtpServer3] Select third NTP server by name or IP address (93.94.224.67) #define USE_TIMERS // Add support for up to 16 timers (+2k2 code) + #define USE_TIMERS_WEB // Add timer webpage support (+4k5 code) // -- Time - Start Daylight Saving Time and timezone offset from UTC in minutes #define TIME_DST North, Last, Sun, Mar, 2, +120 // Northern Hemisphere, Last sunday in march at 02:00 +120 minutes diff --git a/sonoff/webserver.ino b/sonoff/webserver.ino index cc843748f..d314d09e4 100644 --- a/sonoff/webserver.ino +++ b/sonoff/webserver.ino @@ -39,16 +39,19 @@ const char HTTP_HEAD[] PROGMEM = "var cn,x,lt;" "cn=180;" "x=null;" // Allow for abortion + "function eb(s){" + "return document.getElementById(s);" // Save code space + "}" "function u(){" "if(cn>=0){" - "document.getElementById('t').innerHTML='" D_RESTART_IN " '+cn+' " D_SECONDS "';" + "eb('t').innerHTML='" D_RESTART_IN " '+cn+' " D_SECONDS "';" "cn--;" "setTimeout(u,1000);" "}" "}" "function c(l){" - "document.getElementById('s1').value=l.innerText||l.textContent;" - "document.getElementById('p1').focus();" + "eb('s1').value=l.innerText||l.textContent;" + "eb('p1').focus();" "}" "function la(p){" "var a='';" @@ -61,7 +64,7 @@ const char HTTP_HEAD[] PROGMEM = "x.onreadystatechange=function(){" "if(x.readyState==4&&x.status==200){" "var s=x.responseText.replace(/{t}/g,\"\").replace(/{s}/g,\"\").replace(/{c}/g,\"%'>
\").replace(/{m}/g,\"\").replace(/{e}/g,\"
\").replace(/}2/g,\"\");" - "document.getElementById('i').innerHTML=s;" + "eb('i').innerHTML=s;" "}" ""; const char HTTP_MSG_SLIDER1[] PROGMEM = @@ -180,6 +183,11 @@ const char HTTP_BTN_RSTRT[] PROGMEM = "
"; const char HTTP_BTN_MENU2[] PROGMEM = "
" +#ifdef USE_TIMERS +#ifdef USE_TIMERS_WEB + "
" +#endif // USE_TIMERS_WEB +#endif // USE_TIMERS "
"; const char HTTP_BTN_MENU3[] PROGMEM = "
" @@ -206,7 +214,7 @@ const char HTTP_BTN_CONF[] PROGMEM = "

"; const char HTTP_FORM_MODULE[] PROGMEM = "
 " D_MODULE_PARAMETERS " 
" - "" + "" "
" D_MODULE_TYPE " ({mt)

"; const char HTTP_LNK_ITEM[] PROGMEM = "
{v} {i} {r}%
"; @@ -214,7 +222,7 @@ const char HTTP_LNK_SCAN[] PROGMEM = "
"; const char HTTP_FORM_WIFI[] PROGMEM = "
 " D_WIFI_PARAMETERS " " - "" + "" "
" D_AP1_SSID " (" STA_SSID1 ")

" "
" D_AP1_PASSWORD "

" "
" D_AP2_SSID " (" STA_SSID2 ")

" @@ -222,7 +230,7 @@ const char HTTP_FORM_WIFI[] PROGMEM = "
" D_HOSTNAME " (" WIFI_HOSTNAME ")

"; const char HTTP_FORM_MQTT[] PROGMEM = "
 " D_MQTT_PARAMETERS " " - "" + "" "
" D_HOST " (" MQTT_HOST ")

" "
" D_PORT " (" STR(MQTT_PORT) ")

" "
" D_CLIENT " ({m0)

" @@ -232,7 +240,7 @@ const char HTTP_FORM_MQTT[] PROGMEM = "
" D_FULL_TOPIC " (" MQTT_FULLTOPIC ")

"; const char HTTP_FORM_LOG1[] PROGMEM = "
 " D_LOGGING_PARAMETERS " " - ""; + ""; const char HTTP_FORM_LOG2[] PROGMEM = "
{b0 ({b1)

"; const char HTTP_FORM_OTHER[] PROGMEM = "
 " D_OTHER_PARAMETERS " " - "" + "" "
" D_WEB_ADMIN_PASSWORD "

" "
" D_MQTT_ENABLE "
"; const char HTTP_FORM_OTHER2[] PROGMEM = @@ -274,7 +282,7 @@ const char HTTP_FORM_UPG[] PROGMEM = const char HTTP_FORM_RST_UPG[] PROGMEM = "" "

" - "
" + "
" "
" "" ""; @@ -330,6 +338,11 @@ void StartWebserver(int type, IPAddress ipweb) WebServer->on("/", HandleRoot); WebServer->on("/cn", HandleConfiguration); WebServer->on("/md", HandleModuleConfiguration); +#ifdef USE_TIMERS +#ifdef USE_TIMERS_WEB + WebServer->on("/tm", HandleTimerConfiguration); +#endif // USE_TIMERS_WEB +#endif // USE_TIMERS WebServer->on("/w1", HandleWifiConfigurationWithScan); WebServer->on("/w0", HandleWifiConfiguration); if (Settings.flag.mqtt_enabled) { @@ -1001,17 +1014,16 @@ void HandleSaveSettings() char stemp[TOPSZ]; char stemp2[TOPSZ]; - byte what = 0; - byte restart; String result = ""; AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_SAVE_CONFIGURATION); char tmp[100]; - WebGetArg("w", tmp, sizeof(tmp)); - if (strlen(tmp)) { - what = atoi(tmp); - } + WebGetArg("w", tmp, sizeof(tmp)); // Returns "5,1" where 5 is config type and 1 is restart flag + char *p = tmp; + uint8_t what = strtol(p, &p, 10); + p++; // Skip comma + uint8_t restart = strtol(p, &p, 10); switch (what) { case 1: WebGetArg("h", tmp, sizeof(tmp)); @@ -1085,6 +1097,13 @@ void HandleSaveSettings() Settings.seriallog_level, Settings.weblog_level, Settings.syslog_level, Settings.syslog_host, Settings.syslog_port, Settings.tele_period); AddLog(LOG_LEVEL_INFO); break; +#ifdef USE_TIMERS +#ifdef USE_TIMERS_WEB + case 7: + TimerSaveSettings(); + break; +#endif // USE_TIMERS_WEB +#endif // USE_TIMERS #ifdef USE_DOMOTICZ case 4: DomoticzSaveSettings(); @@ -1136,8 +1155,6 @@ void HandleSaveSettings() break; } - WebGetArg("r", tmp, sizeof(tmp)); - restart = (!strlen(tmp)) ? 1 : atoi(tmp); if (restart) { String page = FPSTR(HTTP_HEAD); page.replace(F("{v}"), FPSTR(S_SAVE_CONFIGURATION)); diff --git a/sonoff/xdrv_05_domoticz.ino b/sonoff/xdrv_05_domoticz.ino index 3e1dfdc49..5aa6a7c43 100644 --- a/sonoff/xdrv_05_domoticz.ino +++ b/sonoff/xdrv_05_domoticz.ino @@ -22,7 +22,7 @@ #ifdef USE_WEBSERVER const char HTTP_FORM_DOMOTICZ[] PROGMEM = "
 " D_DOMOTICZ_PARAMETERS " 
" - "" + "" "
"; const char HTTP_FORM_DOMOTICZ_RELAY[] PROGMEM = "" diff --git a/sonoff/xdrv_09_timers.ino b/sonoff/xdrv_09_timers.ino index 350f49dfd..d84b14358 100644 --- a/sonoff/xdrv_09_timers.ino +++ b/sonoff/xdrv_09_timers.ino @@ -36,7 +36,7 @@ enum TimerCommands { CMND_TIMER, CMND_TIMERS }; const char kTimerCommands[] PROGMEM = D_CMND_TIMER "|" D_CMND_TIMERS ; -power_t fired = 0; +uint16_t fired = 0; void TimerEverySecond() { @@ -45,6 +45,7 @@ void TimerEverySecond() uint8_t days = 1 << (RtcTime.day_of_week -1); for (byte i = 0; i < MAX_TIMERS; i++) { + if (Settings.timer[i].device >= devices_present) Settings.timer[i].data = 0; // Reset timer due to change in devices present if (Settings.timer[i].arm) { if (time == Settings.timer[i].time) { if (!bitRead(fired, i) && (Settings.timer[i].days & days)) { @@ -88,67 +89,74 @@ boolean TimerCommand() int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kTimerCommands); if ((CMND_TIMER == command_code) && (index > 0) && (index <= MAX_TIMERS)) { uint8_t error = 0; - if (XdrvMailbox.data_len) { - StaticJsonBuffer<128> jsonBuffer; - JsonObject& root = jsonBuffer.parseObject(dataBufUc); - if (!root.success()) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_TIMER "%d\":\"" D_JSON_INVALID_JSON "\"}"), index); // JSON decode failed - error = 1; - } - else { - char parm_uc[10]; - - index--; - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_ARM))].success()) { - Settings.timer[index].arm = (root[parm_uc] != 0); + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= MAX_TIMERS)) { + if (XdrvMailbox.payload == 0) { + Settings.timer[index -1].data = 0; // Clear timer + } else { + Settings.timer[index -1].data = Settings.timer[XdrvMailbox.payload -1].data; // Copy timer } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_TIME))].success()) { - uint16_t itime = 0; - uint8_t value = 0; - char time_str[10]; + } else { + StaticJsonBuffer<128> jsonBuffer; + JsonObject& root = jsonBuffer.parseObject(dataBufUc); + if (!root.success()) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_TIMER "%d\":\"" D_JSON_INVALID_JSON "\"}"), index); // JSON decode failed + error = 1; + } + else { + char parm_uc[10]; + index--; + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_ARM))].success()) { + Settings.timer[index].arm = (root[parm_uc] != 0); + } + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_TIME))].success()) { + uint16_t itime = 0; + uint8_t value = 0; + char time_str[10]; - snprintf(time_str, sizeof(time_str), root[parm_uc]); - const char *substr = strtok(time_str, ":"); - if (substr != NULL) { - value = atoi(substr); - if (value > 23) value = 23; - itime = value * 60; - substr = strtok(NULL, ":"); + snprintf(time_str, sizeof(time_str), root[parm_uc]); + const char *substr = strtok(time_str, ":"); if (substr != NULL) { value = atoi(substr); - if (value > 59) value = 59; - itime += value; + if (value > 23) value = 23; + itime = value * 60; + substr = strtok(NULL, ":"); + if (substr != NULL) { + value = atoi(substr); + if (value > 59) value = 59; + itime += value; + } + } + Settings.timer[index].time = itime; + } + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_DAYS))].success()) { + // SMTWTFS = 1234567 = 0011001 = 00TW00S = --TW--S + Settings.timer[index].days = 0; + const char *tday = root[parm_uc]; + char ch = '.'; + + uint8_t i = 0; + while ((ch != '\0') && (i < 7)) { + ch = *tday++; + if (ch == '-') ch = '0'; + uint8_t mask = 1 << i++; + Settings.timer[index].days |= (ch == '0') ? 0 : mask; } } - Settings.timer[index].time = itime; - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_DAYS))].success()) { - // SMTWTFS = 1234567 = 0011001 = 00TW00S = --TW--S - Settings.timer[index].days = 0; - const char *tday = root[parm_uc]; - char ch = '.'; - - uint8_t i = 0; - while ((ch != '\0') && (i < 7)) { - ch = *tday++; - if (ch == '-') ch = '0'; - uint8_t mask = 1 << i++; - Settings.timer[index].days |= (ch == '0') ? 0 : mask; + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_REPEAT))].success()) { + Settings.timer[index].repeat = (root[parm_uc] != 0); } - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_REPEAT))].success()) { - Settings.timer[index].repeat = (root[parm_uc] != 0); - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_DEVICE))].success()) { - Settings.timer[index].device = ((uint8_t)root[parm_uc] -1) & 0x0F; - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_POWER))].success()) { - Settings.timer[index].power = (uint8_t)root[parm_uc] & 0x03; - } - if (Settings.timer[index].arm) bitClear(fired, index); + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_DEVICE))].success()) { + uint8_t device = ((uint8_t)root[parm_uc] -1) & 0x0F; + Settings.timer[index].device = (device < devices_present) ? device : devices_present -1; + } + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_POWER))].success()) { + Settings.timer[index].power = (uint8_t)root[parm_uc] & 0x03; + } + if (Settings.timer[index].arm) bitClear(fired, index); - index++; + index++; + } } } if (!error) { @@ -181,6 +189,150 @@ boolean TimerCommand() return serviced; } +/*********************************************************************************************\ + * Presentation +\*********************************************************************************************/ + +#ifdef USE_WEBSERVER +#ifdef USE_TIMERS_WEB +const char HTTP_TIMER_SCRIPT[] PROGMEM = + "var pt=[],ct=99;" + "function qs(s){" // Save code space + "return document.querySelector(s);" + "}" + "function ce(i,q){" // Create select option + "var o=document.createElement('option');" + "o.textContent=i;" + "q.appendChild(o);" + "}" + "function st(){" // Save parameters to hidden area + "var d,h,i,m,n,s,p;" + "h=qs('#ho');" + "m=qs('#mi');" + "d=qs('#d1');" + "s=0;" + "n=1<<30;if(eb('a0').checked){s|=n;}" // Get arm + "n=1<<29;if(eb('r0').checked){s|=n;}" // Get repeat + "for(i=0;i<7;i++){n=1<<(16+i);if(eb('w'+i).checked){s|=n;}}" // Get weekdays + "s|=(eb('p1').value<<27);" // Get power + "s|=(d.selectedIndex<<23);" // Get device + "s|=((h.selectedIndex*60)+m.selectedIndex)&0x7FF;" // Get time + "pt[ct]=s;" + "eb('t0').value=pt.join();" // Save parameters from array to hidden area + "}" + "function ot(t,e){" + "var d,h,i,m,n,s,tl,p,q;" + "h=qs('#ho');" + "m=qs('#mi');" + "d=qs('#d1');" + "if(ct==99){" // Do this once + "pt=eb('t0').value.split(',').map(Number);" // Get parameters from hidden area to array + "for(i=0;i<=23;i++){ce((i<10)?('0'+i):i,h);}" // Create hours select options + "for(i=0;i<=59;i++){ce((i<10)?('0'+i):i,m);}" // Create minutes select options + "for(i=0;i<}1;i++){ce(i+1,d);}" // Create devices + "}else{" + "st();" // Save changes + "}" + "tl=document.getElementsByClassName('tl');" // Remove the background color of all tablinks/buttons + "for(i=0;i>(16+i))&1;eb('w'+i).checked=p;}" // Set weekdays + "p=(s>>23)&0xF;d.value=p+1;" // Set device + "p=(s>>27)&3;eb('p1').value=p;" // Set power + "p=(s>>29)&1;eb('r0').checked=p;" // Set repeat + "p=(s>>30)&1;eb('a0').checked=p;" // Set arm + "ct=t;" + "}"; +const char HTTP_TIMER_STYLE[] PROGMEM = + ".tl{float:left;border-radius:0;border:1px solid #fff;padding:1px;width:6.25%;}" + ""; +const char HTTP_FORM_TIMER[] PROGMEM = + "
 " D_TIMER_PARAMETERS " " + " " + "" D_TIMER_POWER " " + "
" + "
" +// "Time  " + "" D_TIMER_TIME "  :  " + "" D_TIMER_ARM " " + "" D_TIMER_REPEAT "" + "

" + "
"; +const char HTTP_FORM_TIMER2[] PROGMEM = + "type='submit' onclick='st();this.form.submit();'"; + +const char S_CONFIGURE_TIMER[] PROGMEM = D_CONFIGURE_TIMER; + +void HandleTimerConfiguration() +{ + if (HTTP_USER == webserver_state) { + HandleRoot(); + return; + } + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_TIMER); + + String page = FPSTR(HTTP_HEAD); + page.replace(F("{v}"), FPSTR(S_CONFIGURE_TIMER)); + page += FPSTR(HTTP_TIMER_SCRIPT); + page += FPSTR(HTTP_HEAD_STYLE); + page.replace(F(""), FPSTR(HTTP_TIMER_STYLE)); + page += FPSTR(HTTP_FORM_TIMER); + for (byte i = 0; i < MAX_TIMERS; i++) { + if (i > 0) page += F(","); + page += String(Settings.timer[i].data); + } + page += F("' hidden>
"); + for (byte i = 0; i < MAX_TIMERS; i++) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR(""), + i, (0 == i) ? " id='dP'" : "", i +1); + page += mqtt_data; + } + page += FPSTR(HTTP_FORM_TIMER1); + page.replace(F("}1"), String(devices_present)); + char day[4] = { 0 }; + for (byte i = 0; i < 7; i++) { + strncpy_P(day, PSTR(D_DAY3LIST) + (i *3), 3); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s"), i, i, day); + page += mqtt_data; + } + page += F("
"); + + page += FPSTR(HTTP_FORM_END); + page.replace(F("type='submit'"), FPSTR(HTTP_FORM_TIMER2)); + page += F(""); // Get the element with id='defaultOpen' and click on it + page += FPSTR(HTTP_BTN_CONF); + ShowPage(page); +} + +void TimerSaveSettings() +{ + char tmp[MAX_TIMERS *12]; // Need space for MAX_TIMERS x 10 digit numbers separated by a comma + + WebGetArg("t0", tmp, sizeof(tmp)); + char *p = tmp; + for (byte i = 0; i < MAX_TIMERS; i++) { + uint32_t data = strtol(p, &p, 10); + p++; // Skip comma + if ((data & 0x7FF) < 1440) Settings.timer[i].data = data; + } +} +#endif // USE_TIMERS_WEB +#endif // USE_WEBSERVER + /*********************************************************************************************\ * Interface \*********************************************************************************************/ From ae07f77ab9d1d794c88ad708f621e0b5a3501fda Mon Sep 17 00:00:00 2001 From: Theo Arends Date: Thu, 29 Mar 2018 10:13:44 +0200 Subject: [PATCH 2/7] Update Spanish language file Update Spanish language file (#2283) --- sonoff/language/es-AR.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sonoff/language/es-AR.h b/sonoff/language/es-AR.h index 5ad7d344c..bf35cc807 100644 --- a/sonoff/language/es-AR.h +++ b/sonoff/language/es-AR.h @@ -359,14 +359,14 @@ #define D_DOMOTICZ_UPDATE_TIMER "Intervalo de refresco" // xdrv_09_timers.ino -#define D_CONFIGURE_TIMER "Configure Timer" -#define D_TIMER_PARAMETERS "Timer parameters" -#define D_TIMER_ARM "Arm" -#define D_TIMER_TIME "Time" -#define D_TIMER_DAYS "Days" -#define D_TIMER_REPEAT "Repeat" -#define D_TIMER_DEVICE "Device" -#define D_TIMER_POWER "Power" +#define D_CONFIGURE_TIMER "Configuración Temporizadores" +#define D_TIMER_PARAMETERS "Parámetros de Temporizadores" +#define D_TIMER_ARM "Activo" +#define D_TIMER_TIME "Hora" +#define D_TIMER_DAYS "Días" +#define D_TIMER_REPEAT "Repetir" +#define D_TIMER_DEVICE "Salida" +#define D_TIMER_POWER "Estado" // xdrv_03_energy.ino #define D_ENERGY_TODAY "Energía Hoy" From a935f92998b0510fb8f7d310aa541447862f59f7 Mon Sep 17 00:00:00 2001 From: Theo Arends Date: Thu, 29 Mar 2018 13:03:13 +0200 Subject: [PATCH 3/7] v5.12.0i - Add HAss Button discovery 5.12.0i * Add Home Assistant MQTT Discovery for Buttons (#2277) --- sonoff/_releasenotes.ino | 1 + sonoff/settings.ino | 6 +- sonoff/sonoff.ino | 10 ++-- sonoff/user_config.h | 2 + sonoff/webserver.ino | 2 +- sonoff/xdrv_00_mqtt.ino | 4 +- sonoff/xdrv_07_home_assistant.ino | 99 +++++++++++++++++++++++++++---- 7 files changed, 102 insertions(+), 22 deletions(-) diff --git a/sonoff/_releasenotes.ino b/sonoff/_releasenotes.ino index 5583d95fc..5a9ad3773 100644 --- a/sonoff/_releasenotes.ino +++ b/sonoff/_releasenotes.ino @@ -2,6 +2,7 @@ * Add 16 timers using commands Timer and Timers (#1091) * Add commands Timer 0 to clear timer and Timer 1..16 to copy timer * Add optional Timer configuration webpage to be enabled in user_config.h with define USE_TIMERS_WEB + * Add Home Assistant MQTT Discovery for Buttons (#2277) * Change webpage parameter communication * * 5.12.0h diff --git a/sonoff/settings.ino b/sonoff/settings.ino index d17f33d35..e1b0a7dcd 100644 --- a/sonoff/settings.ino +++ b/sonoff/settings.ino @@ -494,7 +494,7 @@ void SettingsDefaultSet2() strlcpy(Settings.mqtt_user, MQTT_USER, sizeof(Settings.mqtt_user)); strlcpy(Settings.mqtt_pwd, MQTT_PASS, sizeof(Settings.mqtt_pwd)); strlcpy(Settings.mqtt_topic, MQTT_TOPIC, sizeof(Settings.mqtt_topic)); - strlcpy(Settings.button_topic, "0", sizeof(Settings.button_topic)); + strlcpy(Settings.button_topic, MQTT_BUTTON_TOPIC, sizeof(Settings.button_topic)); strlcpy(Settings.mqtt_grptopic, MQTT_GRPTOPIC, sizeof(Settings.mqtt_grptopic)); Settings.tele_period = TELE_PERIOD; @@ -544,7 +544,7 @@ void SettingsDefaultSet2() SettingsDefaultSet_3_9_3(); - strlcpy(Settings.switch_topic, "0", sizeof(Settings.switch_topic)); + strlcpy(Settings.switch_topic, MQTT_SWITCH_TOPIC, sizeof(Settings.switch_topic)); strlcpy(Settings.web_password, WEB_PASSWORD, sizeof(Settings.web_password)); @@ -744,7 +744,7 @@ void SettingsDelta() SettingsDefaultSet_3_2_4(); } if (Settings.version < 0x03020500) { // 3.2.5 - Add parameter - GetMqttClient(Settings.friendlyname[0], Settings.mqtt_client, sizeof(Settings.friendlyname[0])); + Format(Settings.friendlyname[0], Settings.mqtt_client, sizeof(Settings.friendlyname[0])); strlcpy(Settings.friendlyname[1], FRIENDLY_NAME"2", sizeof(Settings.friendlyname[1])); strlcpy(Settings.friendlyname[2], FRIENDLY_NAME"3", sizeof(Settings.friendlyname[2])); strlcpy(Settings.friendlyname[3], FRIENDLY_NAME"4", sizeof(Settings.friendlyname[3])); diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 96ade5005..47ee15b9d 100644 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -196,7 +196,7 @@ String backlog[MAX_BACKLOG]; // Command backlog /********************************************************************************************/ -char* GetMqttClient(char* output, const char* input, int size) +char* Format(char* output, const char* input, int size) { char *token; uint8_t digits = 0; @@ -1107,9 +1107,11 @@ boolean send_button_power(byte key, byte device, byte state) char stopic[TOPSZ]; char scommand[CMDSZ]; + char key_topic[sizeof(Settings.button_topic)]; boolean result = false; - char *key_topic = (key) ? Settings.switch_topic : Settings.button_topic; + char *tmp = (key) ? Settings.switch_topic : Settings.button_topic; + Format(key_topic, tmp, sizeof(key_topic)); if (Settings.flag.mqtt_enabled && MqttIsConnected() && (strlen(key_topic) != 0) && strcmp(key_topic, "0")) { if (!key && (device > devices_present)) device = 1; GetTopic_P(stopic, CMND, key_topic, GetPowerDevice(scommand, device, sizeof(scommand), key)); @@ -2328,8 +2330,8 @@ void setup() SetSerialBaudrate(baudrate); - GetMqttClient(mqtt_client, Settings.mqtt_client, sizeof(mqtt_client)); - GetMqttClient(mqtt_topic, Settings.mqtt_topic, sizeof(mqtt_topic)); + Format(mqtt_client, Settings.mqtt_client, sizeof(mqtt_client)); + Format(mqtt_topic, Settings.mqtt_topic, sizeof(mqtt_topic)); if (strstr(Settings.hostname, "%")) { strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname)); diff --git a/sonoff/user_config.h b/sonoff/user_config.h index d49373f67..178f414f6 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -126,6 +126,8 @@ // %topic% token options (also ButtonTopic and SwitchTopic) #define MQTT_TOPIC PROJECT // [Topic] (unique) MQTT device topic #define MQTT_GRPTOPIC "sonoffs" // [GroupTopic] MQTT Group topic +#define MQTT_BUTTON_TOPIC "0" // [ButtonTopic] MQTT button topic +#define MQTT_SWITCH_TOPIC "0" // [SwitchTopic] MQTT switch topic #define MQTT_CLIENT_ID "DVES_%06X" // [MqttClient] Also fall back topic using Chip Id = last 6 characters of MAC address // -- MQTT - Telemetry ---------------------------- diff --git a/sonoff/webserver.ino b/sonoff/webserver.ino index d314d09e4..45bde262f 100644 --- a/sonoff/webserver.ino +++ b/sonoff/webserver.ino @@ -879,7 +879,7 @@ void HandleMqttConfiguration() page += FPSTR(HTTP_HEAD_STYLE); page += FPSTR(HTTP_FORM_MQTT); char str[sizeof(Settings.mqtt_client)]; - page.replace(F("{m0"), GetMqttClient(str, MQTT_CLIENT_ID, sizeof(Settings.mqtt_client))); + page.replace(F("{m0"), Format(str, MQTT_CLIENT_ID, sizeof(Settings.mqtt_client))); page.replace(F("{m1"), Settings.mqtt_host); page.replace(F("{m2"), String(Settings.mqtt_port)); page.replace(F("{m3"), Settings.mqtt_client); diff --git a/sonoff/xdrv_00_mqtt.ino b/sonoff/xdrv_00_mqtt.ino index 6a87f1f25..c1f2867da 100644 --- a/sonoff/xdrv_00_mqtt.ino +++ b/sonoff/xdrv_00_mqtt.ino @@ -637,7 +637,7 @@ bool MqttCommand() if ((data_len > 0) && (data_len < sizeof(Settings.button_topic))) { MakeValidMqtt(0, dataBuf); if (!strcmp(dataBuf, mqtt_client)) payload = 1; - strlcpy(Settings.button_topic, (!strcmp(dataBuf,"0")) ? "" : (1 == payload) ? mqtt_topic : dataBuf, sizeof(Settings.button_topic)); + strlcpy(Settings.button_topic, (!strcmp(dataBuf,"0")) ? "" : (1 == payload) ? MQTT_BUTTON_TOPIC : dataBuf, sizeof(Settings.button_topic)); } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.button_topic); } @@ -645,7 +645,7 @@ bool MqttCommand() if ((data_len > 0) && (data_len < sizeof(Settings.switch_topic))) { MakeValidMqtt(0, dataBuf); if (!strcmp(dataBuf, mqtt_client)) payload = 1; - strlcpy(Settings.switch_topic, (!strcmp(dataBuf,"0")) ? "" : (1 == payload) ? mqtt_topic : dataBuf, sizeof(Settings.switch_topic)); + strlcpy(Settings.switch_topic, (!strcmp(dataBuf,"0")) ? "" : (1 == payload) ? MQTT_SWITCH_TOPIC : dataBuf, sizeof(Settings.switch_topic)); } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.switch_topic); } diff --git a/sonoff/xdrv_07_home_assistant.ino b/sonoff/xdrv_07_home_assistant.ino index eaec8686e..87a07a6f6 100644 --- a/sonoff/xdrv_07_home_assistant.ino +++ b/sonoff/xdrv_07_home_assistant.ino @@ -31,6 +31,16 @@ const char HASS_DISCOVER_SWITCH[] PROGMEM = "\"payload_available\":\"" D_ONLINE "\"," // Online "\"payload_not_available\":\"" D_OFFLINE "\""; // Offline +const char HASS_DISCOVER_BUTTON[] PROGMEM = + "{\"name\":\"%s\"," // dualr2 1 BTN + "\"state_topic\":\"%s\"," // cmnd/dualr2/POWER (implies "\"optimistic\":\"false\",") +// "\"value_template\":\"{{value_json.%s}}\"," // POWER2 + "\"payload_on\":\"%s\"," // TOGGLE +// "\"optimistic\":\"false\"," // false is Hass default when state_topic is set + "\"availability_topic\":\"%s\"," // tele/dualr2/LWT + "\"payload_available\":\"" D_ONLINE "\"," // Online + "\"payload_not_available\":\"" D_OFFLINE "\""; // Offline + const char HASS_DISCOVER_LIGHT_DIMMER[] PROGMEM = "%s,\"brightness_command_topic\":\"%s\"," // cmnd/led2/Dimmer "\"brightness_state_topic\":\"%s\"," // stat/led2/RESULT @@ -55,32 +65,26 @@ const char HASS_DISCOVER_LIGHT_SCHEME[] PROGMEM = "\"effect_value_template\":\"{{value_json." D_CMND_SCHEME "}}\"," "\"effect_list\":[\"0\",\"1\",\"2\",\"3\",\"4\"]"; // string list with reference to scheme parameter. Currently only supports numbers 0 to 11 as it make the mqtt string too long */ -void HAssDiscovery() + +void HAssDiscoverRelay() { char sidx[8]; char stopic[TOPSZ]; bool is_light = false; - // Configure Tasmota for default Home Assistant parameters to keep discovery message as short as possible - if (Settings.flag.hass_discovery) { - Settings.flag.mqtt_response = 0; // Response always as RESULT and not as uppercase command - Settings.flag.decimal_text = 1; // Respond with decimal color values -// Settings.light_scheme = 0; // To just control color it needs to be Scheme 0 -// strncpy_P(Settings.mqtt_fulltopic, PSTR("%prefix%/%topic%/"), sizeof(Settings.mqtt_fulltopic)); // Make MQTT topic as short as possible to make this process posible within MQTT_MAX_PACKET_SIZE - } - - for (int i = 1; i <= devices_present; i++) { + for (int i = 1; i <= MAX_RELAYS; i++) { is_light = ((i == devices_present) && (light_type)); - mqtt_data[0] = '\0'; + mqtt_data[0] = '\0'; // Clear retained message snprintf_P(sidx, sizeof(sidx), PSTR("_%d"), i); // Clear "other" topic first in case the device has been reconfigured snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/%s/%s%s/config"), (is_light) ? "switch" : "light", mqtt_topic, sidx); MqttPublish(stopic, true); + // Clear or Set topic snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/%s/%s%s/config"), (is_light) ? "light" : "switch", mqtt_topic, sidx); - if (Settings.flag.hass_discovery) { + if (Settings.flag.hass_discovery && (i <= devices_present)) { char name[33]; char value_template[33]; char command_topic[TOPSZ]; @@ -129,6 +133,77 @@ void HAssDiscovery() } } +void HAssDiscoverButton() +{ + char sidx[8]; + char stopic[TOPSZ]; + char key_topic[sizeof(Settings.button_topic)]; + + // Send info about buttons + char *tmp = Settings.button_topic; + Format(key_topic, tmp, sizeof(key_topic)); + if ((strlen(key_topic) != 0) && strcmp(key_topic, "0")) { + for (byte button_index = 0; button_index < MAX_KEYS; button_index++) { + uint8_t button_present = 0; + + if (!button_index && ((SONOFF_DUAL == Settings.module) || (CH4 == Settings.module))) { + button_present = 1; + } else { + if (pin[GPIO_KEY1 + button_index] < 99) { + button_present = 1; + } + } + + mqtt_data[0] = '\0'; // Clear retained message + + // Clear or Set topic + snprintf_P(sidx, sizeof(sidx), PSTR("_%d"), button_index+1); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/%s/%s%s/config"), "binary_sensor", key_topic, sidx); + + if (Settings.flag.hass_discovery && button_present) { + char name[33]; + char value_template[33]; + char state_topic[TOPSZ]; + char availability_topic[TOPSZ]; + + if (button_index+1 > MAX_FRIENDLYNAMES) { + snprintf_P(name, sizeof(name), PSTR("%s %d BTN"), Settings.friendlyname[0], button_index+1); + } else { + snprintf_P(name, sizeof(name), PSTR("%s BTN"), Settings.friendlyname[button_index]); + } + GetPowerDevice(value_template, button_index+1, sizeof(value_template)); + GetTopic_P(state_topic, CMND, key_topic, value_template); // State of button is sent as CMND TOGGLE + GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_BUTTON, name, state_topic, Settings.state_text[2], availability_topic); + + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); + } + MqttPublish(stopic, true); + } + } +} + +void HAssDiscovery() +{ + // Configure Tasmota for default Home Assistant parameters to keep discovery message as short as possible + if (Settings.flag.hass_discovery) { + Settings.flag.mqtt_response = 0; // Response always as RESULT and not as uppercase command + Settings.flag.decimal_text = 1; // Respond with decimal color values +// Settings.light_scheme = 0; // To just control color it needs to be Scheme 0 +// strncpy_P(Settings.mqtt_fulltopic, PSTR("%prefix%/%topic%/"), sizeof(Settings.mqtt_fulltopic)); // Make MQTT topic as short as possible to make this process posible within MQTT_MAX_PACKET_SIZE + } + + // Send info about relays and lights + HAssDiscoverRelay(); + + // Send info about buttons + HAssDiscoverButton(); + + // TODO: Send info about switches + + // TODO: Send info about sensors +} + /*********************************************************************************************\ * Interface \*********************************************************************************************/ From dabb4deefc1bed9748b165744dc3e3bac412d973 Mon Sep 17 00:00:00 2001 From: Theo Arends Date: Thu, 29 Mar 2018 14:01:38 +0200 Subject: [PATCH 4/7] Cleanup restart for non-HAss users Cleanup restart for non-HAss users --- sonoff/sonoff.ino | 8 +++++++- sonoff/xdrv_07_home_assistant.ino | 20 ++++++++++---------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 47ee15b9d..9703b62e8 100644 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -572,7 +572,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) switch (index) { case 3: // mqtt case 15: // pwm_control - case 19: // hass_discovery +// case 19: // hass_discovery restart_flag = 2; case 0: // save_state case 1: // button_restrict @@ -587,6 +587,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) case 16: // ws_clock_reverse case 17: // decimal_text case 18: // light_signal + case 19: // hass_discovery case 20: // not_power_linked case 21: // no_power_on_check bitWrite(Settings.flag.data, index, payload); @@ -595,6 +596,11 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) stop_flash_rotate = payload; SettingsSave(2); } +#ifdef USE_HOME_ASSISTANT + if (19 == index) { // hass_discovery + HAssDiscovery(1); + } +#endif // USE_HOME_ASSISTANT } } else { // SetOption32 .. diff --git a/sonoff/xdrv_07_home_assistant.ino b/sonoff/xdrv_07_home_assistant.ino index 87a07a6f6..3f8e001be 100644 --- a/sonoff/xdrv_07_home_assistant.ino +++ b/sonoff/xdrv_07_home_assistant.ino @@ -183,7 +183,7 @@ void HAssDiscoverButton() } } -void HAssDiscovery() +void HAssDiscovery(uint8_t mode) { // Configure Tasmota for default Home Assistant parameters to keep discovery message as short as possible if (Settings.flag.hass_discovery) { @@ -193,15 +193,15 @@ void HAssDiscovery() // strncpy_P(Settings.mqtt_fulltopic, PSTR("%prefix%/%topic%/"), sizeof(Settings.mqtt_fulltopic)); // Make MQTT topic as short as possible to make this process posible within MQTT_MAX_PACKET_SIZE } - // Send info about relays and lights - HAssDiscoverRelay(); + if (Settings.flag.hass_discovery || (1 == mode)) { + // Send info about relays and lights + HAssDiscoverRelay(); + // Send info about buttons + HAssDiscoverButton(); + // TODO: Send info about switches - // Send info about buttons - HAssDiscoverButton(); - - // TODO: Send info about switches - - // TODO: Send info about sensors + // TODO: Send info about sensors + } } /*********************************************************************************************\ @@ -217,7 +217,7 @@ boolean Xdrv07(byte function) if (Settings.flag.mqtt_enabled) { switch (function) { case FUNC_MQTT_INIT: - HAssDiscovery(); + HAssDiscovery(0); break; } } From c85edf15aca858e5395b74a66e084d4975c3ae9d Mon Sep 17 00:00:00 2001 From: Theo Arends Date: Thu, 29 Mar 2018 14:51:34 +0200 Subject: [PATCH 5/7] Change MQTT Energy response topic to SENSOR 5.12.0i * Change MQTT response topic for Energy changes from ENERGY to SENSOR (#2229, #2251) --- sonoff/_releasenotes.ino | 3 ++- sonoff/xdrv_03_energy.ino | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sonoff/_releasenotes.ino b/sonoff/_releasenotes.ino index 5a9ad3773..4b0a6d335 100644 --- a/sonoff/_releasenotes.ino +++ b/sonoff/_releasenotes.ino @@ -2,7 +2,8 @@ * Add 16 timers using commands Timer and Timers (#1091) * Add commands Timer 0 to clear timer and Timer 1..16 to copy timer * Add optional Timer configuration webpage to be enabled in user_config.h with define USE_TIMERS_WEB - * Add Home Assistant MQTT Discovery for Buttons (#2277) + * Change MQTT response topic for Energy changes from ENERGY to SENSOR (#2229, #2251) + * Add Home Assistant MQTT Discovery for Buttons and change SetOption19 response (#2277) * Change webpage parameter communication * * 5.12.0h diff --git a/sonoff/xdrv_03_energy.ino b/sonoff/xdrv_03_energy.ino index 8867ed298..a1052a077 100644 --- a/sonoff/xdrv_03_energy.ino +++ b/sonoff/xdrv_03_energy.ino @@ -778,7 +778,7 @@ void EnergyMqttShow() snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str()); EnergyShow(1); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_ENERGY), Settings.flag.mqtt_sensor_retain); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); energy_power_delta = 0; } From 9f0fdd597c5a59f5fd371973a5615f1b3a7e88d0 Mon Sep 17 00:00:00 2001 From: Theo Arends Date: Thu, 29 Mar 2018 17:21:03 +0200 Subject: [PATCH 6/7] Change Reset on Button Hold time to 40 seconds 5.12.0i * Change default Reset configuration time from 4 seconds to 40 seconds on Button hold (#2268) --- sonoff/_releasenotes.ino | 1 + sonoff/sonoff.h | 1 + sonoff/sonoff.ino | 26 +++++++++++++++++--------- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/sonoff/_releasenotes.ino b/sonoff/_releasenotes.ino index 4b0a6d335..07d30af84 100644 --- a/sonoff/_releasenotes.ino +++ b/sonoff/_releasenotes.ino @@ -5,6 +5,7 @@ * Change MQTT response topic for Energy changes from ENERGY to SENSOR (#2229, #2251) * Add Home Assistant MQTT Discovery for Buttons and change SetOption19 response (#2277) * Change webpage parameter communication + * Change default Reset configuration time from 4 seconds to 40 seconds on Button hold (#2268) * * 5.12.0h * Add optional Arduino OTA support to be enabled in user_config.h (#1998) diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index 5511856c4..61ef81adb 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -80,6 +80,7 @@ typedef unsigned long power_t; // Power (Relay) type #define MAX_POWER_RETRY 5 // Retry count allowing agreed power limit overflow #define STATES 20 // State loops per second +#define IMMINENT_RESET_FACTOR 10 // Factor to extent button hold time for imminent Reset to default 40 seconds using KEY_HOLD_TIME of 40 #define SYSLOG_TIMER 600 // Seconds to restore syslog_level #define SERIALLOG_TIMER 600 // Seconds to disable SerialLog #define OTA_ATTEMPTS 5 // Number of times to try fetching the new firmware diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 9703b62e8..90abae7b5 100644 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -31,7 +31,11 @@ #include // Arduino_Esp8266 version information (ARDUINO_ESP8266_RELEASE and ARDUINO_ESP8266_RELEASE_2_3_0) #include "sonoff.h" // Enumeration used in user_config.h #include "user_config.h" // Fixed user configurable options -#include "user_config_override.h" // Configuration overrides for user_config.h + +//#ifdef USE_CONFIG_OVERRIDE + #include "user_config_override.h" // Configuration overrides for user_config.h +//#endif + #include "i18n.h" // Language support configured by user_config.h #include "sonoff_template.h" // Hardware configuration #include "sonoff_post.h" // Configuration overrides for all previous includes @@ -572,7 +576,6 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) switch (index) { case 3: // mqtt case 15: // pwm_control -// case 19: // hass_discovery restart_flag = 2; case 0: // save_state case 1: // button_restrict @@ -1478,6 +1481,7 @@ void ButtonHandler() { uint8_t button = NOT_PRESSED; uint8_t button_present = 0; + uint8_t hold_time_extent = IMMINENT_RESET_FACTOR; // Extent hold time factor in case of iminnent Reset command char scmnd[20]; uint8_t maxdev = (devices_present > MAX_KEYS) ? MAX_KEYS : devices_present; @@ -1493,6 +1497,7 @@ void ButtonHandler() button = PRESSED; if (0xF500 == dual_button_code) { // Button hold holdbutton[button_index] = (Settings.param[P_HOLD_TIME] * (STATES / 10)) -1; + hold_time_extent = 0; } dual_button_code = 0; } @@ -1545,20 +1550,23 @@ void ButtonHandler() holdbutton[button_index] = 0; } else { holdbutton[button_index]++; - if (Settings.flag.button_single) { // Allow only single button press for immediate action - if (holdbutton[button_index] == Settings.param[P_HOLD_TIME] * (STATES / 10) * 4) { // Button hold for four times longer + if (Settings.flag.button_single) { // Allow only single button press for immediate action + if (holdbutton[button_index] == Settings.param[P_HOLD_TIME] * (STATES / 10) * hold_time_extent) { // Button held for factor times longer // Settings.flag.button_single = 0; snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_SETOPTION "13 0")); // Disable single press only ExecuteCommand(scmnd); } } else { - if (holdbutton[button_index] == Settings.param[P_HOLD_TIME] * (STATES / 10)) { // Button hold - multipress[button_index] = 0; - if (!Settings.flag.button_restrict) { // No button restriction + if (Settings.flag.button_restrict) { // Button restriction + if (holdbutton[button_index] == Settings.param[P_HOLD_TIME] * (STATES / 10)) { // Button hold + multipress[button_index] = 0; + send_button_power(0, button_index +1, 3); // Execute Hold command via MQTT if ButtonTopic is set + } + } else { + if (holdbutton[button_index] == (Settings.param[P_HOLD_TIME] * (STATES / 10)) * hold_time_extent) { // Button held for factor times longer + multipress[button_index] = 0; snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1")); ExecuteCommand(scmnd); - } else { - send_button_power(0, button_index +1, 3); // Execute Hold command via MQTT if ButtonTopic is set } } } From 71f0ab7b2c2203f24b1eabf86452a105df1cebb0 Mon Sep 17 00:00:00 2001 From: Theo Arends Date: Thu, 29 Mar 2018 17:23:47 +0200 Subject: [PATCH 7/7] Change Reset on Button Hold time to 40 seconds 5.12.0i * Change default Reset configuration time from 4 seconds to 40 seconds on Button hold (#2268) --- sonoff/sonoff.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 90abae7b5..6ef37513e 100644 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -1497,7 +1497,7 @@ void ButtonHandler() button = PRESSED; if (0xF500 == dual_button_code) { // Button hold holdbutton[button_index] = (Settings.param[P_HOLD_TIME] * (STATES / 10)) -1; - hold_time_extent = 0; + hold_time_extent = 1; } dual_button_code = 0; }
" D_DOMOTICZ_IDX " {1