4.1.2 20170403
* Rename Unrecognised command to Unknown command
* Remove all command lists
* Remove command SmartConfig (superseded by WifiConfig)
* Fix boot loop when selecting module Sonoff 4CH or Sonoff Touch on non
ESP8285 hardware
* Add optional support for Toshiba and Mitsubishi HVAC IR control (needs
updated IRanother  (#257)
* Add all configured switches to Domoticz Configuration web page (#305)
* Fix compile error when selecting WS2812 DMA (#313)
This commit is contained in:
arendst 2017-04-03 16:38:15 +02:00
parent 6824c8ac8a
commit fcb380ce2c
15 changed files with 192 additions and 216 deletions

View File

@ -1,7 +1,7 @@
## Sonoff-Tasmota
Provide ESP8266 based Sonoff by [iTead Studio](https://www.itead.cc/) and ElectroDragon IoT Relay with Serial, Web and MQTT control allowing 'Over the Air' or OTA firmware updates using Arduino IDE.
Current version is **4.1.1** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/master/sonoff/_releasenotes.ino) for change information.
Current version is **4.1.2** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/master/sonoff/_releasenotes.ino) for change information.
- This version provides all (Sonoff) modules in one file and starts up with Sonoff Basic.
- Once uploaded select module using the configuration webpage or the commands ```Modules``` and ```Module```.

Binary file not shown.

Binary file not shown.

View File

@ -1,4 +1,13 @@
/* 4.1.1 20170329
/* 4.1.2 20170403
* Rename Unrecognised command to Unknown command
* Remove all command lists
* Remove command SmartConfig (superseded by WifiConfig)
* Fix boot loop when selecting module Sonoff 4CH or Sonoff Touch on non ESP8285 hardware
* Add optional support for Toshiba and Mitsubishi HVAC IR control (needs updated IRanother (#257)
* Add all configured switches to Domoticz Configuration web page (#305)
* Fix compile error when selecting WS2812 DMA (#313)
*
* 4.1.1 20170329
* Fix default Telemetry for command Prefix3
* Fix webserver Module parameters for disabled select
* Fix sensor status for enabled switches

View File

@ -125,12 +125,15 @@ int spiffsflag = 0;
/*
* Based on cores/esp8266/Updater.cpp
*/
void setFlashChipMode(byte option, byte mode)
void setFlashMode(byte option, byte mode)
{
char log[LOGSZ];
uint8_t *_buffer;
uint32_t address;
// option 0 - Use absolute address 0
// option 1 - Use OTA/Upgrade relative address
if (option) {
eboot_command ebcmd;
eboot_command_read(&ebcmd);
@ -147,13 +150,20 @@ void setFlashChipMode(byte option, byte mode)
spi_flash_write(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE);
}
interrupts();
snprintf_P(log, sizeof(log), PSTR("FLSH: Updated Flash Chip Mode to %d"), (option) ? mode : ESP.getFlashChipMode());
snprintf_P(log, sizeof(log), PSTR("FLSH: Set Flash Mode to %d"), (option) ? mode : ESP.getFlashChipMode());
addLog(LOG_LEVEL_DEBUG, log);
}
}
delete[] _buffer;
}
void setModuleFlashMode(byte option)
{
uint8_t mode = 0; // QIO - ESP8266
if ((sysCfg.module == SONOFF_TOUCH) || (sysCfg.module == SONOFF_4CH)) mode = 3; // DOUT - ESP8285
setFlashMode(option, mode);
}
boolean spiffsPresent()
{
return (SPIFFS_END - SPIFFS_START);

View File

@ -10,7 +10,7 @@
* ====================================================
*/
#define VERSION 0x04010100 // 4.1.1
#define VERSION 0x04010200 // 4.1.2
enum log_t {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE, LOG_LEVEL_ALL};
enum week_t {Last, First, Second, Third, Fourth};
@ -541,7 +541,7 @@ void mqtt_reconnect()
#ifdef USE_MQTT_TLS
addLog_P(LOG_LEVEL_INFO, PSTR("MQTT: Verify TLS fingerprint..."));
if (!espClient.connect(sysCfg.mqtt_host, sysCfg.mqtt_port)) {
snprintf_P(log, sizeof(log), PSTR("MQTT: TLS CONNECT FAILED USING WRONG MQTTHost (%s) or MQTTPort (%d). Retry in %d seconds"),
snprintf_P(log, sizeof(log), PSTR("MQTT: TLS Connect FAILED to %s:%d. Retry in %d seconds"),
sysCfg.mqtt_host, sysCfg.mqtt_port, mqttcounter);
addLog(LOG_LEVEL_DEBUG, log);
return;
@ -549,7 +549,7 @@ void mqtt_reconnect()
if (espClient.verify(sysCfg.mqtt_fingerprint, sysCfg.mqtt_host)) {
addLog_P(LOG_LEVEL_INFO, PSTR("MQTT: Verified"));
} else {
addLog_P(LOG_LEVEL_DEBUG, PSTR("MQTT: WARNING - Insecure connection due to invalid Fingerprint"));
addLog_P(LOG_LEVEL_DEBUG, PSTR("MQTT: Insecure connection due to invalid Fingerprint"));
}
#endif // USE_MQTT_TLS
mqttClient.setCallback(mqttDataCb);
@ -613,7 +613,8 @@ boolean mqtt_command(boolean grpflg, char *type, uint16_t index, char *dataBuf,
else if (!strcmp(type,"STATETEXT") && (index > 0) && (index <= 3)) {
if ((data_len > 0) && (data_len < sizeof(sysCfg.state_text[0]))) {
for(i = 0; i <= data_len; i++) if (dataBuf[i] == ' ') dataBuf[i] = '_';
strlcpy(sysCfg.state_text[index -1], (payload == 1) ? (index==1)?MQTT_STATUS_OFF:(index==2)?MQTT_STATUS_ON:MQTT_CMND_TOGGLE : dataBuf, sizeof(sysCfg.state_text[0]));
// strlcpy(sysCfg.state_text[index -1], (payload == 1) ? (index==1)?MQTT_STATUS_OFF:(index==2)?MQTT_STATUS_ON:MQTT_CMND_TOGGLE : dataBuf, sizeof(sysCfg.state_text[0]));
strlcpy(sysCfg.state_text[index -1], dataBuf, sizeof(sysCfg.state_text[0]));
}
snprintf_P(svalue, ssvalue, PSTR("{\"StateText%d\":\"%s\"}"), index, getStateText(index -1));
}
@ -899,7 +900,10 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len)
payload--;
byte new_modflg = (sysCfg.module != payload);
sysCfg.module = payload;
if (new_modflg) for (byte i = 0; i < MAX_GPIO_PIN; i++) sysCfg.my_module.gp.io[i] = 0;
if (new_modflg) {
for (byte i = 0; i < MAX_GPIO_PIN; i++) sysCfg.my_module.gp.io[i] = 0;
setModuleFlashMode(0);
}
restartflag = 2;
}
snprintf_P(stemp1, sizeof(stemp1), modules[sysCfg.module].name);
@ -997,11 +1001,11 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len)
}
snprintf_P(svalue, sizeof(svalue), PSTR("{\"Sleep\":\"%d%s (%d%s)\"}"), sleep, (sysCfg.value_units) ? " mS" : "", sysCfg.sleep, (sysCfg.value_units) ? " mS" : "");
}
else if (!strcmp(type,"FLASHCHIPMODE")) {
else if (!strcmp(type,"FLASHMODE")) { // 0 = QIO, 1 = QOUT, 2 = DIO, 3 = DOUT
if ((data_len > 0) && (payload >= 0) && (payload <= 3)) {
if (ESP.getFlashChipMode() != payload) setFlashChipMode(0, payload &3);
if (ESP.getFlashChipMode() != payload) setFlashMode(0, payload &3);
}
snprintf_P(svalue, sizeof(svalue), PSTR("{\"FlashChipMode\":%d}"), ESP.getFlashChipMode());
snprintf_P(svalue, sizeof(svalue), PSTR("{\"FlashMode\":%d}"), ESP.getFlashChipMode());
}
else if (!strcmp(type,"UPGRADE") || !strcmp(type,"UPLOAD")) {
if ((data_len > 0) && (payload == 1)) {
@ -1098,7 +1102,7 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len)
}
snprintf_P(svalue, sizeof(svalue), PSTR("{\"Hostname\":\"%s\"}"), sysCfg.hostname);
}
else if (!strcmp(type,"WIFICONFIG") || !strcmp(type,"SMARTCONFIG")) {
else if (!strcmp(type,"WIFICONFIG")) {
if ((data_len > 0) && (payload >= WIFI_RESTART) && (payload < MAX_WIFI_OPTION)) {
sysCfg.sta_config = payload;
wificheckflag = sysCfg.sta_config;
@ -1267,48 +1271,12 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len)
}
if (type == NULL) {
blinks = 201;
/*
snprintf_P(svalue, sizeof(svalue), PSTR("{\"Commands1\":\"Status, SaveData, SaveSate, Sleep, Upgrade, Otaurl, Restart, Reset, WifiConfig, Seriallog, Syslog, LogHost, LogPort, SSId, Password, AP, IPAddres, NTPServer, Hostname, Module, Modules, GPIO, GPIOs\"}"));
mqtt_publish_topic_P(0, PSTR("COMMANDS1"), svalue);
snprintf_P(svalue, sizeof(svalue), PSTR("{\"Commands2\":\"Mqtt, MqttResponse, MqttHost, MqttPort, MqttUser, MqttPassword, MqttClient, Topic, GroupTopic, ButtonTopic, ButtonRetain, SwitchTopic, SwitchRetain, PowerRetain, Units, Timezone, LedState, LedPower, TelePeriod\"}"));
mqtt_publish_topic_P(0, PSTR("COMMANDS2"), svalue);
snprintf_P(svalue, sizeof(svalue), PSTR("{\"Commands3\":\"Power%s, PulseTime, BlinkTime, BlinkCount, ButtonRestrict"), (sysCfg.module != MOTOR) ? ", PowerOnState" : "");
#ifdef USE_WEBSERVER
snprintf_P(svalue, sizeof(svalue), PSTR("%s, Weblog, Webserver, WebPassword, Emulation"), svalue);
#endif
if (swt_flg) snprintf_P(svalue, sizeof(svalue), PSTR("%s, SwitchMode"), svalue);
if (pwm_flg) snprintf_P(svalue, sizeof(svalue), PSTR("%s, PWM"), svalue);
#ifdef USE_I2C
if (i2c_flg) snprintf_P(svalue, sizeof(svalue), PSTR("%s, I2CScan"), svalue);
#endif // USE_I2C
if (sysCfg.module == SONOFF_LED) snprintf_P(svalue, sizeof(svalue), PSTR("%s, Color, Dimmer, Fade, Speed, Wakeup, WakeupDuration, LedTable"), svalue);
#ifdef USE_WS2812
if (pin[GPIO_WS2812] < 99) snprintf_P(svalue, sizeof(svalue), PSTR("%s, Color, Dimmer, Fade, Speed, Wakeup, LedTable, Pixels, Led, Width, Scheme"), svalue);
#endif
#ifdef USE_IR_REMOTE
if (pin[GPIO_IRSEND] < 99) snprintf_P(svalue, sizeof(svalue), PSTR("%s, IRSend"), svalue);
#endif
snprintf_P(svalue, sizeof(svalue), PSTR("%s\"}"), svalue);
mqtt_publish_topic_P(0, PSTR("COMMANDS3"), svalue);
#ifdef USE_DOMOTICZ
domoticz_commands(svalue, sizeof(svalue));
mqtt_publish_topic_P(0, PSTR("COMMANDS4"), svalue);
#endif // USE_DOMOTICZ
if (hlw_flg) {
hlw_commands(svalue, sizeof(svalue));
mqtt_publish_topic_P(0, PSTR("COMMANDS5"), svalue);
snprintf_P(topicBuf, sizeof(topicBuf), PSTR("COMMAND"));
snprintf_P(svalue, sizeof(svalue), PSTR("{\"Command\":\"Unknown\"}"));
type = (char*)topicBuf;
}
*/
snprintf_P(svalue, sizeof(svalue), PSTR("{\"Command\":\"Invalid command\"}"));
mqtt_publish_topic_P(0, PSTR("COMMAND"), svalue);
} else {
mqtt_publish_topic_P(4, type, svalue);
}
}
/********************************************************************************************/
@ -1465,7 +1433,7 @@ void publish_status(uint8_t payload)
}
if ((payload == 0) || (payload == 4)) {
snprintf_P(svalue, sizeof(svalue), PSTR("{\"StatusMEM\":{\"ProgramSize\":%d, \"Free\":%d, \"Heap\":%d, \"SpiffsStart\":%d, \"SpiffsSize\":%d, \"FlashSize\":%d, \"ProgramFlashSize\":%d, \"FlashChipMode\":%d}}"),
snprintf_P(svalue, sizeof(svalue), PSTR("{\"StatusMEM\":{\"ProgramSize\":%d, \"Free\":%d, \"Heap\":%d, \"SpiffsStart\":%d, \"SpiffsSize\":%d, \"FlashSize\":%d, \"ProgramFlashSize\":%d, \"FlashMode\":%d}}"),
ESP.getSketchSize()/1024, ESP.getFreeSketchSpace()/1024, ESP.getFreeHeap()/1024, ((uint32_t)&_SPIFFS_start - 0x40200000)/1024,
(((uint32_t)&_SPIFFS_end - 0x40200000) - ((uint32_t)&_SPIFFS_start - 0x40200000))/1024, ESP.getFlashChipRealSize()/1024, ESP.getFlashChipSize()/1024, ESP.getFlashChipMode());
mqtt_publish_topic_P(option, PSTR("STATUS4"), svalue);
@ -1879,7 +1847,7 @@ void stateloop()
if (otaflag == 90) { // Allow MQTT to reconnect
otaflag = 0;
if (otaok) {
if ((sysCfg.module == SONOFF_TOUCH) || (sysCfg.module == SONOFF_4CH)) setFlashChipMode(1, 3); // DOUT - ESP8285
setModuleFlashMode(1); // QIO - ESP8266, DOUT - ESP8285 (Sonoff 4CH and Touch)
snprintf_P(svalue, sizeof(svalue), PSTR("Successful. Restarting"));
} else {
snprintf_P(svalue, sizeof(svalue), PSTR("Failed %s"), ESPhttpUpdate.getLastErrorString().c_str());
@ -2160,7 +2128,7 @@ void setup()
if (Serial.baudRate() != Baudrate) {
if (seriallog_level) {
snprintf_P(log, sizeof(log), PSTR("APP: Change baudrate to %d and Serial logging will be disabled in %d seconds"), Baudrate, seriallog_timer);
snprintf_P(log, sizeof(log), PSTR("APP: Change your baudrate to %d"), Baudrate);
addLog(LOG_LEVEL_INFO, log);
}
delay(100);

View File

@ -23,6 +23,9 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
const char JSON_SNS_TEMPHUM[] PROGMEM =
"%s, \"%s\":{\"Temperature\":%s, \"Humidity\":%s}";
/*********************************************************************************************\
* Watchdog extension (https://github.com/esp8266/Arduino/issues/1532)
\*********************************************************************************************/
@ -34,7 +37,11 @@ Ticker tickerOSWatch;
static unsigned long osw_last_loop;
byte osw_flag = 0;
void ICACHE_RAM_ATTR osw_osWatch(void)
#ifndef USE_WS2812_DMA // Collides with Neopixelbus but solves exception
void osw_osWatch() ICACHE_RAM_ATTR;
#endif // USE_WS2812_DMA
void osw_osWatch()
{
unsigned long t = millis();
unsigned long last_run = abs(t - osw_last_loop);
@ -599,7 +606,7 @@ void i2c_scan(char *devs, unsigned int devs_len)
strncat(devs, tstr, devs_len);
any = 1;
}
else if (error == 4) snprintf_P(devs, devs_len, PSTR("{\"I2Cscan\":\"Unknow error at 0x%2x\"}"), address);
else if (error == 4) snprintf_P(devs, devs_len, PSTR("{\"I2Cscan\":\"Unknown error at 0x%2x\"}"), address);
}
if (any) {
strncat(devs, "\"}", devs_len);

View File

@ -90,7 +90,7 @@
#define WEB_PASSWORD "" // [WebPassword] Web server Admin mode Password for WEB_USERNAME (empty string = Disable)
#define FRIENDLY_NAME "Sonoff" // [FriendlyName] Friendlyname up to 32 characters used by webpages and Alexa
#define USE_EMULATION // Enable Belkin WeMo and Hue Bridge emulation for Alexa (+11k code, +2k mem)
#define EMULATION EMUL_NONE // [Emulation] Select Belkin WeMo or Hue Bridge emulation (EMUL_NONE, EMUL_WEMO or EMUL_HUE)
#define EMULATION EMUL_NONE // [Emulation] Select Belkin WeMo (single relay/light) or Hue Bridge emulation (multi relay/light) (EMUL_NONE, EMUL_WEMO or EMUL_HUE)
// -- mDNS ----------------------------------------
#define USE_DISCOVERY // Enable mDNS for the following services (+8k code, +0.3k mem) - Disable by //
@ -138,6 +138,7 @@
#define USE_SHT // Add I2C emulating code for SHT1X sensor
#define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+3k code, 0.3k mem)
// #define USE_IR_HVAC // Support for HVAC system using IR (+2k code)
#define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+8k code, +1k mem) - Disable by //
#define USE_WS2812_CTYPE 1 // WS2812 Color type (0 - RGB, 1 - GRB)

View File

@ -103,7 +103,6 @@ const char HTTP_SCRIPT_CONSOL[] PROGMEM =
"t=document.getElementById('t1');"
"if(p==1){"
"c=document.getElementById('c1');"
// "o='&c1='+c.value;"
"o='&c1='+encodeURI(c.value);"
"c.value='';"
"t.scrollTop=sn;"
@ -223,8 +222,8 @@ const char HTTP_FORM_OTHER2[] PROGMEM =
const char HTTP_FORM_OTHER3[] PROGMEM =
"<br/><fieldset><legend><b>&nbsp;Emulation&nbsp;</b></legend>"
"<br/><input style='width:10%;float:left' id='b2' name='b2' type='radio' value='0'{r2}><b>None</b>"
"<br/><input style='width:10%;float:left' id='b2' name='b2' type='radio' value='1'{r3}><b>Belkin WeMo</b>"
"<br/><input style='width:10%;float:left' id='b2' name='b2' type='radio' value='2'{r4}><b>Hue Bridge</b><br/>";
"<br/><input style='width:10%;float:left' id='b2' name='b2' type='radio' value='1'{r3}><b>Belkin WeMo</b> single device"
"<br/><input style='width:10%;float:left' id='b2' name='b2' type='radio' value='2'{r4}><b>Hue Bridge</b> multi devices<br/>";
#endif // USE_EMULATION
const char HTTP_FORM_END[] PROGMEM =
"<br/><button type='submit'>Save</button></form></fieldset>";
@ -258,6 +257,8 @@ const char HTTP_FORM_CMND[] PROGMEM =
"<input style='width:98%' id='c1' name='c1' length='99' placeholder='Enter command' autofocus><br/>"
// "<br/><button type='submit'>Send command</button>"
"</form>";
const char HTTP_TABLE100[] PROGMEM =
"<table style='width:100%'>";
const char HTTP_COUNTER[] PROGMEM =
"<br/><div id='t' name='t' style='text-align:center;'></div>";
const char HTTP_SNS_TEMP[] PROGMEM =
@ -420,7 +421,8 @@ void handleRoot()
sysCfg.led_dimmer[0]);
page += line;
}
page += F("<table style='width:100%'><tr>");
page += FPSTR(HTTP_TABLE100);
page += F("<tr>");
for (byte idx = 1; idx <= Maxdevice; idx++) {
snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), idx);
snprintf_P(line, sizeof(line), PSTR("<td style='width:%d%'><button onclick='la(\"?o=%d\");'>Toggle%s</button></td>"),
@ -477,16 +479,18 @@ void handleAjax2()
#endif // USE_I2C
String page = "";
if (tpage.length() > 0) {
page += F("<table style='width:100%'>");
page += FPSTR(HTTP_TABLE100);
page += tpage;
page += F("</table>");
}
char line[120];
if (Maxdevice) {
page += F("<table style='width:100%'><tr>");
page += FPSTR(HTTP_TABLE100);
page += F("<tr>");
for (byte idx = 1; idx <= Maxdevice; idx++) {
snprintf_P(line, sizeof(line), PSTR("<td style='width:%d%'><div style='text-align:center;font-weight:bold;font-size:%dpx'>%s</div></td>"),
100 / Maxdevice, 70 - (Maxdevice * 8), (power & (0x01 << (idx -1))) ? "ON" : "OFF");
// 100 / Maxdevice, 70 - (Maxdevice * 8), (power & (0x01 << (idx -1))) ? "ON" : "OFF");
100 / Maxdevice, 70 - (Maxdevice * 8), getStateText(bitRead(power, idx -1)));
page += line;
}
page += F("</tr></table>");
@ -528,6 +532,8 @@ void handleConfig()
boolean inModule(byte val, uint8_t *arr)
{
int offset = 0;
if (!val) return false; // None
#ifndef USE_I2C
if (val == GPIO_I2C_SCL) return true;
@ -539,8 +545,11 @@ boolean inModule(byte val, uint8_t *arr)
#ifndef USE_IR_REMOTE
if (val == GPIO_IRSEND) return true;
#endif
if (((val >= GPIO_REL1) && (val <= GPIO_REL4)) || ((val >= GPIO_LED1) && (val <= GPIO_LED4))) offset = 4;
if (((val >= GPIO_REL1_INV) && (val <= GPIO_REL4_INV)) || ((val >= GPIO_LED1_INV) && (val <= GPIO_LED4_INV))) offset = -4;
for (byte i = 0; i < MAX_GPIO_PIN; i++) {
if (arr[i] == val) return true;
if (arr[i] == val + offset) return true;
}
return false;
}
@ -905,12 +914,14 @@ void handleSave()
memcpy_P(&cmodule, &modules[sysCfg.module], sizeof(cmodule));
String gpios = "";
for (byte i = 0; i < MAX_GPIO_PIN; i++) {
if (new_modflg) sysCfg.my_module.gp.io[i] = 0;
if (cmodule.gp.io[i] == GPIO_USER) {
snprintf_P(stemp, sizeof(stemp), PSTR("g%d"), i);
sysCfg.my_module.gp.io[i] = (new_modflg) ? 0 : (!strlen(webServer->arg(stemp).c_str())) ? 0 : atoi(webServer->arg(stemp).c_str());
sysCfg.my_module.gp.io[i] = (!strlen(webServer->arg(stemp).c_str())) ? 0 : atoi(webServer->arg(stemp).c_str());
gpios += F(", GPIO"); gpios += String(i); gpios += F(" "); gpios += String(sysCfg.my_module.gp.io[i]);
}
}
setModuleFlashMode(0);
snprintf_P(stemp, sizeof(stemp), modules[sysCfg.module].name);
snprintf_P(log, sizeof(log), PSTR("HTTP: %s Module%s"), stemp, gpios.c_str());
addLog(LOG_LEVEL_INFO, log);
@ -1118,7 +1129,7 @@ void handleUploadLoop()
}
if ((sysCfg.module == SONOFF_TOUCH) || (sysCfg.module == SONOFF_4CH)) {
upload.buf[2] = 3; // DOUT - ESP8285
addLog_P(LOG_LEVEL_DEBUG, PSTR("FLSH: Updated Flash Chip Mode to 3"));
addLog_P(LOG_LEVEL_DEBUG, PSTR("FLSH: Set Flash Mode to 3"));
}
}
}

