mirror of https://github.com/arendst/Tasmota.git
Merge branch 'arendst/development' into development
This commit is contained in:
commit
2709aba2c5
|
@ -1,5 +1,11 @@
|
|||
/* 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 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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 "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"
|
||||
#define D_ENERGY_YESTERDAY "Energía Ayer"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 "Энергия Вчера"
|
||||
|
|
|
@ -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 "昨日用电量"
|
||||
|
|
|
@ -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 "昨日用電量"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -31,7 +31,11 @@
|
|||
#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 "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
|
||||
|
@ -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;
|
||||
uint8_t digits = 0;
|
||||
|
@ -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
|
||||
|
@ -587,6 +590,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 +599,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 ..
|
||||
|
@ -1107,9 +1116,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));
|
||||
|
@ -1470,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;
|
||||
|
@ -1485,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 = 1;
|
||||
}
|
||||
dual_button_code = 0;
|
||||
}
|
||||
|
@ -1537,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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2328,8 +2344,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));
|
||||
|
|
|
@ -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 ----------------------------
|
||||
|
@ -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 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
|
||||
|
|
|
@ -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,\"<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);"
|
||||
|
@ -110,9 +113,9 @@ const char HTTP_SCRIPT_CONSOL[] PROGMEM =
|
|||
"var c,o,t;"
|
||||
"clearTimeout(lt);"
|
||||
"o='';"
|
||||
"t=document.getElementById('t1');"
|
||||
"t=eb('t1');"
|
||||
"if(p==1){"
|
||||
"c=document.getElementById('c1');"
|
||||
"c=eb('c1');"
|
||||
"o='&c1='+encodeURIComponent(c.value);"
|
||||
"c.value='';"
|
||||
"t.scrollTop=sn;"
|
||||
|
@ -143,7 +146,7 @@ const char HTTP_SCRIPT_MODULE1[] PROGMEM =
|
|||
"var os;"
|
||||
"function sk(s,g){"
|
||||
"var o=os.replace(\"value='\"+s+\"'\",\"selected value='\"+s+\"'\");"
|
||||
"document.getElementById('g'+g).innerHTML=o;"
|
||||
"eb('g'+g).innerHTML=o;"
|
||||
"}"
|
||||
"function sl(){"
|
||||
"var o0=\"";
|
||||
|
@ -158,7 +161,7 @@ const char HTTP_SCRIPT_INFO_BEGIN[] PROGMEM =
|
|||
const char HTTP_SCRIPT_INFO_END[] PROGMEM =
|
||||
"\";" // "}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>\");"
|
||||
"document.getElementById('i').innerHTML=s;"
|
||||
"eb('i').innerHTML=s;"
|
||||
"}"
|
||||
"</script>";
|
||||
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>";
|
||||
const char HTTP_BTN_MENU2[] PROGMEM =
|
||||
"<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>";
|
||||
const char HTTP_BTN_MENU3[] PROGMEM =
|
||||
"<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>";
|
||||
const char HTTP_FORM_MODULE[] PROGMEM =
|
||||
"<fieldset><legend><b> " D_MODULE_PARAMETERS " </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/>";
|
||||
const char HTTP_LNK_ITEM[] PROGMEM =
|
||||
"<div><a href='#p' onclick='c(this)'>{v}</a> <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/>";
|
||||
const char HTTP_FORM_WIFI[] PROGMEM =
|
||||
"<fieldset><legend><b> " D_WIFI_PARAMETERS " </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_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/>"
|
||||
|
@ -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/>";
|
||||
const char HTTP_FORM_MQTT[] PROGMEM =
|
||||
"<fieldset><legend><b> " D_MQTT_PARAMETERS " </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_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/>"
|
||||
|
@ -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/>";
|
||||
const char HTTP_FORM_LOG1[] PROGMEM =
|
||||
"<fieldset><legend><b> " D_LOGGING_PARAMETERS " </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 =
|
||||
"<br/><b>{b0</b> ({b1)<br/><select id='{b2' name='{b2'>"
|
||||
"<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/>";
|
||||
const char HTTP_FORM_OTHER[] PROGMEM =
|
||||
"<fieldset><legend><b> " D_OTHER_PARAMETERS " </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/><input style='width:10%;' id='b1' name='b1' type='checkbox'{r1><b>" D_MQTT_ENABLE "</b><br/>";
|
||||
const char HTTP_FORM_OTHER2[] PROGMEM =
|
||||
|
@ -274,7 +282,7 @@ const char HTTP_FORM_UPG[] PROGMEM =
|
|||
const char HTTP_FORM_RST_UPG[] PROGMEM =
|
||||
"<form method='post' action='u2' enctype='multipart/form-data'>"
|
||||
"<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>"
|
||||
"</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("/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) {
|
||||
|
@ -866,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);
|
||||
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#ifdef USE_WEBSERVER
|
||||
const char HTTP_FORM_DOMOTICZ[] PROGMEM =
|
||||
"<fieldset><legend><b> " D_DOMOTICZ_PARAMETERS " </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>";
|
||||
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>"
|
||||
|
|
|
@ -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(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
|
||||
\*********************************************************************************************/
|
||||
|
@ -142,7 +217,7 @@ boolean Xdrv07(byte function)
|
|||
if (Settings.flag.mqtt_enabled) {
|
||||
switch (function) {
|
||||
case FUNC_MQTT_INIT:
|
||||
HAssDiscovery();
|
||||
HAssDiscovery(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<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> " D_TIMER_PARAMETERS " </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> <span><select style='width:12%;' id='d1' name='d1'></select></span> "
|
||||
"<b>" D_TIMER_POWER "</b> <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> <input type='time' style='width:25%;' id='s1' name='s1' value='00:00' pattern='[0-9]{2}:[0-9]{2}'> "
|
||||
"<b>" D_TIMER_TIME "</b> <span><select style='width:12%;' id='ho' name='ho'></select></span> : <span><select style='width:12%;' id='mi' name='mi'></select></span> "
|
||||
"<input style='width:5%;' id='a0' name='a0' type='checkbox'><b>" D_TIMER_ARM "</b> "
|
||||
"<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
|
||||
\*********************************************************************************************/
|
||||
|
|
Loading…
Reference in New Issue