5.3.0 20170715
* Major Hue rewrite which might introduce Alexa problems. If so,
initiate an issue
* Add support for Sonoff Led and BN-SZ01 Ceiling Led brightness control
to Hue
* Fix Sonoff Led Power, Dimmer and Color MQTT response (#176)
* Add commands Delay and Backlog to allow multiple commands at once
separated by ";" (#593)
* Use default flashmode DOUT to solve restart hangs on esp8285 chips
(#453, #598)
* Change Web console column width from 99 to 300 (#599)
This commit is contained in:
arendst 2017-07-15 15:07:30 +02:00
parent 88ec2c6a94
commit 7e6b3a2bb1
11 changed files with 559 additions and 355 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 **5.2.4** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/master/sonoff/_releasenotes.ino) for change information.
Current version is **5.3.0** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/master/sonoff/_releasenotes.ino) for change information.
### **** ATTENTION Version 5.x.x specific information ****

View File

@ -1,4 +1,12 @@
/* 5.2.4 20170703
/* 5.3.0 20170715
* Major Hue rewrite which might introduce Alexa problems. If so, initiate an issue
* Add support for Sonoff Led and BN-SZ01 Ceiling Led brightness control to Hue
* Fix Sonoff Led Power, Dimmer and Color MQTT response (#176)
* Add commands Delay and Backlog to allow multiple commands at once separated by ";" (#593)
* Use default flashmode DOUT to solve restart hangs on esp8285 chips (#453, #598)
* Change Web console column width from 99 to 300 (#599)
*
* 5.2.4 20170703
* Removed flash mode update after selecting different module solving esp8285 related problems
* Add device type flag to sonoff_template.ino
* Change Sonoff Led Wakeup and add support for Sonoff BN-SZ01 Led (#567)

View File

@ -130,7 +130,7 @@ extern "C" uint32_t _SPIFFS_end;
// Version 4.2 config = eeprom area
#define CFG_LOCATION SPIFFS_END // No need for SPIFFS as it uses EEPROM area
// Version 5.2 allow for more flash space
#define CFG_ROTATES 8 // Number of additional flash sectors used (handles uploads)
#define CFG_ROTATES 8 // Number of flash sectors used (handles uploads)
uint32_t _cfgHash = 0;
uint32_t _cfgLocation = CFG_LOCATION;
@ -171,16 +171,6 @@ void setFlashMode(byte option, byte mode)
delete[] _buffer;
}
void setModuleFlashMode(byte option)
{
uint8_t mode = 0; // QIO - ESP8266
// if ((SONOFF_TOUCH == sysCfg.module) || (SONOFF_4CH == sysCfg.module)) {
if (sysCfg.my_module.flag &1) {
mode = 3; // DOUT - ESP8285
}
setFlashMode(option, mode);
}
uint32_t getHash()
{
uint32_t hash = 0;

View File

@ -21,10 +21,11 @@
- Change libraries/PubSubClient/src/PubSubClient.h
#define MQTT_MAX_PACKET_SIZE 512
- Select IDE Tools - Flash size: "1M (no SPIFFS)"
- Select IDE Tools - Flash Mode: "DOUT"
- Select IDE Tools - Flash Size: "1M (no SPIFFS)"
====================================================*/
#define VERSION 0x05020400 // 5.2.4
#define VERSION 0x05030000 // 5.3.0
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};
@ -136,7 +137,7 @@ enum emul_t {EMUL_NONE, EMUL_WEMO, EMUL_HUE, EMUL_MAX};
#define SERIALLOG_TIMER 600 // Seconds to disable SerialLog
#define OTA_ATTEMPTS 10 // Number of times to try fetching the new firmware
#define INPUT_BUFFER_SIZE 100 // Max number of characters in serial buffer
#define INPUT_BUFFER_SIZE 250 // Max number of characters in (serial) command buffer
#define CMDSZ 20 // Max number of characters in command
#define TOPSZ 100 // Max number of characters in topic string
#define LOGSZ 128 // Max number of characters in log string
@ -145,6 +146,8 @@ enum emul_t {EMUL_NONE, EMUL_WEMO, EMUL_HUE, EMUL_MAX};
#else
#define MAX_LOG_LINES 20 // Max number of lines in weblog
#endif
#define MAX_BACKLOG 16 // Max number of commands in backlog (chk blogidx and blogptr code)
#define MIN_BACKLOG_DELAY 2 // Minimal backlog delay in 0.1 seconds
#define APP_BAUDRATE 115200 // Default serial baudrate
#define MAX_STATUS 11 // Max number of status lines
@ -179,7 +182,7 @@ enum opt_t {P_HOLD_TIME, P_MAX_POWER_RETRY, P_MAX_PARAM8}; // Index in sysCf
#ifdef USE_I2C
#include <Wire.h> // I2C support library
#endif // USE_I2C
#ifdef USI_SPI
#ifdef USE_SPI
#include <SPI.h> // SPI support, TFT
#endif // USE_SPI
#include "settings.h"
@ -257,6 +260,11 @@ uint8_t blink_powersave; // Blink start power save state
uint16_t mqtt_cmnd_publish = 0; // ignore flag for publish command
uint8_t latching_power = 0; // Power state at latching start
uint8_t latching_relay_pulse = 0; // Latching relay pulse timer
String Backlog[MAX_BACKLOG]; // Command backlog
uint8_t blogidx = 0; // Command backlog index
uint8_t blogptr = 0; // Command backlog pointer
uint8_t blogmutex = 0; // Command backlog pending
uint16_t blogdelay = 0; // Command backlog delay
#ifdef USE_MQTT_TLS
WiFiClientSecure espClient; // Wifi Secure Client
@ -936,6 +944,7 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len)
payload = (int16_t) lnum; // -32766 - 32767
payload16 = (uint16_t) lnum; // 0 - 65535
}
blogdelay = MIN_BACKLOG_DELAY; // Reset backlog delay
if (!strcmp_P(dataBufUc,PSTR("OFF")) || !strcmp_P(dataBufUc,PSTR("FALSE")) || !strcmp_P(dataBufUc,PSTR("STOP")) || !strcmp_P(dataBufUc,PSTR("CELSIUS"))) {
payload = 0;
@ -956,7 +965,34 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len)
// snprintf_P(svalue, sizeof(svalue), PSTR("RSLT: Payload %d, Payload16 %d"), payload, payload16);
// addLog(LOG_LEVEL_DEBUG, svalue);
if (!strcmp_P(type,PSTR("POWER")) && (index > 0) && (index <= Maxdevice)) {
if (!strcmp_P(type,PSTR("BACKLOG"))) {
if (data_len) {
char *blcommand = strtok(dataBuf, ";");
while (blcommand != NULL) {
Backlog[blogidx] = String(blcommand);
blogidx++;
/*
if (blogidx >= MAX_BACKLOG) {
blogidx = 0;
}
*/
blogidx &= 0xF;
blcommand = strtok(NULL, ";");
}
snprintf_P(svalue, sizeof(svalue), PSTR("{\"Backlog\":\"Appended\"}"));
} else {
uint8_t blflag = (blogptr == blogidx);
blogptr = blogidx;
snprintf_P(svalue, sizeof(svalue), PSTR("{\"Backlog\":\"%s\"}"), blflag ? "Empty" : "Aborted");
}
}
else if (!strcmp_P(type,PSTR("DELAY"))) {
if ((payload >= MIN_BACKLOG_DELAY) && (payload <= 3600)) {
blogdelay = payload;
}
snprintf_P(svalue, sizeof(svalue), PSTR("{\"Delay\":%d}"), blogdelay);
}
else if (!strcmp_P(type,PSTR("POWER")) && (index > 0) && (index <= Maxdevice)) {
if ((payload < 0) || (payload > 4)) {
payload = 9;
}
@ -1148,7 +1184,6 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len)
for (byte i = 0; i < MAX_GPIO_PIN; i++) {
sysCfg.my_module.gp.io[i] = 0;
}
// setModuleFlashMode(0); // Fails on esp8285 based devices
}
restartflag = 2;
}
@ -1646,12 +1681,19 @@ void do_cmnd_power(byte device, byte state)
{
// device = Relay number 1 and up
// state 0 = Relay Off
// state 1 = Relay on (turn off after sysCfg.pulsetime * 100 mSec if enabled)
// state 1 = Relay On (turn off after sysCfg.pulsetime * 100 mSec if enabled)
// state 2 = Toggle relay
// state 3 = Blink relay
// state 4 = Stop blinking relay
// state 6 = Relay Off and no publishPowerState
// state 7 = Relay On and no publishPowerState
// state 9 = Show power state
uint8_t publishPower = 1;
if ((6 == state) || (7 == state)) {
state &= 1;
publishPower = 0;
}
if ((device < 1) || (device > Maxdevice)) {
device = 1;
}
@ -1698,7 +1740,9 @@ void do_cmnd_power(byte device, byte state)
}
return;
}
mqtt_publishPowerState(device);
if (publishPower) {
mqtt_publishPowerState(device);
}
}
void stop_all_power_blink()
@ -1718,7 +1762,7 @@ void stop_all_power_blink()
void do_cmnd(char *cmnd)
{
char stopic[CMDSZ];
char svalue[128];
char svalue[INPUT_BUFFER_SIZE];
char *start;
char *token;
@ -2274,9 +2318,25 @@ void stateloop()
}
}
if (blogdelay) {
blogdelay--;
}
if ((blogptr != blogidx) && !blogdelay && !blogmutex) {
blogmutex = 1;
do_cmnd((char*)Backlog[blogptr].c_str());
blogmutex = 0;
blogptr++;
/*
if (blogptr >= MAX_BACKLOG) {
blogptr = 0;
}
*/
blogptr &= 0xF;
}
switch (state) {
case (STATES/10)*2:
if (otaflag) {
if (otaflag && (blogptr == blogidx)) {
otaflag--;
if (2 == otaflag) {
otaretry = OTA_ATTEMPTS;
@ -2304,7 +2364,7 @@ void stateloop()
if (90 == otaflag) { // Allow MQTT to reconnect
otaflag = 0;
if (otaok) {
setModuleFlashMode(1); // QIO - ESP8266, DOUT - ESP8285 (Sonoff 4CH, Touch and BN-SZ01)
setFlashMode(1, 3); // DOUT for both ESP8266 and ESP8285
snprintf_P(svalue, sizeof(svalue), PSTR("Successful. Restarting"));
} else {
snprintf_P(svalue, sizeof(svalue), PSTR("Failed %s"), ESPhttpUpdate.getLastErrorString().c_str());
@ -2318,7 +2378,7 @@ void stateloop()
if (rtc_midnight_now()) {
counter_savestate();
}
if (savedatacounter) {
if (savedatacounter && (blogptr == blogidx)) {
savedatacounter--;
if (savedatacounter <= 0) {
if (sysCfg.flag.savestate) {
@ -2336,7 +2396,7 @@ void stateloop()
savedatacounter = sysCfg.savedata;
}
}
if (restartflag) {
if (restartflag && (blogptr == blogidx)) {
if (211 == restartflag) {
CFG_Default();
restartflag = 2;
@ -2459,7 +2519,7 @@ void GPIO_init()
}
memcpy_P(&def_module, &modules[sysCfg.module], sizeof(def_module));
sysCfg.my_module.flag = def_module.flag;
// sysCfg.my_module.flag = def_module.flag;
strlcpy(my_module.name, def_module.name, sizeof(my_module.name));
for (byte i = 0; i < MAX_GPIO_PIN; i++) {
if (sysCfg.my_module.gp.io[i] > GPIO_NONE) {

View File

@ -158,14 +158,14 @@ typedef struct MYIO {
typedef struct MYTMPLT {
char name[14];
uint8_t flag; // bit 0 = flashmode (0 = esp8266, 1 = esp8285)
uint8_t flag; // not used
myio gp;
} mytmplt;
// Default module settings
const mytmplt modules[MAXMODULE] PROGMEM = {
{ "Sonoff Basic", // Sonoff Basic (ESP8266)
0, // esp8266
0, // not used
GPIO_KEY1, // GPIO00 Button
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
0, // GPIO02
@ -174,7 +174,7 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
0, // GPIO05
0, // GPIO06 (SD_CLK Flash)
0, // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
0, // GPIO08 (SD_DATA1 Flash QIO/DIO)
0, // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
0, // GPIO09 (SD_DATA2 Flash QIO)
0, // GPIO10 (SD_DATA3 Flash QIO)
0, // GPIO11 (SD_CMD Flash)
@ -186,7 +186,7 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
0 // ADC0 Analog input
},
{ "Sonoff RF", // Sonoff RF (ESP8266)
0, // esp8266
0, // not used
GPIO_KEY1, // GPIO00 Button
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
0,
@ -200,7 +200,7 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
0, 0, 0
},
{ "Sonoff SV", // Sonoff SV (ESP8266)
0, // esp8266
0, // not used
GPIO_KEY1, // GPIO00 Button
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
0,
@ -215,7 +215,7 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
GPIO_ADC0 // ADC0 Analog input
},
{ "Sonoff TH", // Sonoff TH10/16 (ESP8266)
0, // esp8266
0, // not used
GPIO_KEY1, // GPIO00 Button
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
0,
@ -229,7 +229,7 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
0, 0, 0
},
{ "Sonoff Dual", // Sonoff Dual (ESP8266)
0, // esp8266
0, // not used
0,
GPIO_TXD, // GPIO01 Relay control
0,
@ -242,7 +242,7 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
0, 0, 0, 0
},
{ "Sonoff Pow", // Sonoff Pow (ESP8266)
0, // esp8266
0, // not used
GPIO_KEY1, // GPIO00 Button
0, 0, 0, 0,
GPIO_HLW_SEL, // GPIO05 HLW8012 Sel output
@ -254,7 +254,7 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
0, 0
},
{ "Sonoff 4CH", // Sonoff 4CH (ESP8285)
1, // esp8285
0, // not used
GPIO_KEY1, // GPIO00 Button 1
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
GPIO_USER, // GPIO02 Optional sensor
@ -264,7 +264,7 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
0, 0, 0, // Flash connection
GPIO_KEY2, // GPIO09 Button 2
GPIO_KEY3, // GPIO10 Button 3
0,
0, // Flash connection
GPIO_REL1, // GPIO12 Red Led and Relay 1 (0 = Off, 1 = On)
GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off)
GPIO_KEY4, // GPIO14 Button 4
@ -272,7 +272,7 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
0, 0
},
{ "S20 Socket", // S20 Smart Socket (ESP8266)
0, // esp8266
0, // not used
GPIO_KEY1, // GPIO00 Button
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
0,
@ -284,7 +284,7 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
0, 0, 0, 0
},
{ "Slampher", // Slampher (ESP8266)
0, // esp8266
0, // not used
GPIO_KEY1, // GPIO00 Button
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
0,
@ -296,20 +296,21 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
0, 0, 0, 0
},
{ "Sonoff Touch", // Sonoff Touch (ESP8285)
1, // esp8285
0, // not used
GPIO_KEY1, // GPIO00 Button
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
0,
GPIO_USER, // GPIO03 Serial TXD and Optional sensor
0, 0,
0, 0, 0, // Flash connection
0, 0, 0,
0, 0,
0, // Flash connection
GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On)
GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off)
0, 0, 0, 0
},
{ "Sonoff LED", // Sonoff LED (ESP8266)
0, // esp8266
0, // not used
GPIO_KEY1, // GPIO00 Button
0, 0, 0,
GPIO_USER, // GPIO04 Optional sensor (PWM3 Green)
@ -321,8 +322,8 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
GPIO_USER, // GPIO15 Optional sensor (PWM4 Blue)
0, 0
},
{ "1 Channel", // 1 Channel Inching/Latching Relay using (PSA-B01 - ESP8266)
0, // esp8266
{ "1 Channel", // 1 Channel Inching/Latching Relay using (PSA-B01 - ESP8266 and PSF-B01 - ESP8285)
0, // not used
GPIO_KEY1, // GPIO00 Button
0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, // Flash connection
@ -331,7 +332,7 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
0, 0, 0, 0
},
{ "4 Channel", // 4 Channel Inching/Latching Relays (ESP8266)
0, // esp8266
0, // not used
0,
GPIO_TXD, // GPIO01 Relay control
0,
@ -343,7 +344,7 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
0, 0, 0, 0
},
{ "Motor C/AC", // Motor Clockwise / Anti clockwise (PSA-B01 - ESP8266)
0, // esp8266
0, // not used
GPIO_KEY1, // GPIO00 Button
0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, // Flash connection
@ -352,7 +353,7 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
0, 0, 0, 0
},
{ "ElectroDragon", // ElectroDragon IoT Relay Board (ESP8266)
0, // esp8266
0, // not used
GPIO_KEY2, // GPIO00 Button 2
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
GPIO_KEY1, // GPIO02 Button 1
@ -369,7 +370,7 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
},
{ "EXS Relay", // Latching relay https://ex-store.de/ESP8266-WiFi-Relay-V31 (ESP8266)
// Module Pin 1 VCC 3V3, Module Pin 6 GND
0, // esp8266
0, // not used
GPIO_KEY1, // GPIO00 Module Pin 8 - Button (firmware flash)
GPIO_USER, // GPIO01 Module Pin 2 = UART0_TXD
GPIO_USER, // GPIO02 Module Pin 7
@ -385,7 +386,7 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
0
},
{ "WiOn", // Indoor Tap https://www.amazon.com/gp/product/B00ZYLUBJU/ref=s9_acsd_al_bw_c_x_3_w (ESP8266)
0, // esp8266
0, // not used
GPIO_USER, // GPIO00 Optional sensor (pm clock)
0,
GPIO_LED1, // GPIO02 Green Led (1 = On, 0 = Off)
@ -398,7 +399,7 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
0, 0
},
{ "WeMos D1 mini", // WeMos and NodeMCU hardware (ESP8266)
0, // esp8266
0, // not used
GPIO_USER, // GPIO00 D3 Wemos Button Shield
GPIO_USER, // GPIO01 TX Serial RXD
GPIO_USER, // GPIO02 D4 Wemos DHT Shield
@ -414,7 +415,7 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
GPIO_ADC0 // ADC0 A0 Analog input
},
{ "Sonoff Dev", // Sonoff Dev (ESP8266)
0, // esp8266
0, // not used
GPIO_KEY1, // GPIO00 E-FW Button
GPIO_USER, // GPIO01 TX Serial RXD and Optional sensor
0, // GPIO02
@ -430,7 +431,7 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
GPIO_ADC0 // ADC0 A0 Analog input
},
{ "H801", // Lixada H801 Wifi (ESP8266)
0, // esp8266
0, // not used
GPIO_KEY1, // GPIO00 E-FW Button
GPIO_LED1, // GPIO01 Green LED
GPIO_TXD, // GPIO02 RX - Pin next to TX on the PCB
@ -445,7 +446,7 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
0, 0
},
{ "Sonoff SC", // Sonoff SC (ESP8266)
0, // esp8266
0, // not used
GPIO_KEY1, // GPIO00 Button
GPIO_TXD, // GPIO01 RXD to ATMEGA328P
GPIO_USER, // GPIO02 Optional sensor
@ -456,11 +457,12 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off)
0, 0, 0, 0
},
{ "Sonoff BN-SZ", // Sonoff BN-SZ01 LED (ESP8285)
1, // esp8285
{ "Sonoff BN-SZ", // Sonoff BN-SZ01 Ceiling led (ESP8285)
0, // not used
0, 0, 0, 0, 0, 0,
0, 0, 0, // Flash connection
0, 0, 0,
0, 0,
0, // Flash connection
GPIO_PWM1, // GPIO12 Light
GPIO_LED1_INV, // GPIO13 Red Led (0 = On, 1 = Off)
0, 0,

View File

@ -753,7 +753,7 @@ uint8_t midnightnow = 0;
String getBuildDateTime()
{
// "2017-03-07T11:08:02"
// "2017-03-07T11:08:02" - ISO8601:2004
char bdt[21];
char *str;
char *p;
@ -784,7 +784,7 @@ String getBuildDateTime()
String getDateTime()
{
// "2017-03-07T11:08:02"
// "2017-03-07T11:08:02" - ISO8601:2004
char dt[21];
snprintf_P(dt, sizeof(dt), PSTR("%04d-%02d-%02dT%02d:%02d:%02d"),
@ -792,6 +792,20 @@ String getDateTime()
return String(dt);
}
String getUTCDateTime()
{
// "2017-03-07T11:08:02" - ISO8601:2004
char dt[21];
TIME_T tmpTime;
breakTime(utctime, tmpTime);
tmpTime.Year += 1970;
snprintf_P(dt, sizeof(dt), PSTR("%04d-%02d-%02dT%02d:%02d:%02d"),
tmpTime.Year, tmpTime.Month, tmpTime.Day, tmpTime.Hour, tmpTime.Minute, tmpTime.Second);
return String(dt);
}
void breakTime(uint32_t timeInput, TIME_T &tm)
{
// break the given timeInput into time components

View File

@ -165,7 +165,7 @@
#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 // WS2812 Led string using library NeoPixelBus (+11k code, +1k mem) - Disable by //
#define USE_WS2812_CTYPE 1 // WS2812 Color type (0 - RGB, 1 - GRB)
// #define USE_WS2812_DMA // DMA supports only GPIO03 (= Serial RXD) (+1k mem)
// When USE_WS2812_DMA is enabled expect Exceptions on Pow

View File

@ -22,7 +22,7 @@
* Web server and WiFi Manager
*
* Enables configuration and reconfiguration of WiFi credentials using a Captive Portal
* Source by AlexT (https://github.com/tzapu)
* Based on source by AlexT (https://github.com/tzapu)
\*********************************************************************************************/
#define STR_HELPER(x) #x
@ -235,7 +235,7 @@ const char HTTP_FORM_RST_UPG[] PROGMEM =
"</div>"
"<div id='f2' name='f2' style='display:none;text-align:center;'><b>Upload started ...</b></div>";
const char HTTP_FORM_CMND[] PROGMEM =
"<br/><textarea readonly id='t1' name='t1' cols='99' wrap='off'></textarea><br/><br/>"
"<br/><textarea readonly id='t1' name='t1' cols='" STR(MESSZ) "' wrap='off'></textarea><br/><br/>"
"<form method='get' onsubmit='return l(1);'>"
"<input style='width:98%' id='c1' name='c1' length='99' placeholder='Enter command' autofocus><br/>"
// "<br/><button type='submit'>Send command</button>"
@ -261,8 +261,11 @@ const char HTTP_END[] PROGMEM =
"</body>"
"</html>";
const char HDR_CCNTL[] PROGMEM = "Cache-Control";
const char HDR_REVAL[] PROGMEM = "no-cache, no-store, must-revalidate";
const char HDR_CTYPE_PLAIN[] PROGMEM = "text/plain";
const char HDR_CTYPE_HTML[] PROGMEM = "text/html";
const char HDR_CTYPE_XML[] PROGMEM = "text/xml";
const char HDR_CTYPE_JSON[] PROGMEM = "application/json";
const char HDR_CTYPE_STREAM[] PROGMEM = "application/octet-stream";
#define DNS_PORT 53
enum http_t {HTTP_OFF, HTTP_USER, HTTP_ADMIN, HTTP_MANAGER};
@ -376,6 +379,13 @@ void pollDnsWeb()
}
}
void setHeader()
{
webServer->sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate"));
webServer->sendHeader(F("Pragma"), F("no-cache"));
webServer->sendHeader(F("Expires"), F("-1"));
}
void showPage(String &page)
{
if((HTTP_ADMIN == _httpflag) && (sysCfg.web_password[0] != 0) && !webServer->authenticate(WEB_USERNAME, sysCfg.web_password)) {
@ -390,16 +400,13 @@ void showPage(String &page)
}
}
page += FPSTR(HTTP_END);
webServer->sendHeader(FPSTR(HDR_CCNTL), FPSTR(HDR_REVAL));
webServer->sendHeader(F("Pragma"), F("no-cache"));
webServer->sendHeader(F("Expires"), F("-1"));
webServer->send(200, F("text/html"), page);
setHeader();
webServer->send(200, FPSTR(HDR_CTYPE_HTML), page);
}
void handleRoot()
{
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle root"));
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Root"));
if (captivePortal()) { // If captive portal redirect instead of displaying the page.
return;
@ -515,7 +522,7 @@ void handleAjax2()
page += line;
}
*/
webServer->send(200, F("text/html"), page);
webServer->send(200, FPSTR(HDR_CTYPE_HTML), page);
}
boolean httpUser()
@ -532,7 +539,7 @@ void handleConfig()
if (httpUser()) {
return;
}
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle config"));
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Config"));
String page = FPSTR(HTTP_HEAD);
page.replace(F("{v}"), F("Configuration"));
@ -594,7 +601,7 @@ void handleModule()
}
char stemp[20], line[128];
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle Module config"));
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Module config"));
String page = FPSTR(HTTP_HEAD);
page.replace(F("{v}"), F("Config module"));
@ -658,7 +665,7 @@ void handleWifi(boolean scan)
}
char log[LOGSZ];
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle Wifi config"));
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Wifi config"));
String page = FPSTR(HTTP_HEAD);
page.replace(F("{v}"), F("Configure Wifi"));
@ -757,7 +764,7 @@ void handleMqtt()
if (httpUser()) {
return;
}
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle MQTT config"));
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: MQTT config"));
String page = FPSTR(HTTP_HEAD);
page.replace(F("{v}"), F("Configure MQTT"));
@ -782,7 +789,7 @@ void handleLog()
if (httpUser()) {
return;
}
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle Log config"));
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Log config"));
String page = FPSTR(HTTP_HEAD);
page.replace(F("{v}"), F("Config logging"));
@ -830,7 +837,7 @@ void handleOther()
if (httpUser()) {
return;
}
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle other config"));
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Other config"));
char stemp[40];
String page = FPSTR(HTTP_HEAD);
@ -871,7 +878,7 @@ void handleDownload()
if (httpUser()) {
return;
}
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle download config"));
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Download config"));
uint8_t buffer[sizeof(sysCfg)];
@ -882,7 +889,7 @@ void handleDownload()
snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=Config_%s_%s.dmp"),
sysCfg.friendlyname[0], Version);
webServer->sendHeader(F("Content-Disposition"), attachment);
webServer->send(200, F("application/octet-stream"), "");
webServer->send(200, FPSTR(HDR_CTYPE_STREAM), "");
memcpy(buffer, &sysCfg, sizeof(sysCfg));
buffer[0] = CONFIG_FILE_SIGN;
buffer[1] = (!CONFIG_FILE_XOR)?0:1;
@ -995,7 +1002,6 @@ void handleSave()
gpios += F(", GPIO"); gpios += String(i); gpios += F(" "); gpios += String(sysCfg.my_module.gp.io[i]);
}
}
// setModuleFlashMode(0); // Fails on esp8285 based devices
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);
@ -1049,7 +1055,7 @@ void handleRestore()
if (httpUser()) {
return;
}
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle restore"));
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Restore"));
String page = FPSTR(HTTP_HEAD);
page.replace(F("{v}"), F("Restore Configuration"));
@ -1068,7 +1074,7 @@ void handleUpgrade()
if (httpUser()) {
return;
}
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle upgrade"));
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Upgrade"));
String page = FPSTR(HTTP_HEAD);
page.replace(F("{v}"), F("Firmware upgrade"));
@ -1218,11 +1224,7 @@ void handleUploadLoop()
_uploaderror = 4;
return;
}
// if ((SONOFF_TOUCH == sysCfg.module) || (SONOFF_4CH == sysCfg.module)) {
if (sysCfg.my_module.flag &1) {
upload.buf[2] = 3; // DOUT - ESP8285
addLog_P(LOG_LEVEL_DEBUG, PSTR("FLSH: Set Flash Mode to 3"));
}
upload.buf[2] = 3; // Force DOUT - ESP8285
}
}
if (_uploadfiletype) { // config
@ -1282,9 +1284,9 @@ void handleCmnd()
if (httpUser()) {
return;
}
char svalue[128]; // was MESSZ
char svalue[INPUT_BUFFER_SIZE]; // big to serve Backlog
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle cmnd"));
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Command"));
uint8_t valid = 1;
if (sysCfg.web_password[0] != 0) {
@ -1332,7 +1334,7 @@ void handleCmnd()
} else {
message = F("Need user=<username>&password=<password>\n");
}
webServer->send(200, F("text/plain"), message);
webServer->send(200, FPSTR(HDR_CTYPE_PLAIN), message);
}
void handleConsole()
@ -1341,7 +1343,7 @@ void handleConsole()
return;
}
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle console"));
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Console"));
String page = FPSTR(HTTP_HEAD);
page.replace(F("{v}"), F("Console"));
@ -1358,7 +1360,7 @@ void handleAjax()
return;
}
char log[LOGSZ];
char svalue[128]; // was MESSZ
char svalue[INPUT_BUFFER_SIZE]; // big to serve Backlog
byte cflg = 1;
byte counter = 99;
@ -1406,7 +1408,7 @@ void handleAjax()
} while (counter != logidx);
}
message += F("</l></r>");
webServer->send(200, F("text/xml"), message);
webServer->send(200, FPSTR(HDR_CTYPE_XML), message);
}
void handleInfo()
@ -1414,7 +1416,7 @@ void handleInfo()
if (httpUser()) {
return;
}
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle info"));
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Info"));
char stopic[TOPSZ];
@ -1440,8 +1442,8 @@ void handleInfo()
page += F("</th><td>"); page += sysCfg.friendlyname[i]; page += F("</td></tr>");
}
page += F("<tr><td>&nbsp;</td></tr>");
// page += F("<tr><th>SSId (RSSI)</th><td>"); page += (sysCfg.sta_active)? sysCfg.sta_ssid2 : sysCfg.sta_ssid1; page += F(" ("); page += WIFI_getRSSIasQuality(WiFi.RSSI()); page += F("%)</td></tr>");
page += F("<tr><th>AP"); page += String(sysCfg.sta_active +1); page += F(" SSId (RSSI)</th><td>"); page += sysCfg.sta_ssid[sysCfg.sta_active]; page += F(" ("); page += WIFI_getRSSIasQuality(WiFi.RSSI()); page += F("%)</td></tr>");
page += F("<tr><th>AP"); page += String(sysCfg.sta_active +1);
page += F(" SSId (RSSI)</th><td>"); page += sysCfg.sta_ssid[sysCfg.sta_active]; page += F(" ("); page += WIFI_getRSSIasQuality(WiFi.RSSI()); page += F("%)</td></tr>");
page += F("<tr><th>Hostname</th><td>"); page += Hostname; page += F("</td></tr>");
if (static_cast<uint32_t>(WiFi.localIP()) != 0) {
page += F("<tr><th>IP address</th><td>"); page += WiFi.localIP().toString(); page += F("</td></tr>");
@ -1555,18 +1557,15 @@ void handleNotFound()
String message = F("File Not Found\n\nURI: ");
message += webServer->uri();
message += F("\nMethod: ");
message += ( webServer->method() == HTTP_GET ) ? F("GET") : F("POST");
message += (webServer->method() == HTTP_GET) ? F("GET") : F("POST");
message += F("\nArguments: ");
message += webServer->args();
message += F("\n");
for ( uint8_t i = 0; i < webServer->args(); i++ ) {
message += " " + webServer->argName ( i ) + ": " + webServer->arg ( i ) + "\n";
message += " " + webServer->argName(i) + ": " + webServer->arg(i) + "\n";
}
webServer->sendHeader(FPSTR(HDR_CCNTL), FPSTR(HDR_REVAL));
webServer->sendHeader(F("Pragma"), F("no-cache"));
webServer->sendHeader(F("Expires"), F("-1"));
webServer->send(404, F("text/plain"), message);
setHeader();
webServer->send(404, FPSTR(HDR_CTYPE_PLAIN), message);
}
}
@ -1577,7 +1576,7 @@ boolean captivePortal()
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Request redirected to captive portal"));
webServer->sendHeader(F("Location"), String("http://") + webServer->client().localIP().toString(), true);
webServer->send(302, F("text/plain"), ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
webServer->send(302, FPSTR(HDR_CTYPE_PLAIN), ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
webServer->client().stop(); // Stop is needed because we sent no content length
return true;
}

View File

@ -51,11 +51,10 @@ void mqtt_publishDomoticzPowerState(byte device)
{
char svalue[64]; // was MESSZ
if (sysCfg.domoticz_relay_idx[device -1]) {
if (sysCfg.flag.mqtt_enabled && sysCfg.domoticz_relay_idx[device -1]) {
if ((device < 1) || (device > Maxdevice)) {
device = 1;
}
if (sfl_flg) {
snprintf_P(svalue, sizeof(svalue), PSTR("{\"idx\":%d,\"nvalue\":2,\"svalue\":\"%d\"}"),
sysCfg.domoticz_relay_idx[device -1], sysCfg.led_dimmer[device -1]);
@ -74,10 +73,10 @@ void mqtt_publishDomoticzPowerState(byte device)
void domoticz_updatePowerState(byte device)
{
if (domoticz_update_flag) {
if (domoticz_update_flag) {
mqtt_publishDomoticzPowerState(device);
}
domoticz_update_flag = 1;
}
domoticz_update_flag = 1;
}
void domoticz_mqttUpdate()

View File

@ -49,6 +49,8 @@ uint8_t sl_wakeupActive = 0;
uint8_t sl_wakeupDimmer = 0;
uint16_t sl_wakeupCntr = 0;
/********************************************************************************************/
void sl_setDim(uint8_t myDimmer)
{
if ((1 == sfl_flg) && (100 == myDimmer)) {
@ -61,11 +63,9 @@ void sl_setDim(uint8_t myDimmer)
sl_dcolor[1] = (uint8_t)fmyWrm;
}
/********************************************************************************************/
void sl_init(void)
{
sysCfg.pwmvalue[0] = 0; // We use dimmer / led_color
sysCfg.pwmvalue[0] = 0; // We use dimmer / led_color
if (2 == sfl_flg) {
sysCfg.pwmvalue[1] = 0; // We use led_color
}
@ -74,6 +74,51 @@ void sl_init(void)
sl_wakeupActive = 0;
}
void sl_setColor(char* colstr)
{
uint8_t my_color[2];
char *p;
uint16_t temp = strtol(colstr, &p, 16);
my_color[1] = temp & 0xFF; // Warm
temp >>= 8;
my_color[0] = temp & 0xFF; // Cold
if (temp < my_color[1]) {
temp = my_color[1];
}
float mDim = (float)temp / 2.55;
sysCfg.led_dimmer[0] = (uint8_t)mDim;
float newDim = 100 / mDim;
float fmyCold = (float)my_color[0] * newDim;
float fmyWarm = (float)my_color[1] * newDim;
sysCfg.led_color[0] = (uint8_t)fmyCold;
sysCfg.led_color[1] = (uint8_t)fmyWarm;
}
void sl_prepPower(char *svalue, uint16_t ssvalue)
{
// do_cmnd_power(index, (sysCfg.led_dimmer[0]>0));
if (sysCfg.led_dimmer[0] && !(power&1)) {
do_cmnd_power(1, 7); // No publishPowerState
}
else if (!sysCfg.led_dimmer[0] && (power&1)) {
do_cmnd_power(1, 6); // No publishPowerState
}
#ifdef USE_DOMOTICZ
mqtt_publishDomoticzPowerState(1);
#endif // USE_DOMOTICZ
sl_setDim(sysCfg.led_dimmer[0]);
if (2 == sfl_flg) {
uint16_t color = (uint16_t)sl_dcolor[0] << 8;
color += (uint16_t)sl_dcolor[1];
snprintf_P(svalue, ssvalue, PSTR("{\"POWER\":\"%s\", \"Dimmer\":%d, \"Color\":\"%04X\"}"),
getStateText(power &1), sysCfg.led_dimmer[0], color);
} else {
snprintf_P(svalue, ssvalue, PSTR("{\"POWER\":\"%s\", \"Dimmer\":%d}"),
getStateText(power &1), sysCfg.led_dimmer[0]);
}
}
void sl_setPower(uint8_t power)
{
sl_power = power &1;
@ -153,6 +198,34 @@ void sl_animate()
}
}
/*********************************************************************************************\
* Hue support
\*********************************************************************************************/
void sl_replaceHSB(String *response)
{
response->replace("{h}", "0");
response->replace("{s}", "0");
response->replace("{b}", String((uint8_t)(2.54f * (float)sysCfg.led_dimmer[0])));
}
void sl_getHSB(float *hue, float *sat, float *bri)
{
*hue = 0;
*sat = 0;
*bri = (2.54f * (float)sysCfg.led_dimmer[0]);
}
void sl_setHSB(float hue, float sat, float bri)
{
char svalue[MESSZ];
uint8_t tmp = (uint8_t)(bri * 100);
sysCfg.led_dimmer[0] = tmp;
sl_prepPower(svalue, sizeof(svalue));
mqtt_publish_topic_P(5, "DIMMER", svalue);
}
/*********************************************************************************************\
* Commands
\*********************************************************************************************/
@ -166,20 +239,7 @@ boolean sl_command(char *type, uint16_t index, char *dataBufUc, uint16_t data_le
uint8_t my_color[2];
char *p;
if (4 == data_len) {
uint16_t temp = strtol(dataBufUc, &p, 16);
my_color[1] = temp & 0xFF; // Warm
temp >>= 8;
my_color[0] = temp & 0xFF; // Cold
if (temp < my_color[1]) {
temp = my_color[1];
}
float mDim = (float)temp / 2.55;
sysCfg.led_dimmer[0] = (uint8_t)mDim;
float newDim = 100 / mDim;
float fmyCold = (float)my_color[0] * newDim;
float fmyWarm = (float)my_color[1] * newDim;
sysCfg.led_color[0] = (uint8_t)fmyCold;
sysCfg.led_color[1] = (uint8_t)fmyWarm;
sl_setColor(dataBufUc);
coldim = true;
} else {
sl_setDim(sysCfg.led_dimmer[0]);
@ -245,26 +305,7 @@ boolean sl_command(char *type, uint16_t index, char *dataBufUc, uint16_t data_le
serviced = false; // Unknown command
}
if (coldim) {
// do_cmnd_power(index, (sysCfg.led_dimmer[0]>0));
if (sysCfg.led_dimmer[0] && !(power&1)) {
do_cmnd_power(1, 1);
}
else if (!sysCfg.led_dimmer[0] && (power&1)) {
do_cmnd_power(1, 0);
}
#ifdef USE_DOMOTICZ
mqtt_publishDomoticzPowerState(1);
#endif // USE_DOMOTICZ
sl_setDim(sysCfg.led_dimmer[0]);
if (2 == sfl_flg) {
uint16_t color = (uint16_t)sl_dcolor[0] << 8;
color += (uint16_t)sl_dcolor[1];
snprintf_P(svalue, ssvalue, PSTR("{\"POWER\":\"%s\", \"Dimmer\":%d, \"Color\":\"%04X\"}"),
getStateText(power &1), sysCfg.led_dimmer[0], color);
} else {
snprintf_P(svalue, ssvalue, PSTR("{\"POWER\":\"%s\", \"Dimmer\":%d}"),
getStateText(power &1), sysCfg.led_dimmer[0]);
}
sl_prepPower(svalue, ssvalue);
}
return serviced;
}

View File

@ -17,6 +17,10 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*********************************************************************************************\
* Belkin WeMo and Philips Hue bridge emulation
\*********************************************************************************************/
#ifdef USE_EMULATION
#define UDP_BUFFER_SIZE 200 // Max UDP buffer size needed for M-SEARCH message
@ -30,6 +34,7 @@ uint32_t portMulticast = 1900; // Multicast address and port
/*********************************************************************************************\
* WeMo UPNP support routines
\*********************************************************************************************/
const char WEMO_MSEARCH[] PROGMEM =
"HTTP/1.1 200 OK\r\n"
"CACHE-CONTROL: max-age=86400\r\n"
@ -83,44 +88,53 @@ void wemo_respondToMSearch()
/*********************************************************************************************\
* Hue Bridge UPNP support routines
* Need to send 3 response packets with varying ST and USN
*
* Using Espressif Inc Mac Address of 5C:CF:7F:00:00:00
* Philips Lighting is 00:17:88:00:00:00
\*********************************************************************************************/
const char HUE_RESPONSE[] PROGMEM =
"HTTP/1.0 200 OK\r\n"
"HTTP/1.1 200 OK\r\n"
"HOST: 239.255.255.250:1900\r\n"
"CACHE-CONTROL: max-age=100\r\n"
"EXT:\r\n"
"LOCATION: http://{r1}:80/description.xml\r\n"
"SERVER: FreeRTOS/7.4.2 UPnP/1.0 IpBridge/1.15.0\r\n"
"SERVER: FreeRTOS/7.4.2 UPnP/1.0 IpBridge/1.16.0\r\n"
"hue-bridgeid: {r2}\r\n";
const char HUE_ST1[] PROGMEM =
"ST: upnp:rootdevice\r\n";
const char HUE_USN1[] PROGMEM =
"ST: upnp:rootdevice\r\n"
"USN: uuid:{r3}::upnp:rootdevice\r\n"
"\r\n";
const char HUE_ST2[] PROGMEM =
"ST: uuid:{r3}\r\n";
const char HUE_USN2[] PROGMEM =
"ST: uuid:{r3}\r\n"
"USN: uuid:{r3}\r\n"
"\r\n";
const char HUE_ST3[] PROGMEM =
"ST: urn:schemas-upnp-org:device:Basic:1\r\n"
"USN: uuid:{r3}\r\n"
"\r\n";
const char HUE_ST3[] PROGMEM =
"ST: urn:schemas-upnp-org:device:basic:1\r\n";
String hue_bridgeid()
{
char bridgeid[16];
String temp = WiFi.macAddress();
temp.replace(":", "");
String bridgeid = temp.substring(0, 6) + "FFFE" + temp.substring(6);
return bridgeid; // 5CCF7FFFFE139F3D
}
snprintf_P(bridgeid, sizeof(bridgeid), PSTR("5CCF7FFFFE%03X"), ESP.getChipId());
return String(bridgeid);
String hue_serial()
{
String serial = WiFi.macAddress();
serial.replace(":", "");
serial.toLowerCase();
return serial; // 5ccf7f139f3d
}
String hue_UUID()
{
char serial[36];
snprintf_P(serial, sizeof(serial), PSTR("f6543a06-da50-11ba-8d8f-5ccf7f%03x"), ESP.getChipId());
return String(serial);
String uuid = F("f6543a06-da50-11ba-8d8f-");
uuid += hue_serial();
return uuid; // f6543a06-da50-11ba-8d8f-5ccf7f139f3d
}
void hue_respondToMSearch()
@ -129,40 +143,35 @@ void hue_respondToMSearch()
char log[LOGSZ];
if (portUDP.beginPacket(portUDP.remoteIP(), portUDP.remotePort())) {
String response = FPSTR(HUE_RESPONSE);
String response_st = FPSTR(HUE_ST1);
String response_usn = FPSTR(HUE_USN1);
response += response_st + response_usn;
response.replace("{r1}", WiFi.localIP().toString());
response.replace("{r2}", hue_bridgeid());
response.replace("{r3}", hue_UUID());
portUDP.write(response.c_str());
portUDP.endPacket();
// addLog(LOG_LEVEL_DEBUG_MORE, response.c_str());
String response1 = FPSTR(HUE_RESPONSE);
response1.replace("{r1}", WiFi.localIP().toString());
response1.replace("{r2}", hue_bridgeid());
response = FPSTR(HUE_RESPONSE);
response_st = FPSTR(HUE_ST2);
response_usn = FPSTR(HUE_USN2);
response += response_st + response_usn;
response.replace("{r1}", WiFi.localIP().toString());
response.replace("{r2}", hue_bridgeid());
String response = response1;
response += FPSTR(HUE_ST1);
response.replace("{r3}", hue_UUID());
portUDP.write(response.c_str());
portUDP.endPacket();
// addLog(LOG_LEVEL_DEBUG_MORE, response.c_str());
response = FPSTR(HUE_RESPONSE);
response_st = FPSTR(HUE_ST3);
response += response_st + response_usn;
response.replace("{r1}", WiFi.localIP().toString());
response.replace("{r2}", hue_bridgeid());
//addLog(LOG_LEVEL_DEBUG_MORE, response.c_str());
response = response1;
response += FPSTR(HUE_ST2);
response.replace("{r3}", hue_UUID());
portUDP.write(response.c_str());
portUDP.endPacket();
//addLog(LOG_LEVEL_DEBUG_MORE, response.c_str());
response = response1;
response += FPSTR(HUE_ST3);
response.replace("{r3}", hue_UUID());
portUDP.write(response.c_str());
portUDP.endPacket();
//addLog(LOG_LEVEL_DEBUG_MORE, response.c_str());
snprintf_P(message, sizeof(message), PSTR("3 response packets sent"));
// addLog(LOG_LEVEL_DEBUG_MORE, response.c_str());
} else {
snprintf_P(message, sizeof(message), PSTR("Failed to send response"));
}
@ -171,7 +180,9 @@ void hue_respondToMSearch()
addLog(LOG_LEVEL_DEBUG, log);
}
/********************************************************************************************/
/*********************************************************************************************\
* Belkin WeMo and Philips Hue bridge UDP multicast support
\*********************************************************************************************/
boolean UDP_Disconnect()
{
@ -211,10 +222,20 @@ void pollUDP()
// addLog_P(LOG_LEVEL_DEBUG_MORE, packetBuffer);
if (request.indexOf("M-SEARCH") >= 0) {
if ((EMUL_WEMO == sysCfg.flag.emulation) &&(request.indexOf("urn:Belkin:device:**") > 0)) {
request.toLowerCase();
request.replace(" ", "");
// addLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: M-SEARCH Packet received"));
// addLog_P(LOG_LEVEL_DEBUG_MORE, request.c_str());
if ((EMUL_WEMO == sysCfg.flag.emulation) && (request.indexOf(F("urn:belkin:device:**")) > 0)) {
wemo_respondToMSearch();
}
else if ((EMUL_HUE == sysCfg.flag.emulation) && ((request.indexOf("ST: urn:schemas-upnp-org:device:basic:1") > 0) || (request.indexOf("ST: upnp:rootdevice") > 0) || (request.indexOf("ST: ssdp:all") > 0))) {
else if ((EMUL_HUE == sysCfg.flag.emulation) &&
((request.indexOf(F("st:urn:schemas-upnp-org:device:basic:1")) > 0) ||
(request.indexOf(F("st:upnp:rootdevice")) > 0) ||
(request.indexOf(F("st:ssdpsearch:all")) > 0) ||
(request.indexOf(F("st:ssdp:all")) > 0))) {
hue_respondToMSearch();
}
}
@ -224,8 +245,9 @@ void pollUDP()
#ifdef USE_WEBSERVER
/*********************************************************************************************\
* Web server additions
* Wemo web server additions
\*********************************************************************************************/
const char WEMO_EVENTSERVICE_XML[] PROGMEM =
"<?scpd xmlns=\"urn:Belkin:service-1-0\"?>"
"<actionList>"
@ -278,45 +300,93 @@ const char WEMO_SETUP_XML[] PROGMEM =
"</device>"
"</root>\r\n"
"\r\n";
/********************************************************************************************/
void handleUPnPevent()
{
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: WeMo basic event"));
String request = webServer->arg(0);
if (request.indexOf(F("State>1</Binary")) > 0) {
// do_cmnd_power(1, 1);
do_cmnd_power(Maxdevice, 1);
}
if (request.indexOf(F("State>0</Binary")) > 0) {
// do_cmnd_power(1, 0);
do_cmnd_power(Maxdevice, 0);
}
webServer->send(200, FPSTR(HDR_CTYPE_PLAIN), "");
}
void handleUPnPservice()
{
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: WeMo event service"));
webServer->send(200, FPSTR(HDR_CTYPE_PLAIN), FPSTR(WEMO_EVENTSERVICE_XML));
}
void handleUPnPsetupWemo()
{
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: WeMo setup"));
String setup_xml = FPSTR(WEMO_SETUP_XML);
setup_xml.replace("{x1}", sysCfg.friendlyname[0]);
setup_xml.replace("{x2}", wemo_UUID());
setup_xml.replace("{x3}", wemo_serial());
webServer->send(200, FPSTR(HDR_CTYPE_XML), setup_xml);
}
/*********************************************************************************************\
* Hue web server additions
\*********************************************************************************************/
const char HUE_DESCRIPTION_XML[] PROGMEM =
"<?xml version=\"1.0\"?>"
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">"
"<specVersion>"
"<major>1</major>"
"<minor>0</minor>"
"<major>1</major>"
"<minor>0</minor>"
"</specVersion>"
"<URLBase>http://{x1}/</URLBase>"
// "<URLBase>http://{x1}/</URLBase>"
"<URLBase>http://{x1}:80/</URLBase>"
"<device>"
"<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>"
"<friendlyName>Amazon-Echo-HA-Bridge ({x1})</friendlyName>"
"<manufacturer>Royal Philips Electronics</manufacturer>"
"<modelName>Philips hue bridge 2012</modelName>"
"<modelNumber>929000226503</modelNumber>"
"<UDN>uuid:{x2}</UDN>"
"<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>"
"<friendlyName>Amazon-Echo-HA-Bridge ({x1})</friendlyName>"
// "<friendlyName>Philips hue ({x1})</friendlyName>"
"<manufacturer>Royal Philips Electronics</manufacturer>"
"<modelDescription>Philips hue Personal Wireless Lighting</modelDescription>"
"<modelName>Philips hue bridge 2012</modelName>"
"<modelNumber>929000226503</modelNumber>"
"<serialNumber>{x3}</serialNumber>"
"<UDN>uuid:{x2}</UDN>"
"</device>"
"</root>\r\n"
"\r\n";
const char HUE_LIGHT_STATUS_JSON[] PROGMEM =
"{\"state\":"
"{\"on\":{state},"
"\"bri\":{b},"
"\"hue\":{h},"
"\"sat\":{s},"
"\"ct\":500,"
"\"xy\":[0.5, 0.5],"
"\"alert\":\"none\","
"\"effect\":\"none\","
"\"colormode\":\"hs\","
"\"reachable\":true"
"},"
"\"on\":{state},"
"\"bri\":{b},"
"\"hue\":{h},"
"\"sat\":{s},"
"\"xy\":[0.5, 0.5],"
"\"ct\":500,"
"\"alert\":\"none\","
"\"effect\":\"none\","
"\"colormode\":\"hs\","
"\"reachable\":true";
const char HUE_LIGHTS_STATUS_JSON[] PROGMEM =
"\"type\":\"Extended color light\","
"\"name\":\"{j1}\","
"\"modelid\":\"LCT007\","
"\"uniqueid\":\"{j2}\","
"\"swversion\":\"5.50.1.19085\""
"}";
const char HUE_LIGHT_RESPONSE_JSON[] PROGMEM =
"{\"success\":{\"{api}/{id}/{cmd}\":{res}}}";
const char HUE_GROUP0_STATUS_JSON[] PROGMEM =
"{\"name\":\"Group 0\","
"\"lights\":[{l1}],"
"\"type\":\"LightGroup\","
"\"action\":{";
// "\"scene\":\"none\",";
const char HUE_CONFIG_RESPONSE_JSON[] PROGMEM =
"{\"name\":\"Philips hue\","
"\"mac\":\"{mac}\","
@ -324,8 +394,9 @@ const char HUE_CONFIG_RESPONSE_JSON[] PROGMEM =
"\"ipaddress\":\"{ip}\","
"\"netmask\":\"{mask}\","
"\"gateway\":\"{gw}\","
"\"proxyaddress\":\"\","
"\"proxyaddress\":\"none\","
"\"proxyport\":0,"
"\"bridgeid\":\"{bid}\","
"\"UTC\":\"{dt}\","
"\"whitelist\":{\"{id}\":{"
"\"last use date\":\"{dt}\","
@ -337,60 +408,37 @@ const char HUE_CONFIG_RESPONSE_JSON[] PROGMEM =
"\"linkbutton\":false,"
"\"portalservices\":false"
"}";
const char HUE_LIGHT_RESPONSE_JSON[] PROGMEM =
"{\"success\":{\"/lights/{id}/state/{cmd}\":{res}}}";
const char HUE_ERROR_JSON[] PROGMEM =
"[{\"error\":{\"type\":901,\"address\":\"/\",\"description\":\"Internal Error\"}}]";
void handleUPnPevent()
{
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle WeMo basic event"));
String request = webServer->arg(0);
if (request.indexOf("State>1</Binary") > 0) {
// do_cmnd_power(1, 1);
do_cmnd_power(Maxdevice, 1);
}
if (request.indexOf("State>0</Binary") > 0) {
// do_cmnd_power(1, 0);
do_cmnd_power(Maxdevice, 0);
}
webServer->send(200, F("text/plain"), "");
}
void handleUPnPservice()
{
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle WeMo event service"));
webServer->send_P(200, PSTR("text/plain"), WEMO_EVENTSERVICE_XML);
}
void handleUPnPsetupWemo()
{
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle WeMo setup"));
String setup_xml = FPSTR(WEMO_SETUP_XML);
setup_xml.replace("{x1}", sysCfg.friendlyname[0]);
setup_xml.replace("{x2}", wemo_UUID());
setup_xml.replace("{x3}", wemo_serial());
webServer->send(200, F("text/xml"), setup_xml);
}
/********************************************************************************************/
String hue_deviceId(uint8_t id)
{
char deviceid[16];
String deviceid = WiFi.macAddress() + F(":00:11-") + String(id);
deviceid.toLowerCase();
return deviceid; // 5c:cf:7f:13:9f:3d:00:11-1
}
snprintf_P(deviceid, sizeof(deviceid), PSTR("5CCF7F%03X-%0d"), ESP.getChipId(), id);
return String(deviceid);
String hue_userId()
{
char userid[7];
snprintf_P(userid, sizeof(userid), PSTR("%03x"), ESP.getChipId());
return String(userid);
}
void handleUPnPsetupHue()
{
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle Hue Bridge setup"));
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Hue Bridge setup"));
String description_xml = FPSTR(HUE_DESCRIPTION_XML);
description_xml.replace("{x1}", WiFi.localIP().toString());
description_xml.replace("{x2}", hue_UUID());
webServer->send(200, F("text/xml"), description_xml);
description_xml.replace("{x3}", hue_serial());
webServer->send(200, FPSTR(HDR_CTYPE_XML), description_xml);
}
void hue_todo(String *path)
@ -399,6 +447,8 @@ void hue_todo(String *path)
snprintf_P(log, sizeof(log), PSTR("HTTP: HUE API not implemented (%s)"),path->c_str());
addLog(LOG_LEVEL_DEBUG_MORE, log);
webServer->send(200, FPSTR(HDR_CTYPE_JSON), "{}");
}
void hue_config_response(String *response)
@ -408,7 +458,35 @@ void hue_config_response(String *response)
response->replace("{ip}", WiFi.localIP().toString());
response->replace("{mask}", WiFi.subnetMask().toString());
response->replace("{gw}", WiFi.gatewayIP().toString());
response->replace("{dt}", getDateTime());
response->replace("{bid}", hue_bridgeid());
response->replace("{dt}", getUTCDateTime());
response->replace("{id}", hue_userId());
}
void hue_config(String *path)
{
String response = "";
hue_config_response(&response);
webServer->send(200, FPSTR(HDR_CTYPE_JSON), response);
}
void hue_light_status(byte device, String *response)
{
*response += FPSTR(HUE_LIGHT_STATUS_JSON);
response->replace("{state}", (power & (0x01 << (device-1))) ? "true" : "false");
if (sfl_flg) {
sl_replaceHSB(response);
#ifdef USE_WS2812
}
else if (pin[GPIO_WS2812] < 99) {
ws2812_replaceHSB(response);
#endif // USE_WS2812
} else {
response->replace("{h}", "0");
response->replace("{s}", "0");
response->replace("{b}", "0");
}
}
void hue_global_cfg(String *path)
@ -416,51 +494,31 @@ void hue_global_cfg(String *path)
String response;
path->remove(0,1); // cut leading / to get <id>
response = "{\"lights\":{\"";
response = F("{\"lights\":{\"");
for (uint8_t i = 1; i <= Maxdevice; i++) {
response += i;
response += "\":";
response += FPSTR(HUE_LIGHT_STATUS_JSON);
response += F("\":{\"state\":{");
hue_light_status(i, &response);
response += "},";
response += FPSTR(HUE_LIGHTS_STATUS_JSON);
response.replace("{j1}", sysCfg.friendlyname[i-1]);
response.replace("{j2}", hue_deviceId(i));
if (i < Maxdevice) {
response += ",\"";
}
response.replace("{state}", (power & (0x01 << (i-1))) ? "true" : "false");
response.replace("{j1}", sysCfg.friendlyname[i-1]);
response.replace("{j2}", hue_deviceId(i));
if (pin[GPIO_WS2812] < 99) {
#ifdef USE_WS2812
ws2812_replaceHSB(&response);
#endif // USE_WS2812
} else {
response.replace("{h}", "0");
response.replace("{s}", "0");
response.replace("{b}", "0");
}
}
response += F("},\"groups\":{},\"schedules\":{},\"config\":");
hue_config_response(&response);
response.replace("{id}", *path);
response += "}";
webServer->send(200, F("application/json"), response);
webServer->send(200, FPSTR(HDR_CTYPE_JSON), response);
}
void hue_auth(String *path)
{
char response[38];
snprintf_P(response, sizeof(response), PSTR("[{\"success\":{\"username\":\"%03x\"}}]"), ESP.getChipId());
webServer->send(200, F("application/json"), response);
}
void hue_config(String *path)
{
String response = "";
path->remove(0,1); // cut leading / to get <id>
hue_config_response(&response);
response.replace("{id}", *path);
webServer->send(200, F("application/json"), response);
snprintf_P(response, sizeof(response), PSTR("[{\"success\":{\"username\":\"%s\"}}]"), hue_userId().c_str());
webServer->send(200, FPSTR(HDR_CTYPE_JSON), response);
}
void hue_lights(String *path)
@ -468,13 +526,14 @@ void hue_lights(String *path)
/*
* http://sonoff/api/username/lights/1/state?1={"on":true,"hue":56100,"sat":254,"bri":254,"alert":"none","transitiontime":40}
*/
String response;
String response;
uint8_t device = 1;
uint16_t tmp=0;
uint16_t tmp = 0;
int16_t pos = 0;
float bri = 0;
float hue = 0;
float sat = 0;
float bri = 0;
float hue = 0;
float sat = 0;
bool resp = false;
bool on = false;
bool change = false;
char id[4];
@ -484,26 +543,18 @@ void hue_lights(String *path)
response = "{\"";
for (uint8_t i = 1; i <= Maxdevice; i++) {
response += i;
response += "\":";
response += FPSTR(HUE_LIGHT_STATUS_JSON);
response += F("\":{\"state\":{");
hue_light_status(i, &response);
response += "},";
response += FPSTR(HUE_LIGHTS_STATUS_JSON);
response.replace("{j1}", sysCfg.friendlyname[i-1]);
response.replace("{j2}", hue_deviceId(i));
if (i < Maxdevice) {
response += ",\"";
}
response.replace("{state}", (power & (0x01 << (i-1))) ? "true" : "false");
response.replace("{j1}", sysCfg.friendlyname[i-1]);
response.replace("{j2}", hue_deviceId(i));
if (pin[GPIO_WS2812] < 99) {
#ifdef USE_WS2812
ws2812_replaceHSB(&response);
#endif // USE_WS2812
} else {
response.replace("{h}", "0");
response.replace("{s}", "0");
response.replace("{b}", "0");
}
}
response += "}";
webServer->send(200, F("application/json"), response);
webServer->send(200, FPSTR(HDR_CTYPE_JSON), response);
}
else if (path->endsWith("/state")) { // Got ID/state
path->remove(0,8); // Remove /lights/
@ -512,15 +563,17 @@ void hue_lights(String *path)
if ((device < 1) || (device > Maxdevice)) {
device = 1;
}
response = "[";
response += FPSTR(HUE_LIGHT_RESPONSE_JSON);
response.replace("{api}", "/lights");
response.replace("{id}", String(device));
response.replace("{cmd}", "state/on");
if (1 == webServer->args()) {
response = "[";
StaticJsonBuffer<400> jsonBuffer;
JsonObject &hue_json = jsonBuffer.parseObject(webServer->arg(0));
if (hue_json.containsKey("on")) {
response += FPSTR(HUE_LIGHT_RESPONSE_JSON);
response.replace("{id}", String(device));
response.replace("{cmd}", "on");
on = hue_json["on"];
switch(on)
{
@ -533,53 +586,76 @@ void hue_lights(String *path)
default : response.replace("{res}", (power & (0x01 << (device-1))) ? "true" : "false");
break;
}
resp = true;
}
if (sfl_flg) {
sl_getHSB(&hue,&sat,&bri);
#ifdef USE_WS2812
ws2812_getHSB(&hue,&sat,&bri);
}
else if (pin[GPIO_WS2812] < 99) {
ws2812_getHSB(&hue,&sat,&bri);
#endif // USE_WS2812
}
if (hue_json.containsKey("bri")) {
tmp = hue_json["bri"];
bri = (float)tmp/254.0f;
response += ",";
bri = (float)tmp / 254.0f;
if (resp) {
response += ",";
}
response += FPSTR(HUE_LIGHT_RESPONSE_JSON);
response.replace("{api}", "/lights");
response.replace("{id}", String(device));
response.replace("{cmd}", "state/bri");
response.replace("{cmd}", "bri");
response.replace("{res}", String(tmp));
resp = true;
change = true;
}
if (hue_json.containsKey("hue")) {
tmp = hue_json["hue"];
hue = (float)tmp/65535.0f;
response += ",";
hue = (float)tmp / 65535.0f;
if (resp) {
response += ",";
}
response += FPSTR(HUE_LIGHT_RESPONSE_JSON);
response.replace("{api}", "/lights");
response.replace("{id}", String(device));
response.replace("{cmd}", "state/hue");
response.replace("{cmd}", "hue");
response.replace("{res}", String(tmp));
resp = true;
change = true;
}
if (hue_json.containsKey("sat")) {
tmp = hue_json["sat"];
sat = (float)tmp/254.0f;
response += ",";
sat = (float)tmp / 254.0f;
if (resp) {
response += ",";
}
response += FPSTR(HUE_LIGHT_RESPONSE_JSON);
response.replace("{api}", "/lights");
response.replace("{id}", String(device));
response.replace("{cmd}", "state/sat");
response.replace("{cmd}", "sat");
response.replace("{res}", String(tmp));
change = true;
}
if (change && (pin[GPIO_WS2812] < 99)) {
ws2812_setHSB(hue, sat, bri);
if (change) {
if (sfl_flg) {
sl_setHSB(hue, sat, bri);
#ifdef USE_WS2812
}
else if (pin[GPIO_WS2812] < 99) {
ws2812_setHSB(hue, sat, bri);
#endif // USE_WS2812
}
change = false;
}
#endif // USE_WS2812
response += "]";
if (2 == response.length()) {
response = FPSTR(HUE_ERROR_JSON);
}
}
else {
response=FPSTR(HUE_ERROR_JSON);
response = FPSTR(HUE_ERROR_JSON);
}
webServer->send(200, F("application/json"), response);
webServer->send(200, FPSTR(HDR_CTYPE_JSON), response);
}
else if(path->indexOf("/lights/") >= 0) { // Got /lights/ID
path->remove(0,8); // Remove /lights/
@ -587,22 +663,35 @@ void hue_lights(String *path)
if ((device < 1) || (device > Maxdevice)) {
device = 1;
}
response = FPSTR(HUE_LIGHT_STATUS_JSON);
response.replace("{state}", (power & (0x01 << (device -1))) ? "true" : "false");
response.replace("{j1}", sysCfg.friendlyname[device -1]);
response += F("{\"state\":{");
hue_light_status(device, &response);
response += "},";
response += FPSTR(HUE_LIGHTS_STATUS_JSON);
response.replace("{j1}", sysCfg.friendlyname[device-1]);
response.replace("{j2}", hue_deviceId(device));
if (pin[GPIO_WS2812] < 99) {
#ifdef USE_WS2812
ws2812_replaceHSB(&response);
#endif // USE_WS2812
} else {
response.replace("{h}", "0");
response.replace("{s}", "0");
response.replace("{b}", "0");
}
webServer->send(200, F("application/json"), response);
webServer->send(200, FPSTR(HDR_CTYPE_JSON), response);
}
else webServer->send(406, F("application/json"), "{}");
else {
webServer->send(406, FPSTR(HDR_CTYPE_JSON), "{}");
}
}
void hue_groups(String *path)
{
String response = "{}";
if (path->endsWith("/0")) {
response = FPSTR(HUE_GROUP0_STATUS_JSON);
String lights = F("\"1\"");
for (uint8_t i = 2; i <= Maxdevice; i++) {
lights += ",\"" + String(i) + "\"";
}
response.replace("{l1}", lights);
hue_light_status(1, &response);
response += F("}}");
}
webServer->send(200, FPSTR(HDR_CTYPE_JSON), response);
}
void handle_hue_api(String *path)
@ -618,7 +707,8 @@ void handle_hue_api(String *path)
uint8_t args = 0;
path->remove(0, 4); // remove /api
snprintf_P(log, sizeof(log), PSTR("HTTP: Handle Hue API (%s)"), path->c_str());
uint16_t apilen = path->length();
snprintf_P(log, sizeof(log), PSTR("HTTP: Hue API (%s)"), path->c_str());
addLog(LOG_LEVEL_DEBUG_MORE, log);
for (args = 0; args < webServer->args(); args++) {
String json = webServer->arg(args);
@ -627,10 +717,11 @@ void handle_hue_api(String *path)
}
if (path->endsWith("/invalid/")) {} // Just ignore
else if (!apilen) hue_auth(path); // New HUE App setup
else if (path->endsWith("/")) hue_auth(path); // New HUE App setup
else if (path->endsWith("/config")) hue_config(path);
else if (path->indexOf("/lights") >= 0) hue_lights(path);
else if (path->endsWith("/groups")) hue_todo(path);
else if (path->indexOf("/groups") >= 0) hue_groups(path);
else if (path->endsWith("/schedules")) hue_todo(path);
else if (path->endsWith("/sensors")) hue_todo(path);
else if (path->endsWith("/scenes")) hue_todo(path);