View File

@ -33,6 +33,15 @@ const char HTTP_FORM_DOMOTICZ[] PROGMEM =
"<br/><table style='width:97%'>"
"<tr><td><b>In topic</b> (" DOMOTICZ_IN_TOPIC ")</td><td style='width:30%'><input id='it' name='it' length=32 placeholder='" DOMOTICZ_IN_TOPIC "' value='{d1}'></td></tr>"
"<tr><td><b>Out topic</b> (" DOMOTICZ_OUT_TOPIC ")</td><td><input id='ot' name='ot' length=32 placeholder='" DOMOTICZ_OUT_TOPIC "' value='{d2}'></td></tr>";
const char HTTP_FORM_DOMOTICZ_RELAY[] PROGMEM =
"<tr><td><b>Idx {1</b></td></td><td><input id='r{1' name='r{1' length=8 placeholder='0' value='{2'></td></tr>"
"<tr><td><b>Key idx {1</b></td><td><input id='k{1' name='k{1' length=8 placeholder='0' value='{3'></td></tr>";
const char HTTP_FORM_DOMOTICZ_SWITCH[] PROGMEM =
"<tr><td><b>Switch idx {1</b></td><td><input id='s{1' name='s{1' length=8 placeholder='0' value='{4'></td></tr>";
const char HTTP_FORM_DOMOTICZ_SENSOR[] PROGMEM =
"<tr><td><b>Sensor idx {1</b> - {2</td><td><input id='l{1' name='l{1' length=8 placeholder='0' value='{5'></td></tr>";
const char HTTP_FORM_DOMOTICZ_TIMER[] PROGMEM =
"<tr><td><b>Update timer</b> (" STR(DOMOTICZ_UPDATE_TIMER) ")</td><td><input id='ut' name='ut' length=32 placeholder='" STR(DOMOTICZ_UPDATE_TIMER) "' value='{6'</td></tr>";
const char domoticz_sensors[DOMOTICZ_MAX_SENSORS][14] PROGMEM =
{ "Temp", "Temp,Hum", "Temp,Hum,Baro", "Power,Energy", "Illuminance" };
@ -173,66 +182,62 @@ boolean domoticz_mqttData(char *topicBuf, uint16_t stopicBuf, char *dataBuf, uin
* Commands
\*********************************************************************************************/
boolean domoticz_command(char *type, uint16_t index, char *dataBuf, uint16_t data_len, int16_t payload, char *svalue, uint16_t ssvalue)
boolean domoticz_command(const char *type, uint16_t index, char *dataBuf, uint16_t data_len, int16_t payload, char *svalue, uint16_t ssvalue)
{
boolean serviced = true;
if (!strcmp(type,"DOMOTICZINTOPIC")) {
if (!strncmp(type,"DOMOTICZ",8)) {
if (!strcmp(type +8,"INTOPIC")) {
if ((data_len > 0) && (data_len < sizeof(sysCfg.domoticz_in_topic))) {
strlcpy(sysCfg.domoticz_in_topic, (payload == 1) ? DOMOTICZ_IN_TOPIC : dataBuf, sizeof(sysCfg.domoticz_in_topic));
restartflag = 2;
}
snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzInTopic\":\"%s\"}"), sysCfg.domoticz_in_topic);
}
else if (!strcmp(type,"DOMOTICZOUTTOPIC")) {
else if (!strcmp(type +8,"OUTTOPIC")) {
if ((data_len > 0) && (data_len < sizeof(sysCfg.domoticz_out_topic))) {
strlcpy(sysCfg.domoticz_out_topic, (payload == 1) ? DOMOTICZ_OUT_TOPIC : dataBuf, sizeof(sysCfg.domoticz_out_topic));
restartflag = 2;
}
snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzOutTopic\":\"%s\"}"), sysCfg.domoticz_out_topic);
}
else if (!strcmp(type,"DOMOTICZIDX") && (index > 0) && (index <= Maxdevice)) {
else if (!strcmp(type +8,"IDX") && (index > 0) && (index <= Maxdevice)) {
if ((data_len > 0) && (payload >= 0)) {
sysCfg.domoticz_relay_idx[index -1] = payload;
restartflag = 2;
}
snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzIdx%d\":%d}"), index, sysCfg.domoticz_relay_idx[index -1]);
}
else if (!strcmp(type,"DOMOTICZKEYIDX") && (index > 0) && (index <= Maxdevice)) {
else if (!strcmp(type +8,"KEYIDX") && (index > 0) && (index <= Maxdevice)) {
if ((data_len > 0) && (payload >= 0)) {
sysCfg.domoticz_key_idx[index -1] = payload;
}
snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzKeyIdx%d\":%d}"), index, sysCfg.domoticz_key_idx[index -1]);
}
else if (!strcmp(type,"DOMOTICZSWITCHIDX") && (index > 0) && (index <= Maxdevice)) {
else if (!strcmp(type +8,"SWITCHIDX") && (index > 0) && (index <= Maxdevice)) {
if ((data_len > 0) && (payload >= 0)) {
sysCfg.domoticz_switch_idx[index -1] = payload;
}
snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzSwitchIdx%d\":%d}"), index, sysCfg.domoticz_key_idx[index -1]);
}
else if (!strcmp(type,"DOMOTICZSENSORIDX") && (index > 0) && (index <= DOMOTICZ_MAX_SENSORS)) {
else if (!strcmp(type +8,"SENSORIDX") && (index > 0) && (index <= DOMOTICZ_MAX_SENSORS)) {
if ((data_len > 0) && (payload >= 0)) {
sysCfg.domoticz_sensor_idx[index -1] = payload;
}
snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzSensorIdx%d\":%d}"), index, sysCfg.domoticz_sensor_idx[index -1]);
}
else if (!strcmp(type,"DOMOTICZUPDATETIMER")) {
else if (!strcmp(type +8,"UPDATETIMER")) {
if ((data_len > 0) && (payload >= 0) && (payload < 3601)) {
sysCfg.domoticz_update_timer = payload;
}
snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzUpdateTimer\":%d}"), sysCfg.domoticz_update_timer);
}
else {
serviced = false;
else serviced = false;
}
else serviced = false;
return serviced;
}
void domoticz_commands(char *svalue, uint16_t ssvalue)
{
snprintf_P(svalue, ssvalue, PSTR("{\"Commands4\":\"DomoticzInTopic, DomoticzOutTopic, DomoticzIdx, DomoticzKeyIdx, DomoticzSwitchIdx, DomoticzSensorIdx, DomoticzUpdateTimer\"}"));
}
boolean domoticz_button(byte key, byte device, byte state, byte svalflg)
{
if ((sysCfg.domoticz_key_idx[device -1] || sysCfg.domoticz_switch_idx[device -1]) && (svalflg)) {
@ -321,24 +326,27 @@ void handleDomoticz()
page += FPSTR(HTTP_FORM_DOMOTICZ);
page.replace("{d1}", String(sysCfg.domoticz_in_topic));
page.replace("{d2}", String(sysCfg.domoticz_out_topic));
for (int i = 0; i < Maxdevice; i++) {
page += F("<tr><td><b>Idx {1</b></td></td><td><input id='r{1' name='r{1' length=8 placeholder='0' value='{2'></td></tr>");
page += F("<tr><td><b>Key idx {1</b></td><td><input id='k{1' name='k{1' length=8 placeholder='0' value='{3'></td></tr>");
page += F("<tr><td><b>Switch idx {1</b></td><td><input id='s{1' name='s{1' length=8 placeholder='0' value='{4'></td></tr>");
page.replace("{1", String(i +1));
for (int i = 0; i < 4; i++) {
if (i < Maxdevice) {
page += FPSTR(HTTP_FORM_DOMOTICZ_RELAY);
page.replace("{2", String((int)sysCfg.domoticz_relay_idx[i]));
page.replace("{3", String((int)sysCfg.domoticz_key_idx[i]));
}
if (pin[GPIO_SWT1 +i] < 99) {
page += FPSTR(HTTP_FORM_DOMOTICZ_SWITCH);
page.replace("{4", String((int)sysCfg.domoticz_switch_idx[i]));
}
page.replace("{1", String(i +1));
}
for (int i = 0; i < DOMOTICZ_MAX_SENSORS; i++) {
page += F("<tr><td><b>Sensor idx {1</b> - {2</td><td><input id='l{1' name='l{1' length=8 placeholder='0' value='{4'></td></tr>");
page += FPSTR(HTTP_FORM_DOMOTICZ_SENSOR);
page.replace("{1", String(i +1));
snprintf_P(stemp, sizeof(stemp), domoticz_sensors[i]);
page.replace("{2", stemp);
page.replace("{4", String((int)sysCfg.domoticz_sensor_idx[i]));
page.replace("{5", String((int)sysCfg.domoticz_sensor_idx[i]));
}
page += F("<tr><td><b>Update timer</b> (" STR(DOMOTICZ_UPDATE_TIMER) ")</td><td><input id='ut' name='ut' length=32 placeholder='" STR(DOMOTICZ_UPDATE_TIMER) "' value='{d7}'</td></tr>");
page.replace("{d7}", String((int)sysCfg.domoticz_update_timer));
page += FPSTR(HTTP_FORM_DOMOTICZ_TIMER);
page.replace("{6", String((int)sysCfg.domoticz_update_timer));
page += F("</table>");
page += FPSTR(HTTP_FORM_END);
page += FPSTR(HTTP_BTN_CONF);

View File

@ -28,13 +28,11 @@ POSSIBILITY OF SUCH DAMAGE.
* IR Remote send using IRremoteESP8266 library
\*********************************************************************************************/
// * Add support for Toshiba and Mitsubishi HVAC IR control (#257)
// #define USE_IR_HVAC // Support for HVAC system using IR (+2k code)
#ifndef USE_IR_HVAC
#include <IRremoteESP8266.h>
#else
#include <IRMitsubishiAC.h> // Currently firmware.elf section `.text' will not fit in region `iram1_0_seg'
#include <IRMitsubishiAC.h>
// HVAC TOSHIBA_
#define HVAC_TOSHIBA_HDR_MARK 4400
#define HVAC_TOSHIBA_HDR_SPACE 4300
@ -44,7 +42,11 @@ POSSIBILITY OF SUCH DAMAGE.
#define HVAC_TOSHIBA_RPT_MARK 440
#define HVAC_TOSHIBA_RPT_SPACE 7048 // Above original iremote limit
#define HVAC_TOSHIBA_DATALEN 9
IRMitsubishiAC *mitsubir = NULL;
const char FANSPEED[] = "A12345S";
const char HVACMODE[] = "HDCA";
#endif
IRsend *irsend = NULL;
@ -143,7 +145,7 @@ boolean ir_send_command(char *type, uint16_t index, char *dataBufUc, uint16_t da
else error = true;
}
} else error = true;
if (error) snprintf_P(svalue, ssvalue, PSTR("{\"IRHVAC\":\"Wrong parameters value for Vendor, Mode and/or FanSpeed\"}"));
if (error) snprintf_P(svalue, ssvalue, PSTR("{\"IRHVAC\":\"Wrong Vendor, Mode and/or FanSpeed\"}"));
}
#endif // USE_IR_HVAC
else {
@ -157,46 +159,30 @@ boolean ir_hvac_toshiba(const char *HVAC_Mode, const char *HVAC_FanMode, boolean
{
unsigned int rawdata[2 + 2*8*HVAC_TOSHIBA_DATALEN + 2];
byte data[HVAC_TOSHIBA_DATALEN] = { 0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00 };
boolean error = false;
if (HVAC_Mode == NULL || !strcmp(HVAC_Mode,"HOT")) { //default HVAC_HOT
data[6] = (byte) B00000011;
char *p, *token;
uint8_t mode;
if (HVAC_Mode == NULL) {
p = (char*)HVACMODE; // default HVAC_HOT
} else {
p = strchr(HVACMODE, HVAC_Mode[0]);
}
else if (HVAC_Mode && !strcmp(HVAC_Mode,"COLD")) {
data[6] = (byte) B00000001;
}
else if (HVAC_Mode && !strcmp(HVAC_Mode,"DRY")) {
data[6] = (byte) B00000010;
}
else if (HVAC_Mode && !strcmp(HVAC_Mode,"AUTO")) {
data[6] = (byte) B00000000;
}
else error = true;
if (!p) return true;
data[6] = (p - HVACMODE) ^ 0x03; // HOT = 0x03, DRY = 0x02, COOL = 0x01, AUTO = 0x00
if (!HVAC_Power) data[6] = (byte) 0x07; // Turn OFF HVAC
if (HVAC_FanMode && !strcmp(HVAC_FanMode,"1")) {
data[6] = data[6] | (byte) B01000000;
if (HVAC_FanMode == NULL) {
p = (char*)FANSPEED; // default FAN_SPEED_AUTO
} else {
p = strchr(FANSPEED, HVAC_FanMode[0]);
}
else if (HVAC_FanMode && !strcmp(HVAC_FanMode,"2")) {
data[6] = data[6] | (byte) B01100000;
}
else if (HVAC_FanMode && !strcmp(HVAC_FanMode,"3")) {
data[6] = data[6] | (byte) B10000000;
}
else if (HVAC_FanMode && !strcmp(HVAC_FanMode,"4")) {
data[6] = data[6] | (byte) B10100000;
}
else if (HVAC_FanMode && !strcmp(HVAC_FanMode,"5")) {
data[6] = data[6] | (byte) B11000000;
}
else if (HVAC_FanMode == NULL || !strcmp(HVAC_FanMode,"AUTO")) { // default FAN_SPEED_AUTO
data[6] = data[6] | (byte) B00000000;
}
else if (HVAC_FanMode && !strcmp(HVAC_FanMode,"SILENT")) {
data[6] = data[6] | (byte) B00000000;
}
else error = true;
if (!p) return true;
mode = p - FANSPEED +1;
if ((mode == 1) || (mode == 7)) mode = 0;
mode = mode << 5; // AUTO = 0x00, SPEED = 0x40, 0x60, 0x80, 0xA0, 0xC0, SILENT = 0x00
data[6] = data[6] | mode;
byte Temp;
if (HVAC_Temp > 30) {
@ -243,63 +229,46 @@ boolean ir_hvac_toshiba(const char *HVAC_Mode, const char *HVAC_FanMode, boolean
irsend->sendRaw(rawdata,i,38);
interrupts();
return error;
return false;
}
boolean ir_hvac_mitsubishi(const char *HVAC_Mode,const char *HVAC_FanMode, boolean HVAC_Power, int HVAC_Temp)
{
boolean error = false;
char *p, *token;
uint8_t mode;
char log[LOGSZ];
mitsubir->stateReset();
if (HVAC_Mode == NULL || !strcmp(HVAC_Mode,"HOT")) { // default HVAC_HOT
mitsubir->setMode(MITSUBISHI_AC_HEAT);
if (HVAC_Mode == NULL) {
p = (char*)HVACMODE; // default HVAC_HOT
} else {
p = strchr(HVACMODE, HVAC_Mode[0]);
}
else if (HVAC_Mode && !strcmp(HVAC_Mode,"COLD")) {
mitsubir->setMode(MITSUBISHI_AC_COOL);
}
else if (HVAC_Mode && !strcmp(HVAC_Mode,"DRY")) {
mitsubir->setMode(MITSUBISHI_AC_DRY);
}
else if (HVAC_Mode && !strcmp(HVAC_Mode,"AUTO")) {
mitsubir->setMode(MITSUBISHI_AC_AUTO);
} else error = true;
if (!p) return true;
mode = (p - HVACMODE +1) << 3; // HOT = 0x08, DRY = 0x10, COOL = 0x18, AUTO = 0x20
mitsubir->setMode(mode);
mitsubir->setPower(~HVAC_Power);
if (HVAC_FanMode && !strcmp(HVAC_FanMode,"1")) {
mitsubir->setFan(1);
if (HVAC_FanMode == NULL) {
p = (char*)FANSPEED; // default FAN_SPEED_AUTO
} else {
p = strchr(FANSPEED, HVAC_FanMode[0]);
}
else if (HVAC_FanMode && !strcmp(HVAC_FanMode,"2")) {
mitsubir->setFan(2);
}
else if (HVAC_FanMode && !strcmp(HVAC_FanMode,"3")) {
mitsubir->setFan(3);
}
else if (HVAC_FanMode && !strcmp(HVAC_FanMode,"4")) {
mitsubir->setFan(4);
}
else if (HVAC_FanMode && !strcmp(HVAC_FanMode,"5")) {
mitsubir->setFan(5);
}
else if (HVAC_FanMode == NULL || !strcmp(HVAC_FanMode,"AUTO")) { // default FAN_SPEED_AUTO
mitsubir->setFan(MITSUBISHI_AC_FAN_AUTO);
}
else if (HVAC_FanMode && !strcmp(HVAC_FanMode,"SILENT")) {
mitsubir->setFan(MITSUBISHI_AC_FAN_SILENT);
}
else error = true;
if (!p) return true;
mode = p - FANSPEED; // AUTO = 0, SPEED = 1 .. 5, SILENT = 6
mitsubir->setFan(mode);
mitsubir->setTemp(HVAC_Temp);
mitsubir->setVane(MITSUBISHI_AC_VANE_AUTO);
mitsubir->send();
snprintf_P(log, sizeof(log), PSTR("IRHVAC: Sent to Mitsubishi. Power %d, Mode %d, FanSpeed %d, Temp %d, VaneMode %d"),
mitsubir->getPower(), mitsubir->getMode(), mitsubir->getFan(), mitsubir->getTemp(), mitsubir->getVane());
addLog(LOG_LEVEL_DEBUG, log);
return error;
// snprintf_P(log, sizeof(log), PSTR("IRHVAC: Mitsubishi Power %d, Mode %d, FanSpeed %d, Temp %d, VaneMode %d"),
// mitsubir->getPower(), mitsubir->getMode(), mitsubir->getFan(), mitsubir->getTemp(), mitsubir->getVane());
// addLog(LOG_LEVEL_DEBUG, log);
return false;
}
#endif // USE_IR_HVAC
#endif // USE_IR_REMOTE

View File

@ -198,8 +198,9 @@ void dht_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson)
if (dht_readTempHum(TEMP_CONVERSION, t, h)) { // Read temperature
dtostrf(t, 1, TEMP_RESOLUTION &3, stemp1);
dtostrf(h, 1, HUMIDITY_RESOLUTION &3, stemp2);
snprintf_P(svalue, ssvalue, PSTR("%s, \"%s\":{\"Temperature\":%s, \"Humidity\":%s}"),
svalue, dhtstype, stemp1, stemp2);
// snprintf_P(svalue, ssvalue, PSTR("%s, \"%s\":{\"Temperature\":%s, \"Humidity\":%s}"),
// svalue, dhtstype, stemp1, stemp2);
snprintf_P(svalue, ssvalue, JSON_SNS_TEMPHUM, svalue, dhtstype, stemp1, stemp2);
*djson = 1;
#ifdef USE_DOMOTICZ
domoticz_sensor2(stemp1, stemp2);

View File

@ -511,12 +511,6 @@ boolean hlw_command(char *type, uint16_t index, char *dataBuf, uint16_t data_len
return serviced;
}
void hlw_commands(char *svalue, uint16_t ssvalue)
{
snprintf_P(svalue, ssvalue, PSTR("{\"Commands5\":\"PowerLow, PowerHigh, VoltageLow, VoltageHigh, CurrentLow, CurrentHigh, HlwPcal, HlwUcal, HlwIcal%s\"}"),
(FEATURE_POWER_LIMIT)?", SafePower, SafePowerHold, SafePowerWindow, MaxPower, MaxPowerHold, MaxPowerWindow, MaxEnergy, MaxEnergyStart":"");
}
/*********************************************************************************************\
* Presentation
\*********************************************************************************************/

View File

@ -262,8 +262,7 @@ void htu_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson)
h = htu21_compensatedHumidity(h, t);
dtostrf(t, 1, TEMP_RESOLUTION &3, stemp1);
dtostrf(h, 1, HUMIDITY_RESOLUTION &3, stemp2);
snprintf_P(svalue, ssvalue, PSTR("%s, \"%s\":{\"Temperature\":%s, \"Humidity\":%s}"),
svalue, htustype, stemp1, stemp2);
snprintf_P(svalue, ssvalue, JSON_SNS_TEMPHUM, svalue, htustype, stemp1, stemp2);
*djson = 1;
#ifdef USE_DOMOTICZ
domoticz_sensor2(stemp1, stemp2);

View File

@ -183,8 +183,7 @@ void sht_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson)
char stemp[10], shum[10];
if (sht_readCharTempHum(stemp, shum)) {
snprintf_P(svalue, ssvalue, PSTR("%s, \"SHT1X\":{\"Temperature\":%s, \"Humidity\":%s}"),
svalue, stemp, shum);
snprintf_P(svalue, ssvalue, JSON_SNS_TEMPHUM, svalue, "SHT1X", stemp, shum);
*djson = 1;
#ifdef USE_DOMOTICZ
domoticz_sensor2(stemp, shum);