Tasmota/tasmota/support_command.ino

1647 lines
62 KiB
Arduino
Raw Normal View History

/*
support_command.ino - command support for Tasmota
Copyright (C) 2019 Theo Arends
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
const char kTasmotaCommands[] PROGMEM = "|" // No prefix
D_CMND_BACKLOG "|" D_CMND_DELAY "|" D_CMND_POWER "|" D_CMND_STATUS "|" D_CMND_STATE "|" D_CMND_SLEEP "|" D_CMND_UPGRADE "|" D_CMND_UPLOAD "|" D_CMND_OTAURL "|"
D_CMND_SERIALLOG "|" D_CMND_RESTART "|" D_CMND_POWERONSTATE "|" D_CMND_PULSETIME "|" D_CMND_BLINKTIME "|" D_CMND_BLINKCOUNT "|" D_CMND_SAVEDATA "|"
D_CMND_SETOPTION "|" D_CMND_TEMPERATURE_RESOLUTION "|" D_CMND_HUMIDITY_RESOLUTION "|" D_CMND_PRESSURE_RESOLUTION "|" D_CMND_POWER_RESOLUTION "|"
D_CMND_VOLTAGE_RESOLUTION "|" D_CMND_FREQUENCY_RESOLUTION "|" D_CMND_CURRENT_RESOLUTION "|" D_CMND_ENERGY_RESOLUTION "|" D_CMND_WEIGHT_RESOLUTION "|"
D_CMND_MODULE "|" D_CMND_MODULES "|" D_CMND_GPIO "|" D_CMND_GPIOS "|" D_CMND_TEMPLATE "|" D_CMND_PWM "|" D_CMND_PWMFREQUENCY "|" D_CMND_PWMRANGE "|"
D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SYSLOG "|" D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALCONFIG "|"
D_CMND_SERIALDELIMITER "|" D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|"
D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_INTERLOCK "|" D_CMND_TELEPERIOD "|" D_CMND_RESET "|" D_CMND_TIME "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|"
D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" D_CMND_WIFIPOWER "|" D_CMND_TEMPOFFSET "|"
#ifdef USE_I2C
D_CMND_I2CSCAN "|" D_CMND_I2CDRIVER "|"
#endif
D_CMND_SENSOR "|" D_CMND_DRIVER ;
void (* const TasmotaCommand[])(void) PROGMEM = {
&CmndBacklog, &CmndDelay, &CmndPower, &CmndStatus, &CmndState, &CmndSleep, &CmndUpgrade, &CmndUpgrade, &CmndOtaUrl,
&CmndSeriallog, &CmndRestart, &CmndPowerOnState, &CmndPulsetime, &CmndBlinktime, &CmndBlinkcount, &CmndSavedata,
&CmndSetoption, &CmndTemperatureResolution, &CmndHumidityResolution, &CmndPressureResolution, &CmndPowerResolution,
&CmndVoltageResolution, &CmndFrequencyResolution, &CmndCurrentResolution, &CmndEnergyResolution, &CmndWeightResolution,
&CmndModule, &CmndModules, &CmndGpio, &CmndGpios, &CmndTemplate, &CmndPwm, &CmndPwmfrequency, &CmndPwmrange,
&CmndButtonDebounce, &CmndSwitchDebounce, &CmndSyslog, &CmndLoghost, &CmndLogport, &CmndSerialSend, &CmndBaudrate, &CmndSerialConfig,
&CmndSerialDelimiter, &CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig,
&CmndFriendlyname, &CmndSwitchMode, &CmndInterlock, &CmndTeleperiod, &CmndReset, &CmndTime, &CmndTimezone, &CmndTimeStd,
&CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndWifiPower, &CmndTempOffset,
#ifdef USE_I2C
&CmndI2cScan, CmndI2cDriver,
#endif
&CmndSensor, &CmndDriver};
2019-10-22 16:29:21 +01:00
const char kWifiConfig[] PROGMEM =
D_WCFG_0_RESTART "||" D_WCFG_2_WIFIMANAGER "||" D_WCFG_4_RETRY "|" D_WCFG_5_WAIT "|" D_WCFG_6_SERIAL "|" D_WCFG_7_WIFIMANAGER_RESET_ONLY;
/********************************************************************************************/
2019-08-03 12:01:34 +01:00
void ResponseCmndNumber(int value)
{
Response_P(S_JSON_COMMAND_NVALUE, XdrvMailbox.command, value);
}
void ResponseCmndFloat(float value, uint32_t decimals)
{
char stemp1[TOPSZ];
dtostrfd(value, decimals, stemp1);
Response_P(S_JSON_COMMAND_XVALUE, XdrvMailbox.command, stemp1); // Return float value without quotes
}
2019-08-03 12:01:34 +01:00
void ResponseCmndIdxNumber(int value)
{
Response_P(S_JSON_COMMAND_INDEX_NVALUE, XdrvMailbox.command, XdrvMailbox.index, value);
}
void ResponseCmndChar(const char* value)
{
Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, value);
}
void ResponseCmndStateText(uint32_t value)
{
ResponseCmndChar(GetStateText(value));
}
void ResponseCmndDone(void)
{
ResponseCmndChar(D_JSON_DONE);
}
void ResponseCmndIdxChar(const char* value)
{
Response_P(S_JSON_COMMAND_INDEX_SVALUE, XdrvMailbox.command, XdrvMailbox.index, value);
}
/********************************************************************************************/
void ExecuteCommand(const char *cmnd, uint32_t source)
{
// cmnd: "status 0" = stopic "status" and svalue " 0"
// cmnd: "var1 =1" = stopic "var1" and svalue " =1"
// cmnd: "var1=1" = stopic "var1" and svalue "=1"
#ifdef USE_DEBUG_DRIVER
ShowFreeMem(PSTR("ExecuteCommand"));
#endif
ShowSource(source);
const char *pos = cmnd;
while (*pos && isspace(*pos)) {
pos++; // Skip all spaces
}
const char *start = pos;
// Get a command. Commands can only use letters, digits and underscores
while (*pos && (isalpha(*pos) || isdigit(*pos) || '_' == *pos || '/' == *pos)) {
2019-11-03 14:37:33 +00:00
if ('/' == *pos) { // Skip possible cmnd/tasmota/ preamble
start = pos + 1;
}
pos++;
}
if ('\0' == *start || pos <= start) {
return; // Did not find any command to execute
}
uint32_t size = pos - start;
char stopic[size + 2]; // with leader '/' and end '\0'
stopic[0] = '/';
memcpy(stopic+1, start, size);
stopic[size+1] = '\0';
char svalue[strlen(pos) +1]; // pos point to the start of parameters
strlcpy(svalue, pos, sizeof(svalue));
CommandHandler(stopic, svalue, strlen(svalue));
}
/********************************************************************************************/
// topicBuf: /power1 dataBuf: toggle = Console command
2019-11-03 14:37:33 +00:00
// topicBuf: cmnd/tasmota/power1 dataBuf: toggle = Mqtt command using topic
// topicBuf: cmnd/tasmotas/power1 dataBuf: toggle = Mqtt command using a group topic
// topicBuf: cmnd/DVES_83BB10_fb/power1 dataBuf: toggle = Mqtt command using fallback topic
2019-07-24 12:09:42 +01:00
void CommandHandler(char* topicBuf, char* dataBuf, uint32_t data_len)
{
#ifdef USE_DEBUG_DRIVER
ShowFreeMem(PSTR("CommandHandler"));
#endif
while (*dataBuf && isspace(*dataBuf)) {
dataBuf++; // Skip leading spaces in data
data_len--;
}
bool grpflg = (strstr(topicBuf, Settings.mqtt_grptopic) != nullptr);
char stemp1[TOPSZ];
GetFallbackTopic_P(stemp1, ""); // Full Fallback topic = cmnd/DVES_xxxxxxxx_fb/
fallback_topic_flag = (!strncmp(topicBuf, stemp1, strlen(stemp1)));
char *type = strrchr(topicBuf, '/'); // Last part of received topic is always the command (type)
uint32_t index = 1;
bool user_index = false;
if (type != nullptr) {
type++;
2019-10-30 13:08:43 +00:00
uint32_t i;
for (i = 0; i < strlen(type); i++) {
type[i] = toupper(type[i]);
}
while (isdigit(type[i-1])) {
i--;
}
if (i < strlen(type)) {
index = atoi(type +i);
user_index = true;
}
type[i] = '\0';
}
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CMD: " D_GROUP " %d, " D_INDEX " %d, " D_COMMAND " \"%s\", " D_DATA " \"%s\""), grpflg, index, type, dataBuf);
if (type != nullptr) {
Response_P(PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_ERROR "\"}"));
if (Settings.ledstate &0x02) { blinks++; }
if (!strcmp(dataBuf,"?")) { data_len = 0; }
char *p;
int32_t payload = strtol(dataBuf, &p, 0); // decimal, octal (0) or hex (0x)
if (p == dataBuf) { payload = -99; }
int temp_payload = GetStateNumber(dataBuf);
if (temp_payload > -1) { payload = temp_payload; }
DEBUG_CORE_LOG(PSTR("CMD: Payload %d"), payload);
// backlog_delay = millis() + (100 * MIN_BACKLOG_DELAY);
backlog_delay = millis() + Settings.param[P_BACKLOG_DELAY];
char command[CMDSZ];
XdrvMailbox.command = command;
XdrvMailbox.index = index;
XdrvMailbox.data_len = data_len;
XdrvMailbox.payload = payload;
XdrvMailbox.grpflg = grpflg;
XdrvMailbox.usridx = user_index;
XdrvMailbox.topic = type;
XdrvMailbox.data = dataBuf;
2019-09-24 07:51:09 +01:00
#ifdef USE_SCRIPT_SUB_COMMAND
// allow overwrite tasmota cmds
2019-09-30 09:25:02 +01:00
if (!Script_SubCmd()) {
2019-09-24 07:51:09 +01:00
if (!DecodeCommand(kTasmotaCommands, TasmotaCommand)) {
2019-09-30 09:25:02 +01:00
if (!XdrvCall(FUNC_COMMAND)) {
if (!XsnsCall(FUNC_COMMAND)) {
2019-09-24 07:51:09 +01:00
type = nullptr; // Unknown command
2019-09-30 09:25:02 +01:00
}
2019-09-24 07:51:09 +01:00
}
}
}
2019-09-30 09:25:02 +01:00
#else //USE_SCRIPT_SUB_COMMAND
if (!DecodeCommand(kTasmotaCommands, TasmotaCommand)) {
if (!XdrvCall(FUNC_COMMAND)) {
if (!XsnsCall(FUNC_COMMAND)) {
type = nullptr; // Unknown command
}
}
}
2019-09-30 09:25:02 +01:00
#endif //USE_SCRIPT_SUB_COMMAND
2019-09-24 07:51:09 +01:00
}
if (type == nullptr) {
blinks = 201;
snprintf_P(topicBuf, sizeof(topicBuf), PSTR(D_JSON_COMMAND));
Response_P(PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_UNKNOWN "\"}"));
type = (char*)topicBuf;
}
if (mqtt_data[0] != '\0') {
MqttPublishPrefixTopic_P(RESULT_OR_STAT, type);
XdrvRulesProcess();
}
fallback_topic_flag = false;
}
/********************************************************************************************/
void CmndBacklog(void)
{
if (XdrvMailbox.data_len) {
Support If in Rules The major features of IF statement are: - Support IF, ELSEIF, ELSE - Support not only comparison but also using logical expression as condition - Support run multiple commands - Support nested IF statement - All the commands run by IF statement will go to the BACKLOG! - No limit for logical operators, parenthesis and nested IF statement. Depends on your RAM! Extended Backus-Naur Form of IF statement: <if-statement> ::= IF "(" <logical-expression> ")" <statement-list> {ELSEIF "(" <logical-expression> ")" <statement-list>} [ELSE <statement-list>] ENDIF <logical-expression> := <comparison-expression> | (<comparison-expression> | <logical-expression>) {(AND | OR) <logical-expression>} | "(" <logical-expression ")" {(AND | OR) <logical expression>} <comparison-expression> ::= <math-expression> ("=" | "<" | ">" | "|" | "==" | "<=" | ">=" | "!=") <math-expression> <statement-list> ::= <statement> {";" <statement>} <statement> ::= <Sonoff-Tasmota-command> | <if-statement> In English: If statement support 3 format: 1. IF (<condition>) <statement-list> ENDIF 2. IF (<condition>) <statement-list> ELSE <statement-list> ENDIF 3. IF (<condition>) <statement-list> [ELSEIF (<condition>) <statement-list> ]* ELSE <statement-list> ENDIF <condition> is a logical expression which can be: 1. A comparison expression for example: VAR1 >= 10 2. Multiple comparison expression with logical operator "AND" or "OR" between them. "AND" has higher priority than "OR". Fox example: UPTIME > 100 AND MEM1 == 1 OR MEM2 == 1 3. Parenthesis can be used to change the priority of logical expression. For example: UPTIME > 100 AND (MEM1 == 1 OR MEM2 == 1) <statement-list> can be: 1. A Sonoff-Tasmota command. For example: ledpower on 2. A IF statement ("IF .... ENDIF") 3. Multiple Sonoff-Tasmota command or IF statement split with ";". For example: Power1 off; Ledpower on; if (mem1 == 0) Var1 Var1+1; mem1==1 endif; Delay 10; POWER1 on 4. Do not need to lead with "BACKLOG" for multiple commands.
2019-09-09 16:24:27 +01:00
#ifdef SUPPORT_IF_STATEMENT
char *blcommand = strtok(XdrvMailbox.data, ";");
while ((blcommand != nullptr) && (backlog.size() < MAX_BACKLOG))
#else
uint32_t bl_pointer = (!backlog_pointer) ? MAX_BACKLOG -1 : backlog_pointer;
bl_pointer--;
char *blcommand = strtok(XdrvMailbox.data, ";");
Support If in Rules The major features of IF statement are: - Support IF, ELSEIF, ELSE - Support not only comparison but also using logical expression as condition - Support run multiple commands - Support nested IF statement - All the commands run by IF statement will go to the BACKLOG! - No limit for logical operators, parenthesis and nested IF statement. Depends on your RAM! Extended Backus-Naur Form of IF statement: <if-statement> ::= IF "(" <logical-expression> ")" <statement-list> {ELSEIF "(" <logical-expression> ")" <statement-list>} [ELSE <statement-list>] ENDIF <logical-expression> := <comparison-expression> | (<comparison-expression> | <logical-expression>) {(AND | OR) <logical-expression>} | "(" <logical-expression ")" {(AND | OR) <logical expression>} <comparison-expression> ::= <math-expression> ("=" | "<" | ">" | "|" | "==" | "<=" | ">=" | "!=") <math-expression> <statement-list> ::= <statement> {";" <statement>} <statement> ::= <Sonoff-Tasmota-command> | <if-statement> In English: If statement support 3 format: 1. IF (<condition>) <statement-list> ENDIF 2. IF (<condition>) <statement-list> ELSE <statement-list> ENDIF 3. IF (<condition>) <statement-list> [ELSEIF (<condition>) <statement-list> ]* ELSE <statement-list> ENDIF <condition> is a logical expression which can be: 1. A comparison expression for example: VAR1 >= 10 2. Multiple comparison expression with logical operator "AND" or "OR" between them. "AND" has higher priority than "OR". Fox example: UPTIME > 100 AND MEM1 == 1 OR MEM2 == 1 3. Parenthesis can be used to change the priority of logical expression. For example: UPTIME > 100 AND (MEM1 == 1 OR MEM2 == 1) <statement-list> can be: 1. A Sonoff-Tasmota command. For example: ledpower on 2. A IF statement ("IF .... ENDIF") 3. Multiple Sonoff-Tasmota command or IF statement split with ";". For example: Power1 off; Ledpower on; if (mem1 == 0) Var1 Var1+1; mem1==1 endif; Delay 10; POWER1 on 4. Do not need to lead with "BACKLOG" for multiple commands.
2019-09-09 16:24:27 +01:00
while ((blcommand != nullptr) && (backlog_index != bl_pointer))
#endif
{
while(true) {
blcommand = Trim(blcommand);
if (!strncasecmp_P(blcommand, PSTR(D_CMND_BACKLOG), strlen(D_CMND_BACKLOG))) {
blcommand += strlen(D_CMND_BACKLOG); // Skip unnecessary command Backlog
} else {
break;
}
}
if (*blcommand != '\0') {
Support If in Rules The major features of IF statement are: - Support IF, ELSEIF, ELSE - Support not only comparison but also using logical expression as condition - Support run multiple commands - Support nested IF statement - All the commands run by IF statement will go to the BACKLOG! - No limit for logical operators, parenthesis and nested IF statement. Depends on your RAM! Extended Backus-Naur Form of IF statement: <if-statement> ::= IF "(" <logical-expression> ")" <statement-list> {ELSEIF "(" <logical-expression> ")" <statement-list>} [ELSE <statement-list>] ENDIF <logical-expression> := <comparison-expression> | (<comparison-expression> | <logical-expression>) {(AND | OR) <logical-expression>} | "(" <logical-expression ")" {(AND | OR) <logical expression>} <comparison-expression> ::= <math-expression> ("=" | "<" | ">" | "|" | "==" | "<=" | ">=" | "!=") <math-expression> <statement-list> ::= <statement> {";" <statement>} <statement> ::= <Sonoff-Tasmota-command> | <if-statement> In English: If statement support 3 format: 1. IF (<condition>) <statement-list> ENDIF 2. IF (<condition>) <statement-list> ELSE <statement-list> ENDIF 3. IF (<condition>) <statement-list> [ELSEIF (<condition>) <statement-list> ]* ELSE <statement-list> ENDIF <condition> is a logical expression which can be: 1. A comparison expression for example: VAR1 >= 10 2. Multiple comparison expression with logical operator "AND" or "OR" between them. "AND" has higher priority than "OR". Fox example: UPTIME > 100 AND MEM1 == 1 OR MEM2 == 1 3. Parenthesis can be used to change the priority of logical expression. For example: UPTIME > 100 AND (MEM1 == 1 OR MEM2 == 1) <statement-list> can be: 1. A Sonoff-Tasmota command. For example: ledpower on 2. A IF statement ("IF .... ENDIF") 3. Multiple Sonoff-Tasmota command or IF statement split with ";". For example: Power1 off; Ledpower on; if (mem1 == 0) Var1 Var1+1; mem1==1 endif; Delay 10; POWER1 on 4. Do not need to lead with "BACKLOG" for multiple commands.
2019-09-09 16:24:27 +01:00
#ifdef SUPPORT_IF_STATEMENT
if (backlog.size() < MAX_BACKLOG) {
backlog.add(blcommand);
}
#else
backlog[backlog_index] = String(blcommand);
backlog_index++;
if (backlog_index >= MAX_BACKLOG) backlog_index = 0;
Support If in Rules The major features of IF statement are: - Support IF, ELSEIF, ELSE - Support not only comparison but also using logical expression as condition - Support run multiple commands - Support nested IF statement - All the commands run by IF statement will go to the BACKLOG! - No limit for logical operators, parenthesis and nested IF statement. Depends on your RAM! Extended Backus-Naur Form of IF statement: <if-statement> ::= IF "(" <logical-expression> ")" <statement-list> {ELSEIF "(" <logical-expression> ")" <statement-list>} [ELSE <statement-list>] ENDIF <logical-expression> := <comparison-expression> | (<comparison-expression> | <logical-expression>) {(AND | OR) <logical-expression>} | "(" <logical-expression ")" {(AND | OR) <logical expression>} <comparison-expression> ::= <math-expression> ("=" | "<" | ">" | "|" | "==" | "<=" | ">=" | "!=") <math-expression> <statement-list> ::= <statement> {";" <statement>} <statement> ::= <Sonoff-Tasmota-command> | <if-statement> In English: If statement support 3 format: 1. IF (<condition>) <statement-list> ENDIF 2. IF (<condition>) <statement-list> ELSE <statement-list> ENDIF 3. IF (<condition>) <statement-list> [ELSEIF (<condition>) <statement-list> ]* ELSE <statement-list> ENDIF <condition> is a logical expression which can be: 1. A comparison expression for example: VAR1 >= 10 2. Multiple comparison expression with logical operator "AND" or "OR" between them. "AND" has higher priority than "OR". Fox example: UPTIME > 100 AND MEM1 == 1 OR MEM2 == 1 3. Parenthesis can be used to change the priority of logical expression. For example: UPTIME > 100 AND (MEM1 == 1 OR MEM2 == 1) <statement-list> can be: 1. A Sonoff-Tasmota command. For example: ledpower on 2. A IF statement ("IF .... ENDIF") 3. Multiple Sonoff-Tasmota command or IF statement split with ";". For example: Power1 off; Ledpower on; if (mem1 == 0) Var1 Var1+1; mem1==1 endif; Delay 10; POWER1 on 4. Do not need to lead with "BACKLOG" for multiple commands.
2019-09-09 16:24:27 +01:00
#endif
}
blcommand = strtok(nullptr, ";");
}
2019-08-03 12:01:34 +01:00
// ResponseCmndChar(D_JSON_APPENDED);
mqtt_data[0] = '\0';
} else {
Support If in Rules The major features of IF statement are: - Support IF, ELSEIF, ELSE - Support not only comparison but also using logical expression as condition - Support run multiple commands - Support nested IF statement - All the commands run by IF statement will go to the BACKLOG! - No limit for logical operators, parenthesis and nested IF statement. Depends on your RAM! Extended Backus-Naur Form of IF statement: <if-statement> ::= IF "(" <logical-expression> ")" <statement-list> {ELSEIF "(" <logical-expression> ")" <statement-list>} [ELSE <statement-list>] ENDIF <logical-expression> := <comparison-expression> | (<comparison-expression> | <logical-expression>) {(AND | OR) <logical-expression>} | "(" <logical-expression ")" {(AND | OR) <logical expression>} <comparison-expression> ::= <math-expression> ("=" | "<" | ">" | "|" | "==" | "<=" | ">=" | "!=") <math-expression> <statement-list> ::= <statement> {";" <statement>} <statement> ::= <Sonoff-Tasmota-command> | <if-statement> In English: If statement support 3 format: 1. IF (<condition>) <statement-list> ENDIF 2. IF (<condition>) <statement-list> ELSE <statement-list> ENDIF 3. IF (<condition>) <statement-list> [ELSEIF (<condition>) <statement-list> ]* ELSE <statement-list> ENDIF <condition> is a logical expression which can be: 1. A comparison expression for example: VAR1 >= 10 2. Multiple comparison expression with logical operator "AND" or "OR" between them. "AND" has higher priority than "OR". Fox example: UPTIME > 100 AND MEM1 == 1 OR MEM2 == 1 3. Parenthesis can be used to change the priority of logical expression. For example: UPTIME > 100 AND (MEM1 == 1 OR MEM2 == 1) <statement-list> can be: 1. A Sonoff-Tasmota command. For example: ledpower on 2. A IF statement ("IF .... ENDIF") 3. Multiple Sonoff-Tasmota command or IF statement split with ";". For example: Power1 off; Ledpower on; if (mem1 == 0) Var1 Var1+1; mem1==1 endif; Delay 10; POWER1 on 4. Do not need to lead with "BACKLOG" for multiple commands.
2019-09-09 16:24:27 +01:00
bool blflag = BACKLOG_EMPTY;
#ifdef SUPPORT_IF_STATEMENT
backlog.clear();
#else
backlog_pointer = backlog_index;
Support If in Rules The major features of IF statement are: - Support IF, ELSEIF, ELSE - Support not only comparison but also using logical expression as condition - Support run multiple commands - Support nested IF statement - All the commands run by IF statement will go to the BACKLOG! - No limit for logical operators, parenthesis and nested IF statement. Depends on your RAM! Extended Backus-Naur Form of IF statement: <if-statement> ::= IF "(" <logical-expression> ")" <statement-list> {ELSEIF "(" <logical-expression> ")" <statement-list>} [ELSE <statement-list>] ENDIF <logical-expression> := <comparison-expression> | (<comparison-expression> | <logical-expression>) {(AND | OR) <logical-expression>} | "(" <logical-expression ")" {(AND | OR) <logical expression>} <comparison-expression> ::= <math-expression> ("=" | "<" | ">" | "|" | "==" | "<=" | ">=" | "!=") <math-expression> <statement-list> ::= <statement> {";" <statement>} <statement> ::= <Sonoff-Tasmota-command> | <if-statement> In English: If statement support 3 format: 1. IF (<condition>) <statement-list> ENDIF 2. IF (<condition>) <statement-list> ELSE <statement-list> ENDIF 3. IF (<condition>) <statement-list> [ELSEIF (<condition>) <statement-list> ]* ELSE <statement-list> ENDIF <condition> is a logical expression which can be: 1. A comparison expression for example: VAR1 >= 10 2. Multiple comparison expression with logical operator "AND" or "OR" between them. "AND" has higher priority than "OR". Fox example: UPTIME > 100 AND MEM1 == 1 OR MEM2 == 1 3. Parenthesis can be used to change the priority of logical expression. For example: UPTIME > 100 AND (MEM1 == 1 OR MEM2 == 1) <statement-list> can be: 1. A Sonoff-Tasmota command. For example: ledpower on 2. A IF statement ("IF .... ENDIF") 3. Multiple Sonoff-Tasmota command or IF statement split with ";". For example: Power1 off; Ledpower on; if (mem1 == 0) Var1 Var1+1; mem1==1 endif; Delay 10; POWER1 on 4. Do not need to lead with "BACKLOG" for multiple commands.
2019-09-09 16:24:27 +01:00
#endif
2019-08-03 12:01:34 +01:00
ResponseCmndChar(blflag ? D_JSON_EMPTY : D_JSON_ABORTED);
}
}
void CmndDelay(void)
{
if ((XdrvMailbox.payload >= (MIN_BACKLOG_DELAY / 100)) && (XdrvMailbox.payload <= 3600)) {
backlog_delay = millis() + (100 * XdrvMailbox.payload);
}
uint32_t bl_delay = 0;
long bl_delta = TimePassedSince(backlog_delay);
if (bl_delta < 0) { bl_delay = (bl_delta *-1) / 100; }
2019-08-03 12:01:34 +01:00
ResponseCmndNumber(bl_delay);
}
void CmndPower(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= devices_present)) {
if ((XdrvMailbox.payload < POWER_OFF) || (XdrvMailbox.payload > POWER_BLINK_STOP)) {
XdrvMailbox.payload = POWER_SHOW_STATE;
}
2019-11-03 11:33:36 +00:00
// Settings.flag.device_index_enable = XdrvMailbox.usridx; // SetOption26 - Switch between POWER or POWER1
ExecuteCommandPower(XdrvMailbox.index, XdrvMailbox.payload, SRC_IGNORE);
mqtt_data[0] = '\0';
}
else if (0 == XdrvMailbox.index) {
if ((XdrvMailbox.payload < POWER_OFF) || (XdrvMailbox.payload > POWER_TOGGLE)) {
XdrvMailbox.payload = POWER_SHOW_STATE;
}
SetAllPower(XdrvMailbox.payload, SRC_IGNORE);
mqtt_data[0] = '\0';
}
}
void CmndStatus(void)
{
uint32_t payload = ((XdrvMailbox.payload < 0) || (XdrvMailbox.payload > MAX_STATUS)) ? 99 : XdrvMailbox.payload;
uint32_t option = STAT;
char stemp[MAX_FRIENDLYNAMES * (sizeof(Settings.friendlyname[0]) +MAX_FRIENDLYNAMES)];
char stemp2[100];
// Workaround MQTT - TCP/IP stack queueing when SUB_PREFIX = PUB_PREFIX
if (!strcmp(Settings.mqtt_prefix[0],Settings.mqtt_prefix[1]) && (!payload)) { option++; } // TELE
2019-11-03 11:33:36 +00:00
if ((!Settings.flag.mqtt_enabled) && (6 == payload)) { payload = 99; } // SetOption3 - Enable MQTT
if (!energy_flg && (9 == payload)) { payload = 99; }
if ((0 == payload) || (99 == payload)) {
uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present;
#ifdef USE_SONOFF_IFAN
if (IsModuleIfan()) { maxfn = 1; }
#endif // USE_SONOFF_IFAN
stemp[0] = '\0';
for (uint32_t i = 0; i < maxfn; i++) {
snprintf_P(stemp, sizeof(stemp), PSTR("%s%s\"%s\"" ), stemp, (i > 0 ? "," : ""), Settings.friendlyname[i]);
}
stemp2[0] = '\0';
for (uint32_t i = 0; i < MAX_SWITCHES; i++) {
snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%d" ), stemp2, (i > 0 ? "," : ""), Settings.switchmode[i]);
}
Response_P(PSTR("{\"" D_CMND_STATUS "\":{\"" D_CMND_MODULE "\":%d,\"" D_CMND_FRIENDLYNAME "\":[%s],\"" D_CMND_TOPIC "\":\"%s\",\""
D_CMND_BUTTONTOPIC "\":\"%s\",\"" D_CMND_POWER "\":%d,\"" D_CMND_POWERONSTATE "\":%d,\"" D_CMND_LEDSTATE "\":%d,\""
D_CMND_LEDMASK "\":\"%04X\",\"" D_CMND_SAVEDATA "\":%d,\"" D_JSON_SAVESTATE "\":%d,\"" D_CMND_SWITCHTOPIC "\":\"%s\",\""
D_CMND_SWITCHMODE "\":[%s],\"" D_CMND_BUTTONRETAIN "\":%d,\"" D_CMND_SWITCHRETAIN "\":%d,\"" D_CMND_SENSORRETAIN "\":%d,\"" D_CMND_POWERRETAIN "\":%d}}"),
ModuleNr(), stemp, mqtt_topic,
Settings.button_topic, power, Settings.poweronstate, Settings.ledstate,
2019-11-03 11:33:36 +00:00
Settings.ledmask, Settings.save_data,
Settings.flag.save_state, // SetOption0 - Save power state and use after restart
Settings.switch_topic,
stemp2,
Settings.flag.mqtt_button_retain, // CMND_BUTTONRETAIN
Settings.flag.mqtt_switch_retain, // CMND_SWITCHRETAIN
Settings.flag.mqtt_sensor_retain, // CMND_SENSORRETAIN
Settings.flag.mqtt_power_retain); // CMND_POWERRETAIN
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS));
}
if ((0 == payload) || (1 == payload)) {
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS1_PARAMETER "\":{\"" D_JSON_BAUDRATE "\":%d,\"" D_CMND_GROUPTOPIC "\":\"%s\",\"" D_CMND_OTAURL "\":\"%s\",\""
D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\",\"" D_JSON_STARTUPUTC "\":\"%s\",\"" D_CMND_SLEEP "\":%d,\""
D_JSON_CONFIG_HOLDER "\":%d,\"" D_JSON_BOOTCOUNT "\":%d,\"" D_JSON_SAVECOUNT "\":%d,\"" D_JSON_SAVEADDRESS "\":\"%X\"}}"),
baudrate, Settings.mqtt_grptopic, Settings.ota_url,
2019-11-20 20:49:57 +00:00
GetResetReasonInfo().c_str(), GetUptime().c_str(), GetDateAndTime(DT_RESTART).c_str(), Settings.sleep,
Settings.cfg_holder, Settings.bootcount, Settings.save_flag, GetSettingsAddress());
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "1"));
}
if ((0 == payload) || (2 == payload)) {
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS2_FIRMWARE "\":{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\",\""
2019-12-02 09:31:33 +00:00
D_JSON_BOOTVERSION "\":%d,\"" D_JSON_COREVERSION "\":\"" ARDUINO_ESP8266_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\","
"\"Hardware\":\"%s\"}}"),
my_version, my_image, GetBuildDateAndTime().c_str(),
2019-12-02 09:31:33 +00:00
ESP.getBootVersion(), ESP.getSdkVersion(), GetDeviceHardware().c_str());
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "2"));
}
if ((0 == payload) || (3 == payload)) {
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS3_LOGGING "\":{\"" D_CMND_SERIALLOG "\":%d,\"" D_CMND_WEBLOG "\":%d,\"" D_CMND_MQTTLOG "\":%d,\"" D_CMND_SYSLOG "\":%d,\""
D_CMND_LOGHOST "\":\"%s\",\"" D_CMND_LOGPORT "\":%d,\"" D_CMND_SSID "\":[\"%s\",\"%s\"],\"" D_CMND_TELEPERIOD "\":%d,\""
2019-10-31 11:12:48 +00:00
D_JSON_RESOLUTION "\":\"%08X\",\"" D_CMND_SETOPTION "\":[\"%08X\",\"%s\",\"%08X\",\"%08X\"]}}"),
Settings.seriallog_level, Settings.weblog_level, Settings.mqttlog_level, Settings.syslog_level,
Settings.syslog_host, Settings.syslog_port, Settings.sta_ssid[0], Settings.sta_ssid[1], Settings.tele_period,
2019-10-31 11:12:48 +00:00
Settings.flag2.data, Settings.flag.data, ToHex_P((unsigned char*)Settings.param, PARAM8_SIZE, stemp2, sizeof(stemp2)),
Settings.flag3.data, Settings.flag4.data);
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "3"));
}
if ((0 == payload) || (4 == payload)) {
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS4_MEMORY "\":{\"" D_JSON_PROGRAMSIZE "\":%d,\"" D_JSON_FREEMEMORY "\":%d,\"" D_JSON_HEAPSIZE "\":%d,\""
D_JSON_PROGRAMFLASHSIZE "\":%d,\"" D_JSON_FLASHSIZE "\":%d,\"" D_JSON_FLASHCHIPID "\":\"%06X\",\"" D_JSON_FLASHMODE "\":%d,\""
D_JSON_FEATURES "\":[\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\"]"),
ESP.getSketchSize()/1024, ESP.getFreeSketchSpace()/1024, ESP.getFreeHeap()/1024,
ESP.getFlashChipSize()/1024, ESP.getFlashChipRealSize()/1024, ESP.getFlashChipId(), ESP.getFlashChipMode(),
LANGUAGE_LCID, feature_drv1, feature_drv2, feature_sns1, feature_sns2, feature5);
XsnsDriverState();
ResponseAppend_P(PSTR(",\"Sensors\":"));
XsnsSensorState();
ResponseJsonEndEnd();
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "4"));
}
if ((0 == payload) || (5 == payload)) {
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS5_NETWORK "\":{\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"" D_JSON_GATEWAY "\":\"%s\",\""
D_JSON_SUBNETMASK "\":\"%s\",\"" D_JSON_DNSSERVER "\":\"%s\",\"" D_JSON_MAC "\":\"%s\",\""
D_CMND_WEBSERVER "\":%d,\"" D_CMND_WIFICONFIG "\":%d}}"),
my_hostname, WiFi.localIP().toString().c_str(), IPAddress(Settings.ip_address[1]).toString().c_str(),
IPAddress(Settings.ip_address[2]).toString().c_str(), IPAddress(Settings.ip_address[3]).toString().c_str(), WiFi.macAddress().c_str(),
Settings.webserver, Settings.sta_config);
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "5"));
}
2019-11-03 11:33:36 +00:00
if (((0 == payload) || (6 == payload)) && Settings.flag.mqtt_enabled) { // SetOption3 - Enable MQTT
#ifdef USE_MQTT_AWS_IOT
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\""
D_CMND_MQTTCLIENT "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"),
Settings.mqtt_user, Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client,
mqtt_client, MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE);
#else
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\""
D_CMND_MQTTCLIENT "\":\"%s\",\"" D_CMND_MQTTUSER "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"),
Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client,
mqtt_client, Settings.mqtt_user, MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE);
#endif
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "6"));
}
if ((0 == payload) || (7 == payload)) {
if (99 == Settings.timezone) {
snprintf_P(stemp, sizeof(stemp), PSTR("%d" ), Settings.timezone);
} else {
snprintf_P(stemp, sizeof(stemp), PSTR("\"%s\"" ), GetTimeZone().c_str());
}
#if defined(USE_TIMERS) && defined(USE_SUNRISE)
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS7_TIME "\":{\"" D_JSON_UTC_TIME "\":\"%s\",\"" D_JSON_LOCAL_TIME "\":\"%s\",\"" D_JSON_STARTDST "\":\"%s\",\""
D_JSON_ENDDST "\":\"%s\",\"" D_CMND_TIMEZONE "\":%s,\"" D_JSON_SUNRISE "\":\"%s\",\"" D_JSON_SUNSET "\":\"%s\"}}"),
GetTime(0).c_str(), GetTime(1).c_str(), GetTime(2).c_str(),
GetTime(3).c_str(), stemp, GetSun(0).c_str(), GetSun(1).c_str());
#else
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS7_TIME "\":{\"" D_JSON_UTC_TIME "\":\"%s\",\"" D_JSON_LOCAL_TIME "\":\"%s\",\"" D_JSON_STARTDST "\":\"%s\",\""
D_JSON_ENDDST "\":\"%s\",\"" D_CMND_TIMEZONE "\":%s}}"),
GetTime(0).c_str(), GetTime(1).c_str(), GetTime(2).c_str(),
GetTime(3).c_str(), stemp);
#endif // USE_TIMERS and USE_SUNRISE
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "7"));
}
#if defined(USE_ENERGY_SENSOR) && defined(USE_ENERGY_MARGIN_DETECTION)
if (energy_flg) {
if ((0 == payload) || (9 == payload)) {
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS9_MARGIN "\":{\"" D_CMND_POWERDELTA "\":%d,\"" D_CMND_POWERLOW "\":%d,\"" D_CMND_POWERHIGH "\":%d,\""
D_CMND_VOLTAGELOW "\":%d,\"" D_CMND_VOLTAGEHIGH "\":%d,\"" D_CMND_CURRENTLOW "\":%d,\"" D_CMND_CURRENTHIGH "\":%d}}"),
Settings.energy_power_delta, Settings.energy_min_power, Settings.energy_max_power,
Settings.energy_min_voltage, Settings.energy_max_voltage, Settings.energy_min_current, Settings.energy_max_current);
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "9"));
}
}
#endif // USE_ENERGY_MARGIN_DETECTION
if ((0 == payload) || (8 == payload) || (10 == payload)) {
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS10_SENSOR "\":"));
MqttShowSensor();
ResponseJsonEnd();
if (8 == payload) {
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "8"));
} else {
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "10"));
}
}
if ((0 == payload) || (11 == payload)) {
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS11_STATUS "\":"));
MqttShowState();
ResponseJsonEnd();
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "11"));
}
2019-10-10 12:39:52 +01:00
if ((0 == payload) || (12 == payload)) {
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS12_STATUS "\":"));
CrashDump();
ResponseJsonEnd();
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "12"));
}
2019-10-10 12:39:52 +01:00
#ifdef USE_SCRIPT_STATUS
if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">U",2,mqtt_data);
#endif
mqtt_data[0] = '\0';
}
void CmndState(void)
{
mqtt_data[0] = '\0';
MqttShowState();
2019-11-03 12:51:22 +00:00
if (Settings.flag3.hass_tele_on_power) { // SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_STATE), MQTT_TELE_RETAIN);
}
#ifdef USE_HOME_ASSISTANT
2019-11-03 12:51:22 +00:00
if (Settings.flag.hass_discovery) { // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
HAssPublishStatus();
}
#endif // USE_HOME_ASSISTANT
}
void CmndTempOffset(void)
{
if (XdrvMailbox.data_len > 0) {
int value = (int)(CharToFloat(XdrvMailbox.data) * 10);
if ((value > -127) && (value < 127)) {
Settings.temp_comp = value;
}
}
ResponseCmndFloat((float)(Settings.temp_comp) / 10, 1);
}
void CmndSleep(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 251)) {
Settings.sleep = XdrvMailbox.payload;
sleep = XdrvMailbox.payload;
WiFiSetSleepMode();
}
2019-10-22 17:34:41 +01:00
Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, XdrvMailbox.command, Settings.sleep, sleep);
}
void CmndUpgrade(void)
{
// Check if the payload is numerically 1, and had no trailing chars.
// e.g. "1foo" or "1.2.3" could fool us.
// Check if the version we have been asked to upgrade to is higher than our current version.
// We also need at least 3 chars to make a valid version number string.
if (((1 == XdrvMailbox.data_len) && (1 == XdrvMailbox.payload)) || ((XdrvMailbox.data_len >= 3) && NewerVersion(XdrvMailbox.data))) {
ota_state_flag = 3;
char stemp1[TOPSZ];
Response_P(PSTR("{\"%s\":\"" D_JSON_VERSION " %s " D_JSON_FROM " %s\"}"), XdrvMailbox.command, my_version, GetOtaUrl(stemp1, sizeof(stemp1)));
} else {
Response_P(PSTR("{\"%s\":\"" D_JSON_ONE_OR_GT "\"}"), XdrvMailbox.command, my_version);
}
}
void CmndOtaUrl(void)
{
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.ota_url))) {
strlcpy(Settings.ota_url, (SC_DEFAULT == Shortcut()) ? OTA_URL : XdrvMailbox.data, sizeof(Settings.ota_url));
}
2019-08-03 12:01:34 +01:00
ResponseCmndChar(Settings.ota_url);
}
void CmndSeriallog(void)
{
if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) {
2019-11-03 11:33:36 +00:00
Settings.flag.mqtt_serial = 0; // CMND_SERIALSEND and CMND_SERIALLOG
SetSeriallog(XdrvMailbox.payload);
}
Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, XdrvMailbox.command, Settings.seriallog_level, seriallog_level);
}
void CmndRestart(void)
{
switch (XdrvMailbox.payload) {
case 1:
restart_flag = 2;
2019-08-03 12:01:34 +01:00
ResponseCmndChar(D_JSON_RESTARTING);
break;
2019-12-07 10:00:32 +00:00
case -1:
CmndCrash(); // force a crash
break;
case 99:
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING));
EspRestart();
break;
default:
2019-08-03 12:01:34 +01:00
ResponseCmndChar(D_JSON_ONE_TO_RESTART);
}
}
void CmndPowerOnState(void)
{
if (my_module_type != MOTOR) {
/* 0 = Keep relays off after power on
* 1 = Turn relays on after power on, if PulseTime set wait for PulseTime seconds, and turn relays off
* 2 = Toggle relays after power on
* 3 = Set relays to last saved state after power on
* 4 = Turn relays on and disable any relay control (used for Sonoff Pow to always measure power)
* 5 = Keep relays off after power on, if PulseTime set wait for PulseTime seconds, and turn relays on
*/
if ((XdrvMailbox.payload >= POWER_ALL_OFF) && (XdrvMailbox.payload <= POWER_ALL_OFF_PULSETIME_ON)) {
Settings.poweronstate = XdrvMailbox.payload;
if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) {
for (uint32_t i = 1; i <= devices_present; i++) {
ExecuteCommandPower(i, POWER_ON, SRC_IGNORE);
}
}
}
2019-08-03 12:01:34 +01:00
ResponseCmndNumber(Settings.poweronstate);
}
}
void CmndPulsetime(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_PULSETIMERS)) {
uint32_t items = 1;
if (!XdrvMailbox.usridx && !XdrvMailbox.data_len) {
items = MAX_PULSETIMERS;
} else {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 65536)) {
Settings.pulse_timer[XdrvMailbox.index -1] = XdrvMailbox.payload; // 0 - 65535
SetPulseTimer(XdrvMailbox.index -1, XdrvMailbox.payload);
}
}
mqtt_data[0] = '\0';
for (uint32_t i = 0; i < items; i++) {
uint32_t index = (1 == items) ? XdrvMailbox.index : i +1;
ResponseAppend_P(PSTR("%c\"%s%d\":{\"" D_JSON_SET "\":%d,\"" D_JSON_REMAINING "\":%d}"),
(i) ? ',' : '{',
XdrvMailbox.command, index,
Settings.pulse_timer[index -1], GetPulseTimer(index -1));
}
ResponseJsonEnd();
}
}
void CmndBlinktime(void)
{
if ((XdrvMailbox.payload > 1) && (XdrvMailbox.payload <= 3600)) {
Settings.blinktime = XdrvMailbox.payload;
if (blink_timer > 0) { blink_timer = millis() + (100 * XdrvMailbox.payload); }
}
2019-08-03 12:01:34 +01:00
ResponseCmndNumber(Settings.blinktime);
}
void CmndBlinkcount(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 65536)) {
Settings.blinkcount = XdrvMailbox.payload; // 0 - 65535
if (blink_counter) { blink_counter = Settings.blinkcount *2; }
}
2019-08-03 12:01:34 +01:00
ResponseCmndNumber(Settings.blinkcount);
}
void CmndSavedata(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3600)) {
Settings.save_data = XdrvMailbox.payload;
save_data_counter = Settings.save_data;
}
SettingsSaveAll();
char stemp1[TOPSZ];
if (Settings.save_data > 1) {
snprintf_P(stemp1, sizeof(stemp1), PSTR(D_JSON_EVERY " %d " D_UNIT_SECOND), Settings.save_data);
}
2019-08-03 12:01:34 +01:00
ResponseCmndChar((Settings.save_data > 1) ? stemp1 : GetStateText(Settings.save_data));
}
void CmndSetoption(void)
{
2019-10-31 11:12:48 +00:00
if (XdrvMailbox.index < 114) {
uint32_t ptype;
uint32_t pindex;
if (XdrvMailbox.index <= 31) { // SetOption0 .. 31 = Settings.flag
2019-10-31 12:18:00 +00:00
ptype = 2;
pindex = XdrvMailbox.index; // 0 .. 31
}
else if (XdrvMailbox.index <= 49) { // SetOption32 .. 49 = Settings.param
2019-10-31 12:18:00 +00:00
ptype = 1;
pindex = XdrvMailbox.index -32; // 0 .. 17 (= PARAM8_SIZE -1)
}
2019-10-31 11:12:48 +00:00
else if (XdrvMailbox.index <= 81) { // SetOption50 .. 81 = Settings.flag3
2019-10-31 12:18:00 +00:00
ptype = 3;
pindex = XdrvMailbox.index -50; // 0 .. 31
}
2019-10-31 11:12:48 +00:00
else { // SetOption82 .. 113 = Settings.flag4
2019-10-31 12:18:00 +00:00
ptype = 4;
2019-10-31 11:12:48 +00:00
pindex = XdrvMailbox.index -82; // 0 .. 31
}
2019-10-31 12:18:00 +00:00
if (XdrvMailbox.payload >= 0) {
2019-10-31 12:18:00 +00:00
if (1 == ptype) { // SetOption32 .. 49
uint32_t param_low = 0;
uint32_t param_high = 255;
switch (pindex) {
case P_HOLD_TIME:
case P_MAX_POWER_RETRY:
param_low = 1;
param_high = 250;
break;
}
if ((XdrvMailbox.payload >= param_low) && (XdrvMailbox.payload <= param_high)) {
Settings.param[pindex] = XdrvMailbox.payload;
#ifdef USE_LIGHT
if (P_RGB_REMAP == pindex) {
LightUpdateColorMapping();
2019-10-31 12:18:00 +00:00
restart_flag = 2; // SetOption37 needs a reboot in most cases
}
#endif
#if (defined(USE_IR_REMOTE) && defined(USE_IR_RECEIVE)) || defined(USE_IR_REMOTE_FULL)
if (P_IR_UNKNOW_THRESHOLD == pindex) {
IrReceiveUpdateThreshold();
}
#endif
2019-10-31 12:18:00 +00:00
} else {
ptype = 99; // Command Error
}
} else {
if (XdrvMailbox.payload <= 1) {
if (2 == ptype) { // SetOption0 .. 31
switch (pindex) {
case 5: // mqtt_power_retain (CMND_POWERRETAIN)
case 6: // mqtt_button_retain (CMND_BUTTONRETAIN)
case 7: // mqtt_switch_retain (CMND_SWITCHRETAIN)
case 9: // mqtt_sensor_retain (CMND_SENSORRETAIN)
case 14: // interlock (CMND_INTERLOCK)
case 22: // mqtt_serial (SerialSend and SerialLog)
case 23: // mqtt_serial_raw (SerialSend)
case 25: // knx_enabled (Web config)
case 27: // knx_enable_enhancement (Web config)
ptype = 99; // Command Error
break; // Ignore command SetOption
case 3: // mqtt
case 15: // pwm_control
restart_flag = 2;
default:
bitWrite(Settings.flag.data, pindex, XdrvMailbox.payload);
}
if (12 == pindex) { // stop_flash_rotate
stop_flash_rotate = XdrvMailbox.payload;
SettingsSave(2);
}
#ifdef USE_HOME_ASSISTANT
if ((19 == pindex) || (30 == pindex)) {
HAssDiscover(); // Delayed execution to provide enough resources during hass_discovery or hass_light
}
#endif // USE_HOME_ASSISTANT
}
else if (3 == ptype) { // SetOption50 .. 81
bitWrite(Settings.flag3.data, pindex, XdrvMailbox.payload);
switch (pindex) {
case 5: // SetOption55
if (0 == XdrvMailbox.payload) {
restart_flag = 2; // Disable mDNS needs restart
}
break;
case 10: // SetOption60 enable or disable traditional sleep
WiFiSetSleepMode(); // Update WiFi sleep mode accordingly
break;
case 18: // SetOption68 for multi-channel PWM, requires a reboot
case 25: // SetOption75 grouptopic change
2019-10-31 12:18:00 +00:00
restart_flag = 2;
break;
}
}
else if (4 == ptype) { // SetOption82 .. 113
bitWrite(Settings.flag4.data, pindex, XdrvMailbox.payload);
}
} else {
ptype = 99; // Command Error
}
}
}
2019-10-31 12:18:00 +00:00
if (ptype < 99) {
2019-10-31 12:18:00 +00:00
if (1 == ptype) {
2019-10-31 11:12:48 +00:00
ResponseCmndIdxNumber(Settings.param[pindex]);
} else {
uint32_t flag = Settings.flag.data;
2019-10-31 12:18:00 +00:00
if (3 == ptype) {
2019-10-31 11:12:48 +00:00
flag = Settings.flag3.data;
}
2019-10-31 12:18:00 +00:00
else if (4 == ptype) {
2019-10-31 11:12:48 +00:00
flag = Settings.flag4.data;
}
ResponseCmndIdxChar(GetStateText(bitRead(flag, pindex)));
}
}
}
}
void CmndTemperatureResolution(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) {
Settings.flag2.temperature_resolution = XdrvMailbox.payload;
}
2019-08-03 12:01:34 +01:00
ResponseCmndNumber(Settings.flag2.temperature_resolution);
}
void CmndHumidityResolution(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) {
Settings.flag2.humidity_resolution = XdrvMailbox.payload;
}
2019-08-03 12:01:34 +01:00
ResponseCmndNumber(Settings.flag2.humidity_resolution);
}
void CmndPressureResolution(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) {
Settings.flag2.pressure_resolution = XdrvMailbox.payload;
}
2019-08-03 12:01:34 +01:00
ResponseCmndNumber(Settings.flag2.pressure_resolution);
}
void CmndPowerResolution(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) {
Settings.flag2.wattage_resolution = XdrvMailbox.payload;
}
2019-08-03 12:01:34 +01:00
ResponseCmndNumber(Settings.flag2.wattage_resolution);
}
void CmndVoltageResolution(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) {
Settings.flag2.voltage_resolution = XdrvMailbox.payload;
}
2019-08-03 12:01:34 +01:00
ResponseCmndNumber(Settings.flag2.voltage_resolution);
}
void CmndFrequencyResolution(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) {
Settings.flag2.frequency_resolution = XdrvMailbox.payload;
}
2019-08-03 12:01:34 +01:00
ResponseCmndNumber(Settings.flag2.frequency_resolution);
}
void CmndCurrentResolution(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) {
Settings.flag2.current_resolution = XdrvMailbox.payload;
}
2019-08-03 12:01:34 +01:00
ResponseCmndNumber(Settings.flag2.current_resolution);
}
void CmndEnergyResolution(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 5)) {
Settings.flag2.energy_resolution = XdrvMailbox.payload;
}
2019-08-03 12:01:34 +01:00
ResponseCmndNumber(Settings.flag2.energy_resolution);
}
void CmndWeightResolution(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) {
Settings.flag2.weight_resolution = XdrvMailbox.payload;
}
2019-08-03 12:01:34 +01:00
ResponseCmndNumber(Settings.flag2.weight_resolution);
}
void CmndModule(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= MAXMODULE)) {
bool present = false;
if (0 == XdrvMailbox.payload) {
XdrvMailbox.payload = USER_MODULE;
present = true;
} else {
XdrvMailbox.payload--;
present = ValidTemplateModule(XdrvMailbox.payload);
}
if (present) {
Settings.last_module = Settings.module;
Settings.module = XdrvMailbox.payload;
SetModuleType();
if (Settings.last_module != XdrvMailbox.payload) {
for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) {
Settings.my_gp.io[i] = GPIO_NONE;
}
}
restart_flag = 2;
}
}
Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, ModuleNr(), ModuleName().c_str());
}
void CmndModules(void)
{
uint32_t midx = USER_MODULE;
uint32_t lines = 1;
bool jsflg = false;
for (uint32_t i = 0; i <= sizeof(kModuleNiceList); i++) {
if (i > 0) { midx = pgm_read_byte(kModuleNiceList + i -1); }
if (!jsflg) {
Response_P(PSTR("{\"" D_CMND_MODULES "%d\":{"), lines);
} else {
ResponseAppend_P(PSTR(","));
}
jsflg = true;
uint32_t j = i ? midx +1 : 0;
if ((ResponseAppend_P(PSTR("\"%d\":\"%s\""), j, AnyModuleName(midx).c_str()) > (LOGSZ - TOPSZ)) || (i == sizeof(kModuleNiceList))) {
ResponseJsonEndEnd();
MqttPublishPrefixTopic_P(RESULT_OR_STAT, UpperCase(XdrvMailbox.command, XdrvMailbox.command));
jsflg = false;
lines++;
}
}
mqtt_data[0] = '\0';
}
void CmndGpio(void)
{
if (XdrvMailbox.index < sizeof(Settings.my_gp)) {
myio cmodule;
ModuleGpios(&cmodule);
if (ValidGPIO(XdrvMailbox.index, cmodule.io[XdrvMailbox.index]) && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < GPIO_SENSOR_END)) {
bool present = false;
for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) {
uint32_t midx = pgm_read_byte(kGpioNiceList + i);
if (midx == XdrvMailbox.payload) { present = true; }
}
if (present) {
for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) {
if (ValidGPIO(i, cmodule.io[i]) && (Settings.my_gp.io[i] == XdrvMailbox.payload)) {
Settings.my_gp.io[i] = GPIO_NONE;
}
}
Settings.my_gp.io[XdrvMailbox.index] = XdrvMailbox.payload;
restart_flag = 2;
}
}
Response_P(PSTR("{"));
bool jsflg = false;
for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) {
if (ValidGPIO(i, cmodule.io[i]) || ((GPIO_USER == XdrvMailbox.payload) && !FlashPin(i))) {
if (jsflg) { ResponseAppend_P(PSTR(",")); }
jsflg = true;
uint8_t sensor_type = Settings.my_gp.io[i];
if (!ValidGPIO(i, cmodule.io[i])) {
sensor_type = cmodule.io[i];
if (GPIO_USER == sensor_type) { // A user GPIO equals a not connected (=GPIO_NONE) GPIO here
sensor_type = GPIO_NONE;
}
}
uint8_t sensor_name_idx = sensor_type;
const char *sensor_names = kSensorNames;
if (sensor_type > GPIO_FIX_START) {
sensor_name_idx = sensor_type - GPIO_FIX_START -1;
sensor_names = kSensorNamesFixed;
}
char stemp1[TOPSZ];
ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":{\"%d\":\"%s\"}"),
i, sensor_type, GetTextIndexed(stemp1, sizeof(stemp1), sensor_name_idx, sensor_names));
}
}
if (jsflg) {
ResponseJsonEnd();
} else {
2019-08-03 12:01:34 +01:00
ResponseCmndChar(D_JSON_NOT_SUPPORTED);
}
}
}
void CmndGpios(void)
{
myio cmodule;
ModuleGpios(&cmodule);
uint32_t lines = 1;
bool jsflg = false;
for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) {
2019-10-30 13:08:43 +00:00
uint32_t midx = pgm_read_byte(kGpioNiceList + i);
if ((XdrvMailbox.payload != 255) && GetUsedInModule(midx, cmodule.io)) { continue; }
if (!jsflg) {
Response_P(PSTR("{\"" D_CMND_GPIOS "%d\":{"), lines);
} else {
ResponseAppend_P(PSTR(","));
}
jsflg = true;
char stemp1[TOPSZ];
if ((ResponseAppend_P(PSTR("\"%d\":\"%s\""), midx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)) > (LOGSZ - TOPSZ)) || (i == sizeof(kGpioNiceList) -1)) {
ResponseJsonEndEnd();
MqttPublishPrefixTopic_P(RESULT_OR_STAT, UpperCase(XdrvMailbox.command, XdrvMailbox.command));
jsflg = false;
lines++;
}
}
mqtt_data[0] = '\0';
}
void CmndTemplate(void)
{
// {"NAME":"Generic","GPIO":[17,254,29,254,7,254,254,254,138,254,139,254,254],"FLAG":1,"BASE":255}
bool error = false;
if (strstr(XdrvMailbox.data, "{") == nullptr) { // If no JSON it must be parameter
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= MAXMODULE)) {
XdrvMailbox.payload--;
if (ValidTemplateModule(XdrvMailbox.payload)) {
ModuleDefault(XdrvMailbox.payload); // Copy template module
if (USER_MODULE == Settings.module) { restart_flag = 2; }
}
}
else if (0 == XdrvMailbox.payload) { // Copy current template to user template
if (Settings.module != USER_MODULE) {
ModuleDefault(Settings.module);
}
}
else if (255 == XdrvMailbox.payload) { // Copy current module with user configured GPIO
if (Settings.module != USER_MODULE) {
ModuleDefault(Settings.module);
}
snprintf_P(Settings.user_template.name, sizeof(Settings.user_template.name), PSTR("Merged"));
uint32_t j = 0;
for (uint32_t i = 0; i < sizeof(mycfgio); i++) {
if (6 == i) { j = 9; }
if (8 == i) { j = 12; }
if (my_module.io[j] > GPIO_NONE) {
Settings.user_template.gp.io[i] = my_module.io[j];
}
j++;
}
}
}
else {
if (JsonTemplate(XdrvMailbox.data)) { // Free 336 bytes StaticJsonBuffer stack space by moving code to function
if (USER_MODULE == Settings.module) { restart_flag = 2; }
} else {
2019-08-03 12:01:34 +01:00
ResponseCmndChar(D_JSON_INVALID_JSON);
error = true;
}
}
if (!error) { TemplateJson(); }
}
void CmndPwm(void)
{
if (pwm_present && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_PWMS)) {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= Settings.pwm_range) && (pin[GPIO_PWM1 + XdrvMailbox.index -1] < 99)) {
Settings.pwm_value[XdrvMailbox.index -1] = XdrvMailbox.payload;
analogWrite(pin[GPIO_PWM1 + XdrvMailbox.index -1], bitRead(pwm_inverted, XdrvMailbox.index -1) ? Settings.pwm_range - XdrvMailbox.payload : XdrvMailbox.payload);
}
Response_P(PSTR("{"));
MqttShowPWMState(); // Render the PWM status to MQTT
ResponseJsonEnd();
}
}
void CmndPwmfrequency(void)
{
if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload >= PWM_MIN) && (XdrvMailbox.payload <= PWM_MAX))) {
Settings.pwm_frequency = (1 == XdrvMailbox.payload) ? PWM_FREQ : XdrvMailbox.payload;
analogWriteFreq(Settings.pwm_frequency); // Default is 1000 (core_esp8266_wiring_pwm.c)
}
2019-08-03 12:01:34 +01:00
ResponseCmndNumber(Settings.pwm_frequency);
}
void CmndPwmrange(void)
{
if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload > 254) && (XdrvMailbox.payload < 1024))) {
Settings.pwm_range = (1 == XdrvMailbox.payload) ? PWM_RANGE : XdrvMailbox.payload;
for (uint32_t i = 0; i < MAX_PWMS; i++) {
if (Settings.pwm_value[i] > Settings.pwm_range) {
Settings.pwm_value[i] = Settings.pwm_range;
}
}
analogWriteRange(Settings.pwm_range); // Default is 1023 (Arduino.h)
}
2019-08-03 12:01:34 +01:00
ResponseCmndNumber(Settings.pwm_range);
}
void CmndButtonDebounce(void)
{
if ((XdrvMailbox.payload > 39) && (XdrvMailbox.payload < 1001)) {
Settings.button_debounce = XdrvMailbox.payload;
}
2019-08-03 12:01:34 +01:00
ResponseCmndNumber(Settings.button_debounce);
}
void CmndSwitchDebounce(void)
{
if ((XdrvMailbox.payload > 39) && (XdrvMailbox.payload < 1001)) {
Settings.switch_debounce = XdrvMailbox.payload;
}
2019-08-03 12:01:34 +01:00
ResponseCmndNumber(Settings.switch_debounce);
}
void CmndBaudrate(void)
{
if (XdrvMailbox.payload >= 300) {
XdrvMailbox.payload /= 300; // Make it a valid baudrate
baudrate = (XdrvMailbox.payload & 0xFFFF) * 300;
SetSerialBaudrate(baudrate);
}
ResponseCmndNumber(Settings.baudrate * 300);
}
void CmndSerialConfig(void)
{
// See TasmotaSerialConfig for possible options
// SerialConfig 0..23 where 3 equals 8N1
// SerialConfig 8N1
if (XdrvMailbox.data_len > 0) {
if (XdrvMailbox.data_len < 3) { // Use 0..23 as serial config option
if ((XdrvMailbox.payload >= TS_SERIAL_5N1) && (XdrvMailbox.payload <= TS_SERIAL_8O2)) {
SetSerialConfig(XdrvMailbox.payload);
}
}
else if ((XdrvMailbox.payload >= 5) && (XdrvMailbox.payload <= 8)) {
uint8_t serial_config = XdrvMailbox.payload -5; // Data bits 5, 6, 7 or 8, No parity and 1 stop bit
bool valid = true;
char parity = (XdrvMailbox.data[1] & 0xdf);
if ('E' == parity) {
serial_config += 0x08; // Even parity
}
else if ('O' == parity) {
serial_config += 0x10; // Odd parity
}
else if ('N' != parity) {
valid = false;
}
if ('2' == XdrvMailbox.data[2]) {
serial_config += 0x04; // Stop bits 2
}
else if ('1' != XdrvMailbox.data[2]) {
valid = false;
}
if (valid) {
SetSerialConfig(serial_config);
}
}
}
ResponseCmndChar(GetSerialConfig().c_str());
}
void CmndSerialSend(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 5)) {
SetSeriallog(LOG_LEVEL_NONE);
2019-11-03 11:33:36 +00:00
Settings.flag.mqtt_serial = 1; // CMND_SERIALSEND and CMND_SERIALLOG
Settings.flag.mqtt_serial_raw = (XdrvMailbox.index > 3) ? 1 : 0; // CMND_SERIALSEND3
if (XdrvMailbox.data_len > 0) {
if (1 == XdrvMailbox.index) {
Serial.printf("%s\n", XdrvMailbox.data); // "Hello Tiger\n"
}
else if (2 == XdrvMailbox.index || 4 == XdrvMailbox.index) {
for (uint32_t i = 0; i < XdrvMailbox.data_len; i++) {
Serial.write(XdrvMailbox.data[i]); // "Hello Tiger" or "A0"
}
}
else if (3 == XdrvMailbox.index) {
uint32_t dat_len = XdrvMailbox.data_len;
Serial.printf("%s", Unescape(XdrvMailbox.data, &dat_len)); // "Hello\f"
}
else if (5 == XdrvMailbox.index) {
SerialSendRaw(RemoveSpace(XdrvMailbox.data)); // "AA004566" as hex values
}
2019-08-03 12:01:34 +01:00
ResponseCmndDone();
}
}
}
void CmndSerialDelimiter(void)
{
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.payload < 256)) {
if (XdrvMailbox.payload > 0) {
Settings.serial_delimiter = XdrvMailbox.payload;
} else {
uint32_t dat_len = XdrvMailbox.data_len;
Unescape(XdrvMailbox.data, &dat_len);
Settings.serial_delimiter = XdrvMailbox.data[0];
}
}
2019-08-03 12:01:34 +01:00
ResponseCmndNumber(Settings.serial_delimiter);
}
void CmndSyslog(void)
{
if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) {
SetSyslog(XdrvMailbox.payload);
}
Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, XdrvMailbox.command, Settings.syslog_level, syslog_level);
}
void CmndLoghost(void)
{
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.syslog_host))) {
strlcpy(Settings.syslog_host, (SC_DEFAULT == Shortcut()) ? SYS_LOG_HOST : XdrvMailbox.data, sizeof(Settings.syslog_host));
}
2019-08-03 12:01:34 +01:00
ResponseCmndChar(Settings.syslog_host);
}
void CmndLogport(void)
{
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 65536)) {
Settings.syslog_port = (1 == XdrvMailbox.payload) ? SYS_LOG_PORT : XdrvMailbox.payload;
}
2019-08-03 12:01:34 +01:00
ResponseCmndNumber(Settings.syslog_port);
}
void CmndIpAddress(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) {
uint32_t address;
if (ParseIp(&address, XdrvMailbox.data)) {
Settings.ip_address[XdrvMailbox.index -1] = address;
// restart_flag = 2;
}
char stemp1[TOPSZ];
snprintf_P(stemp1, sizeof(stemp1), PSTR(" (%s)"), WiFi.localIP().toString().c_str());
Response_P(S_JSON_COMMAND_INDEX_SVALUE_SVALUE, XdrvMailbox.command, XdrvMailbox.index, IPAddress(Settings.ip_address[XdrvMailbox.index -1]).toString().c_str(), (1 == XdrvMailbox.index) ? stemp1:"");
}
}
void CmndNtpServer(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 3)) {
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.ntp_server[0]))) {
strlcpy(Settings.ntp_server[XdrvMailbox.index -1],
(SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1==XdrvMailbox.index)?NTP_SERVER1:(2==XdrvMailbox.index)?NTP_SERVER2:NTP_SERVER3 : XdrvMailbox.data,
sizeof(Settings.ntp_server[0]));
for (uint32_t i = 0; i < strlen(Settings.ntp_server[XdrvMailbox.index -1]); i++) {
if (Settings.ntp_server[XdrvMailbox.index -1][i] == ',') Settings.ntp_server[XdrvMailbox.index -1][i] = '.';
}
// restart_flag = 2; // Issue #3890
ntp_force_sync = true;
}
2019-08-03 12:01:34 +01:00
ResponseCmndIdxChar(Settings.ntp_server[XdrvMailbox.index -1]);
}
}
void CmndAp(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) {
switch (XdrvMailbox.payload) {
case 0: // Toggle
Settings.sta_active ^= 1;
break;
case 1: // AP1
case 2: // AP2
Settings.sta_active = XdrvMailbox.payload -1;
}
restart_flag = 2;
}
Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active]);
}
void CmndSsid(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) {
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.sta_ssid[0]))) {
strlcpy(Settings.sta_ssid[XdrvMailbox.index -1],
(SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? STA_SSID1 : STA_SSID2 : XdrvMailbox.data,
sizeof(Settings.sta_ssid[0]));
Settings.sta_active = XdrvMailbox.index -1;
restart_flag = 2;
}
2019-08-03 12:01:34 +01:00
ResponseCmndIdxChar(Settings.sta_ssid[XdrvMailbox.index -1]);
}
}
void CmndPassword(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) {
if ((XdrvMailbox.data_len > 4 || SC_CLEAR == Shortcut() || SC_DEFAULT == Shortcut()) && (XdrvMailbox.data_len < sizeof(Settings.sta_pwd[0]))) {
strlcpy(Settings.sta_pwd[XdrvMailbox.index -1],
(SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? STA_PASS1 : STA_PASS2 : XdrvMailbox.data,
sizeof(Settings.sta_pwd[0]));
Settings.sta_active = XdrvMailbox.index -1;
restart_flag = 2;
2019-08-03 12:01:34 +01:00
ResponseCmndIdxChar(Settings.sta_pwd[XdrvMailbox.index -1]);
} else {
Response_P(S_JSON_COMMAND_INDEX_ASTERISK, XdrvMailbox.command, XdrvMailbox.index);
}
}
}
void CmndHostname(void)
{
if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.hostname))) {
strlcpy(Settings.hostname, (SC_DEFAULT == Shortcut()) ? WIFI_HOSTNAME : XdrvMailbox.data, sizeof(Settings.hostname));
if (strstr(Settings.hostname, "%") != nullptr) {
strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname));
}
restart_flag = 2;
}
2019-08-03 12:01:34 +01:00
ResponseCmndChar(Settings.hostname);
}
void CmndWifiConfig(void)
{
if ((XdrvMailbox.payload >= WIFI_RESTART) && (XdrvMailbox.payload < MAX_WIFI_OPTION)) {
2019-10-22 15:46:07 +01:00
if ((EX_WIFI_SMARTCONFIG == XdrvMailbox.payload) || (EX_WIFI_WPSCONFIG == XdrvMailbox.payload)) {
XdrvMailbox.payload = WIFI_MANAGER;
}
Settings.sta_config = XdrvMailbox.payload;
wifi_state_flag = Settings.sta_config;
if (WifiState() > WIFI_RESTART) {
restart_flag = 2;
}
}
2019-10-22 16:29:21 +01:00
char stemp1[TOPSZ];
Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, Settings.sta_config, GetTextIndexed(stemp1, sizeof(stemp1), Settings.sta_config, kWifiConfig));
}
void CmndFriendlyname(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_FRIENDLYNAMES)) {
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.friendlyname[0]))) {
char stemp1[TOPSZ];
if (1 == XdrvMailbox.index) {
snprintf_P(stemp1, sizeof(stemp1), PSTR(FRIENDLY_NAME));
} else {
snprintf_P(stemp1, sizeof(stemp1), PSTR(FRIENDLY_NAME "%d"), XdrvMailbox.index);
}
strlcpy(Settings.friendlyname[XdrvMailbox.index -1], (SC_DEFAULT == Shortcut()) ? stemp1 : XdrvMailbox.data, sizeof(Settings.friendlyname[XdrvMailbox.index -1]));
}
2019-08-03 12:01:34 +01:00
ResponseCmndIdxChar(Settings.friendlyname[XdrvMailbox.index -1]);
}
}
void CmndSwitchMode(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SWITCHES)) {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < MAX_SWITCH_OPTION)) {
Settings.switchmode[XdrvMailbox.index -1] = XdrvMailbox.payload;
}
2019-08-03 12:01:34 +01:00
ResponseCmndIdxNumber(Settings.switchmode[XdrvMailbox.index-1]);
}
}
void CmndInterlock(void)
{
// Interlock 0 - Off, Interlock 1 - On, Interlock 1,2 3,4 5,6,7
uint32_t max_relays = devices_present;
if (light_type) { max_relays--; }
if (max_relays > sizeof(Settings.interlock[0]) * 8) { max_relays = sizeof(Settings.interlock[0]) * 8; }
if (max_relays > 1) { // Only interlock with more than 1 relay
if (XdrvMailbox.data_len > 0) {
if (strstr(XdrvMailbox.data, ",") != nullptr) { // Interlock entry
for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { Settings.interlock[i] = 0; } // Reset current interlocks
char *group;
char *q;
uint32_t group_index = 0;
power_t relay_mask = 0;
for (group = strtok_r(XdrvMailbox.data, " ", &q); group && group_index < MAX_INTERLOCKS; group = strtok_r(nullptr, " ", &q)) {
char *str;
char *p;
for (str = strtok_r(group, ",", &p); str; str = strtok_r(nullptr, ",", &p)) {
int pbit = atoi(str);
if ((pbit > 0) && (pbit <= max_relays)) { // Only valid relays
pbit--;
if (!bitRead(relay_mask, pbit)) { // Only relay once
bitSet(relay_mask, pbit);
bitSet(Settings.interlock[group_index], pbit);
}
}
}
group_index++;
}
for (uint32_t i = 0; i < group_index; i++) {
uint32_t minimal_bits = 0;
for (uint32_t j = 0; j < max_relays; j++) {
if (bitRead(Settings.interlock[i], j)) { minimal_bits++; }
}
if (minimal_bits < 2) { Settings.interlock[i] = 0; } // Discard single relay as interlock
}
} else {
2019-11-03 11:33:36 +00:00
Settings.flag.interlock = XdrvMailbox.payload &1; // CMND_INTERLOCK - Enable/disable interlock
if (Settings.flag.interlock) {
SetDevicePower(power, SRC_IGNORE); // Remove multiple relays if set
}
}
}
Response_P(PSTR("{\"" D_CMND_INTERLOCK "\":\"%s\",\"" D_JSON_GROUPS "\":\""), GetStateText(Settings.flag.interlock));
uint32_t anygroup = 0;
for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) {
if (Settings.interlock[i]) {
anygroup++;
ResponseAppend_P(PSTR("%s"), (anygroup > 1) ? " " : "");
uint32_t anybit = 0;
power_t mask = 1;
for (uint32_t j = 0; j < max_relays; j++) {
if (Settings.interlock[i] & mask) {
anybit++;
ResponseAppend_P(PSTR("%s%d"), (anybit > 1) ? "," : "", j +1);
}
mask <<= 1;
}
}
}
if (!anygroup) {
for (uint32_t j = 1; j <= max_relays; j++) {
ResponseAppend_P(PSTR("%s%d"), (j > 1) ? "," : "", j);
}
}
ResponseAppend_P(PSTR("\"}"));
} else {
2019-11-03 11:33:36 +00:00
Settings.flag.interlock = 0; // CMND_INTERLOCK - Enable/disable interlock
2019-08-03 12:01:34 +01:00
ResponseCmndStateText(Settings.flag.interlock);
}
}
void CmndTeleperiod(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) {
Settings.tele_period = (1 == XdrvMailbox.payload) ? TELE_PERIOD : XdrvMailbox.payload;
if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) Settings.tele_period = 10; // Do not allow periods < 10 seconds
tele_period = Settings.tele_period;
}
2019-10-22 17:34:41 +01:00
ResponseCmndNumber(Settings.tele_period);
}
void CmndReset(void)
{
switch (XdrvMailbox.payload) {
case 1:
restart_flag = 211;
2019-08-03 12:01:34 +01:00
ResponseCmndChar(D_JSON_RESET_AND_RESTARTING);
break;
case 2 ... 6:
restart_flag = 210 + XdrvMailbox.payload;
Response_P(PSTR("{\"" D_CMND_RESET "\":\"" D_JSON_ERASE ", " D_JSON_RESET_AND_RESTARTING "\"}"));
break;
case 99:
Settings.bootcount = 0;
ResponseCmndDone();
break;
default:
2019-08-03 12:01:34 +01:00
ResponseCmndChar(D_JSON_ONE_TO_RESET);
}
}
void CmndTime(void)
{
// payload 0 = (re-)enable NTP
// payload 1 = Time format {"Time":"2019-09-04T14:31:29"}
// payload 2 = Time format {"Time":"2019-09-04T14:31:29","Epoch":1567600289}
// payload 3 = Time format {"Time":1567600289}
// payload 4 = reserved
// payload 1451602800 - disable NTP and set time to epoch
uint32_t format = Settings.flag2.time_format;
if (XdrvMailbox.data_len > 0) {
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 4)) {
Settings.flag2.time_format = XdrvMailbox.payload -1;
format = Settings.flag2.time_format;
} else {
format = 1; // {"Time":"2019-09-04T14:31:29","Epoch":1567600289}
RtcSetTime(XdrvMailbox.payload);
}
}
mqtt_data[0] = '\0';
ResponseAppendTimeFormat(format);
ResponseJsonEnd();
}
void CmndTimezone(void)
{
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.payload >= -13)) {
Settings.timezone = XdrvMailbox.payload;
Settings.timezone_minutes = 0;
if (XdrvMailbox.payload < 15) {
char *p = strtok (XdrvMailbox.data, ":");
if (p) {
p = strtok (nullptr, ":");
if (p) {
Settings.timezone_minutes = strtol(p, nullptr, 10);
if (Settings.timezone_minutes > 59) { Settings.timezone_minutes = 59; }
}
}
} else {
Settings.timezone = 99;
}
ntp_force_sync = true;
}
if (99 == Settings.timezone) {
2019-08-03 12:01:34 +01:00
ResponseCmndNumber(Settings.timezone);
} else {
char stemp1[TOPSZ];
snprintf_P(stemp1, sizeof(stemp1), PSTR("%+03d:%02d"), Settings.timezone, Settings.timezone_minutes);
2019-08-03 12:01:34 +01:00
ResponseCmndChar(stemp1);
}
}
void CmndTimeStdDst(uint32_t ts)
{
// TimeStd 0/1, 0/1/2/3/4, 1..12, 1..7, 0..23, +/-780
if (XdrvMailbox.data_len > 0) {
if (strstr(XdrvMailbox.data, ",") != nullptr) { // Process parameter entry
uint32_t tpos = 0; // Parameter index
int value = 0;
char *p = XdrvMailbox.data; // Parameters like "1, 2,3 , 4 ,5, -120" or ",,,,,+240"
char *q = p; // Value entered flag
while (p && (tpos < 7)) {
if (p > q) { // Any value entered
if (1 == tpos) { Settings.tflag[ts].hemis = value &1; }
if (2 == tpos) { Settings.tflag[ts].week = (value < 0) ? 0 : (value > 4) ? 4 : value; }
if (3 == tpos) { Settings.tflag[ts].month = (value < 1) ? 1 : (value > 12) ? 12 : value; }
if (4 == tpos) { Settings.tflag[ts].dow = (value < 1) ? 1 : (value > 7) ? 7 : value; }
if (5 == tpos) { Settings.tflag[ts].hour = (value < 0) ? 0 : (value > 23) ? 23 : value; }
if (6 == tpos) { Settings.toffset[ts] = (value < -900) ? -900 : (value > 900) ? 900 : value; }
}
p = Trim(p); // Skip spaces
if (tpos && (*p == ',')) { p++; } // Skip separator
p = Trim(p); // Skip spaces
q = p; // Reset any value entered flag
value = strtol(p, &p, 10);
tpos++; // Next parameter
}
ntp_force_sync = true;
} else {
if (0 == XdrvMailbox.payload) {
if (0 == ts) {
SettingsResetStd();
} else {
SettingsResetDst();
}
}
ntp_force_sync = true;
}
}
Response_P(PSTR("{\"%s\":{\"Hemisphere\":%d,\"Week\":%d,\"Month\":%d,\"Day\":%d,\"Hour\":%d,\"Offset\":%d}}"),
XdrvMailbox.command, Settings.tflag[ts].hemis, Settings.tflag[ts].week, Settings.tflag[ts].month, Settings.tflag[ts].dow, Settings.tflag[ts].hour, Settings.toffset[ts]);
}
void CmndTimeStd(void)
{
CmndTimeStdDst(0);
}
void CmndTimeDst(void)
{
CmndTimeStdDst(1);
}
void CmndAltitude(void)
{
if ((XdrvMailbox.data_len > 0) && ((XdrvMailbox.payload >= -30000) && (XdrvMailbox.payload <= 30000))) {
Settings.altitude = XdrvMailbox.payload;
}
2019-08-03 12:01:34 +01:00
ResponseCmndNumber(Settings.altitude);
}
void CmndLedPower(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_LEDS)) {
if (99 == pin[GPIO_LEDLNK]) { XdrvMailbox.index = 1; }
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) {
Settings.ledstate &= 8; // Disable power control
uint32_t mask = 1 << (XdrvMailbox.index -1); // Led to control
switch (XdrvMailbox.payload) {
case 0: // Off
led_power &= (0xFF ^ mask);
Settings.ledstate = 0;
break;
case 1: // On
led_power |= mask;
Settings.ledstate = 8;
break;
case 2: // Toggle
led_power ^= mask;
Settings.ledstate ^= 8;
break;
}
blinks = 0;
if (99 == pin[GPIO_LEDLNK]) {
SetLedPower(Settings.ledstate &8);
} else {
SetLedPowerIdx(XdrvMailbox.index -1, (led_power & mask));
}
}
bool state = bitRead(led_power, XdrvMailbox.index -1);
if (99 == pin[GPIO_LEDLNK]) {
state = bitRead(Settings.ledstate, 3);
}
2019-08-03 12:01:34 +01:00
ResponseCmndIdxChar(GetStateText(state));
}
}
void CmndLedState(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < MAX_LED_OPTION)) {
Settings.ledstate = XdrvMailbox.payload;
if (!Settings.ledstate) {
SetLedPowerAll(0);
SetLedLink(0);
}
}
2019-08-03 12:01:34 +01:00
ResponseCmndNumber(Settings.ledstate);
}
void CmndLedMask(void)
{
if (XdrvMailbox.data_len > 0) {
Settings.ledmask = XdrvMailbox.payload;
}
char stemp1[TOPSZ];
snprintf_P(stemp1, sizeof(stemp1), PSTR("%d (0x%04X)"), Settings.ledmask, Settings.ledmask);
2019-08-03 12:01:34 +01:00
ResponseCmndChar(stemp1);
}
void CmndWifiPower(void)
{
if (XdrvMailbox.data_len > 0) {
Settings.wifi_output_power = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10);
if (Settings.wifi_output_power > 205) {
Settings.wifi_output_power = 205;
}
WifiSetOutputPower();
}
ResponseCmndFloat((float)(Settings.wifi_output_power) / 10, 1);
}
#ifdef USE_I2C
void CmndI2cScan(void)
{
if (i2c_flg) {
I2cScan(mqtt_data, sizeof(mqtt_data));
}
}
void CmndI2cDriver(void)
{
if (XdrvMailbox.index < MAX_I2C_DRIVERS) {
if (XdrvMailbox.payload >= 0) {
bitWrite(Settings.i2c_drivers[XdrvMailbox.index / 32], XdrvMailbox.index % 32, XdrvMailbox.payload &1);
2019-11-03 16:57:22 +00:00
restart_flag = 2;
}
}
Response_P(PSTR("{\"" D_CMND_I2CDRIVER "\":"));
2019-11-04 09:38:05 +00:00
I2cDriverState();
ResponseJsonEnd();
}
#endif // USE_I2C
void CmndSensor(void)
{
XsnsCall(FUNC_COMMAND_SENSOR);
}
void CmndDriver(void)
{
XdrvCall(FUNC_COMMAND_DRIVER);
}