Merge branch 'arendst/development' into development

This commit is contained in:
reloxx13 2018-03-30 00:45:48 +02:00
commit 2709aba2c5
25 changed files with 512 additions and 112 deletions

View File

@ -1,5 +1,11 @@
/* 5.12.0i /* 5.12.0i
* Add 16 timers using commands Timer and Timers (#1091) * 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 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 * 5.12.0h
* Add optional Arduino OTA support to be enabled in user_config.h (#1998) * Add optional Arduino OTA support to be enabled in user_config.h (#1998)

View File

@ -358,6 +358,16 @@
#define D_DOMOTICZ_AIRQUALITY "AirQuality" #define D_DOMOTICZ_AIRQUALITY "AirQuality"
#define D_DOMOTICZ_UPDATE_TIMER "Aktualizace stopek" #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 // xdrv_03_energy.ino
#define D_ENERGY_TODAY "Energie Dnes" #define D_ENERGY_TODAY "Energie Dnes"
#define D_ENERGY_YESTERDAY "Energie Včera" #define D_ENERGY_YESTERDAY "Energie Včera"

View File

@ -358,6 +358,16 @@
#define D_DOMOTICZ_AIRQUALITY "AirQuality" #define D_DOMOTICZ_AIRQUALITY "AirQuality"
#define D_DOMOTICZ_UPDATE_TIMER "Update timer" #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 // xdrv_03_energy.ino
#define D_ENERGY_TODAY "Energie heute" #define D_ENERGY_TODAY "Energie heute"
#define D_ENERGY_YESTERDAY "Energie gestern" #define D_ENERGY_YESTERDAY "Energie gestern"

View File

@ -358,6 +358,16 @@
#define D_DOMOTICZ_AIRQUALITY "AirQuality" #define D_DOMOTICZ_AIRQUALITY "AirQuality"
#define D_DOMOTICZ_UPDATE_TIMER "Update timer" #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 // xdrv_03_energy.ino
#define D_ENERGY_TODAY "Energy Today" #define D_ENERGY_TODAY "Energy Today"
#define D_ENERGY_YESTERDAY "Energy Yesterday" #define D_ENERGY_YESTERDAY "Energy Yesterday"

View File

@ -358,6 +358,16 @@
#define D_DOMOTICZ_AIRQUALITY "Calidad del Aire" #define D_DOMOTICZ_AIRQUALITY "Calidad del Aire"
#define D_DOMOTICZ_UPDATE_TIMER "Intervalo de refresco" #define D_DOMOTICZ_UPDATE_TIMER "Intervalo de refresco"
// xdrv_09_timers.ino
#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 // xdrv_03_energy.ino
#define D_ENERGY_TODAY "Energía Hoy" #define D_ENERGY_TODAY "Energía Hoy"
#define D_ENERGY_YESTERDAY "Energía Ayer" #define D_ENERGY_YESTERDAY "Energía Ayer"

View File

@ -358,6 +358,16 @@
#define D_DOMOTICZ_AIRQUALITY "AirQuality" #define D_DOMOTICZ_AIRQUALITY "AirQuality"
#define D_DOMOTICZ_UPDATE_TIMER "Update timer" #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 // xsns_03_energy.ino
#define D_ENERGY_TODAY "Energie aujourd'hui" #define D_ENERGY_TODAY "Energie aujourd'hui"
#define D_ENERGY_YESTERDAY "Energie hier" #define D_ENERGY_YESTERDAY "Energie hier"

View File

@ -358,6 +358,16 @@
#define D_DOMOTICZ_AIRQUALITY "Légminőség" #define D_DOMOTICZ_AIRQUALITY "Légminőség"
#define D_DOMOTICZ_UPDATE_TIMER "Update időzítő" #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 // xdrv_03_energy.ino
#define D_ENERGY_TODAY "Mai Energia" #define D_ENERGY_TODAY "Mai Energia"
#define D_ENERGY_YESTERDAY "Tegnapi Energia" #define D_ENERGY_YESTERDAY "Tegnapi Energia"

View File

@ -358,6 +358,16 @@
#define D_DOMOTICZ_AIRQUALITY "AirQuality" #define D_DOMOTICZ_AIRQUALITY "AirQuality"
#define D_DOMOTICZ_UPDATE_TIMER "Intervallo di aggiornamento" #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 // xdrv_03_energy.ino
#define D_ENERGY_TODAY "Energia Oggi" #define D_ENERGY_TODAY "Energia Oggi"
#define D_ENERGY_YESTERDAY "Energia Ieri" #define D_ENERGY_YESTERDAY "Energia Ieri"

View File

@ -110,7 +110,7 @@
#define D_OFF "Uit" #define D_OFF "Uit"
#define D_OFFLINE "Offline" #define D_OFFLINE "Offline"
#define D_OK "Ok" #define D_OK "Ok"
#define D_ON "Ann" #define D_ON "Aan"
#define D_ONLINE "Online" #define D_ONLINE "Online"
#define D_PASSWORD "Wachtwoord" #define D_PASSWORD "Wachtwoord"
#define D_PORT "Poort" #define D_PORT "Poort"
@ -358,6 +358,16 @@
#define D_DOMOTICZ_AIRQUALITY "AirQuality" #define D_DOMOTICZ_AIRQUALITY "AirQuality"
#define D_DOMOTICZ_UPDATE_TIMER "Bijwerk timer" #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 // xdrv_03_energy.ino
#define D_ENERGY_TODAY "Verbruik vandaag" #define D_ENERGY_TODAY "Verbruik vandaag"
#define D_ENERGY_YESTERDAY "Verbruik gisteren" #define D_ENERGY_YESTERDAY "Verbruik gisteren"

View File

@ -358,6 +358,16 @@
#define D_DOMOTICZ_AIRQUALITY "AirQuality" #define D_DOMOTICZ_AIRQUALITY "AirQuality"
#define D_DOMOTICZ_UPDATE_TIMER "Zaktualizuj czasomierz" #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 // xdrv_03_energy.ino
#define D_ENERGY_TODAY "Energia Dzisiaj" #define D_ENERGY_TODAY "Energia Dzisiaj"
#define D_ENERGY_YESTERDAY "Energia Wczoraj" #define D_ENERGY_YESTERDAY "Energia Wczoraj"

View File

@ -358,6 +358,16 @@
#define D_DOMOTICZ_AIRQUALITY "Qualidade do Ar" #define D_DOMOTICZ_AIRQUALITY "Qualidade do Ar"
#define D_DOMOTICZ_UPDATE_TIMER "Tempo de atualização" #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 // xdrv_03_energy.ino
#define D_ENERGY_TODAY "Consumo energético de hoje" #define D_ENERGY_TODAY "Consumo energético de hoje"
#define D_ENERGY_YESTERDAY "Consumo energético de ontem" #define D_ENERGY_YESTERDAY "Consumo energético de ontem"

View File

@ -358,6 +358,16 @@
#define D_DOMOTICZ_AIRQUALITY "AirQuality" #define D_DOMOTICZ_AIRQUALITY "AirQuality"
#define D_DOMOTICZ_UPDATE_TIMER "Update timer" #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 // xdrv_03_energy.ino
#define D_ENERGY_TODAY "Энергия Сегодня" #define D_ENERGY_TODAY "Энергия Сегодня"
#define D_ENERGY_YESTERDAY "Энергия Вчера" #define D_ENERGY_YESTERDAY "Энергия Вчера"

View File

@ -358,6 +358,16 @@
#define D_DOMOTICZ_AIRQUALITY "空气质量" #define D_DOMOTICZ_AIRQUALITY "空气质量"
#define D_DOMOTICZ_UPDATE_TIMER "更新计时器" #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 // xdrv_03_energy.ino
#define D_ENERGY_TODAY "今日用电量" #define D_ENERGY_TODAY "今日用电量"
#define D_ENERGY_YESTERDAY "昨日用电量" #define D_ENERGY_YESTERDAY "昨日用电量"

View File

@ -358,6 +358,16 @@
#define D_DOMOTICZ_AIRQUALITY "空氣品質" #define D_DOMOTICZ_AIRQUALITY "空氣品質"
#define D_DOMOTICZ_UPDATE_TIMER "更新計時器" #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 // xdrv_03_energy.ino
#define D_ENERGY_TODAY "今日用電量" #define D_ENERGY_TODAY "今日用電量"
#define D_ENERGY_YESTERDAY "昨日用電量" #define D_ENERGY_YESTERDAY "昨日用電量"

View File

@ -93,10 +93,10 @@ typedef union {
uint32_t data; uint32_t data;
struct { struct {
uint32_t time : 11; // bits 0 - 10 = minutes in a day 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 days : 7; // bits 16 - 22 = week day mask
uint32_t device : 4; // bits 23 - 26 = 16 devices 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 repeat : 1; // bit 29
uint32_t arm : 1; // bit 30 uint32_t arm : 1; // bit 30
uint32_t spare : 1; // bit 31 uint32_t spare : 1; // bit 31

View File

@ -494,7 +494,7 @@ void SettingsDefaultSet2()
strlcpy(Settings.mqtt_user, MQTT_USER, sizeof(Settings.mqtt_user)); strlcpy(Settings.mqtt_user, MQTT_USER, sizeof(Settings.mqtt_user));
strlcpy(Settings.mqtt_pwd, MQTT_PASS, sizeof(Settings.mqtt_pwd)); strlcpy(Settings.mqtt_pwd, MQTT_PASS, sizeof(Settings.mqtt_pwd));
strlcpy(Settings.mqtt_topic, MQTT_TOPIC, sizeof(Settings.mqtt_topic)); 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)); strlcpy(Settings.mqtt_grptopic, MQTT_GRPTOPIC, sizeof(Settings.mqtt_grptopic));
Settings.tele_period = TELE_PERIOD; Settings.tele_period = TELE_PERIOD;
@ -544,7 +544,7 @@ void SettingsDefaultSet2()
SettingsDefaultSet_3_9_3(); 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)); strlcpy(Settings.web_password, WEB_PASSWORD, sizeof(Settings.web_password));
@ -744,7 +744,7 @@ void SettingsDelta()
SettingsDefaultSet_3_2_4(); SettingsDefaultSet_3_2_4();
} }
if (Settings.version < 0x03020500) { // 3.2.5 - Add parameter 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[1], FRIENDLY_NAME"2", sizeof(Settings.friendlyname[1]));
strlcpy(Settings.friendlyname[2], FRIENDLY_NAME"3", sizeof(Settings.friendlyname[2])); strlcpy(Settings.friendlyname[2], FRIENDLY_NAME"3", sizeof(Settings.friendlyname[2]));
strlcpy(Settings.friendlyname[3], FRIENDLY_NAME"4", sizeof(Settings.friendlyname[3])); strlcpy(Settings.friendlyname[3], FRIENDLY_NAME"4", sizeof(Settings.friendlyname[3]));

View File

@ -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 MAX_POWER_RETRY 5 // Retry count allowing agreed power limit overflow
#define STATES 20 // State loops per second #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 SYSLOG_TIMER 600 // Seconds to restore syslog_level
#define SERIALLOG_TIMER 600 // Seconds to disable SerialLog #define SERIALLOG_TIMER 600 // Seconds to disable SerialLog
#define OTA_ATTEMPTS 5 // Number of times to try fetching the new firmware #define OTA_ATTEMPTS 5 // Number of times to try fetching the new firmware

View File

@ -31,7 +31,11 @@
#include <core_version.h> // Arduino_Esp8266 version information (ARDUINO_ESP8266_RELEASE and ARDUINO_ESP8266_RELEASE_2_3_0) #include <core_version.h> // Arduino_Esp8266 version information (ARDUINO_ESP8266_RELEASE and ARDUINO_ESP8266_RELEASE_2_3_0)
#include "sonoff.h" // Enumeration used in user_config.h #include "sonoff.h" // Enumeration used in user_config.h
#include "user_config.h" // Fixed user configurable options #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 "i18n.h" // Language support configured by user_config.h
#include "sonoff_template.h" // Hardware configuration #include "sonoff_template.h" // Hardware configuration
#include "sonoff_post.h" // Configuration overrides for all previous includes #include "sonoff_post.h" // Configuration overrides for all previous includes
@ -196,7 +200,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; char *token;
uint8_t digits = 0; uint8_t digits = 0;
@ -572,7 +576,6 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
switch (index) { switch (index) {
case 3: // mqtt case 3: // mqtt
case 15: // pwm_control case 15: // pwm_control
case 19: // hass_discovery
restart_flag = 2; restart_flag = 2;
case 0: // save_state case 0: // save_state
case 1: // button_restrict case 1: // button_restrict
@ -587,6 +590,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
case 16: // ws_clock_reverse case 16: // ws_clock_reverse
case 17: // decimal_text case 17: // decimal_text
case 18: // light_signal case 18: // light_signal
case 19: // hass_discovery
case 20: // not_power_linked case 20: // not_power_linked
case 21: // no_power_on_check case 21: // no_power_on_check
bitWrite(Settings.flag.data, index, payload); bitWrite(Settings.flag.data, index, payload);
@ -595,6 +599,11 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
stop_flash_rotate = payload; stop_flash_rotate = payload;
SettingsSave(2); SettingsSave(2);
} }
#ifdef USE_HOME_ASSISTANT
if (19 == index) { // hass_discovery
HAssDiscovery(1);
}
#endif // USE_HOME_ASSISTANT
} }
} }
else { // SetOption32 .. else { // SetOption32 ..
@ -1107,9 +1116,11 @@ boolean send_button_power(byte key, byte device, byte state)
char stopic[TOPSZ]; char stopic[TOPSZ];
char scommand[CMDSZ]; char scommand[CMDSZ];
char key_topic[sizeof(Settings.button_topic)];
boolean result = false; 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 (Settings.flag.mqtt_enabled && MqttIsConnected() && (strlen(key_topic) != 0) && strcmp(key_topic, "0")) {
if (!key && (device > devices_present)) device = 1; if (!key && (device > devices_present)) device = 1;
GetTopic_P(stopic, CMND, key_topic, GetPowerDevice(scommand, device, sizeof(scommand), key)); GetTopic_P(stopic, CMND, key_topic, GetPowerDevice(scommand, device, sizeof(scommand), key));
@ -1470,6 +1481,7 @@ void ButtonHandler()
{ {
uint8_t button = NOT_PRESSED; uint8_t button = NOT_PRESSED;
uint8_t button_present = 0; 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]; char scmnd[20];
uint8_t maxdev = (devices_present > MAX_KEYS) ? MAX_KEYS : devices_present; uint8_t maxdev = (devices_present > MAX_KEYS) ? MAX_KEYS : devices_present;
@ -1485,6 +1497,7 @@ void ButtonHandler()
button = PRESSED; button = PRESSED;
if (0xF500 == dual_button_code) { // Button hold if (0xF500 == dual_button_code) { // Button hold
holdbutton[button_index] = (Settings.param[P_HOLD_TIME] * (STATES / 10)) -1; holdbutton[button_index] = (Settings.param[P_HOLD_TIME] * (STATES / 10)) -1;
hold_time_extent = 1;
} }
dual_button_code = 0; dual_button_code = 0;
} }
@ -1538,19 +1551,22 @@ void ButtonHandler()
} else { } else {
holdbutton[button_index]++; holdbutton[button_index]++;
if (Settings.flag.button_single) { // Allow only single button press for immediate action 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 (holdbutton[button_index] == Settings.param[P_HOLD_TIME] * (STATES / 10) * hold_time_extent) { // Button held for factor times longer
// Settings.flag.button_single = 0; // Settings.flag.button_single = 0;
snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_SETOPTION "13 0")); // Disable single press only snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_SETOPTION "13 0")); // Disable single press only
ExecuteCommand(scmnd); ExecuteCommand(scmnd);
} }
} else { } else {
if (Settings.flag.button_restrict) { // Button restriction
if (holdbutton[button_index] == Settings.param[P_HOLD_TIME] * (STATES / 10)) { // Button hold if (holdbutton[button_index] == Settings.param[P_HOLD_TIME] * (STATES / 10)) { // Button hold
multipress[button_index] = 0; multipress[button_index] = 0;
if (!Settings.flag.button_restrict) { // No button restriction 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")); snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1"));
ExecuteCommand(scmnd); ExecuteCommand(scmnd);
} else {
send_button_power(0, button_index +1, 3); // Execute Hold command via MQTT if ButtonTopic is set
} }
} }
} }
@ -2328,8 +2344,8 @@ void setup()
SetSerialBaudrate(baudrate); SetSerialBaudrate(baudrate);
GetMqttClient(mqtt_client, Settings.mqtt_client, sizeof(mqtt_client)); Format(mqtt_client, Settings.mqtt_client, sizeof(mqtt_client));
GetMqttClient(mqtt_topic, Settings.mqtt_topic, sizeof(mqtt_topic)); Format(mqtt_topic, Settings.mqtt_topic, sizeof(mqtt_topic));
if (strstr(Settings.hostname, "%")) { if (strstr(Settings.hostname, "%")) {
strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname)); strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname));

View File

@ -126,6 +126,8 @@
// %topic% token options (also ButtonTopic and SwitchTopic) // %topic% token options (also ButtonTopic and SwitchTopic)
#define MQTT_TOPIC PROJECT // [Topic] (unique) MQTT device topic #define MQTT_TOPIC PROJECT // [Topic] (unique) MQTT device topic
#define MQTT_GRPTOPIC "sonoffs" // [GroupTopic] MQTT Group 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 #define MQTT_CLIENT_ID "DVES_%06X" // [MqttClient] Also fall back topic using Chip Id = last 6 characters of MAC address
// -- MQTT - Telemetry ---------------------------- // -- MQTT - Telemetry ----------------------------
@ -163,6 +165,7 @@
#define NTP_SERVER3 "0.nl.pool.ntp.org" // [NtpServer3] Select third NTP server by name or IP address (93.94.224.67) #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 // 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 // -- 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 #define TIME_DST North, Last, Sun, Mar, 2, +120 // Northern Hemisphere, Last sunday in march at 02:00 +120 minutes

View File

@ -39,16 +39,19 @@ const char HTTP_HEAD[] PROGMEM =
"var cn,x,lt;" "var cn,x,lt;"
"cn=180;" "cn=180;"
"x=null;" // Allow for abortion "x=null;" // Allow for abortion
"function eb(s){"
"return document.getElementById(s);" // Save code space
"}"
"function u(){" "function u(){"
"if(cn>=0){" "if(cn>=0){"
"document.getElementById('t').innerHTML='" D_RESTART_IN " '+cn+' " D_SECONDS "';" "eb('t').innerHTML='" D_RESTART_IN " '+cn+' " D_SECONDS "';"
"cn--;" "cn--;"
"setTimeout(u,1000);" "setTimeout(u,1000);"
"}" "}"
"}" "}"
"function c(l){" "function c(l){"
"document.getElementById('s1').value=l.innerText||l.textContent;" "eb('s1').value=l.innerText||l.textContent;"
"document.getElementById('p1').focus();" "eb('p1').focus();"
"}" "}"
"function la(p){" "function la(p){"
"var a='';" "var a='';"
@ -61,7 +64,7 @@ const char HTTP_HEAD[] PROGMEM =
"x.onreadystatechange=function(){" "x.onreadystatechange=function(){"
"if(x.readyState==4&&x.status==200){" "if(x.readyState==4&&x.status==200){"
"var s=x.responseText.replace(/{t}/g,\"<table style='width:100%'>\").replace(/{s}/g,\"<tr><th>\").replace(/{m}/g,\"</th><td>\").replace(/{e}/g,\"</td></tr>\").replace(/{c}/g,\"%'><div style='text-align:center;font-weight:\");" "var s=x.responseText.replace(/{t}/g,\"<table style='width:100%'>\").replace(/{s}/g,\"<tr><th>\").replace(/{m}/g,\"</th><td>\").replace(/{e}/g,\"</td></tr>\").replace(/{c}/g,\"%'><div style='text-align:center;font-weight:\");"
"document.getElementById('l1').innerHTML=s;" "eb('l1').innerHTML=s;"
"}" "}"
"};" "};"
"x.open('GET','ay'+a,true);" "x.open('GET','ay'+a,true);"
@ -110,9 +113,9 @@ const char HTTP_SCRIPT_CONSOL[] PROGMEM =
"var c,o,t;" "var c,o,t;"
"clearTimeout(lt);" "clearTimeout(lt);"
"o='';" "o='';"
"t=document.getElementById('t1');" "t=eb('t1');"
"if(p==1){" "if(p==1){"
"c=document.getElementById('c1');" "c=eb('c1');"
"o='&c1='+encodeURIComponent(c.value);" "o='&c1='+encodeURIComponent(c.value);"
"c.value='';" "c.value='';"
"t.scrollTop=sn;" "t.scrollTop=sn;"
@ -143,7 +146,7 @@ const char HTTP_SCRIPT_MODULE1[] PROGMEM =
"var os;" "var os;"
"function sk(s,g){" "function sk(s,g){"
"var o=os.replace(\"value='\"+s+\"'\",\"selected value='\"+s+\"'\");" "var o=os.replace(\"value='\"+s+\"'\",\"selected value='\"+s+\"'\");"
"document.getElementById('g'+g).innerHTML=o;" "eb('g'+g).innerHTML=o;"
"}" "}"
"function sl(){" "function sl(){"
"var o0=\""; "var o0=\"";
@ -158,7 +161,7 @@ const char HTTP_SCRIPT_INFO_BEGIN[] PROGMEM =
const char HTTP_SCRIPT_INFO_END[] PROGMEM = const char HTTP_SCRIPT_INFO_END[] PROGMEM =
"\";" // "}1" and "}2" means do not use "}x" in Information text "\";" // "}1" and "}2" means do not use "}x" in Information text
"s=o.replace(/}1/g,\"</td></tr><tr><th>\").replace(/}2/g,\"</th><td>\");" "s=o.replace(/}1/g,\"</td></tr><tr><th>\").replace(/}2/g,\"</th><td>\");"
"document.getElementById('i').innerHTML=s;" "eb('i').innerHTML=s;"
"}" "}"
"</script>"; "</script>";
const char HTTP_MSG_SLIDER1[] PROGMEM = const char HTTP_MSG_SLIDER1[] PROGMEM =
@ -180,6 +183,11 @@ const char HTTP_BTN_RSTRT[] PROGMEM =
"<br/><form action='rb' method='get' onsubmit='return confirm(\"" D_CONFIRM_RESTART "\");'><button>" D_RESTART "</button></form>"; "<br/><form action='rb' method='get' onsubmit='return confirm(\"" D_CONFIRM_RESTART "\");'><button>" D_RESTART "</button></form>";
const char HTTP_BTN_MENU2[] PROGMEM = const char HTTP_BTN_MENU2[] PROGMEM =
"<br/><form action='md' method='get'><button>" D_CONFIGURE_MODULE "</button></form>" "<br/><form action='md' method='get'><button>" D_CONFIGURE_MODULE "</button></form>"
#ifdef USE_TIMERS
#ifdef USE_TIMERS_WEB
"<br/><form action='tm' method='get'><button>" D_CONFIGURE_TIMER "</button></form>"
#endif // USE_TIMERS_WEB
#endif // USE_TIMERS
"<br/><form action='w0' method='get'><button>" D_CONFIGURE_WIFI "</button></form>"; "<br/><form action='w0' method='get'><button>" D_CONFIGURE_WIFI "</button></form>";
const char HTTP_BTN_MENU3[] PROGMEM = const char HTTP_BTN_MENU3[] PROGMEM =
"<br/><form action='mq' method='get'><button>" D_CONFIGURE_MQTT "</button></form>" "<br/><form action='mq' method='get'><button>" D_CONFIGURE_MQTT "</button></form>"
@ -206,7 +214,7 @@ const char HTTP_BTN_CONF[] PROGMEM =
"<br/><br/><form action='cn' method='get'><button>" D_CONFIGURATION "</button></form>"; "<br/><br/><form action='cn' method='get'><button>" D_CONFIGURATION "</button></form>";
const char HTTP_FORM_MODULE[] PROGMEM = const char HTTP_FORM_MODULE[] PROGMEM =
"<fieldset><legend><b>&nbsp;" D_MODULE_PARAMETERS "&nbsp;</b></legend><form method='get' action='sv'>" "<fieldset><legend><b>&nbsp;" D_MODULE_PARAMETERS "&nbsp;</b></legend><form method='get' action='sv'>"
"<input id='w' name='w' value='6' hidden><input id='r' name='r' value='1' hidden>" "<input id='w' name='w' value='6,1' hidden>"
"<br/><b>" D_MODULE_TYPE "</b> ({mt)<br/><select id='g99' name='g99'></select><br/>"; "<br/><b>" D_MODULE_TYPE "</b> ({mt)<br/><select id='g99' name='g99'></select><br/>";
const char HTTP_LNK_ITEM[] PROGMEM = const char HTTP_LNK_ITEM[] PROGMEM =
"<div><a href='#p' onclick='c(this)'>{v}</a>&nbsp;<span class='q'>{i} {r}%</span></div>"; "<div><a href='#p' onclick='c(this)'>{v}</a>&nbsp;<span class='q'>{i} {r}%</span></div>";
@ -214,7 +222,7 @@ const char HTTP_LNK_SCAN[] PROGMEM =
"<div><a href='/w1'>" D_SCAN_FOR_WIFI_NETWORKS "</a></div><br/>"; "<div><a href='/w1'>" D_SCAN_FOR_WIFI_NETWORKS "</a></div><br/>";
const char HTTP_FORM_WIFI[] PROGMEM = const char HTTP_FORM_WIFI[] PROGMEM =
"<fieldset><legend><b>&nbsp;" D_WIFI_PARAMETERS "&nbsp;</b></legend><form method='get' action='sv'>" "<fieldset><legend><b>&nbsp;" D_WIFI_PARAMETERS "&nbsp;</b></legend><form method='get' action='sv'>"
"<input id='w' name='w' value='1' hidden><input id='r' name='r' value='1' hidden>" "<input id='w' name='w' value='1,1' hidden>"
"<br/><b>" D_AP1_SSID "</b> (" STA_SSID1 ")<br/><input id='s1' name='s1' placeholder='" STA_SSID1 "' value='{s1'><br/>" "<br/><b>" D_AP1_SSID "</b> (" STA_SSID1 ")<br/><input id='s1' name='s1' placeholder='" STA_SSID1 "' value='{s1'><br/>"
"<br/><b>" D_AP1_PASSWORD "</b><br/><input id='p1' name='p1' type='password' placeholder='" D_AP1_PASSWORD "' value='********'><br/>" "<br/><b>" D_AP1_PASSWORD "</b><br/><input id='p1' name='p1' type='password' placeholder='" D_AP1_PASSWORD "' value='********'><br/>"
"<br/><b>" D_AP2_SSID "</b> (" STA_SSID2 ")<br/><input id='s2' name='s2' placeholder='" STA_SSID2 "' value='{s2'><br/>" "<br/><b>" D_AP2_SSID "</b> (" STA_SSID2 ")<br/><input id='s2' name='s2' placeholder='" STA_SSID2 "' value='{s2'><br/>"
@ -222,7 +230,7 @@ const char HTTP_FORM_WIFI[] PROGMEM =
"<br/><b>" D_HOSTNAME "</b> (" WIFI_HOSTNAME ")<br/><input id='h' name='h' placeholder='" WIFI_HOSTNAME" ' value='{h1'><br/>"; "<br/><b>" D_HOSTNAME "</b> (" WIFI_HOSTNAME ")<br/><input id='h' name='h' placeholder='" WIFI_HOSTNAME" ' value='{h1'><br/>";
const char HTTP_FORM_MQTT[] PROGMEM = const char HTTP_FORM_MQTT[] PROGMEM =
"<fieldset><legend><b>&nbsp;" D_MQTT_PARAMETERS "&nbsp;</b></legend><form method='get' action='sv'>" "<fieldset><legend><b>&nbsp;" D_MQTT_PARAMETERS "&nbsp;</b></legend><form method='get' action='sv'>"
"<input id='w' name='w' value='2' hidden><input id='r' name='r' value='1' hidden>" "<input id='w' name='w' value='2,1' hidden>"
"<br/><b>" D_HOST "</b> (" MQTT_HOST ")<br/><input id='mh' name='mh' placeholder='" MQTT_HOST" ' value='{m1'><br/>" "<br/><b>" D_HOST "</b> (" MQTT_HOST ")<br/><input id='mh' name='mh' placeholder='" MQTT_HOST" ' value='{m1'><br/>"
"<br/><b>" D_PORT "</b> (" STR(MQTT_PORT) ")<br/><input id='ml' name='ml' placeholder='" STR(MQTT_PORT) "' value='{m2'><br/>" "<br/><b>" D_PORT "</b> (" STR(MQTT_PORT) ")<br/><input id='ml' name='ml' placeholder='" STR(MQTT_PORT) "' value='{m2'><br/>"
"<br/><b>" D_CLIENT "</b> ({m0)<br/><input id='mc' name='mc' placeholder='" MQTT_CLIENT_ID "' value='{m3'><br/>" "<br/><b>" D_CLIENT "</b> ({m0)<br/><input id='mc' name='mc' placeholder='" MQTT_CLIENT_ID "' value='{m3'><br/>"
@ -232,7 +240,7 @@ const char HTTP_FORM_MQTT[] PROGMEM =
"<br/><b>" D_FULL_TOPIC "</b> (" MQTT_FULLTOPIC ")<br/><input id='mf' name='mf' placeholder='" MQTT_FULLTOPIC" ' value='{m7'><br/>"; "<br/><b>" D_FULL_TOPIC "</b> (" MQTT_FULLTOPIC ")<br/><input id='mf' name='mf' placeholder='" MQTT_FULLTOPIC" ' value='{m7'><br/>";
const char HTTP_FORM_LOG1[] PROGMEM = const char HTTP_FORM_LOG1[] PROGMEM =
"<fieldset><legend><b>&nbsp;" D_LOGGING_PARAMETERS "&nbsp;</b></legend><form method='get' action='sv'>" "<fieldset><legend><b>&nbsp;" D_LOGGING_PARAMETERS "&nbsp;</b></legend><form method='get' action='sv'>"
"<input id='w' name='w' value='3' hidden><input id='r' name='r' value='0' hidden>"; "<input id='w' name='w' value='3,0' hidden>";
const char HTTP_FORM_LOG2[] PROGMEM = const char HTTP_FORM_LOG2[] PROGMEM =
"<br/><b>{b0</b> ({b1)<br/><select id='{b2' name='{b2'>" "<br/><b>{b0</b> ({b1)<br/><select id='{b2' name='{b2'>"
"<option{a0value='0'>0 " D_NONE "</option>" "<option{a0value='0'>0 " D_NONE "</option>"
@ -247,7 +255,7 @@ const char HTTP_FORM_LOG3[] PROGMEM =
"<br/><b>" D_TELEMETRY_PERIOD "</b> (" STR(TELE_PERIOD) ")<br/><input id='lt' name='lt' placeholder='" STR(TELE_PERIOD) "' value='{l4'><br/>"; "<br/><b>" D_TELEMETRY_PERIOD "</b> (" STR(TELE_PERIOD) ")<br/><input id='lt' name='lt' placeholder='" STR(TELE_PERIOD) "' value='{l4'><br/>";
const char HTTP_FORM_OTHER[] PROGMEM = const char HTTP_FORM_OTHER[] PROGMEM =
"<fieldset><legend><b>&nbsp;" D_OTHER_PARAMETERS "&nbsp;</b></legend><form method='get' action='sv'>" "<fieldset><legend><b>&nbsp;" D_OTHER_PARAMETERS "&nbsp;</b></legend><form method='get' action='sv'>"
"<input id='w' name='w' value='5' hidden><input id='r' name='r' value='1' hidden>" "<input id='w' name='w' value='5,1' hidden>"
"<br/><b>" D_WEB_ADMIN_PASSWORD "</b><br/><input id='p1' name='p1' type='password' placeholder='" D_WEB_ADMIN_PASSWORD "' value='********'><br/>" "<br/><b>" D_WEB_ADMIN_PASSWORD "</b><br/><input id='p1' name='p1' type='password' placeholder='" D_WEB_ADMIN_PASSWORD "' value='********'><br/>"
"<br/><input style='width:10%;' id='b1' name='b1' type='checkbox'{r1><b>" D_MQTT_ENABLE "</b><br/>"; "<br/><input style='width:10%;' id='b1' name='b1' type='checkbox'{r1><b>" D_MQTT_ENABLE "</b><br/>";
const char HTTP_FORM_OTHER2[] PROGMEM = const char HTTP_FORM_OTHER2[] PROGMEM =
@ -274,7 +282,7 @@ const char HTTP_FORM_UPG[] PROGMEM =
const char HTTP_FORM_RST_UPG[] PROGMEM = const char HTTP_FORM_RST_UPG[] PROGMEM =
"<form method='post' action='u2' enctype='multipart/form-data'>" "<form method='post' action='u2' enctype='multipart/form-data'>"
"<br/><input type='file' name='u2'><br/>" "<br/><input type='file' name='u2'><br/>"
"<br/><button type='submit' onclick='document.getElementById(\"f1\").style.display=\"none\";document.getElementById(\"f2\").style.display=\"block\";this.form.submit();'>" D_START " {r1</button></form>" "<br/><button type='submit' onclick='eb(\"f1\").style.display=\"none\";eb(\"f2\").style.display=\"block\";this.form.submit();'>" D_START " {r1</button></form>"
"</fieldset>" "</fieldset>"
"</div>" "</div>"
"<div id='f2' name='f2' style='display:none;text-align:center;'><b>" D_UPLOAD_STARTED " ...</b></div>"; "<div id='f2' name='f2' style='display:none;text-align:center;'><b>" D_UPLOAD_STARTED " ...</b></div>";
@ -330,6 +338,11 @@ void StartWebserver(int type, IPAddress ipweb)
WebServer->on("/", HandleRoot); WebServer->on("/", HandleRoot);
WebServer->on("/cn", HandleConfiguration); WebServer->on("/cn", HandleConfiguration);
WebServer->on("/md", HandleModuleConfiguration); 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("/w1", HandleWifiConfigurationWithScan);
WebServer->on("/w0", HandleWifiConfiguration); WebServer->on("/w0", HandleWifiConfiguration);
if (Settings.flag.mqtt_enabled) { if (Settings.flag.mqtt_enabled) {
@ -866,7 +879,7 @@ void HandleMqttConfiguration()
page += FPSTR(HTTP_HEAD_STYLE); page += FPSTR(HTTP_HEAD_STYLE);
page += FPSTR(HTTP_FORM_MQTT); page += FPSTR(HTTP_FORM_MQTT);
char str[sizeof(Settings.mqtt_client)]; 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("{m1"), Settings.mqtt_host);
page.replace(F("{m2"), String(Settings.mqtt_port)); page.replace(F("{m2"), String(Settings.mqtt_port));
page.replace(F("{m3"), Settings.mqtt_client); page.replace(F("{m3"), Settings.mqtt_client);
@ -1001,17 +1014,16 @@ void HandleSaveSettings()
char stemp[TOPSZ]; char stemp[TOPSZ];
char stemp2[TOPSZ]; char stemp2[TOPSZ];
byte what = 0;
byte restart;
String result = ""; String result = "";
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_SAVE_CONFIGURATION); AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_SAVE_CONFIGURATION);
char tmp[100]; char tmp[100];
WebGetArg("w", tmp, sizeof(tmp)); WebGetArg("w", tmp, sizeof(tmp)); // Returns "5,1" where 5 is config type and 1 is restart flag
if (strlen(tmp)) { char *p = tmp;
what = atoi(tmp); uint8_t what = strtol(p, &p, 10);
} p++; // Skip comma
uint8_t restart = strtol(p, &p, 10);
switch (what) { switch (what) {
case 1: case 1:
WebGetArg("h", tmp, sizeof(tmp)); 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); Settings.seriallog_level, Settings.weblog_level, Settings.syslog_level, Settings.syslog_host, Settings.syslog_port, Settings.tele_period);
AddLog(LOG_LEVEL_INFO); AddLog(LOG_LEVEL_INFO);
break; break;
#ifdef USE_TIMERS
#ifdef USE_TIMERS_WEB
case 7:
TimerSaveSettings();
break;
#endif // USE_TIMERS_WEB
#endif // USE_TIMERS
#ifdef USE_DOMOTICZ #ifdef USE_DOMOTICZ
case 4: case 4:
DomoticzSaveSettings(); DomoticzSaveSettings();
@ -1136,8 +1155,6 @@ void HandleSaveSettings()
break; break;
} }
WebGetArg("r", tmp, sizeof(tmp));
restart = (!strlen(tmp)) ? 1 : atoi(tmp);
if (restart) { if (restart) {
String page = FPSTR(HTTP_HEAD); String page = FPSTR(HTTP_HEAD);
page.replace(F("{v}"), FPSTR(S_SAVE_CONFIGURATION)); page.replace(F("{v}"), FPSTR(S_SAVE_CONFIGURATION));

View File

@ -637,7 +637,7 @@ bool MqttCommand()
if ((data_len > 0) && (data_len < sizeof(Settings.button_topic))) { if ((data_len > 0) && (data_len < sizeof(Settings.button_topic))) {
MakeValidMqtt(0, dataBuf); MakeValidMqtt(0, dataBuf);
if (!strcmp(dataBuf, mqtt_client)) payload = 1; 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); 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))) { if ((data_len > 0) && (data_len < sizeof(Settings.switch_topic))) {
MakeValidMqtt(0, dataBuf); MakeValidMqtt(0, dataBuf);
if (!strcmp(dataBuf, mqtt_client)) payload = 1; 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); snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.switch_topic);
} }

View File

@ -778,7 +778,7 @@ void EnergyMqttShow()
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str()); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str());
EnergyShow(1); EnergyShow(1);
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); 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; energy_power_delta = 0;
} }

View File

@ -22,7 +22,7 @@
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
const char HTTP_FORM_DOMOTICZ[] PROGMEM = const char HTTP_FORM_DOMOTICZ[] PROGMEM =
"<fieldset><legend><b>&nbsp;" D_DOMOTICZ_PARAMETERS "&nbsp;</b></legend><form method='post' action='sv'>" "<fieldset><legend><b>&nbsp;" D_DOMOTICZ_PARAMETERS "&nbsp;</b></legend><form method='post' action='sv'>"
"<input id='w' name='w' value='4' hidden><input id='r' name='r' value='1' hidden>" "<input id='w' name='w' value='4,1' hidden>"
"<br/><table>"; "<br/><table>";
const char HTTP_FORM_DOMOTICZ_RELAY[] PROGMEM = const char HTTP_FORM_DOMOTICZ_RELAY[] PROGMEM =
"<tr><td style='width:260px'><b>" D_DOMOTICZ_IDX " {1</b></td><td style='width:70px'><input id='r{1' name='r{1' placeholder='0' value='{2'></td></tr>" "<tr><td style='width:260px'><b>" D_DOMOTICZ_IDX " {1</b></td><td style='width:70px'><input id='r{1' name='r{1' placeholder='0' value='{2'></td></tr>"

View File

@ -31,6 +31,16 @@ const char HASS_DISCOVER_SWITCH[] PROGMEM =
"\"payload_available\":\"" D_ONLINE "\"," // Online "\"payload_available\":\"" D_ONLINE "\"," // Online
"\"payload_not_available\":\"" D_OFFLINE "\""; // Offline "\"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 = const char HASS_DISCOVER_LIGHT_DIMMER[] PROGMEM =
"%s,\"brightness_command_topic\":\"%s\"," // cmnd/led2/Dimmer "%s,\"brightness_command_topic\":\"%s\"," // cmnd/led2/Dimmer
"\"brightness_state_topic\":\"%s\"," // stat/led2/RESULT "\"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_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 "\"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 sidx[8];
char stopic[TOPSZ]; char stopic[TOPSZ];
bool is_light = false; bool is_light = false;
// Configure Tasmota for default Home Assistant parameters to keep discovery message as short as possible for (int i = 1; i <= MAX_RELAYS; i++) {
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++) {
is_light = ((i == devices_present) && (light_type)); 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); snprintf_P(sidx, sizeof(sidx), PSTR("_%d"), i);
// Clear "other" topic first in case the device has been reconfigured // 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); snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/%s/%s%s/config"), (is_light) ? "switch" : "light", mqtt_topic, sidx);
MqttPublish(stopic, true); 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); 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 name[33];
char value_template[33]; char value_template[33];
char command_topic[TOPSZ]; 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(uint8_t mode)
{
// 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
}
if (Settings.flag.hass_discovery || (1 == mode)) {
// Send info about relays and lights
HAssDiscoverRelay();
// Send info about buttons
HAssDiscoverButton();
// TODO: Send info about switches
// TODO: Send info about sensors
}
}
/*********************************************************************************************\ /*********************************************************************************************\
* Interface * Interface
\*********************************************************************************************/ \*********************************************************************************************/
@ -142,7 +217,7 @@ boolean Xdrv07(byte function)
if (Settings.flag.mqtt_enabled) { if (Settings.flag.mqtt_enabled) {
switch (function) { switch (function) {
case FUNC_MQTT_INIT: case FUNC_MQTT_INIT:
HAssDiscovery(); HAssDiscovery(0);
break; break;
} }
} }

