mirror of https://github.com/arendst/Tasmota.git
2867 lines
107 KiB
C++
2867 lines
107 KiB
C++
/*
|
|
support_command.ino - command support for Tasmota
|
|
|
|
Copyright (C) 2021 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
|
|
// SetOptions synonyms
|
|
D_SO_WIFINOSLEEP "|"
|
|
// Other commands
|
|
D_CMND_UPGRADE "|" D_CMND_UPLOAD "|" D_CMND_OTAURL "|" D_CMND_SERIALLOG "|" D_CMND_RESTART "|"
|
|
#ifndef FIRMWARE_MINIMAL_ONLY
|
|
D_CMND_BACKLOG "|" D_CMND_DELAY "|" D_CMND_POWER "|" D_CMND_TIMEDPOWER "|" D_CMND_STATUS "|" D_CMND_STATE "|" D_CMND_SLEEP "|"
|
|
D_CMND_POWERONSTATE "|" D_CMND_PULSETIME "|" D_CMND_BLINKTIME "|" D_CMND_BLINKCOUNT "|" D_CMND_SAVEDATA "|"
|
|
D_CMND_SO "|" 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_GPIOREAD "|" 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_SERIALBUFFER "|" 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_WIFI "|" D_CMND_DNSTIMEOUT "|"
|
|
D_CMND_DEVICENAME "|" D_CMND_FN "|" 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_LEDPWM_ON "|" D_CMND_LEDPWM_OFF "|" D_CMND_LEDPWM_MODE "|"
|
|
D_CMND_WIFIPOWER "|" D_CMND_TEMPOFFSET "|" D_CMND_HUMOFFSET "|" D_CMND_SPEEDUNIT "|" D_CMND_GLOBAL_TEMP "|" D_CMND_GLOBAL_HUM"|" D_CMND_GLOBAL_PRESS "|" D_CMND_SWITCHTEXT "|" D_CMND_WIFISCAN "|" D_CMND_WIFITEST "|"
|
|
D_CMND_ZIGBEE_BATTPERCENT "|"
|
|
#ifdef USE_I2C
|
|
D_CMND_I2CSCAN "|" D_CMND_I2CDRIVER "|"
|
|
#endif
|
|
#ifdef USE_DEVICE_GROUPS
|
|
D_CMND_DEVGROUP_NAME "|"
|
|
#ifdef USE_DEVICE_GROUPS_SEND
|
|
D_CMND_DEVGROUP_SEND "|"
|
|
#endif // USE_DEVICE_GROUPS_SEND
|
|
D_CMND_DEVGROUP_SHARE "|" D_CMND_DEVGROUPSTATUS "|" D_CMND_DEVGROUP_TIE "|"
|
|
#endif // USE_DEVICE_GROUPS
|
|
D_CMND_SETSENSOR "|" D_CMND_SENSOR "|" D_CMND_DRIVER "|" D_CMND_JSON
|
|
#ifdef ESP32
|
|
"|Info|"
|
|
#if defined(SOC_TOUCH_VERSION_1) || defined(SOC_TOUCH_VERSION_2)
|
|
D_CMND_TOUCH_CAL "|" D_CMND_TOUCH_THRES "|"
|
|
#endif // ESP32 SOC_TOUCH_VERSION_1 or SOC_TOUCH_VERSION_2
|
|
D_CMND_CPU_FREQUENCY
|
|
#endif // ESP32
|
|
#endif //FIRMWARE_MINIMAL_ONLY
|
|
;
|
|
|
|
SO_SYNONYMS(kTasmotaSynonyms,
|
|
127,
|
|
);
|
|
|
|
void (* const TasmotaCommand[])(void) PROGMEM = {
|
|
&CmndUpgrade, &CmndUpgrade, &CmndOtaUrl, &CmndSeriallog, &CmndRestart,
|
|
#ifndef FIRMWARE_MINIMAL_ONLY
|
|
&CmndBacklog, &CmndDelay, &CmndPower, &CmndTimedPower, &CmndStatus, &CmndState, &CmndSleep,
|
|
&CmndPowerOnState, &CmndPulsetime, &CmndBlinktime, &CmndBlinkcount, &CmndSavedata,
|
|
&CmndSetoption, &CmndSetoption, &CmndTemperatureResolution, &CmndHumidityResolution, &CmndPressureResolution, &CmndPowerResolution,
|
|
&CmndVoltageResolution, &CmndFrequencyResolution, &CmndCurrentResolution, &CmndEnergyResolution, &CmndWeightResolution,
|
|
&CmndModule, &CmndModules, &CmndGpio, &CmndGpioRead, &CmndGpios, &CmndTemplate, &CmndPwm, &CmndPwmfrequency, &CmndPwmrange,
|
|
&CmndButtonDebounce, &CmndSwitchDebounce, &CmndSyslog, &CmndLoghost, &CmndLogport,
|
|
&CmndSerialBuffer, &CmndSerialSend, &CmndBaudrate, &CmndSerialConfig, &CmndSerialDelimiter,
|
|
&CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig, &CmndWifi, &CmndDnsTimeout,
|
|
&CmndDevicename, &CmndFriendlyname, &CmndFriendlyname, &CmndSwitchMode, &CmndInterlock, &CmndTeleperiod, &CmndReset, &CmndTime, &CmndTimezone, &CmndTimeStd,
|
|
&CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndLedPwmOn, &CmndLedPwmOff, &CmndLedPwmMode,
|
|
&CmndWifiPower, &CmndTempOffset, &CmndHumOffset, &CmndSpeedUnit, &CmndGlobalTemp, &CmndGlobalHum, &CmndGlobalPress, &CmndSwitchText, &CmndWifiScan, &CmndWifiTest,
|
|
&CmndBatteryPercent,
|
|
#ifdef USE_I2C
|
|
&CmndI2cScan, &CmndI2cDriver,
|
|
#endif
|
|
#ifdef USE_DEVICE_GROUPS
|
|
&CmndDevGroupName,
|
|
#ifdef USE_DEVICE_GROUPS_SEND
|
|
&CmndDevGroupSend,
|
|
#endif // USE_DEVICE_GROUPS_SEND
|
|
&CmndDevGroupShare, &CmndDevGroupStatus, &CmndDevGroupTie,
|
|
#endif // USE_DEVICE_GROUPS
|
|
&CmndSetSensor, &CmndSensor, &CmndDriver, &CmndJson
|
|
#ifdef ESP32
|
|
, &CmndInfo,
|
|
#if defined(SOC_TOUCH_VERSION_1) || defined(SOC_TOUCH_VERSION_2)
|
|
&CmndTouchCal, &CmndTouchThres,
|
|
#endif // ESP32 SOC_TOUCH_VERSION_1 or SOC_TOUCH_VERSION_2
|
|
&CmndCpuFrequency
|
|
#endif // ESP32
|
|
#endif //FIRMWARE_MINIMAL_ONLY
|
|
};
|
|
|
|
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;
|
|
|
|
/********************************************************************************************/
|
|
|
|
#ifndef FIRMWARE_MINIMAL_ONLY
|
|
void CmndWifiScan(void)
|
|
{
|
|
if (XdrvMailbox.data_len > 0) {
|
|
if ( !Wifi.scan_state || Wifi.scan_state > 7 ) {
|
|
ResponseCmndChar(D_JSON_SCANNING);
|
|
Wifi.scan_state = 6;
|
|
} else {
|
|
ResponseCmndChar(D_JSON_BUSY);
|
|
}
|
|
} else {
|
|
if ( !Wifi.scan_state ) {
|
|
ResponseCmndChar(D_JSON_NOT_STARTED);
|
|
} else if ( Wifi.scan_state >= 1 && Wifi.scan_state <= 5 ) {
|
|
ResponseCmndChar(D_JSON_BUSY);
|
|
} else if ( Wifi.scan_state >= 6 && Wifi.scan_state <= 7 ) {
|
|
ResponseCmndChar(D_JSON_SCANNING);
|
|
} else { //show scan result
|
|
Response_P(PSTR("{\"" D_CMND_WIFISCAN "\":"));
|
|
|
|
if (WiFi.scanComplete() > 0) {
|
|
// Sort networks by RSSI
|
|
uint32_t indexes[WiFi.scanComplete()];
|
|
for (uint32_t i = 0; i < WiFi.scanComplete(); i++) {
|
|
indexes[i] = i;
|
|
}
|
|
for (uint32_t i = 0; i < WiFi.scanComplete(); i++) {
|
|
for (uint32_t j = i + 1; j < WiFi.scanComplete(); j++) {
|
|
if (WiFi.RSSI(indexes[j]) > WiFi.RSSI(indexes[i])) {
|
|
std::swap(indexes[i], indexes[j]);
|
|
}
|
|
}
|
|
}
|
|
delay(0);
|
|
|
|
ResponseAppend_P(PSTR("{"));
|
|
for (uint32_t i = 0; i < WiFi.scanComplete(); i++) {
|
|
ResponseAppend_P(PSTR("\"" D_STATUS5_NETWORK "%d\":{\"" D_SSID "\":\"%s\",\"" D_BSSID "\":\"%s\",\"" D_CHANNEL
|
|
"\":\"%d\",\"" D_JSON_SIGNAL "\":\"%d\",\"" D_RSSI "\":\"%d\",\"" D_JSON_ENCRYPTION "\":\"%s\"}"),
|
|
i+1,
|
|
WiFi.SSID(indexes[i]).c_str(),
|
|
WiFi.BSSIDstr(indexes[i]).c_str(),
|
|
WiFi.channel(indexes[i]),
|
|
WiFi.RSSI(indexes[i]),
|
|
WifiGetRssiAsQuality(WiFi.RSSI(indexes[i])),
|
|
WifiEncryptionType(indexes[i]).c_str());
|
|
if ( ResponseSize() < ResponseLength() + 300 ) { break; }
|
|
if ( i < WiFi.scanComplete() -1 ) { ResponseAppend_P(PSTR(",")); }
|
|
//AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "MAX SIZE: %d, SIZE: %d"),ResponseSize(),ResponseLength());
|
|
}
|
|
ResponseJsonEnd();
|
|
} else {
|
|
ResponseAppend_P(PSTR("\"" D_NO_NETWORKS_FOUND "\""));
|
|
}
|
|
ResponseJsonEnd();
|
|
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_CMND_WIFISCAN));
|
|
}
|
|
}
|
|
}
|
|
|
|
void CmndWifiTest(void)
|
|
{
|
|
// Test WIFI Connection to Router if Tasmota is in AP mode since in AP mode, a STA connection can be established
|
|
// at the same time for testing the connection.
|
|
|
|
#ifdef USE_WEBSERVER
|
|
if (!WifiIsInManagerMode()) { ResponseCmndError(); return; }
|
|
|
|
if ( (XdrvMailbox.data_len > 0) ) {
|
|
|
|
if (Wifi.wifiTest != WIFI_TESTING) { // Start Test
|
|
char* pos = strchr(XdrvMailbox.data, '+');
|
|
if (pos != nullptr) {
|
|
char ssid_test[XdrvMailbox.data_len];
|
|
char pswd_test[XdrvMailbox.data_len];
|
|
subStr(ssid_test, XdrvMailbox.data, "+", 1);
|
|
subStr(pswd_test, XdrvMailbox.data, "+", 2);
|
|
ResponseCmndIdxChar(D_JSON_TESTING);
|
|
//Response_P(PSTR("{\"%s%d\":{\"Network\":\"%s,\"PASS\":\"%s\"}}"), XdrvMailbox.command, XdrvMailbox.index, ssid_test, pswd_test);
|
|
|
|
if (WIFI_NOT_TESTING == Wifi.wifiTest) {
|
|
if (MAX_WIFI_OPTION == Wifi.old_wificonfig) { Wifi.old_wificonfig = Settings->sta_config; }
|
|
TasmotaGlobal.wifi_state_flag = Settings->sta_config = WIFI_MANAGER;
|
|
Wifi.save_data_counter = TasmotaGlobal.save_data_counter;
|
|
}
|
|
|
|
Wifi.wifi_test_counter = 9; // seconds to test user's proposed AP
|
|
Wifi.wifiTest = WIFI_TESTING;
|
|
TasmotaGlobal.save_data_counter = 0; // Stop auto saving data - Updating Settings
|
|
Settings->save_data = 0;
|
|
TasmotaGlobal.sleep = 0; // Disable sleep
|
|
TasmotaGlobal.restart_flag = 0; // No restart
|
|
TasmotaGlobal.ota_state_flag = 0; // No OTA
|
|
|
|
Wifi.wifi_Test_Restart = false;
|
|
Wifi.wifi_Test_Save_SSID2 = false;
|
|
if (0 == XdrvMailbox.index) { Wifi.wifi_Test_Restart = true; } // If WifiTest is successful, save data on SSID1 and restart
|
|
if (2 == XdrvMailbox.index) { Wifi.wifi_Test_Save_SSID2 = true; } // If WifiTest is successful, save data on SSID2
|
|
|
|
if (3 != XdrvMailbox.index) { // WifiTest3 never ever makes anything persistent, thus works without webserver
|
|
SettingsUpdateText(Wifi.wifi_Test_Save_SSID2 ? SET_STASSID2 : SET_STASSID1, ssid_test);
|
|
SettingsUpdateText(Wifi.wifi_Test_Save_SSID2 ? SET_STAPWD2 : SET_STAPWD1, pswd_test);
|
|
}
|
|
|
|
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECTING_TO_AP " %s " D_AS " %s ..."),
|
|
ssid_test, TasmotaGlobal.hostname);
|
|
|
|
WiFi.begin(ssid_test,pswd_test);
|
|
}
|
|
} else {
|
|
ResponseCmndChar(D_JSON_BUSY);
|
|
}
|
|
|
|
} else {
|
|
switch (Wifi.wifiTest) {
|
|
case WIFI_TESTING:
|
|
ResponseCmndChar(D_JSON_TESTING);
|
|
break;
|
|
case WIFI_NOT_TESTING:
|
|
ResponseCmndChar(D_JSON_NOT_STARTED);
|
|
break;
|
|
case WIFI_TEST_FINISHED:
|
|
ResponseCmndChar(Wifi.wifi_test_AP_TIMEOUT ? D_CONNECT_FAILED_AP_TIMEOUT : D_JSON_SUCCESSFUL);
|
|
break;
|
|
case WIFI_TEST_FINISHED_BAD:
|
|
|
|
switch (WiFi.status()) {
|
|
case WL_CONNECTED:
|
|
ResponseCmndChar(D_CONNECT_FAILED_NO_IP_ADDRESS);
|
|
break;
|
|
case WL_NO_SSID_AVAIL:
|
|
ResponseCmndChar(D_CONNECT_FAILED_AP_NOT_REACHED);
|
|
break;
|
|
case WL_CONNECT_FAILED:
|
|
ResponseCmndChar(D_CONNECT_FAILED_WRONG_PASSWORD);
|
|
break;
|
|
default: // WL_IDLE_STATUS and WL_DISCONNECTED - SSId in range but no answer from the router
|
|
ResponseCmndChar(D_CONNECT_FAILED_AP_TIMEOUT);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
#else
|
|
ResponseCmndError();
|
|
#endif //USE_WEBSERVER
|
|
}
|
|
|
|
#endif // not defined FIRMWARE_MINIMAL_ONLY
|
|
|
|
void ResponseCmnd(void) {
|
|
Response_P(PSTR("{\"%s\":"), XdrvMailbox.command);
|
|
}
|
|
|
|
void ResponseCmndNumber(int value) {
|
|
Response_P(S_JSON_COMMAND_NVALUE, XdrvMailbox.command, value);
|
|
}
|
|
|
|
void ResponseCmndFloat(float value, uint32_t decimals) {
|
|
Response_P(PSTR("{\"%s\":%*_f}"), XdrvMailbox.command, decimals, &value); // Return float value without quotes
|
|
}
|
|
|
|
void ResponseCmndIdxFloat(float value, uint32_t decimals) {
|
|
Response_P(PSTR("{\"%s%d\":%*_f}"), XdrvMailbox.command, XdrvMailbox.index, decimals, &value); // Return float value without quotes
|
|
}
|
|
|
|
void ResponseCmndIdxNumber(int value) {
|
|
Response_P(S_JSON_COMMAND_INDEX_NVALUE, XdrvMailbox.command, XdrvMailbox.index, value);
|
|
}
|
|
|
|
void ResponseCmndChar_P(const char* value) {
|
|
Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, value);
|
|
}
|
|
|
|
void ResponseCmndChar(const char* value) {
|
|
Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, EscapeJSONString(value).c_str());
|
|
}
|
|
|
|
void ResponseCmndStateText(uint32_t value) {
|
|
ResponseCmndChar(GetStateText(value));
|
|
}
|
|
|
|
void ResponseCmndDone(void) {
|
|
ResponseCmndChar_P(PSTR(D_JSON_DONE));
|
|
}
|
|
|
|
void ResponseCmndError(void) {
|
|
ResponseCmndChar_P(PSTR(D_JSON_ERROR));
|
|
}
|
|
|
|
void ResponseCmndFailed(void) {
|
|
ResponseCmndChar_P(PSTR(D_JSON_FAILED));
|
|
}
|
|
|
|
void ResponseCmndIdxChar(const char* value) {
|
|
Response_P(S_JSON_COMMAND_INDEX_SVALUE, XdrvMailbox.command, XdrvMailbox.index, EscapeJSONString(value).c_str());
|
|
}
|
|
|
|
void ResponseCmndIdxError(void) {
|
|
ResponseCmndIdxChar(PSTR(D_JSON_ERROR));
|
|
}
|
|
|
|
void ResponseCmndAll(uint32_t text_index, uint32_t count) {
|
|
uint32_t real_index = text_index;
|
|
ResponseClear();
|
|
#ifdef MQTT_DATA_STRING
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
if ((SET_MQTT_GRP_TOPIC == text_index) && (1 == i)) { real_index = SET_MQTT_GRP_TOPIC2 -1; }
|
|
if ((SET_BUTTON1 == text_index) && (16 == i)) { real_index = SET_BUTTON17 -16; }
|
|
ResponseAppend_P(PSTR("%c\"%s%d\":\"%s\""), (i)?',':'{', XdrvMailbox.command, i +1, EscapeJSONString(SettingsText(real_index +i)).c_str());
|
|
}
|
|
ResponseJsonEnd();
|
|
#else
|
|
bool jsflg = false;
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
if ((SET_MQTT_GRP_TOPIC == text_index) && (1 == i)) { real_index = SET_MQTT_GRP_TOPIC2 -1; }
|
|
if ((ResponseAppend_P(PSTR("%c\"%s%d\":\"%s\""), (jsflg)?',':'{', XdrvMailbox.command, i +1, EscapeJSONString(SettingsText(real_index +i)).c_str()) > (MAX_LOGSZ - TOPSZ)) || (i == count -1)) {
|
|
ResponseJsonEnd();
|
|
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, XdrvMailbox.command);
|
|
ResponseClear();
|
|
jsflg = false;
|
|
} else {
|
|
jsflg = true;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/********************************************************************************************/
|
|
|
|
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"
|
|
SHOW_FREE_MEM(PSTR("ExecuteCommand"));
|
|
ShowSource(source);
|
|
TasmotaGlobal.last_command_source = 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)) {
|
|
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
|
|
// 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
|
|
|
|
void CommandHandler(char* topicBuf, char* dataBuf, uint32_t data_len) {
|
|
SHOW_FREE_MEM(PSTR("CommandHandler"));
|
|
|
|
bool grpflg = false;
|
|
uint32_t real_index = SET_MQTT_GRP_TOPIC;
|
|
for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) {
|
|
if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; }
|
|
char *group_topic = SettingsText(real_index +i);
|
|
if (*group_topic && strstr(topicBuf, group_topic) != nullptr) {
|
|
grpflg = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
char stemp1[TOPSZ];
|
|
GetFallbackTopic_P(stemp1, ""); // Full Fallback topic = cmnd/DVES_xxxxxxxx_fb/
|
|
TasmotaGlobal.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++;
|
|
uint32_t i;
|
|
int nLen; // strlen(type)
|
|
char *s = type;
|
|
for (nLen = 0; *s; s++, nLen++) {
|
|
*s=toupper(*s);
|
|
}
|
|
i = nLen;
|
|
if (i > 0) { // may be 0
|
|
while (isdigit(type[i-1])) {
|
|
i--;
|
|
}
|
|
}
|
|
if (i < nLen) {
|
|
index = atoi(type + i);
|
|
user_index = true;
|
|
}
|
|
type[i] = '\0';
|
|
|
|
bool binary_data = (index > 299); // Suppose binary data on topic index > 299
|
|
if (!binary_data) {
|
|
bool keep_spaces = ((strstr_P(type, PSTR("SERIALSEND")) != nullptr) && (index > 9)); // Do not skip leading spaces on (s)serialsend10 and up
|
|
if (!keep_spaces) {
|
|
while (*dataBuf && isspace(*dataBuf)) {
|
|
dataBuf++; // Skip leading spaces in data
|
|
data_len--;
|
|
}
|
|
}
|
|
}
|
|
|
|
int32_t payload = -99;
|
|
if (!binary_data) {
|
|
if (!strcmp(dataBuf,"?")) { data_len = 0; }
|
|
|
|
char *p;
|
|
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; }
|
|
}
|
|
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("CMD: Grp %d, Cmd '%s', Idx %d, Len %d, Pld %d, Data '%s'"),
|
|
grpflg, type, index, data_len, payload, (binary_data) ? HexToString((uint8_t*)dataBuf, data_len).c_str() : dataBuf);
|
|
|
|
Response_P(PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_ERROR "\"}"));
|
|
|
|
if (Settings->ledstate &0x02) { TasmotaGlobal.blinks++; }
|
|
|
|
// TasmotaGlobal.backlog_timer = millis() + (100 * MIN_BACKLOG_DELAY);
|
|
TasmotaGlobal.backlog_timer = millis() + Settings->param[P_BACKLOG_DELAY]; // SetOption34
|
|
|
|
char command[CMDSZ] = { 0 };
|
|
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;
|
|
|
|
#ifdef USE_SCRIPT_SUB_COMMAND
|
|
// allow overwrite tasmota cmds
|
|
if (!Script_SubCmd()) {
|
|
if (!DecodeCommand(kTasmotaCommands, TasmotaCommand, kTasmotaSynonyms)) {
|
|
if (!XdrvCall(FUNC_COMMAND)) {
|
|
if (!XsnsCall(FUNC_COMMAND)) {
|
|
type = nullptr; // Unknown command
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#else // USE_SCRIPT_SUB_COMMAND
|
|
if (!DecodeCommand(kTasmotaCommands, TasmotaCommand, kTasmotaSynonyms)) {
|
|
if (!XdrvCall(FUNC_COMMAND)) {
|
|
if (!XsnsCall(FUNC_COMMAND)) {
|
|
type = nullptr; // Unknown command
|
|
}
|
|
}
|
|
}
|
|
#endif // USE_SCRIPT_SUB_COMMAND
|
|
|
|
}
|
|
|
|
if (type == nullptr) {
|
|
TasmotaGlobal.blinks = 201;
|
|
snprintf_P(stemp1, sizeof(stemp1), PSTR(D_JSON_COMMAND));
|
|
Response_P(PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_UNKNOWN "\"}"));
|
|
type = (char*)stemp1;
|
|
}
|
|
|
|
if (ResponseLength()) {
|
|
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, type);
|
|
}
|
|
TasmotaGlobal.fallback_topic_flag = false;
|
|
}
|
|
|
|
void CmndBacklog(void) {
|
|
// Backlog command1;command2;.. Execute commands in sequence with a delay in between set with SetOption34
|
|
// Backlog0 command1;command2;.. Execute commands in sequence with no delay
|
|
|
|
if (XdrvMailbox.data_len) {
|
|
if (0 == XdrvMailbox.index) {
|
|
TasmotaGlobal.backlog_nodelay = true;
|
|
}
|
|
|
|
char *blcommand = strtok(XdrvMailbox.data, ";");
|
|
while (blcommand != nullptr) {
|
|
// Ignore semicolon (; = end of single command) between brackets {}
|
|
char *next = strchr(blcommand, '\0') +1; // Prepare for next ;
|
|
while ((next != nullptr) && (ChrCount(blcommand, "{") != ChrCount(blcommand, "}"))) { // Check for valid {} pair
|
|
next--; // Select end of line
|
|
*next = ';'; // Restore ; removed by strtok()
|
|
next = strtok(nullptr, ";"); // Point to begin of next string up to next ; or nullptr
|
|
}
|
|
// Skip unnecessary command Backlog at start of blcommand
|
|
while(true) {
|
|
blcommand = Trim(blcommand);
|
|
if (0 == strncasecmp_P(blcommand, PSTR(D_CMND_BACKLOG), strlen(D_CMND_BACKLOG))) {
|
|
blcommand += strlen(D_CMND_BACKLOG);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
// Do not allow command Reset in backlog
|
|
if ((*blcommand != '\0') && (strncasecmp_P(blcommand, PSTR(D_CMND_RESET), strlen(D_CMND_RESET)) != 0)) {
|
|
char* temp = (char*)malloc(strlen(blcommand)+1);
|
|
if (temp != nullptr) {
|
|
strcpy(temp, blcommand);
|
|
char* &elem = backlog.addToLast();
|
|
elem = temp;
|
|
}
|
|
}
|
|
blcommand = strtok(nullptr, ";");
|
|
}
|
|
// ResponseCmndChar(D_JSON_APPENDED);
|
|
ResponseClear();
|
|
TasmotaGlobal.backlog_timer = millis();
|
|
} else {
|
|
bool blflag = BACKLOG_EMPTY;
|
|
for (auto &elem : backlog) {
|
|
free(elem);
|
|
backlog.remove(&elem);
|
|
}
|
|
ResponseCmndChar(blflag ? PSTR(D_JSON_EMPTY) : PSTR(D_JSON_ABORTED));
|
|
}
|
|
}
|
|
|
|
void CmndJson(void) {
|
|
// Json {"template":{"NAME":"Dummy","GPIO":[320,0,321],"FLAG":0,"BASE":18},"power":2,"HSBColor":"51,97,100","Channel":[100,85,3]}
|
|
//
|
|
// Escape lower level tokens and add quotes around it
|
|
// Input:
|
|
// {"template":{"NAME":"Dummy","GPIO":[320,0,321],"FLAG":0,"BASE":18},"power":2,"HSBColor":"51,97,100","Channel":[100,85,3]}
|
|
// Output (escaped subtokens):
|
|
// {"template":"{\"NAME\":\"Dummy\",\"GPIO\":[320,0,321],\"FLAG\":0,\"BASE\":18}","power":2,"HSBColor":"51,97,100","Channel":[100,85,3]}
|
|
uint32_t bracket = 0;
|
|
String data_buf("");
|
|
data_buf.reserve(XdrvMailbox.data_len); // We need at least the same amount of characters
|
|
for (uint32_t index = 0; index < XdrvMailbox.data_len; index++) {
|
|
char c = (char)XdrvMailbox.data[index];
|
|
if (c == '{') {
|
|
bracket++;
|
|
if (2 == bracket) { data_buf += '"'; } // Add start quote
|
|
}
|
|
if (bracket > 1) {
|
|
if (c == '\"') { data_buf += '\\'; } // Escape any quote within second level token
|
|
}
|
|
data_buf += c;
|
|
if (c == '}') {
|
|
bracket--;
|
|
if (1 == bracket) { data_buf += '"'; } // Add end quote
|
|
}
|
|
}
|
|
|
|
JsonParser parser((char*)data_buf.c_str());
|
|
JsonParserObject root = parser.getRootObject();
|
|
if (root) {
|
|
// Convert to backlog commands
|
|
// Input (escaped subtokens):
|
|
// {"template":"{\"NAME\":\"Dummy\",\"GPIO\":[320,0,321],\"FLAG\":0,\"BASE\":18}","power":2,"HSBColor":"51,97,100","Channel":[100,85,3]}
|
|
// Output:
|
|
// template {"NAME":"Dummy","GPIO":[320,0,321],"FLAG":0,"BASE":18};power 2;HSBColor 51,97,100;Channel1 100;Channel2 85;Channel3 3
|
|
String backlog; // We might need a larger string than XdrvMailbox.data_len accomodating decoded arrays
|
|
for (auto command_key : root) {
|
|
const char *command = command_key.getStr();
|
|
JsonParserToken parameters = command_key.getValue();
|
|
if (parameters.isArray()) {
|
|
JsonParserArray parameter_arr = parameters.getArray();
|
|
uint32_t index = 1;
|
|
for (auto value : parameter_arr) {
|
|
if (backlog.length()) { backlog += ";"; }
|
|
backlog += command;
|
|
backlog += index++;
|
|
backlog += " ";
|
|
backlog += value.getStr(); // Channel1 100;Channel2 85;Channel3 3
|
|
}
|
|
} else if (parameters.isObject()) { // Should have been escaped
|
|
// AddLog(LOG_LEVEL_DEBUG, PSTR("JSN: Object"));
|
|
} else {
|
|
String cmnd_param = command;
|
|
cmnd_param += " ";
|
|
cmnd_param += parameters.getStr();
|
|
if (cmnd_param.indexOf(";") == -1) { // Rule1 ON Clock#Timer=1 DO Backlog Color #FF000000D0; Wakeup 100 ENDON
|
|
if (backlog.length()) { backlog += ";"; }
|
|
backlog += cmnd_param; // HSBColor 51,97,100
|
|
} else {
|
|
ExecuteCommand((char*)cmnd_param.c_str(), SRC_FILE);
|
|
}
|
|
}
|
|
}
|
|
if (backlog.length()) {
|
|
XdrvMailbox.data = (char*)backlog.c_str(); // Backlog commands
|
|
XdrvMailbox.data_len = 1; // Any data
|
|
XdrvMailbox.index = 0; // Backlog0 - no delay
|
|
CmndBacklog();
|
|
}
|
|
} else {
|
|
ResponseCmndChar(PSTR(D_JSON_EMPTY));
|
|
}
|
|
}
|
|
|
|
void CmndDelay(void) {
|
|
// Delay -1 - Wait until next second
|
|
// Delay 1 - Wait default time (200ms)
|
|
// Delay 2 - Wait 2 x 100ms
|
|
// Delay 10 - Wait 10 x 100ms
|
|
if (XdrvMailbox.payload == -1) {
|
|
TasmotaGlobal.backlog_timer = millis() + (1000 - RtcMillis()); // Next second (#18984)
|
|
}
|
|
else if ((XdrvMailbox.payload >= (MIN_BACKLOG_DELAY / 100)) && (XdrvMailbox.payload <= 3600)) {
|
|
TasmotaGlobal.backlog_timer = millis() + (100 * XdrvMailbox.payload);
|
|
}
|
|
uint32_t bl_delay = 0;
|
|
long bl_delta = TimePassedSince(TasmotaGlobal.backlog_timer);
|
|
if (bl_delta < 0) { bl_delay = (bl_delta *-1) / 100; }
|
|
ResponseCmndNumber(bl_delay);
|
|
}
|
|
|
|
void CmndPower(void)
|
|
{
|
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.devices_present)) {
|
|
if ((XdrvMailbox.payload < POWER_OFF) || (XdrvMailbox.payload > POWER_BLINK_STOP)) {
|
|
XdrvMailbox.payload = POWER_SHOW_STATE;
|
|
}
|
|
// Settings->flag.device_index_enable = XdrvMailbox.usridx; // SetOption26 - Switch between POWER or POWER1
|
|
ExecuteCommandPower(XdrvMailbox.index, XdrvMailbox.payload, SRC_IGNORE);
|
|
ResponseClear();
|
|
}
|
|
else if (0 == XdrvMailbox.index) {
|
|
if ((XdrvMailbox.payload < POWER_OFF) || (XdrvMailbox.payload > POWER_TOGGLE)) {
|
|
XdrvMailbox.payload = POWER_SHOW_STATE;
|
|
}
|
|
SetAllPower(XdrvMailbox.payload, SRC_IGNORE);
|
|
if (Settings->flag3.hass_tele_on_power) { // SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT
|
|
MqttPublishTeleState();
|
|
}
|
|
ResponseClear();
|
|
}
|
|
}
|
|
|
|
/********************************************************************************************/
|
|
|
|
typedef struct {
|
|
uint32_t time;
|
|
char* command;
|
|
} tTimedCmnd;
|
|
LList<tTimedCmnd> timed_cmnd; // Timed command buffer
|
|
|
|
bool SetTimedCmnd(uint32_t time, const char *command) {
|
|
// Remove command if present
|
|
for (auto &elem : timed_cmnd) {
|
|
if (strcmp(command, elem.command) == 0) { // Equal
|
|
free(elem.command);
|
|
timed_cmnd.remove(&elem);
|
|
break;
|
|
}
|
|
}
|
|
// Add command
|
|
char* cmnd = (char*)malloc(strlen(command) +1);
|
|
if (cmnd) {
|
|
strcpy(cmnd, command);
|
|
tTimedCmnd &elem = timed_cmnd.addToLast();
|
|
elem.time = millis() + time;
|
|
elem.command = cmnd;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ResetTimedCmnd(const char *command) {
|
|
for (auto &elem : timed_cmnd) {
|
|
if (strncmp(command, elem.command, strlen(command)) == 0) { // StartsWith
|
|
free(elem.command);
|
|
timed_cmnd.remove(&elem);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ShowTimedCmnd(const char *command) {
|
|
bool found = false;
|
|
uint32_t now = millis();
|
|
ResponseCmnd(); // {"TimedPower":
|
|
for (auto &elem : timed_cmnd) {
|
|
if (strncmp(command, elem.command, strlen(command)) == 0) { // StartsWith
|
|
ResponseAppend_P(PSTR("%s{\"" D_JSON_REMAINING "\":%d,\"" D_JSON_COMMAND "\":\"%s\"}"),
|
|
(found) ? "," : "[", elem.time - now, elem.command);
|
|
found = true;
|
|
}
|
|
}
|
|
ResponseAppend_P((found) ? PSTR("]}") : PSTR("\"" D_JSON_EMPTY "\"}"));
|
|
}
|
|
|
|
void LoopTimedCmnd(void) {
|
|
for (auto &elem : timed_cmnd) {
|
|
if (TimeReached(elem.time)) {
|
|
char* command = elem.command;
|
|
timed_cmnd.remove(&elem);
|
|
ExecuteCommand(command, SRC_TIMER);
|
|
free(command);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------------------*/
|
|
|
|
void CmndTimedPower(void) {
|
|
/*
|
|
Allow timed power changes on a 50ms granularity
|
|
TimedPower<index> <milliseconds>[,0|1|2|3]
|
|
TimedPower - Show remaining timers
|
|
TimedPower 2000 - Turn power1 on and then off after 2 seconds
|
|
TimedPower1 - Clear active Power1 timers
|
|
TimedPower1 0 - Stop timer and perform timed action
|
|
TimedPower0 3000 - Turn all power on and then off after 3 seconds
|
|
TimedPower1 2000 - Turn power1 on and then off after 2 seconds
|
|
TimedPower2 2000,0|off - Turn power2 off and then on after 2 seconds
|
|
TimedPower1 2200,1|on - Turn power1 on and then off after 2.2 seconds
|
|
TimedPower2 2000,2|toggle - Toggle power2 and then toggle again after 2 seconds
|
|
TimedPower2 2500,3|blink - Blink power2 and then stop blink after 2.5 seconds
|
|
*/
|
|
if ((XdrvMailbox.index >= 0) && (XdrvMailbox.index <= TasmotaGlobal.devices_present)) {
|
|
if (XdrvMailbox.data_len > 0) {
|
|
uint32_t time = (XdrvMailbox.payload < 50) ? 50 : XdrvMailbox.payload;
|
|
int start_state = POWER_ON; // Default on
|
|
if (ArgC() > 1) {
|
|
char state_text[XdrvMailbox.data_len];
|
|
ArgV(state_text, 2);
|
|
start_state = GetStateNumber(Trim(state_text));
|
|
if (start_state < 0) {
|
|
start_state = atoi(state_text);
|
|
}
|
|
start_state &= 0x03; // POWER_OFF, POWER_ON, POWER_TOGGLE, POWER_BLINK
|
|
}
|
|
const uint8_t end_state[] = { POWER_ON, POWER_OFF, POWER_TOGGLE, POWER_BLINK_STOP };
|
|
char cmnd[CMDSZ];
|
|
snprintf_P(cmnd, sizeof(cmnd), PSTR(D_CMND_POWER "%d %d"), XdrvMailbox.index, end_state[start_state]);
|
|
if (SetTimedCmnd(time, cmnd)) { // Skip if no more room for timers
|
|
XdrvMailbox.payload = start_state;
|
|
CmndPower();
|
|
}
|
|
} else {
|
|
if (!XdrvMailbox.usridx) {
|
|
// ResetTimedCmnd(D_CMND_POWER); // Remove all POWER timed command
|
|
ShowTimedCmnd(D_CMND_POWER); // Show remaining timers
|
|
return;
|
|
} else {
|
|
char cmnd[CMDSZ];
|
|
snprintf_P(cmnd, sizeof(cmnd), PSTR(D_CMND_POWER "%d"), XdrvMailbox.index);
|
|
ResetTimedCmnd(cmnd); // Remove POWER<index> timed command
|
|
}
|
|
ResponseCmndDone();
|
|
}
|
|
}
|
|
}
|
|
|
|
/********************************************************************************************/
|
|
|
|
void CmndStatusResponse(uint32_t index) {
|
|
static String all_status = (const char*) nullptr;
|
|
|
|
if (0 == XdrvMailbox.index) { // Command status0
|
|
if (99 == index) {
|
|
all_status.replace("}{", ",");
|
|
char cmnd_status[10]; // STATUS11
|
|
snprintf_P(cmnd_status, sizeof(cmnd_status), PSTR(D_CMND_STATUS "0"));
|
|
MqttPublishPayloadPrefixTopicRulesProcess_P(STAT, cmnd_status, all_status.c_str(), Settings->flag5.mqtt_status_retain);
|
|
all_status = (const char*) nullptr;
|
|
} else {
|
|
if (0 == index) { all_status = ""; }
|
|
all_status += ResponseData();
|
|
}
|
|
}
|
|
else if (index < 99) {
|
|
char cmnd_status[10]; // STATUS11
|
|
char number[4] = { 0 };
|
|
snprintf_P(cmnd_status, sizeof(cmnd_status), PSTR(D_CMND_STATUS "%s"), (index) ? itoa(index, number, 10) : "");
|
|
MqttPublishPrefixTopicRulesProcess_P(STAT, cmnd_status, Settings->flag5.mqtt_status_retain);
|
|
}
|
|
}
|
|
|
|
void CmndStatus(void)
|
|
{
|
|
int32_t payload = XdrvMailbox.payload;
|
|
|
|
if (0 == XdrvMailbox.index) { payload = 0; } // All status messages in one MQTT message (status0)
|
|
|
|
if (payload > MAX_STATUS) { return; } // {"Command":"Error"}
|
|
if (!Settings->flag.mqtt_enabled && (6 == payload)) { return; } // SetOption3 - Enable MQTT
|
|
if (!TasmotaGlobal.energy_driver && (9 == payload)) { return; }
|
|
if (!CrashFlag() && (12 == payload)) { return; }
|
|
if (!Settings->flag3.shutter_mode && (13 == payload)) { return; }
|
|
|
|
char stemp[200];
|
|
char stemp2[TOPSZ];
|
|
|
|
if ((0 == payload) || (-99 == payload)) {
|
|
uint32_t maxfn = (TasmotaGlobal.devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!TasmotaGlobal.devices_present) ? 1 : TasmotaGlobal.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 ? "," : ""), EscapeJSONString(SettingsText(SET_FRIENDLYNAME1 +i)).c_str());
|
|
}
|
|
stemp2[0] = '\0';
|
|
for (uint32_t i = 0; i < MAX_SWITCHES_SET; 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_DEVICENAME "\":\"%s\",\"" 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,\""
|
|
D_CMND_INFORETAIN "\":%d,\"" D_CMND_STATERETAIN "\":%d,\"" D_CMND_STATUSRETAIN "\":%d}}"),
|
|
ModuleNr(), EscapeJSONString(SettingsText(SET_DEVICENAME)).c_str(), stemp, TasmotaGlobal.mqtt_topic,
|
|
SettingsText(SET_MQTT_BUTTON_TOPIC), TasmotaGlobal.power, Settings->poweronstate, Settings->ledstate,
|
|
Settings->ledmask, Settings->save_data,
|
|
Settings->flag.save_state, // SetOption0 - Save power state and use after restart
|
|
SettingsText(SET_MQTT_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
|
|
Settings->flag5.mqtt_info_retain, // CMND_INFORETAIN
|
|
Settings->flag5.mqtt_state_retain, // CMND_STATERETAIN
|
|
Settings->flag5.mqtt_status_retain // CMND_STATUSRETAIN
|
|
);
|
|
CmndStatusResponse(0);
|
|
}
|
|
|
|
if ((0 == payload) || (1 == payload)) {
|
|
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS1_PARAMETER "\":{\"" D_JSON_BAUDRATE "\":%d,\"" D_CMND_SERIALCONFIG "\":\"%s\",\"" 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,\"BCResetTime\":\"%s\",\"" D_JSON_SAVECOUNT "\":%d"
|
|
#ifdef ESP8266
|
|
",\"" D_JSON_SAVEADDRESS "\":\"%X\""
|
|
#endif
|
|
"}}"),
|
|
TasmotaGlobal.baudrate, GetSerialConfig().c_str(), SettingsText(SET_MQTT_GRP_TOPIC), SettingsText(SET_OTAURL),
|
|
GetResetReason().c_str(), GetUptime().c_str(), GetDateAndTime(DT_RESTART).c_str(), Settings->sleep,
|
|
Settings->cfg_holder, Settings->bootcount, GetDateAndTime(DT_BOOTCOUNT).c_str(), Settings->save_flag
|
|
#ifdef ESP8266
|
|
, GetSettingsAddress()
|
|
#endif
|
|
);
|
|
CmndStatusResponse(1);
|
|
}
|
|
|
|
if ((0 == payload) || (2 == payload)) {
|
|
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS2_FIRMWARE "\":{\"" D_JSON_VERSION "\":\"%s%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\""
|
|
#ifdef ESP8266
|
|
",\"" D_JSON_BOOTVERSION "\":%d"
|
|
#endif
|
|
",\"" D_JSON_COREVERSION "\":\"" ARDUINO_CORE_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\","
|
|
"\"CpuFrequency\":%d,\"Hardware\":\"%s\""
|
|
"%s}}"),
|
|
TasmotaGlobal.version, TasmotaGlobal.image_name, GetCodeCores().c_str(), GetBuildDateAndTime().c_str()
|
|
#ifdef ESP8266
|
|
, ESP.getBootVersion()
|
|
#endif
|
|
, ESP.getSdkVersion(),
|
|
ESP.getCpuFreqMHz(), GetDeviceHardwareRevision().c_str(),
|
|
GetStatistics().c_str());
|
|
CmndStatusResponse(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,\""
|
|
D_JSON_RESOLUTION "\":\"%08X\",\"" D_CMND_SETOPTION "\":[\"%08X\",\"%s\",\"%08X\",\"%08X\",\"%08X\",\"%08X\"]}}"),
|
|
Settings->seriallog_level, Settings->weblog_level, Settings->mqttlog_level, Settings->syslog_level,
|
|
SettingsText(SET_SYSLOG_HOST), Settings->syslog_port, EscapeJSONString(SettingsText(SET_STASSID1)).c_str(), EscapeJSONString(SettingsText(SET_STASSID2)).c_str(), Settings->tele_period,
|
|
Settings->flag2.data, Settings->flag.data, ToHex_P((unsigned char*)Settings->param, PARAM8_SIZE, stemp2, sizeof(stemp2)),
|
|
Settings->flag3.data, Settings->flag4.data, Settings->flag5.data, Settings->flag6.data);
|
|
CmndStatusResponse(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,\""
|
|
#ifdef ESP32
|
|
D_JSON_STACKLOWMARK "\":%d,\"" D_JSON_PSRMAXMEMORY "\":%d,\"" D_JSON_PSRFREEMEMORY "\":%d,\""
|
|
#endif // ESP32
|
|
D_JSON_PROGRAMFLASHSIZE "\":%d,\"" D_JSON_FLASHSIZE "\":%d"
|
|
",\"" D_JSON_FLASHCHIPID "\":\"%06X\""
|
|
",\"FlashFrequency\":%d,\"" D_JSON_FLASHMODE "\":\"" D_TASMOTA_FLASHMODE "\""),
|
|
ESP_getSketchSize()/1024, ESP_getFreeSketchSpace()/1024, ESP_getFreeHeap1024(),
|
|
#ifdef ESP32
|
|
uxTaskGetStackHighWaterMark(nullptr) / 1024, ESP.getPsramSize()/1024, ESP.getFreePsram()/1024,
|
|
ESP_getFlashChipMagicSize()/1024, ESP.getFlashChipSize()/1024
|
|
#endif // ESP32
|
|
#ifdef ESP8266
|
|
ESP_getFlashChipSize()/1024, ESP.getFlashChipRealSize()/1024
|
|
#endif // ESP8266
|
|
, ESP_getFlashChipId()
|
|
, ESP.getFlashChipSpeed()/1000000);
|
|
ResponseAppendFeatures();
|
|
XsnsDriverState();
|
|
ResponseAppend_P(PSTR(",\"Sensors\":"));
|
|
XsnsSensorState(0);
|
|
#ifdef USE_I2C
|
|
ResponseAppend_P(PSTR(",\"" D_CMND_I2CDRIVER "\":"));
|
|
I2cDriverState();
|
|
#endif
|
|
ResponseJsonEndEnd();
|
|
CmndStatusResponse(4);
|
|
}
|
|
|
|
if ((0 == payload) || (5 == payload)) {
|
|
#ifdef USE_IPV6
|
|
if (5 == payload) { WifiDumpAddressesIPv6(); }
|
|
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS5_NETWORK "\":{\"" D_CMND_HOSTNAME "\":\"%s\",\""
|
|
D_CMND_IPADDRESS "\":\"%_I\",\"" D_JSON_GATEWAY "\":\"%_I\",\"" D_JSON_SUBNETMASK "\":\"%_I\",\""
|
|
D_JSON_DNSSERVER "1\":\"%s\",\"" D_JSON_DNSSERVER "2\":\"%s\",\""
|
|
D_JSON_MAC "\":\"%s\""
|
|
",\"" D_JSON_IP6_GLOBAL "\":\"%s\",\"" D_JSON_IP6_LOCAL "\":\"%s\""),
|
|
TasmotaGlobal.hostname,
|
|
(uint32_t)WiFi.localIP(), Settings->ipv4_address[1], Settings->ipv4_address[2],
|
|
DNSGetIPStr(0).c_str(), DNSGetIPStr(1).c_str(),
|
|
WiFi.macAddress().c_str()
|
|
,WifiGetIPv6Str().c_str(), WifiGetIPv6LinkLocalStr().c_str());
|
|
#else // USE_IPV6
|
|
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS5_NETWORK "\":{\"" D_CMND_HOSTNAME "\":\"%s\",\""
|
|
D_CMND_IPADDRESS "\":\"%_I\",\"" D_JSON_GATEWAY "\":\"%_I\",\"" D_JSON_SUBNETMASK "\":\"%_I\",\""
|
|
D_JSON_DNSSERVER "1\":\"%_I\",\"" D_JSON_DNSSERVER "2\":\"%_I\",\""
|
|
D_JSON_MAC "\":\"%s\""),
|
|
TasmotaGlobal.hostname,
|
|
(uint32_t)WiFi.localIP(), Settings->ipv4_address[1], Settings->ipv4_address[2],
|
|
Settings->ipv4_address[3], Settings->ipv4_address[4],
|
|
WiFi.macAddress().c_str());
|
|
#endif // USE_IPV6
|
|
#ifdef USE_TASMESH
|
|
ResponseAppend_P(PSTR(",\"SoftAPMac\":\"%s\""), WiFi.softAPmacAddress().c_str());
|
|
#endif // USE_TASMESH
|
|
//#if defined(ESP32) && CONFIG_IDF_TARGET_ESP32 && defined(USE_ETHERNET)
|
|
#if defined(ESP32) && defined(USE_ETHERNET)
|
|
#ifdef USE_IPV6
|
|
ResponseAppend_P(PSTR(",\"Ethernet\":{\"" D_CMND_HOSTNAME "\":\"%s\",\""
|
|
D_CMND_IPADDRESS "\":\"%_I\",\"" D_JSON_GATEWAY "\":\"%_I\",\"" D_JSON_SUBNETMASK "\":\"%_I\",\""
|
|
D_JSON_DNSSERVER "1\":\"%s\",\"" D_JSON_DNSSERVER "2\":\"%s\",\""
|
|
D_JSON_MAC "\":\"%s\",\"" D_JSON_IP6_GLOBAL "\":\"%s\",\"" D_JSON_IP6_LOCAL "\":\"%s\"}"),
|
|
EthernetHostname(),
|
|
(uint32_t)EthernetLocalIP(), Settings->eth_ipv4_address[1], Settings->eth_ipv4_address[2],
|
|
DNSGetIPStr(0).c_str(), DNSGetIPStr(1).c_str(),
|
|
EthernetMacAddress().c_str(),
|
|
EthernetGetIPv6Str().c_str(), EthernetGetIPv6LinkLocalStr().c_str());
|
|
#else // USE_IPV6
|
|
ResponseAppend_P(PSTR(",\"Ethernet\":{\"" D_CMND_HOSTNAME "\":\"%s\",\""
|
|
D_CMND_IPADDRESS "\":\"%_I\",\"" D_JSON_GATEWAY "\":\"%_I\",\"" D_JSON_SUBNETMASK "\":\"%_I\",\""
|
|
D_JSON_DNSSERVER "1\":\"%_I\",\"" D_JSON_DNSSERVER "2\":\"%_I\",\""
|
|
D_JSON_MAC "\":\"%s\"}"),
|
|
EthernetHostname(),
|
|
(uint32_t)EthernetLocalIP(), Settings->eth_ipv4_address[1], Settings->eth_ipv4_address[2],
|
|
Settings->eth_ipv4_address[3], Settings->eth_ipv4_address[4],
|
|
EthernetMacAddress().c_str());
|
|
|
|
#endif // USE_IPV6
|
|
#endif // USE_ETHERNET
|
|
float wifi_tx_power = WifiGetOutputPower();
|
|
ResponseAppend_P(PSTR(",\"" D_CMND_WEBSERVER "\":%d,\"HTTP_API\":%d,\"" D_CMND_WIFICONFIG "\":%d,\"" D_CMND_WIFIPOWER "\":%1_f}}"),
|
|
Settings->webserver, Settings->flag5.disable_referer_chk, Settings->sta_config, &wifi_tx_power);
|
|
CmndStatusResponse(5);
|
|
}
|
|
|
|
if (((0 == payload) || (6 == payload)) && Settings->flag.mqtt_enabled) { // SetOption3 - Enable MQTT
|
|
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,\"SOCKET_TIMEOUT\":%d}}"),
|
|
SettingsText(SET_MQTT_HOST), Settings->mqtt_port, EscapeJSONString(SettingsText(SET_MQTT_CLIENT)).c_str(),
|
|
TasmotaGlobal.mqtt_client, EscapeJSONString(SettingsText(SET_MQTT_USER)).c_str(), MqttConnectCount(), MQTT_MAX_PACKET_SIZE, Settings->mqtt_keepalive, Settings->mqtt_socket_timeout);
|
|
CmndStatusResponse(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\"}}"),
|
|
GetDateAndTime(DT_UTC).c_str(), GetDateAndTime(DT_LOCALNOTZ).c_str(), GetDateAndTime(DT_DST).c_str(),
|
|
GetDateAndTime(DT_STD).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}}"),
|
|
GetDateAndTime(DT_UTC).c_str(), GetDateAndTime(DT_LOCALNOTZ).c_str(), GetDateAndTime(DT_DST).c_str(),
|
|
GetDateAndTime(DT_STD).c_str(), stemp);
|
|
#endif // USE_TIMERS and USE_SUNRISE
|
|
CmndStatusResponse(7);
|
|
}
|
|
|
|
#if defined(USE_ENERGY_SENSOR) && defined(USE_ENERGY_MARGIN_DETECTION)
|
|
if (TasmotaGlobal.energy_driver) {
|
|
if ((0 == payload) || (9 == payload)) {
|
|
EnergyMarginStatus();
|
|
CmndStatusResponse(9);
|
|
}
|
|
}
|
|
#endif // USE_ENERGY_MARGIN_DETECTION
|
|
|
|
if ((0 == payload) || (8 == payload) || (10 == payload)) {
|
|
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS10_SENSOR "\":"));
|
|
MqttShowSensor(true);
|
|
ResponseJsonEnd();
|
|
CmndStatusResponse((8 == payload) ? 8 : 10);
|
|
}
|
|
|
|
if ((0 == payload) || (11 == payload)) {
|
|
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS11_STATUS "\":"));
|
|
MqttShowState();
|
|
ResponseJsonEnd();
|
|
CmndStatusResponse(11);
|
|
}
|
|
|
|
if (CrashFlag()) {
|
|
if ((0 == payload) || (12 == payload)) {
|
|
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS12_STATUS "\":"));
|
|
CrashDump();
|
|
ResponseJsonEnd();
|
|
CmndStatusResponse(12);
|
|
}
|
|
}
|
|
|
|
#ifdef USE_SHUTTER
|
|
if ((0 == payload) || (13 == payload)) {
|
|
if (ShutterStatus()) { CmndStatusResponse(13); }
|
|
}
|
|
#endif
|
|
|
|
CmndStatusResponse(99);
|
|
|
|
ResponseClear();
|
|
}
|
|
|
|
void CmndState(void)
|
|
{
|
|
ResponseClear();
|
|
MqttShowState();
|
|
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), Settings->flag5.mqtt_state_retain);
|
|
}
|
|
#ifdef USE_HOME_ASSISTANT
|
|
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 CmndHumOffset(void)
|
|
{
|
|
if (XdrvMailbox.data_len > 0) {
|
|
int value = (int)(CharToFloat(XdrvMailbox.data) * 10);
|
|
if ((value > -101) && (value < 101)) {
|
|
Settings->hum_comp = value;
|
|
}
|
|
}
|
|
ResponseCmndFloat((float)(Settings->hum_comp) / 10, 1);
|
|
}
|
|
|
|
void CmndGlobalTemp(void) {
|
|
if (2 == XdrvMailbox.index) {
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 251)) {
|
|
Settings->global_sensor_index[0] = XdrvMailbox.payload;
|
|
TasmotaGlobal.user_globals[0] = 0;
|
|
}
|
|
ResponseCmndIdxNumber(Settings->global_sensor_index[0]);
|
|
} else {
|
|
if (XdrvMailbox.data_len > 0) {
|
|
// Set temperature based on SO8 (Celsius or Fahrenheit)
|
|
float temperature = ConvertTempToCelsius(CharToFloat(XdrvMailbox.data));
|
|
// Temperature is now Celsius
|
|
if ((temperature >= -50.0f) && (temperature <= 100.0f)) {
|
|
TasmotaGlobal.temperature_celsius = temperature;
|
|
TasmotaGlobal.global_update = TasmotaGlobal.uptime;
|
|
TasmotaGlobal.user_globals[0] = 1;
|
|
}
|
|
}
|
|
ResponseCmndFloat(TasmotaGlobal.temperature_celsius, 1);
|
|
}
|
|
}
|
|
|
|
void CmndGlobalHum(void) {
|
|
if (2 == XdrvMailbox.index) {
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 251)) {
|
|
Settings->global_sensor_index[1] = XdrvMailbox.payload;
|
|
TasmotaGlobal.user_globals[1] = 0;
|
|
}
|
|
ResponseCmndIdxNumber(Settings->global_sensor_index[1]);
|
|
} else {
|
|
if (XdrvMailbox.data_len > 0) {
|
|
float humidity = CharToFloat(XdrvMailbox.data);
|
|
if ((humidity >= 0.0f) && (humidity <= 100.0f)) {
|
|
TasmotaGlobal.humidity = humidity;
|
|
TasmotaGlobal.global_update = TasmotaGlobal.uptime;
|
|
TasmotaGlobal.user_globals[1] = 1;
|
|
}
|
|
}
|
|
ResponseCmndFloat(TasmotaGlobal.humidity, 1);
|
|
}
|
|
}
|
|
|
|
void CmndGlobalPress(void) {
|
|
if (2 == XdrvMailbox.index) {
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 251)) {
|
|
Settings->global_sensor_index[2] = XdrvMailbox.payload;
|
|
TasmotaGlobal.user_globals[2] = 0;
|
|
}
|
|
ResponseCmndIdxNumber(Settings->global_sensor_index[2]);
|
|
} else {
|
|
if (XdrvMailbox.data_len > 0) {
|
|
// Set pressure based on SO24 (hPa or mmHg (or inHg based on SO139))
|
|
float pressure = ConvertHgToHpa(CharToFloat(XdrvMailbox.data));
|
|
// Pressure is now hPa
|
|
if ((pressure >= 0.0f) && (pressure <= 1200.0f)) {
|
|
TasmotaGlobal.pressure_hpa = pressure;
|
|
TasmotaGlobal.global_update = TasmotaGlobal.uptime;
|
|
TasmotaGlobal.user_globals[2] = 1;
|
|
}
|
|
}
|
|
ResponseCmndFloat(TasmotaGlobal.pressure_hpa, 1);
|
|
}
|
|
}
|
|
|
|
void CmndSleep(void)
|
|
{
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 251)) {
|
|
Settings->sleep = XdrvMailbox.payload;
|
|
TasmotaGlobal.sleep = XdrvMailbox.payload;
|
|
WiFiSetSleepMode();
|
|
}
|
|
Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, XdrvMailbox.command, Settings->sleep, TasmotaGlobal.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.
|
|
// Upload 1 - OTA upload binary
|
|
// Upload 2 - (ESP32 only) OTA upload safeboot binary if partition is present
|
|
if (((1 == XdrvMailbox.data_len) && (1 == XdrvMailbox.payload)) || ((XdrvMailbox.data_len >= 3) && NewerVersion(XdrvMailbox.data))) {
|
|
#ifdef ESP32
|
|
TasmotaGlobal.ota_factory = false; // Reset in case of failed safeboot upgrade
|
|
#endif // ESP32 and WEBCLIENT_HTTPS
|
|
TasmotaGlobal.ota_state_flag = 3;
|
|
char stemp1[TOPSZ];
|
|
Response_P(PSTR("{\"%s\":\"" D_JSON_VERSION " %s " D_JSON_FROM " %s\"}"), XdrvMailbox.command, TasmotaGlobal.version, GetOtaUrl(stemp1, sizeof(stemp1)));
|
|
}
|
|
#if defined(ESP32) && defined(USE_WEBCLIENT_HTTPS)
|
|
else if (EspSingleOtaPartition() && !EspRunningFactoryPartition() && (1 == XdrvMailbox.data_len) && (2 == XdrvMailbox.payload)) {
|
|
TasmotaGlobal.ota_factory = true;
|
|
TasmotaGlobal.ota_state_flag = 3;
|
|
ResponseCmndChar(PSTR("Safeboot"));
|
|
}
|
|
#endif // ESP32 and WEBCLIENT_HTTPS
|
|
else {
|
|
Response_P(PSTR("{\"%s\":\"" D_JSON_ONE_OR_GT "\"}"), XdrvMailbox.command, TasmotaGlobal.version);
|
|
}
|
|
}
|
|
|
|
void CmndOtaUrl(void)
|
|
{
|
|
if (XdrvMailbox.data_len > 0) {
|
|
SettingsUpdateText(SET_OTAURL, (SC_DEFAULT == Shortcut()) ? PSTR(OTA_URL) : XdrvMailbox.data);
|
|
}
|
|
ResponseCmndChar(SettingsText(SET_OTAURL));
|
|
}
|
|
|
|
void CmndSeriallog(void)
|
|
{
|
|
if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) {
|
|
|
|
#ifdef ESP32
|
|
if (tasconsole_serial) {
|
|
#endif // ESP32
|
|
|
|
Settings->flag.mqtt_serial = 0; // CMND_SERIALSEND and CMND_SERIALLOG
|
|
|
|
#ifdef ESP32
|
|
}
|
|
#endif // ESP32
|
|
|
|
SetTasConlog(XdrvMailbox.payload);
|
|
}
|
|
Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, XdrvMailbox.command, Settings->seriallog_level, TasmotaGlobal.seriallog_level);
|
|
}
|
|
|
|
void CmndRestart(void)
|
|
{
|
|
switch (XdrvMailbox.payload) {
|
|
case 1:
|
|
TasmotaGlobal.restart_flag = 2;
|
|
ResponseCmndChar(PSTR(D_JSON_RESTARTING));
|
|
break;
|
|
case 2:
|
|
TasmotaGlobal.restart_flag = 2;
|
|
TasmotaGlobal.restart_halt = true;
|
|
ResponseCmndChar(PSTR(D_JSON_HALTING));
|
|
break;
|
|
#ifdef ESP32
|
|
case 3:
|
|
if (EspPrepSwitchPartition(2)) { // Toggle partition between safeboot and production
|
|
TasmotaGlobal.restart_flag = 2;
|
|
ResponseCmndChar(PSTR("Switching"));
|
|
}
|
|
break;
|
|
#endif // ESP32
|
|
case 9:
|
|
TasmotaGlobal.restart_flag = 2;
|
|
TasmotaGlobal.restart_deepsleep = true;
|
|
ResponseCmndChar(PSTR("Go to sleep"));
|
|
break;
|
|
case -1:
|
|
CmndCrash(); // force a crash
|
|
break;
|
|
case -2:
|
|
CmndWDT();
|
|
break;
|
|
case -3:
|
|
CmndBlockedLoop();
|
|
break;
|
|
case 99:
|
|
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING));
|
|
EspRestart();
|
|
break;
|
|
default:
|
|
ResponseCmndChar_P(PSTR(D_JSON_ONE_TO_RESTART));
|
|
}
|
|
}
|
|
|
|
void CmndPowerOnState(void)
|
|
{
|
|
#ifdef ESP8266
|
|
if (TasmotaGlobal.module_type != MOTOR)
|
|
#endif // ESP8266
|
|
{
|
|
/* 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 <= TasmotaGlobal.devices_present; i++) {
|
|
ExecuteCommandPower(i, POWER_ON, SRC_IGNORE);
|
|
}
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
ResponseClear();
|
|
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();
|
|
*/
|
|
if (!XdrvMailbox.usridx && !XdrvMailbox.data_len) {
|
|
Response_P(PSTR("{\"" D_CMND_PULSETIME "\":{\"" D_JSON_SET "\":["));
|
|
for (uint32_t i = 0; i < MAX_PULSETIMERS; i++) {
|
|
ResponseAppend_P(PSTR("%s%d"), (i)?",":"", Settings->pulse_timer[i]);
|
|
}
|
|
ResponseAppend_P(PSTR("],\"" D_JSON_REMAINING "\":["));
|
|
for (uint32_t i = 0; i < MAX_PULSETIMERS; i++) {
|
|
ResponseAppend_P(PSTR("%s%d"), (i)?",":"", GetPulseTimer(i));
|
|
}
|
|
ResponseAppend_P(PSTR("]}}"));
|
|
} else {
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 65536)) {
|
|
Settings->pulse_timer[XdrvMailbox.index -1] = XdrvMailbox.payload; // 0 - 65535
|
|
SetPulseTimer(XdrvMailbox.index -1, XdrvMailbox.payload);
|
|
}
|
|
uint32_t index = XdrvMailbox.index;
|
|
Response_P(PSTR("{\"%s%d\":{\"" D_JSON_SET "\":%d,\"" D_JSON_REMAINING "\":%d}}"),
|
|
XdrvMailbox.command, index,
|
|
Settings->pulse_timer[index -1], GetPulseTimer(index -1));
|
|
}
|
|
}
|
|
}
|
|
|
|
void CmndBlinktime(void)
|
|
{
|
|
if ((XdrvMailbox.payload > 1) && (XdrvMailbox.payload <= 3600)) {
|
|
Settings->blinktime = XdrvMailbox.payload;
|
|
if (TasmotaGlobal.blink_timer > 0) {
|
|
TasmotaGlobal.blink_timer = millis() + (100 * XdrvMailbox.payload);
|
|
}
|
|
}
|
|
ResponseCmndNumber(Settings->blinktime);
|
|
}
|
|
|
|
void CmndBlinkcount(void)
|
|
{
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 32000)) {
|
|
Settings->blinkcount = XdrvMailbox.payload; // 0 - 65535
|
|
if (TasmotaGlobal.blink_counter) { TasmotaGlobal.blink_counter = Settings->blinkcount *2; }
|
|
}
|
|
ResponseCmndNumber(Settings->blinkcount);
|
|
}
|
|
|
|
void CmndSavedata(void)
|
|
{
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3600)) {
|
|
Settings->save_data = XdrvMailbox.payload;
|
|
TasmotaGlobal.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);
|
|
}
|
|
ResponseCmndChar((Settings->save_data > 1) ? stemp1 : GetStateText(Settings->save_data));
|
|
}
|
|
|
|
void CmndSetoption(void) {
|
|
snprintf_P(XdrvMailbox.command, CMDSZ, PSTR(D_CMND_SETOPTION)); // Rename result shortcut command SO to SetOption
|
|
CmndSetoptionBase(1);
|
|
}
|
|
|
|
// Code called by SetOption and by Berry
|
|
bool SetoptionDecode(uint32_t index, uint32_t *ptype, uint32_t *pindex) {
|
|
if (index < 178) {
|
|
if (index <= 31) { // SetOption0 .. 31 = Settings->flag
|
|
*ptype = 2;
|
|
*pindex = index; // 0 .. 31
|
|
}
|
|
else if (index <= 49) { // SetOption32 .. 49 = Settings->param
|
|
*ptype = 1;
|
|
*pindex = index -32; // 0 .. 17 (= PARAM8_SIZE -1)
|
|
}
|
|
else if (index <= 81) { // SetOption50 .. 81 = Settings->flag3
|
|
*ptype = 3;
|
|
*pindex = index -50; // 0 .. 31
|
|
}
|
|
else if (index <= 113) { // SetOption82 .. 113 = Settings->flag4
|
|
*ptype = 4;
|
|
*pindex = index -82; // 0 .. 31
|
|
}
|
|
else if (index <= 145) { // SetOption114 .. 145 = Settings->flag5
|
|
*ptype = 5;
|
|
*pindex = index -114; // 0 .. 31
|
|
}
|
|
else { // SetOption146 .. 177 = Settings->flag6
|
|
*ptype = 6;
|
|
*pindex = index -146; // 0 .. 31
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint32_t GetOption(uint32_t index) {
|
|
uint32_t ptype;
|
|
uint32_t pindex;
|
|
if (SetoptionDecode(index, &ptype, &pindex)) {
|
|
if (1 == ptype) {
|
|
return Settings->param[pindex];
|
|
} else {
|
|
uint32_t flag = Settings->flag.data;
|
|
if (3 == ptype) {
|
|
flag = Settings->flag3.data;
|
|
}
|
|
else if (4 == ptype) {
|
|
flag = Settings->flag4.data;
|
|
}
|
|
else if (5 == ptype) {
|
|
flag = Settings->flag5.data;
|
|
}
|
|
else if (6 == ptype) {
|
|
flag = Settings->flag6.data;
|
|
}
|
|
return bitRead(flag, pindex);
|
|
}
|
|
} else {
|
|
return 0; // fallback
|
|
}
|
|
}
|
|
|
|
void CmndSetoptionBase(bool indexed) {
|
|
// Allow a command to access a single SetOption by it's command name
|
|
// indexed = 0 : No index will be returned attached to the command
|
|
// {"ClockDirection":"OFF"}
|
|
// indexed = 1 : The SetOption index will be returned with the command
|
|
// {"SetOption16":"OFF"}
|
|
uint32_t ptype;
|
|
uint32_t pindex;
|
|
if (SetoptionDecode(XdrvMailbox.index, &ptype, &pindex)) {
|
|
|
|
if (XdrvMailbox.payload >= 0) {
|
|
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:
|
|
case P_BISTABLE_PULSE:
|
|
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();
|
|
TasmotaGlobal.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(); // SetOption38
|
|
}
|
|
if (P_IR_TOLERANCE == pindex) {
|
|
IrReceiveUpdateTolerance(); // SetOption44
|
|
}
|
|
#endif
|
|
#ifdef ROTARY_V1
|
|
if (P_ROTARY_MAX_STEP == pindex) {
|
|
RotaryInitMaxSteps(); // SetOption43
|
|
}
|
|
#endif
|
|
} 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
|
|
TasmotaGlobal.restart_flag = 2;
|
|
default:
|
|
bitWrite(Settings->flag.data, pindex, XdrvMailbox.payload);
|
|
}
|
|
if (12 == pindex) { // stop_flash_rotate
|
|
TasmotaGlobal.stop_flash_rotate = XdrvMailbox.payload;
|
|
SettingsSave(2);
|
|
}
|
|
#ifdef USE_HOME_ASSISTANT
|
|
if ((19 == pindex) || (30 == pindex) || (114 == pindex)) {
|
|
HAssDiscover(); // Delayed execution to provide enough resources during hass_discovery or hass_light
|
|
}
|
|
#endif // USE_HOME_ASSISTANT
|
|
#ifdef USE_TASMOTA_DISCOVERY
|
|
if (19 == pindex) {
|
|
TasRediscover();
|
|
}
|
|
#endif // USE_TASMOTA_DISCOVERY
|
|
}
|
|
else if (3 == ptype) { // SetOption50 .. 81
|
|
bitWrite(Settings->flag3.data, pindex, XdrvMailbox.payload);
|
|
switch (pindex) {
|
|
case 5: // SetOption55
|
|
if (0 == XdrvMailbox.payload) {
|
|
TasmotaGlobal.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
|
|
TasmotaGlobal.restart_flag = 2;
|
|
break;
|
|
}
|
|
}
|
|
else if (4 == ptype) { // SetOption82 .. 113
|
|
bitWrite(Settings->flag4.data, pindex, XdrvMailbox.payload);
|
|
switch (pindex) {
|
|
#ifdef USE_LIGHT
|
|
case 0: // SetOption 82 - (Alexa) Reduced CT range for Alexa (1)
|
|
setAlexaCTRange();
|
|
break;
|
|
#endif
|
|
case 3: // SetOption85 - Enable Device Groups
|
|
case 6: // SetOption88 - PWM Dimmer Buttons control remote devices
|
|
case 15: // SetOption97 - Set Baud rate for TuyaMCU serial communication (0 = 9600 or 1 = 115200)
|
|
case 20: // SetOption102 - Set Baud rate for Teleinfo serial communication (0 = 1200 or 1 = 9600)
|
|
case 21: // SetOption103 - Enable TLS mode (requires TLS version)
|
|
case 22: // SetOption104 - No Retain - disable all MQTT retained messages, some brokers don't support it: AWS IoT, Losant
|
|
case 24: // SetOption106 - Virtual CT - Creates a virtual White ColorTemp for RGBW lights
|
|
case 25: // SetOption107 - Virtual CT Channel - signals whether the hardware white is cold CW (true) or warm WW (false)
|
|
TasmotaGlobal.restart_flag = 2;
|
|
break;
|
|
#ifdef USE_PWM_DIMMER
|
|
case 5: // SetOption87 - (PWM Dimmer) Turn red LED on (1) when powered off
|
|
TasmotaGlobal.restore_powered_off_led_counter = 1;
|
|
break;
|
|
#endif // USE_PWM_DIMMER
|
|
}
|
|
}
|
|
else if (5 == ptype) { // SetOption114 .. 145
|
|
bitWrite(Settings->flag5.data, pindex, XdrvMailbox.payload);
|
|
switch (pindex) {
|
|
case 1: // SetOption115 - Enable ESP32 MI32
|
|
if (0 == XdrvMailbox.payload) {
|
|
TasmotaGlobal.restart_flag = 2;
|
|
}
|
|
break;
|
|
case 18: // SetOption132 - TLS Fingerprint
|
|
TasmotaGlobal.restart_flag = 2;
|
|
break;
|
|
}
|
|
}
|
|
else if (6 == ptype) { // SetOption146 .. 177
|
|
bitWrite(Settings->flag6.data, pindex, XdrvMailbox.payload);
|
|
switch (pindex) {
|
|
case 5: // SetOption151 - Matter enabled
|
|
case 6: // SetOption152 - (Power) Use single pin bistable
|
|
case 7: // SetOption153 - (Berry) Disable autoexec.be on restart (1)
|
|
TasmotaGlobal.restart_flag = 2;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
ptype = 99; // Command Error
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ptype < 99) {
|
|
if (1 == ptype) {
|
|
if (indexed) {
|
|
ResponseCmndIdxNumber(Settings->param[pindex]);
|
|
} else {
|
|
ResponseCmndNumber(Settings->param[pindex]);
|
|
}
|
|
} else {
|
|
uint32_t flag = Settings->flag.data;
|
|
if (3 == ptype) {
|
|
flag = Settings->flag3.data;
|
|
}
|
|
else if (4 == ptype) {
|
|
flag = Settings->flag4.data;
|
|
}
|
|
else if (5 == ptype) {
|
|
flag = Settings->flag5.data;
|
|
}
|
|
else if (6 == ptype) {
|
|
flag = Settings->flag6.data;
|
|
}
|
|
if (indexed) {
|
|
ResponseCmndIdxChar(GetStateText(bitRead(flag, pindex)));
|
|
} else {
|
|
ResponseCmndChar(GetStateText(bitRead(flag, pindex)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CmndTemperatureResolution(void)
|
|
{
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) {
|
|
Settings->flag2.temperature_resolution = XdrvMailbox.payload;
|
|
}
|
|
ResponseCmndNumber(Settings->flag2.temperature_resolution);
|
|
}
|
|
|
|
void CmndHumidityResolution(void)
|
|
{
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) {
|
|
Settings->flag2.humidity_resolution = XdrvMailbox.payload;
|
|
}
|
|
ResponseCmndNumber(Settings->flag2.humidity_resolution);
|
|
}
|
|
|
|
void CmndPressureResolution(void)
|
|
{
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) {
|
|
Settings->flag2.pressure_resolution = XdrvMailbox.payload;
|
|
}
|
|
ResponseCmndNumber(Settings->flag2.pressure_resolution);
|
|
}
|
|
|
|
void CmndPowerResolution(void)
|
|
{
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) {
|
|
Settings->flag2.wattage_resolution = XdrvMailbox.payload;
|
|
}
|
|
ResponseCmndNumber(Settings->flag2.wattage_resolution);
|
|
}
|
|
|
|
void CmndVoltageResolution(void)
|
|
{
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) {
|
|
Settings->flag2.voltage_resolution = XdrvMailbox.payload;
|
|
}
|
|
ResponseCmndNumber(Settings->flag2.voltage_resolution);
|
|
}
|
|
|
|
void CmndFrequencyResolution(void)
|
|
{
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) {
|
|
Settings->flag2.frequency_resolution = XdrvMailbox.payload;
|
|
}
|
|
ResponseCmndNumber(Settings->flag2.frequency_resolution);
|
|
}
|
|
|
|
void CmndCurrentResolution(void)
|
|
{
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) {
|
|
Settings->flag2.current_resolution = XdrvMailbox.payload;
|
|
}
|
|
ResponseCmndNumber(Settings->flag2.current_resolution);
|
|
}
|
|
|
|
void CmndEnergyResolution(void)
|
|
{
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 5)) {
|
|
Settings->flag2.energy_resolution = XdrvMailbox.payload;
|
|
}
|
|
ResponseCmndNumber(Settings->flag2.energy_resolution);
|
|
}
|
|
|
|
void CmndWeightResolution(void)
|
|
{
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) {
|
|
Settings->flag2.weight_resolution = XdrvMailbox.payload;
|
|
}
|
|
ResponseCmndNumber(Settings->flag2.weight_resolution);
|
|
}
|
|
|
|
void CmndSpeedUnit(void)
|
|
{
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 6)) {
|
|
Settings->flag2.speed_conversion = XdrvMailbox.payload;
|
|
}
|
|
ResponseCmndNumber(Settings->flag2.speed_conversion);
|
|
}
|
|
|
|
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) {
|
|
if (XdrvMailbox.index == 2) {
|
|
Settings->fallback_module = XdrvMailbox.payload;
|
|
} else {
|
|
Settings->last_module = Settings->module;
|
|
Settings->module = XdrvMailbox.payload;
|
|
SetModuleType();
|
|
if (Settings->last_module != XdrvMailbox.payload) {
|
|
for (uint32_t i = 0; i < nitems(Settings->my_gp.io); i++) {
|
|
Settings->my_gp.io[i] = GPIO_NONE;
|
|
}
|
|
}
|
|
TasmotaGlobal.restart_flag = 2;
|
|
}
|
|
}
|
|
}
|
|
uint8_t module_real = Settings->module;
|
|
uint8_t module_number = ModuleNr();
|
|
if (XdrvMailbox.index == 2) {
|
|
module_real = Settings->fallback_module;
|
|
module_number = (USER_MODULE == Settings->fallback_module) ? 0 : Settings->fallback_module +1;
|
|
strcat(XdrvMailbox.command, "2");
|
|
}
|
|
Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, module_number, AnyModuleName(module_real).c_str());
|
|
}
|
|
|
|
void CmndModules(void)
|
|
{
|
|
uint32_t midx = USER_MODULE;
|
|
#ifdef MQTT_DATA_STRING
|
|
Response_P(PSTR("{\"" D_CMND_MODULES "\":{"));
|
|
for (uint32_t i = 0; i <= sizeof(kModuleNiceList); i++) {
|
|
if (i > 0) {
|
|
midx = pgm_read_byte(kModuleNiceList + i -1);
|
|
ResponseAppend_P(PSTR(","));
|
|
}
|
|
uint32_t j = i ? midx +1 : 0;
|
|
ResponseAppend_P(PSTR("\"%d\":\"%s\""), j, AnyModuleName(midx).c_str());
|
|
}
|
|
ResponseJsonEndEnd();
|
|
#else
|
|
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()) > (MAX_LOGSZ - TOPSZ)) || (i == sizeof(kModuleNiceList))) {
|
|
ResponseJsonEndEnd();
|
|
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, XdrvMailbox.command);
|
|
jsflg = false;
|
|
lines++;
|
|
}
|
|
}
|
|
ResponseClear();
|
|
#endif
|
|
}
|
|
|
|
void CmndGpio(void)
|
|
{
|
|
if (XdrvMailbox.index < nitems(Settings->my_gp.io)) {
|
|
myio template_gp;
|
|
TemplateGpios(&template_gp);
|
|
if (ValidGPIO(XdrvMailbox.index, template_gp.io[XdrvMailbox.index]) && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < AGPIO(GPIO_SENSOR_END))) {
|
|
bool present = false;
|
|
for (uint32_t i = 0; i < nitems(kGpioNiceList); i++) {
|
|
uint32_t midx = pgm_read_word(kGpioNiceList + i);
|
|
uint32_t max_midx = ((midx & 0x001F) > 0) ? midx : midx +1;
|
|
if ((XdrvMailbox.payload >= (midx & 0xFFE0)) && (XdrvMailbox.payload < max_midx)) {
|
|
present = true;
|
|
break;
|
|
}
|
|
}
|
|
if (present) {
|
|
for (uint32_t i = 0; i < nitems(Settings->my_gp.io); i++) {
|
|
if (ValidGPIO(i, template_gp.io[i]) && (Settings->my_gp.io[i] == XdrvMailbox.payload)) {
|
|
Settings->my_gp.io[i] = GPIO_NONE;
|
|
}
|
|
}
|
|
Settings->my_gp.io[XdrvMailbox.index] = XdrvMailbox.payload;
|
|
TasmotaGlobal.restart_flag = 2;
|
|
}
|
|
}
|
|
bool jsflg = false;
|
|
bool jsflg2 = false;
|
|
for (uint32_t i = 0; i < nitems(Settings->my_gp.io); i++) {
|
|
if (ValidGPIO(i, template_gp.io[i]) || ((255 == XdrvMailbox.payload) && !FlashPin(i))) {
|
|
if (!jsflg) {
|
|
Response_P(PSTR("{"));
|
|
} else {
|
|
ResponseAppend_P(PSTR(","));
|
|
}
|
|
jsflg = true;
|
|
uint32_t sensor_type = Settings->my_gp.io[i];
|
|
if (!ValidGPIO(i, template_gp.io[i])) {
|
|
sensor_type = template_gp.io[i];
|
|
if (AGPIO(GPIO_USER) == sensor_type) { // A user GPIO equals a not connected (=GPIO_NONE) GPIO here
|
|
sensor_type = GPIO_NONE;
|
|
}
|
|
}
|
|
char sindex[4] = { 0 };
|
|
uint32_t sensor_name_idx = BGPIO(sensor_type);
|
|
uint32_t nice_list_search = sensor_type & 0xFFE0;
|
|
for (uint32_t j = 0; j < nitems(kGpioNiceList); j++) {
|
|
uint32_t nls_idx = pgm_read_word(kGpioNiceList + j);
|
|
if (((nls_idx & 0xFFE0) == nice_list_search) && ((nls_idx & 0x001F) > 0)) {
|
|
snprintf_P(sindex, sizeof(sindex), PSTR("%d"), (sensor_type & 0x001F) +1);
|
|
break;
|
|
}
|
|
}
|
|
const char *sensor_names = kSensorNames;
|
|
if (sensor_name_idx > GPIO_FIX_START) {
|
|
sensor_name_idx = sensor_name_idx - GPIO_FIX_START -1;
|
|
sensor_names = kSensorNamesFixed;
|
|
}
|
|
char stemp1[TOPSZ];
|
|
#ifdef MQTT_DATA_STRING
|
|
ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":{\"%d\":\"%s%s\"}"), i, sensor_type, GetTextIndexed(stemp1, sizeof(stemp1), sensor_name_idx, sensor_names), sindex);
|
|
jsflg2 = true;
|
|
#else
|
|
if ((ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":{\"%d\":\"%s%s\"}"), i, sensor_type, GetTextIndexed(stemp1, sizeof(stemp1), sensor_name_idx, sensor_names), sindex) > (MAX_LOGSZ - TOPSZ))) {
|
|
ResponseJsonEnd();
|
|
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, XdrvMailbox.command);
|
|
ResponseClear();
|
|
jsflg2 = true;
|
|
jsflg = false;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
if (jsflg) {
|
|
ResponseJsonEnd();
|
|
} else {
|
|
if (!jsflg2) {
|
|
ResponseCmndChar(PSTR(D_JSON_NOT_SUPPORTED));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CmndGpioRead(void) {
|
|
// Perform a digitalRead on each configured GPIO
|
|
myio template_gp;
|
|
TemplateGpios(&template_gp);
|
|
bool jsflg = false;
|
|
Response_P(PSTR("{\"" D_CMND_GPIOREAD "\":{"));
|
|
for (uint32_t i = 0; i < nitems(Settings->my_gp.io); i++) {
|
|
uint32_t sensor_type = template_gp.io[i]; // Template GPIO
|
|
if (((sensor_type != GPIO_NONE) && (AGPIO(GPIO_USER) != sensor_type)) ||
|
|
(Settings->my_gp.io[i] != GPIO_NONE)) { // Module GPIO
|
|
if (jsflg) {
|
|
ResponseAppend_P(PSTR(","));
|
|
}
|
|
jsflg = true;
|
|
bool state = digitalRead(i);
|
|
ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":%d"), i, state);
|
|
}
|
|
}
|
|
ResponseAppend_P(PSTR("}}"));
|
|
}
|
|
|
|
void ShowGpios(const uint16_t *NiceList, uint32_t size, uint32_t offset, uint32_t &lines) {
|
|
uint32_t ridx;
|
|
uint32_t midx;
|
|
bool jsflg = false;
|
|
for (uint32_t i = offset; i < size; i++) { // Skip ADC_NONE
|
|
if (NiceList == nullptr) {
|
|
ridx = AGPIO(i);
|
|
midx = i;
|
|
} else {
|
|
ridx = pgm_read_word(NiceList + i) & 0xFFE0;
|
|
midx = BGPIO(ridx);
|
|
}
|
|
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\""), ridx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)) > (MAX_LOGSZ - TOPSZ)) || (i == size -1)) {
|
|
ResponseJsonEndEnd();
|
|
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, XdrvMailbox.command);
|
|
jsflg = false;
|
|
lines++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CmndGpios(void)
|
|
{
|
|
uint32_t lines = 1;
|
|
if (XdrvMailbox.payload == 255) {
|
|
// DumpConvertTable();
|
|
ShowGpios(nullptr, GPIO_SENSOR_END, 0, lines);
|
|
} else {
|
|
ShowGpios(kGpioNiceList, nitems(kGpioNiceList), 0, lines);
|
|
#ifdef ESP8266
|
|
#ifndef USE_ADC_VCC
|
|
ShowGpios(kAdcNiceList, nitems(kAdcNiceList), 1, lines);
|
|
#endif // USE_ADC_VCC
|
|
#endif // ESP8266
|
|
}
|
|
ResponseClear();
|
|
}
|
|
|
|
void CmndTemplate(void)
|
|
{
|
|
// {"NAME":"Shelly 2.5","GPIO":[320,0,32,0,224,193,0,0,640,192,608,225,3456,4736],"FLAG":0,"BASE":18}
|
|
|
|
bool error = false;
|
|
|
|
if (strchr(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) { TasmotaGlobal.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);
|
|
}
|
|
SettingsUpdateText(SET_TEMPLATE_NAME, PSTR("Merged"));
|
|
uint32_t j = 0;
|
|
for (uint32_t i = 0; i < nitems(Settings->user_template.gp.io); i++) {
|
|
#ifdef ESP8266
|
|
if (6 == i) { j = 9; }
|
|
if (8 == i) { j = 12; }
|
|
#endif // ESP8266
|
|
#ifdef ESP32
|
|
#if CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6
|
|
// No change
|
|
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
|
|
// if (22 == i) { j = 33; } // TODO 20230821 verify
|
|
#else // ESP32
|
|
// if (28 == i) { j = 32; } // TODO 20230821 verify
|
|
#endif // Non plain ESP32
|
|
#endif // ESP32
|
|
if (TasmotaGlobal.my_module.io[j] > GPIO_NONE) {
|
|
Settings->user_template.gp.io[i] = TasmotaGlobal.my_module.io[j];
|
|
}
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
#ifndef FIRMWARE_MINIMAL // if tasmota-minimal, `Template` is read-only
|
|
if (JsonTemplate(XdrvMailbox.data)) {
|
|
if (USER_MODULE == Settings->module) { TasmotaGlobal.restart_flag = 2; }
|
|
} else {
|
|
ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON));
|
|
error = true;
|
|
}
|
|
#endif // FIRMWARE_MINIMAL
|
|
}
|
|
if (!error) { TemplateJson(); }
|
|
}
|
|
|
|
void CmndButtonDebounce(void)
|
|
{
|
|
if ((XdrvMailbox.payload > 39) && (XdrvMailbox.payload < 1001)) {
|
|
Settings->button_debounce = XdrvMailbox.payload;
|
|
}
|
|
ResponseCmndNumber(Settings->button_debounce);
|
|
}
|
|
|
|
void CmndSwitchDebounce(void)
|
|
{
|
|
if ((XdrvMailbox.payload > 39) && (XdrvMailbox.payload < 1010)) {
|
|
Settings->switch_debounce = XdrvMailbox.payload;
|
|
}
|
|
ResponseCmndNumber(Settings->switch_debounce);
|
|
}
|
|
|
|
void CmndBaudrate(void)
|
|
{
|
|
if (XdrvMailbox.payload >= 300) {
|
|
XdrvMailbox.payload /= 300; // Make it a valid baudrate
|
|
TasmotaGlobal.baudrate = (XdrvMailbox.payload & 0xFFFF) * 300;
|
|
SetSerialBaudrate(TasmotaGlobal.baudrate);
|
|
}
|
|
ResponseCmndNumber(TasmotaGlobal.baudrate);
|
|
}
|
|
|
|
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)) {
|
|
int8_t serial_config = ParseSerialConfig(XdrvMailbox.data);
|
|
if (serial_config >= 0) {
|
|
SetSerialConfig(serial_config);
|
|
}
|
|
}
|
|
}
|
|
ResponseCmndChar(GetSerialConfig().c_str());
|
|
}
|
|
|
|
void CmndSerialBuffer(void) {
|
|
// Allow non-pesistent serial receive buffer size change
|
|
// between MIN_INPUT_BUFFER_SIZE and MAX_INPUT_BUFFER_SIZE characters
|
|
size_t size = 0;
|
|
if (XdrvMailbox.data_len > 0) {
|
|
size = XdrvMailbox.payload;
|
|
if (1 == XdrvMailbox.payload) {
|
|
size = INPUT_BUFFER_SIZE;
|
|
}
|
|
else if (XdrvMailbox.payload < MIN_INPUT_BUFFER_SIZE) {
|
|
size = MIN_INPUT_BUFFER_SIZE;
|
|
}
|
|
else if (XdrvMailbox.payload > MAX_INPUT_BUFFER_SIZE) {
|
|
size = MAX_INPUT_BUFFER_SIZE;
|
|
}
|
|
Serial.setRxBufferSize(size);
|
|
}
|
|
#ifdef ESP8266
|
|
ResponseCmndNumber(Serial.getRxBufferSize());
|
|
#endif
|
|
#ifdef ESP32
|
|
if (size) {
|
|
ResponseCmndNumber(size);
|
|
} else {
|
|
ResponseCmndDone();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CmndSerialSend(void) {
|
|
if (XdrvMailbox.index > 9) { XdrvMailbox.index -= 10; }
|
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 6)) {
|
|
SetSeriallog(LOG_LEVEL_NONE);
|
|
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
|
|
}
|
|
else if (6 == XdrvMailbox.index) {
|
|
SerialSendDecimal(XdrvMailbox.data);
|
|
}
|
|
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];
|
|
}
|
|
}
|
|
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, TasmotaGlobal.syslog_level);
|
|
}
|
|
|
|
void CmndLoghost(void)
|
|
{
|
|
if (XdrvMailbox.data_len > 0) {
|
|
SettingsUpdateText(SET_SYSLOG_HOST, (SC_DEFAULT == Shortcut()) ? SYS_LOG_HOST : XdrvMailbox.data);
|
|
}
|
|
ResponseCmndChar(SettingsText(SET_SYSLOG_HOST));
|
|
}
|
|
|
|
void CmndLogport(void)
|
|
{
|
|
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 65536)) {
|
|
Settings->syslog_port = (1 == XdrvMailbox.payload) ? SYS_LOG_PORT : XdrvMailbox.payload;
|
|
}
|
|
ResponseCmndNumber(Settings->syslog_port);
|
|
}
|
|
|
|
void CmndIpAddress(void)
|
|
{
|
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 5)) {
|
|
char network_address[22];
|
|
ext_snprintf_P(network_address, sizeof(network_address), PSTR(" (%_I)"), (uint32_t)WiFi.localIP());
|
|
if (!XdrvMailbox.usridx) {
|
|
ResponseClear();
|
|
for (uint32_t i = 0; i < 5; i++) {
|
|
ResponseAppend_P(PSTR("%c\"%s%d\":\"%_I%s\""), (i)?',':'{', XdrvMailbox.command, i +1, Settings->ipv4_address[i], (0 == i)?network_address:"");
|
|
}
|
|
ResponseJsonEnd();
|
|
} else {
|
|
uint32_t ipv4_address;
|
|
if (ParseIPv4(&ipv4_address, XdrvMailbox.data)) {
|
|
Settings->ipv4_address[XdrvMailbox.index -1] = ipv4_address;
|
|
}
|
|
Response_P(PSTR("{\"%s%d\":\"%_I%s\"}"), XdrvMailbox.command, XdrvMailbox.index, Settings->ipv4_address[XdrvMailbox.index -1], (1 == XdrvMailbox.index)?network_address:"");
|
|
}
|
|
}
|
|
}
|
|
|
|
void CmndNtpServer(void)
|
|
{
|
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_NTP_SERVERS)) {
|
|
if (!XdrvMailbox.usridx) {
|
|
ResponseCmndAll(SET_NTPSERVER1, MAX_NTP_SERVERS);
|
|
} else {
|
|
uint32_t ntp_server = SET_NTPSERVER1 + XdrvMailbox.index -1;
|
|
if (XdrvMailbox.data_len > 0) {
|
|
SettingsUpdateText(ntp_server,
|
|
(SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? PSTR(NTP_SERVER1) : (2 == XdrvMailbox.index) ? PSTR(NTP_SERVER2) : PSTR(NTP_SERVER3) : XdrvMailbox.data);
|
|
SettingsUpdateText(ntp_server, ReplaceCommaWithDot(SettingsText(ntp_server)));
|
|
// TasmotaGlobal.restart_flag = 2; // Issue #3890
|
|
TasmotaGlobal.ntp_force_sync = true;
|
|
}
|
|
ResponseCmndIdxChar(SettingsText(ntp_server));
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
Settings->wifi_channel = 0; // Disable stored AP
|
|
TasmotaGlobal.restart_flag = 2;
|
|
}
|
|
Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, Settings->sta_active +1, EscapeJSONString(SettingsText(SET_STASSID1 + Settings->sta_active)).c_str());
|
|
}
|
|
|
|
void CmndSsid(void)
|
|
{
|
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SSIDS)) {
|
|
if (!XdrvMailbox.usridx) {
|
|
ResponseCmndAll(SET_STASSID1, MAX_SSIDS);
|
|
} else {
|
|
if (XdrvMailbox.data_len > 0) {
|
|
SettingsUpdateText(SET_STASSID1 + XdrvMailbox.index -1,
|
|
(SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? STA_SSID1 : STA_SSID2 : XdrvMailbox.data);
|
|
Settings->sta_active = XdrvMailbox.index -1;
|
|
TasmotaGlobal.restart_flag = 2;
|
|
}
|
|
ResponseCmndIdxChar(SettingsText(SET_STASSID1 + XdrvMailbox.index -1));
|
|
}
|
|
}
|
|
}
|
|
|
|
void CmndPassword(void)
|
|
{
|
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) {
|
|
bool show_asterisk = (XdrvMailbox.index > 2);
|
|
if (show_asterisk) {
|
|
XdrvMailbox.index -= 2;
|
|
}
|
|
if ((XdrvMailbox.data_len > 4) || (SC_CLEAR == Shortcut()) || (SC_DEFAULT == Shortcut())) {
|
|
SettingsUpdateText(SET_STAPWD1 + XdrvMailbox.index -1,
|
|
(SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? STA_PASS1 : STA_PASS2 : XdrvMailbox.data);
|
|
Settings->sta_active = XdrvMailbox.index -1;
|
|
TasmotaGlobal.restart_flag = 2;
|
|
if (!show_asterisk) {
|
|
ResponseCmndIdxChar(SettingsText(SET_STAPWD1 + XdrvMailbox.index -1));
|
|
}
|
|
} else {
|
|
show_asterisk = true;
|
|
}
|
|
if (show_asterisk) {
|
|
Response_P(S_JSON_COMMAND_INDEX_ASTERISK, XdrvMailbox.command, XdrvMailbox.index);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CmndHostname(void)
|
|
{
|
|
if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) {
|
|
SettingsUpdateText(SET_HOSTNAME, (SC_DEFAULT == Shortcut()) ? WIFI_HOSTNAME : XdrvMailbox.data);
|
|
if (strchr(SettingsText(SET_HOSTNAME), '%') != nullptr) {
|
|
SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME);
|
|
}
|
|
TasmotaGlobal.restart_flag = 2;
|
|
}
|
|
ResponseCmndChar(SettingsText(SET_HOSTNAME));
|
|
}
|
|
|
|
void CmndWifiConfig(void)
|
|
{
|
|
if ((XdrvMailbox.payload >= WIFI_RESTART) && (XdrvMailbox.payload < MAX_WIFI_OPTION)) {
|
|
if ((EX_WIFI_SMARTCONFIG == XdrvMailbox.payload) || (EX_WIFI_WPSCONFIG == XdrvMailbox.payload)) {
|
|
XdrvMailbox.payload = WIFI_MANAGER;
|
|
}
|
|
Settings->sta_config = XdrvMailbox.payload;
|
|
TasmotaGlobal.wifi_state_flag = Settings->sta_config;
|
|
if (WifiState() > WIFI_RESTART) {
|
|
TasmotaGlobal.restart_flag = 2;
|
|
}
|
|
}
|
|
char stemp1[TOPSZ];
|
|
Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, Settings->sta_config, GetTextIndexed(stemp1, sizeof(stemp1), Settings->sta_config, kWifiConfig));
|
|
}
|
|
|
|
void CmndDevicename(void)
|
|
{
|
|
if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) {
|
|
SettingsUpdateText(SET_DEVICENAME, ('"' == XdrvMailbox.data[0]) ? "" : (SC_DEFAULT == Shortcut()) ? SettingsText(SET_FRIENDLYNAME1) : XdrvMailbox.data);
|
|
}
|
|
ResponseCmndChar(SettingsText(SET_DEVICENAME));
|
|
}
|
|
|
|
void CmndFriendlyname(void)
|
|
{
|
|
snprintf_P(XdrvMailbox.command, CMDSZ, PSTR(D_CMND_FRIENDLYNAME)); // Rename result shortcut command FN to FriendlyName
|
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_FRIENDLYNAMES)) {
|
|
if (!XdrvMailbox.usridx && !XdrvMailbox.data_len) {
|
|
ResponseCmndAll(SET_FRIENDLYNAME1, MAX_FRIENDLYNAMES);
|
|
} else {
|
|
if (XdrvMailbox.data_len > 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);
|
|
}
|
|
SettingsUpdateText(SET_FRIENDLYNAME1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : (SC_DEFAULT == Shortcut()) ? stemp1 : XdrvMailbox.data);
|
|
}
|
|
ResponseCmndIdxChar(SettingsText(SET_FRIENDLYNAME1 + XdrvMailbox.index -1));
|
|
}
|
|
}
|
|
}
|
|
|
|
void CmndSwitchText(void) {
|
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SWITCHES_TXT)) {
|
|
if (!XdrvMailbox.usridx && !XdrvMailbox.data_len) {
|
|
ResponseCmndAll(SET_SWITCH_TXT1, MAX_SWITCHES_TXT);
|
|
} else {
|
|
if (XdrvMailbox.data_len > 0) {
|
|
RemoveSpace(XdrvMailbox.data);
|
|
SettingsUpdateText(SET_SWITCH_TXT1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data);
|
|
}
|
|
ResponseCmndIdxChar(SettingsText(SET_SWITCH_TXT1 + XdrvMailbox.index -1));
|
|
}
|
|
}
|
|
}
|
|
|
|
void CmndSwitchMode(void) {
|
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SWITCHES_SET)) {
|
|
// SwitchMode1 - Show SwitchMode1
|
|
// SwitchMode1 2 - Set SwitchMode tot 2
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < MAX_SWITCH_OPTION)) {
|
|
Settings->switchmode[XdrvMailbox.index -1] = XdrvMailbox.payload;
|
|
}
|
|
ResponseCmndIdxNumber(Settings->switchmode[XdrvMailbox.index-1]);
|
|
}
|
|
else if (0 == XdrvMailbox.index) {
|
|
// SwitchMode0 - Show all SwitchMode like {"SwitchMode":[2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}
|
|
// SwitchMode0 2 - Set all SwitchMode to 2
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < MAX_SWITCH_OPTION)) {
|
|
for (uint32_t i = 0; i < MAX_SWITCHES_SET; i++) {
|
|
Settings->switchmode[i] = XdrvMailbox.payload;
|
|
}
|
|
}
|
|
Response_P(PSTR("{\"%s\":["), XdrvMailbox.command);
|
|
for (uint32_t i = 0; i < MAX_SWITCHES_SET; i++) {
|
|
ResponseAppend_P(PSTR("%s%d"), (i>0)?",":"", Settings->switchmode[i]);
|
|
}
|
|
ResponseAppend_P(PSTR("]}"));
|
|
}
|
|
}
|
|
|
|
void CmndInterlock(void)
|
|
{
|
|
// Interlock 0 - Off, Interlock 1 - On, Interlock 1,2 3,4 5,6,7
|
|
uint32_t max_relays = TasmotaGlobal.devices_present;
|
|
if (TasmotaGlobal.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 (strchr(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 {
|
|
Settings->flag.interlock = XdrvMailbox.payload &1; // CMND_INTERLOCK - Enable/disable interlock
|
|
if (Settings->flag.interlock) {
|
|
SetDevicePower(TasmotaGlobal.power, SRC_IGNORE); // Remove multiple relays if set
|
|
}
|
|
}
|
|
#ifdef USE_SHUTTER
|
|
if (Settings->flag3.shutter_mode) { // SetOption80 - Enable shutter support
|
|
ShutterInit(); // to update shutter mode
|
|
}
|
|
#endif // USE_SHUTTER
|
|
}
|
|
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 {
|
|
// never ever reset interlock mode inadvertently if we forced it upon compilation
|
|
Settings->flag.interlock = APP_INTERLOCK_MODE; // CMND_INTERLOCK - Enable/disable interlock
|
|
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
|
|
}
|
|
}
|
|
TasmotaGlobal.tele_period = (Settings->tele_period) ? Settings->tele_period : 3601; // Show teleperiod data also on empty command
|
|
ResponseCmndNumber(Settings->tele_period);
|
|
}
|
|
|
|
void CmndReset(void)
|
|
{
|
|
switch (XdrvMailbox.payload) {
|
|
case 1:
|
|
TasmotaGlobal.restart_flag = 211;
|
|
ResponseCmndChar(PSTR(D_JSON_RESET_AND_RESTARTING));
|
|
break;
|
|
case 2 ... 6:
|
|
TasmotaGlobal.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;
|
|
Settings->bootcount_reset_time = 0;
|
|
ResponseCmndDone();
|
|
break;
|
|
default:
|
|
ResponseCmndChar(PSTR(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 = Time format {"Time":"2019-09-04T14:31:29.123"}
|
|
// 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 < 5)) {
|
|
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);
|
|
}
|
|
}
|
|
ResponseClear();
|
|
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;
|
|
}
|
|
TasmotaGlobal.ntp_force_sync = true;
|
|
}
|
|
if (99 == Settings->timezone) {
|
|
ResponseCmndNumber(Settings->timezone);
|
|
} else {
|
|
char stemp1[TOPSZ];
|
|
snprintf_P(stemp1, sizeof(stemp1), PSTR("%+03d:%02d"), Settings->timezone, Settings->timezone_minutes);
|
|
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 (strchr(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
|
|
}
|
|
TasmotaGlobal.ntp_force_sync = true;
|
|
} else {
|
|
if (0 == XdrvMailbox.payload) {
|
|
if (0 == ts) {
|
|
SettingsResetStd();
|
|
} else {
|
|
SettingsResetDst();
|
|
}
|
|
}
|
|
TasmotaGlobal.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;
|
|
}
|
|
ResponseCmndNumber(Settings->altitude);
|
|
}
|
|
|
|
void CmndLedPower(void) {
|
|
// If GPIO_LEDLINK (used for network status) then allow up to 4 GPIO_LEDx control using TasmotaGlobal.led_power
|
|
// If no GPIO_LEDLINK then allow legacy single led GPIO_LED1 control using Settings->ledstate
|
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_LEDS)) {
|
|
if (!PinUsed(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
|
|
TasmotaGlobal.led_power &= (0xFF ^ mask);
|
|
Settings->ledstate = 0;
|
|
break;
|
|
case 1: // On
|
|
TasmotaGlobal.led_power |= mask;
|
|
Settings->ledstate = 8;
|
|
break;
|
|
case 2: // Toggle
|
|
TasmotaGlobal.led_power ^= mask;
|
|
Settings->ledstate ^= 8;
|
|
break;
|
|
}
|
|
TasmotaGlobal.blinks = 0;
|
|
if (!PinUsed(GPIO_LEDLNK)) {
|
|
SetLedPower(Settings->ledstate &8);
|
|
} else {
|
|
SetLedPowerIdx(XdrvMailbox.index -1, (TasmotaGlobal.led_power & mask));
|
|
}
|
|
}
|
|
bool state = bitRead(TasmotaGlobal.led_power, XdrvMailbox.index -1);
|
|
if (!PinUsed(GPIO_LEDLNK)) {
|
|
state = bitRead(Settings->ledstate, 3);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
ResponseCmndNumber(Settings->ledstate);
|
|
}
|
|
|
|
void CmndLedMask(void) {
|
|
if (XdrvMailbox.data_len > 0) {
|
|
#ifdef USE_PWM_DIMMER
|
|
PWMDimmerSetBrightnessLeds(0);
|
|
#endif // USE_PWM_DIMMER
|
|
Settings->ledmask = XdrvMailbox.payload;
|
|
#ifdef USE_PWM_DIMMER
|
|
PWMDimmerSetBrightnessLeds(-1);
|
|
#endif // USE_PWM_DIMMER
|
|
}
|
|
char stemp1[TOPSZ];
|
|
snprintf_P(stemp1, sizeof(stemp1), PSTR("%d (0x%04X)"), Settings->ledmask, Settings->ledmask);
|
|
ResponseCmndChar(stemp1);
|
|
}
|
|
|
|
void CmndLedPwmOff(void) {
|
|
if (XdrvMailbox.data_len > 0) {
|
|
if (XdrvMailbox.payload < 0) {
|
|
Settings->ledpwm_off = 0;
|
|
}
|
|
else if (XdrvMailbox.payload > 255) {
|
|
Settings->ledpwm_off = 255;
|
|
} else {
|
|
Settings->ledpwm_off = XdrvMailbox.payload;
|
|
}
|
|
UpdateLedPowerAll();
|
|
}
|
|
ResponseCmndNumber(Settings->ledpwm_off);
|
|
}
|
|
|
|
void CmndLedPwmOn(void) {
|
|
if (XdrvMailbox.data_len > 0) {
|
|
if (XdrvMailbox.payload < 0) {
|
|
Settings->ledpwm_on = 0;
|
|
}
|
|
else if (XdrvMailbox.payload > 255) {
|
|
Settings->ledpwm_on = 255;
|
|
} else {
|
|
Settings->ledpwm_on = XdrvMailbox.payload;
|
|
}
|
|
UpdateLedPowerAll();
|
|
}
|
|
ResponseCmndNumber(Settings->ledpwm_on);
|
|
}
|
|
|
|
void CmndLedPwmMode(void) {
|
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_LEDS)) {
|
|
if (!PinUsed(GPIO_LEDLNK)) { XdrvMailbox.index = 1; }
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) {
|
|
uint32_t mask = 1 << (XdrvMailbox.index -1); // Led to configure
|
|
switch (XdrvMailbox.payload) {
|
|
case 0: // digital
|
|
Settings->ledpwm_mask &= (0xFF ^ mask);
|
|
break;
|
|
case 1: // pwm
|
|
Settings->ledpwm_mask |= mask;
|
|
break;
|
|
case 2: // toggle
|
|
Settings->ledpwm_mask ^= mask;
|
|
break;
|
|
}
|
|
UpdateLedPowerAll();
|
|
}
|
|
bool state = bitRead(Settings->ledpwm_mask, XdrvMailbox.index -1);
|
|
ResponseCmndIdxChar(GetStateText(state));
|
|
}
|
|
}
|
|
|
|
void CmndWifiPower(void) {
|
|
if (XdrvMailbox.data_len > 0) {
|
|
Settings->wifi_output_power = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10);
|
|
if (10 == Settings->wifi_output_power) {
|
|
// WifiPower 1
|
|
Settings->wifi_output_power = MAX_TX_PWR_DBM_54g;
|
|
}
|
|
else if (Settings->wifi_output_power > MAX_TX_PWR_DBM_11b) {
|
|
Settings->wifi_output_power = MAX_TX_PWR_DBM_11b;
|
|
}
|
|
WifiSetOutputPower();
|
|
}
|
|
ResponseCmndFloat(WifiGetOutputPower(), 1);
|
|
}
|
|
|
|
void CmndWifi(void)
|
|
{
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) {
|
|
Settings->flag4.network_wifi = XdrvMailbox.payload;
|
|
if (Settings->flag4.network_wifi) {
|
|
#ifdef ESP32
|
|
WifiConnect();
|
|
#else
|
|
WifiEnable();
|
|
#endif
|
|
}
|
|
} else if ((XdrvMailbox.payload >= 2) && (XdrvMailbox.payload <= 4)) {
|
|
// Wifi 2 = B
|
|
// Wifi 3 = BG
|
|
// Wifi 4 = BGN
|
|
#ifdef ESP32
|
|
Wifi.phy_mode = XdrvMailbox.payload - 1;
|
|
#endif
|
|
WiFi.setPhyMode(WiFiPhyMode_t(XdrvMailbox.payload - 1)); // 1-B/2-BG/3-BGN
|
|
}
|
|
Response_P(PSTR("{\"" D_JSON_WIFI "\":\"%s\",\"" D_JSON_WIFI_MODE "\":\"11%c\"}"), GetStateText(Settings->flag4.network_wifi), pgm_read_byte(&kWifiPhyMode[WiFi.getPhyMode() & 0x3]) );
|
|
}
|
|
|
|
void CmndDnsTimeout(void) {
|
|
// Set timeout between 100 and 20000 mSec
|
|
if ((XdrvMailbox.payload >= 100) && (XdrvMailbox.payload <= 20000)) {
|
|
Settings->dns_timeout = XdrvMailbox.payload;
|
|
}
|
|
ResponseCmndNumber(Settings->dns_timeout);
|
|
}
|
|
|
|
void UpdateBatteryPercent(int batt_percentage) {
|
|
if (batt_percentage > 101) { batt_percentage = 100; }
|
|
if (batt_percentage >= 0) {
|
|
Settings->battery_level_percent = batt_percentage;
|
|
}
|
|
}
|
|
|
|
void CmndBatteryPercent(void) {
|
|
UpdateBatteryPercent(XdrvMailbox.payload);
|
|
ResponseCmndNumber(Settings->battery_level_percent);
|
|
}
|
|
|
|
#ifdef USE_I2C
|
|
void CmndI2cScan(void) {
|
|
// I2CScan0 - Scan bus1 and bus2
|
|
// I2CScan - Scan bus1
|
|
// I2CScan2 - Scan bus2
|
|
if (TasmotaGlobal.i2c_enabled) {
|
|
if ((0 == XdrvMailbox.index) || (1 == XdrvMailbox.index)) {
|
|
I2cScan();
|
|
}
|
|
}
|
|
#ifdef ESP32
|
|
if (TasmotaGlobal.i2c_enabled_2) {
|
|
if ((0 == XdrvMailbox.index) || (2 == XdrvMailbox.index)) {
|
|
I2cScan(1);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
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);
|
|
TasmotaGlobal.restart_flag = 2;
|
|
}
|
|
}
|
|
Response_P(PSTR("{\"" D_CMND_I2CDRIVER "\":"));
|
|
I2cDriverState();
|
|
ResponseJsonEnd();
|
|
}
|
|
#endif // USE_I2C
|
|
|
|
#ifdef USE_DEVICE_GROUPS
|
|
void CmndDevGroupName(void)
|
|
{
|
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DEV_GROUP_NAMES)) {
|
|
if (XdrvMailbox.data_len > 0) {
|
|
if (XdrvMailbox.data_len > TOPSZ)
|
|
XdrvMailbox.data[TOPSZ - 1] = 0;
|
|
else if (1 == XdrvMailbox.data_len && ('"' == XdrvMailbox.data[0] || '0' == XdrvMailbox.data[0]))
|
|
XdrvMailbox.data[0] = 0;
|
|
SettingsUpdateText(SET_DEV_GROUP_NAME1 + XdrvMailbox.index - 1, XdrvMailbox.data);
|
|
TasmotaGlobal.restart_flag = 2;
|
|
}
|
|
ResponseCmndAll(SET_DEV_GROUP_NAME1, MAX_DEV_GROUP_NAMES);
|
|
}
|
|
}
|
|
|
|
#ifdef USE_DEVICE_GROUPS_SEND
|
|
void CmndDevGroupSend(void)
|
|
{
|
|
uint8_t device_group_index = (XdrvMailbox.usridx ? XdrvMailbox.index - 1 : 0);
|
|
if (device_group_index < device_group_count) {
|
|
if (!_SendDeviceGroupMessage(-device_group_index, (DevGroupMessageType)(DGR_MSGTYPE_UPDATE_COMMAND + DGR_MSGTYPFLAG_WITH_LOCAL))) {
|
|
ResponseCmndChar(XdrvMailbox.data);
|
|
}
|
|
}
|
|
}
|
|
#endif // USE_DEVICE_GROUPS_SEND
|
|
|
|
void CmndDevGroupShare(void)
|
|
{
|
|
uint32_t parm[2] = { Settings->device_group_share_in, Settings->device_group_share_out };
|
|
ParseParameters(2, parm);
|
|
Settings->device_group_share_in = parm[0];
|
|
Settings->device_group_share_out = parm[1];
|
|
Response_P(PSTR("{\"" D_CMND_DEVGROUP_SHARE "\":{\"In\":\"%X\",\"Out\":\"%X\"}}"), Settings->device_group_share_in, Settings->device_group_share_out);
|
|
}
|
|
|
|
void CmndDevGroupStatus(void)
|
|
{
|
|
DeviceGroupStatus((XdrvMailbox.usridx ? XdrvMailbox.index - 1 : 0));
|
|
}
|
|
|
|
void CmndDevGroupTie(void)
|
|
{
|
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DEV_GROUP_NAMES)) {
|
|
if (XdrvMailbox.data_len > 0) {
|
|
Settings->device_group_tie[XdrvMailbox.index - 1] = XdrvMailbox.payload;
|
|
}
|
|
Response_P(PSTR("{"));
|
|
for (uint32_t i = 0; i < MAX_DEV_GROUP_NAMES; i++) {
|
|
ResponseAppend_P(PSTR("%s\"%s%u\":%u"), (i)?",":"", D_CMND_DEVGROUP_TIE, i + 1, Settings->device_group_tie[i]);
|
|
}
|
|
ResponseJsonEnd();
|
|
}
|
|
}
|
|
#endif // USE_DEVICE_GROUPS
|
|
|
|
void CmndSetSensor(void)
|
|
{
|
|
if (XdrvMailbox.index < MAX_XSNS_DRIVERS) {
|
|
if (XdrvMailbox.payload >= 0) {
|
|
bitWrite(Settings->sensors[0][XdrvMailbox.index / 32], XdrvMailbox.index % 32, XdrvMailbox.payload &1);
|
|
if (1 == XdrvMailbox.payload) {
|
|
TasmotaGlobal.restart_flag = 2; // To safely re-enable a sensor currently most sensor need to follow complete restart init cycle
|
|
}
|
|
}
|
|
Response_P(PSTR("{\"" D_CMND_SETSENSOR "\":"));
|
|
XsnsSensorState(0);
|
|
ResponseJsonEnd();
|
|
}
|
|
}
|
|
|
|
void CmndSensor(void)
|
|
{
|
|
XsnsCall(FUNC_COMMAND_SENSOR);
|
|
}
|
|
|
|
void CmndDriver(void)
|
|
{
|
|
XdrvCall(FUNC_COMMAND_DRIVER);
|
|
}
|
|
|
|
#ifdef ESP32
|
|
|
|
void CmndInfo(void) {
|
|
NvsInfo();
|
|
ResponseCmndDone();
|
|
}
|
|
|
|
void CmndCpuFrequency(void) {
|
|
if ((80 == XdrvMailbox.payload) || (160 == XdrvMailbox.payload) || (240 == XdrvMailbox.payload)) {
|
|
setCpuFrequencyMhz(XdrvMailbox.payload);
|
|
}
|
|
ResponseCmndNumber(getCpuFrequencyMhz());
|
|
}
|
|
|
|
#if defined(SOC_TOUCH_VERSION_1) || defined(SOC_TOUCH_VERSION_2)
|
|
void CmndTouchCal(void) {
|
|
if (XdrvMailbox.payload >= 0) {
|
|
if (XdrvMailbox.payload == 0) {
|
|
TouchButton.calibration = 0;
|
|
}
|
|
else if (XdrvMailbox.payload < MAX_KEYS + 1) {
|
|
TouchButton.calibration = bitSet(TouchButton.calibration, XdrvMailbox.payload);
|
|
}
|
|
else if (XdrvMailbox.payload == 255) {
|
|
TouchButton.calibration = 0x0FFFFFFF; // All MAX_KEYS pins
|
|
}
|
|
}
|
|
ResponseCmndNumber(TouchButton.calibration);
|
|
AddLog(LOG_LEVEL_INFO, PSTR("Button Touchvalue Hits,"));
|
|
}
|
|
|
|
void CmndTouchThres(void) {
|
|
if (XdrvMailbox.data_len > 0) {
|
|
Settings->touch_threshold = XdrvMailbox.payload;
|
|
}
|
|
ResponseCmndNumber(Settings->touch_threshold);
|
|
}
|
|
#endif // ESP32 SOC_TOUCH_VERSION_1 or SOC_TOUCH_VERSION_2
|
|
|
|
#endif // ESP32
|