View File

@ -36,7 +36,7 @@
enum TimerCommands { CMND_TIMER, CMND_TIMERS }; enum TimerCommands { CMND_TIMER, CMND_TIMERS };
const char kTimerCommands[] PROGMEM = D_CMND_TIMER "|" D_CMND_TIMERS ; const char kTimerCommands[] PROGMEM = D_CMND_TIMER "|" D_CMND_TIMERS ;
power_t fired = 0; uint16_t fired = 0;
void TimerEverySecond() void TimerEverySecond()
{ {
@ -45,6 +45,7 @@ void TimerEverySecond()
uint8_t days = 1 << (RtcTime.day_of_week -1); uint8_t days = 1 << (RtcTime.day_of_week -1);
for (byte i = 0; i < MAX_TIMERS; i++) { 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 (Settings.timer[i].arm) {
if (time == Settings.timer[i].time) { if (time == Settings.timer[i].time) {
if (!bitRead(fired, i) && (Settings.timer[i].days & days)) { if (!bitRead(fired, i) && (Settings.timer[i].days & days)) {
@ -88,8 +89,14 @@ boolean TimerCommand()
int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kTimerCommands); int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kTimerCommands);
if ((CMND_TIMER == command_code) && (index > 0) && (index <= MAX_TIMERS)) { if ((CMND_TIMER == command_code) && (index > 0) && (index <= MAX_TIMERS)) {
uint8_t error = 0; uint8_t error = 0;
if (XdrvMailbox.data_len) { if (XdrvMailbox.data_len) {
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
}
} else {
StaticJsonBuffer<128> jsonBuffer; StaticJsonBuffer<128> jsonBuffer;
JsonObject& root = jsonBuffer.parseObject(dataBufUc); JsonObject& root = jsonBuffer.parseObject(dataBufUc);
if (!root.success()) { if (!root.success()) {
@ -98,7 +105,6 @@ boolean TimerCommand()
} }
else { else {
char parm_uc[10]; char parm_uc[10];
index--; index--;
if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_ARM))].success()) { if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_ARM))].success()) {
Settings.timer[index].arm = (root[parm_uc] != 0); Settings.timer[index].arm = (root[parm_uc] != 0);
@ -141,7 +147,8 @@ boolean TimerCommand()
Settings.timer[index].repeat = (root[parm_uc] != 0); Settings.timer[index].repeat = (root[parm_uc] != 0);
} }
if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_DEVICE))].success()) { if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_DEVICE))].success()) {
Settings.timer[index].device = ((uint8_t)root[parm_uc] -1) & 0x0F; 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()) { if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_POWER))].success()) {
Settings.timer[index].power = (uint8_t)root[parm_uc] & 0x03; Settings.timer[index].power = (uint8_t)root[parm_uc] & 0x03;
@ -151,6 +158,7 @@ boolean TimerCommand()
index++; index++;
} }
} }
}
if (!error) { if (!error) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{")); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{"));
PrepShowTimer(index); PrepShowTimer(index);
@ -181,6 +189,150 @@ boolean TimerCommand()
return serviced; 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<tl.length;i++){tl[i].style.cssText=\"background-color:#ccc;color:#fff;font-weight:normal;\"}"
// Add the specific color to the button used to open the tab content
"e.style.cssText=\"background-color:#fff;color:#000;font-weight:bold;\";"
"s=pt[t];" // Get parameters from array
"p=s&0x7FF;" // Get time
"q=Math.floor(p/60);if(q<10){q='0'+q;}h.value=q;" // Set hours
"q=p%60;if(q<10){q='0'+q;}m.value=q;" // Set minutes
"for(i=0;i<7;i++){p=(s>>(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%;}"
"</style>";
const char HTTP_FORM_TIMER[] PROGMEM =
"<fieldset style='min-width:470px;text-align:center;'><legend style='text-align:left;'><b>&nbsp;" D_TIMER_PARAMETERS "&nbsp;</b></legend><form method='post' action='sv'>"
"<input id='w' name='w' value='7,0' hidden><input id='t0' name='t0' value='";
const char HTTP_FORM_TIMER1[] PROGMEM =
"</div><br/><br/><br/>"
"<div>"
"<b>" D_TIMER_DEVICE "</b>&nbsp;<span><select style='width:12%;' id='d1' name='d1'></select></span>&emsp;"
"<b>" D_TIMER_POWER "</b>&nbsp;<select style='width:25%;' id='p1' name='p1'>"
"<option value='0'>" D_OFF "</option>"
"<option value='1'>" D_ON "</option>"
"<option value='2'>" D_TOGGLE "</option>"
"<option value='3'>" D_BLINK "</option>"
"</select>"
"</div><br/>"
"<div>"
// "<b>Time</b>&nbsp;<input type='time' style='width:25%;' id='s1' name='s1' value='00:00' pattern='[0-9]{2}:[0-9]{2}'>&emsp;"
"<b>" D_TIMER_TIME "</b>&nbsp;<span><select style='width:12%;' id='ho' name='ho'></select></span>&nbsp;:&nbsp;<span><select style='width:12%;' id='mi' name='mi'></select></span>&emsp;"
"<input style='width:5%;' id='a0' name='a0' type='checkbox'><b>" D_TIMER_ARM "</b>&emsp;"
"<input style='width:5%;' id='r0' name='r0' type='checkbox'><b>" D_TIMER_REPEAT "</b>"
"</div><br/>"
"<div>";
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("</style>"), 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><div>");
for (byte i = 0; i < MAX_TIMERS; i++) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("<button type='button' class='tl' onclick=\"ot(%d,this)\"%s>%d</button>"),
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("<input style='width:5%%;' id='w%d' name='w%d' type='checkbox'><b>%s</b>"), i, i, day);
page += mqtt_data;
}
page += F("</div>");
page += FPSTR(HTTP_FORM_END);
page.replace(F("type='submit'"), FPSTR(HTTP_FORM_TIMER2));
page += F("<script>eb('dP').click();</script>"); // 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 * Interface
\*********************************************************************************************/ \*********************************************************************************************/