mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-26 20:26:32 +00:00
Add defines USE_EMULATION_WEMO and USE_EMULATION_HUE
* Remove define USE_EMULATION from my_user_config.h (#5826) * Add defines USE_EMULATION_WEMO and USE_EMULATION_HUE to my_user_config.h to control emulation features at compile time (#5826)
This commit is contained in:
parent
1a6acf5078
commit
d599f21758
@ -3,6 +3,8 @@
|
|||||||
* Add initial support for Scripts as replacement for Rules. Default disabled but can be enabled in my_user_config.h (#5689)
|
* Add initial support for Scripts as replacement for Rules. Default disabled but can be enabled in my_user_config.h (#5689)
|
||||||
* Add rule System#Save executed just before a planned restart
|
* Add rule System#Save executed just before a planned restart
|
||||||
* Add HX711 weight restore after controlled restart or after power restore just before executing command Sensor34 7 (#5367, #5786)
|
* Add HX711 weight restore after controlled restart or after power restore just before executing command Sensor34 7 (#5367, #5786)
|
||||||
|
* Remove define USE_EMULATION from my_user_config.h (#5826)
|
||||||
|
* Add defines USE_EMULATION_WEMO and USE_EMULATION_HUE to my_user_config.h to control emulation features at compile time (#5826)
|
||||||
*
|
*
|
||||||
* 6.5.0.10 20190513
|
* 6.5.0.10 20190513
|
||||||
* Enable ADC0 by default in my_user_config.h (#5671)
|
* Enable ADC0 by default in my_user_config.h (#5671)
|
||||||
|
@ -275,7 +275,8 @@
|
|||||||
#define USE_WEBSERVER // Enable web server and Wifi Manager (+66k code, +8k mem)
|
#define USE_WEBSERVER // Enable web server and Wifi Manager (+66k code, +8k mem)
|
||||||
#define WEB_PORT 80 // Web server Port for User and Admin mode
|
#define WEB_PORT 80 // Web server Port for User and Admin mode
|
||||||
#define WEB_USERNAME "admin" // Web server Admin mode user name
|
#define WEB_USERNAME "admin" // Web server Admin mode user name
|
||||||
#define USE_EMULATION // Enable Belkin WeMo and Hue Bridge emulation for Alexa (+18k code, +2k mem)
|
#define USE_EMULATION_HUE // Enable Hue Bridge emulation for Alexa (+14k code, +2k mem common)
|
||||||
|
#define USE_EMULATION_WEMO // Enable Belkin WeMo emulation for Alexa (+6k code, +2k mem common)
|
||||||
|
|
||||||
// -- mDNS ----------------------------------------
|
// -- mDNS ----------------------------------------
|
||||||
#define USE_DISCOVERY // Enable mDNS for the following services (+8k code, +0.3k mem)
|
#define USE_DISCOVERY // Enable mDNS for the following services (+8k code, +0.3k mem)
|
||||||
|
@ -2652,6 +2652,13 @@ void setup(void)
|
|||||||
sleep = Settings.sleep;
|
sleep = Settings.sleep;
|
||||||
#ifndef USE_EMULATION
|
#ifndef USE_EMULATION
|
||||||
Settings.flag2.emulation = 0;
|
Settings.flag2.emulation = 0;
|
||||||
|
#else
|
||||||
|
#ifndef USE_EMULATION_WEMO
|
||||||
|
if (EMUL_WEMO == Settings.flag2.emulation) { Settings.flag2.emulation = 0; }
|
||||||
|
#endif
|
||||||
|
#ifndef USE_EMULATION_HUE
|
||||||
|
if (EMUL_HUE == Settings.flag2.emulation) { Settings.flag2.emulation = 0; }
|
||||||
|
#endif
|
||||||
#endif // USE_EMULATION
|
#endif // USE_EMULATION
|
||||||
|
|
||||||
if (Settings.param[P_BOOT_LOOP_OFFSET]) {
|
if (Settings.param[P_BOOT_LOOP_OFFSET]) {
|
||||||
|
@ -46,6 +46,13 @@ void KNX_CB_Action(message_t const &msg, void *arg);
|
|||||||
* Default global defines
|
* Default global defines
|
||||||
\*********************************************************************************************/
|
\*********************************************************************************************/
|
||||||
|
|
||||||
|
#ifdef USE_EMULATION_HUE
|
||||||
|
#define USE_EMULATION
|
||||||
|
#endif
|
||||||
|
#ifdef USE_EMULATION_WEMO
|
||||||
|
#define USE_EMULATION
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef MODULE
|
#ifndef MODULE
|
||||||
#define MODULE SONOFF_BASIC // [Module] Select default model
|
#define MODULE SONOFF_BASIC // [Module] Select default model
|
||||||
#endif
|
#endif
|
||||||
|
@ -49,8 +49,8 @@ void GetFeatures(void)
|
|||||||
#ifdef WEBSERVER_ADVERTISE
|
#ifdef WEBSERVER_ADVERTISE
|
||||||
feature_drv1 |= 0x00000100; // xdrv_02_webserver.ino
|
feature_drv1 |= 0x00000100; // xdrv_02_webserver.ino
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_EMULATION
|
#ifdef USE_EMULATION_HUE
|
||||||
feature_drv1 |= 0x00000200; // xplg_wemohue.ino
|
feature_drv1 |= 0x00000200; // xdrv_20_hue.ino
|
||||||
#endif
|
#endif
|
||||||
#if (MQTT_LIBRARY_TYPE == MQTT_PUBSUBCLIENT)
|
#if (MQTT_LIBRARY_TYPE == MQTT_PUBSUBCLIENT)
|
||||||
feature_drv1 |= 0x00000400; // xdrv_01_mqtt.ino
|
feature_drv1 |= 0x00000400; // xdrv_01_mqtt.ino
|
||||||
@ -183,8 +183,10 @@ void GetFeatures(void)
|
|||||||
#ifdef USE_SCRIPT
|
#ifdef USE_SCRIPT
|
||||||
feature_drv2 |= 0x00080000; // xdrv_10_scripter.ino
|
feature_drv2 |= 0x00080000; // xdrv_10_scripter.ino
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_EMULATION_WEMO
|
||||||
|
feature_drv2 |= 0x00100000; // xdrv_21_wemo.ino
|
||||||
|
#endif
|
||||||
|
|
||||||
// feature_drv2 |= 0x00100000;
|
|
||||||
// feature_drv2 |= 0x00200000;
|
// feature_drv2 |= 0x00200000;
|
||||||
// feature_drv2 |= 0x00400000;
|
// feature_drv2 |= 0x00400000;
|
||||||
|
|
||||||
|
136
sonoff/support_udp.ino
Normal file
136
sonoff/support_udp.ino
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
support_udp.ino - Udp support for Sonoff-Tasmota
|
||||||
|
|
||||||
|
Copyright (C) 2019 Heiko Krupp and 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef USE_EMULATION
|
||||||
|
|
||||||
|
#define UDP_BUFFER_SIZE 200 // Max UDP buffer size needed for M-SEARCH message
|
||||||
|
#define UDP_MSEARCH_SEND_DELAY 1500 // Delay in ms before M-Search response is send
|
||||||
|
|
||||||
|
#include <Ticker.h>
|
||||||
|
Ticker TickerMSearch;
|
||||||
|
|
||||||
|
IPAddress udp_remote_ip; // M-Search remote IP address
|
||||||
|
uint16_t udp_remote_port; // M-Search remote port
|
||||||
|
|
||||||
|
bool udp_connected = false;
|
||||||
|
bool udp_response_mutex = false; // M-Search response mutex to control re-entry
|
||||||
|
|
||||||
|
/*********************************************************************************************\
|
||||||
|
* UPNP/SSDP search targets
|
||||||
|
\*********************************************************************************************/
|
||||||
|
|
||||||
|
const char URN_BELKIN_DEVICE[] PROGMEM = "urn:belkin:device:**";
|
||||||
|
const char UPNP_ROOTDEVICE[] PROGMEM = "upnp:rootdevice";
|
||||||
|
const char SSDPSEARCH_ALL[] PROGMEM = "ssdpsearch:all";
|
||||||
|
const char SSDP_ALL[] PROGMEM = "ssdp:all";
|
||||||
|
|
||||||
|
/*********************************************************************************************\
|
||||||
|
* UDP support routines
|
||||||
|
\*********************************************************************************************/
|
||||||
|
|
||||||
|
bool UdpDisconnect(void)
|
||||||
|
{
|
||||||
|
if (udp_connected) {
|
||||||
|
PortUdp.flush();
|
||||||
|
WiFiUDP::stopAll();
|
||||||
|
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_MULTICAST_DISABLED));
|
||||||
|
udp_connected = false;
|
||||||
|
}
|
||||||
|
return udp_connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UdpConnect(void)
|
||||||
|
{
|
||||||
|
if (!udp_connected) {
|
||||||
|
// Simple Service Discovery Protocol (SSDP)
|
||||||
|
if (PortUdp.beginMulticast(WiFi.localIP(), IPAddress(239,255,255,250), 1900)) {
|
||||||
|
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_REJOINED));
|
||||||
|
udp_response_mutex = false;
|
||||||
|
udp_connected = true;
|
||||||
|
} else {
|
||||||
|
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_JOIN_FAILED));
|
||||||
|
udp_connected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return udp_connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PollUdp(void)
|
||||||
|
{
|
||||||
|
if (udp_connected) {
|
||||||
|
if (PortUdp.parsePacket()) {
|
||||||
|
char packet_buffer[UDP_BUFFER_SIZE]; // buffer to hold incoming UDP/SSDP packet
|
||||||
|
|
||||||
|
int len = PortUdp.read(packet_buffer, UDP_BUFFER_SIZE -1);
|
||||||
|
packet_buffer[len] = 0;
|
||||||
|
|
||||||
|
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: Packet (%d)"), len);
|
||||||
|
// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("\n%s"), packet_buffer);
|
||||||
|
|
||||||
|
// Simple Service Discovery Protocol (SSDP)
|
||||||
|
if (devices_present && !udp_response_mutex && (strstr_P(packet_buffer, PSTR("M-SEARCH")) != nullptr)) {
|
||||||
|
udp_response_mutex = true;
|
||||||
|
|
||||||
|
udp_remote_ip = PortUdp.remoteIP();
|
||||||
|
udp_remote_port = PortUdp.remotePort();
|
||||||
|
|
||||||
|
// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: M-SEARCH Packet from %s:%d\n%s"),
|
||||||
|
// udp_remote_ip.toString().c_str(), udp_remote_port, packet_buffer);
|
||||||
|
|
||||||
|
uint32_t response_delay = UDP_MSEARCH_SEND_DELAY + ((millis() &0x7) * 100); // 1500 - 2200 msec
|
||||||
|
|
||||||
|
LowerCase(packet_buffer, packet_buffer);
|
||||||
|
RemoveSpace(packet_buffer);
|
||||||
|
|
||||||
|
#ifdef USE_EMULATION_WEMO
|
||||||
|
if (EMUL_WEMO == Settings.flag2.emulation) {
|
||||||
|
if (strstr_P(packet_buffer, URN_BELKIN_DEVICE) != nullptr) { // type1 echo dot 2g, echo 1g's
|
||||||
|
TickerMSearch.attach_ms(response_delay, WemoRespondToMSearch, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if ((strstr_P(packet_buffer, UPNP_ROOTDEVICE) != nullptr) || // type2 Echo 2g (echo & echo plus)
|
||||||
|
(strstr_P(packet_buffer, SSDPSEARCH_ALL) != nullptr) ||
|
||||||
|
(strstr_P(packet_buffer, SSDP_ALL) != nullptr)) {
|
||||||
|
TickerMSearch.attach_ms(response_delay, WemoRespondToMSearch, 2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // USE_EMULATION_WEMO
|
||||||
|
|
||||||
|
#ifdef USE_EMULATION_HUE
|
||||||
|
if (EMUL_HUE == Settings.flag2.emulation) {
|
||||||
|
if ((strstr_P(packet_buffer, PSTR(":device:basic:1")) != nullptr) ||
|
||||||
|
(strstr_P(packet_buffer, UPNP_ROOTDEVICE) != nullptr) ||
|
||||||
|
(strstr_P(packet_buffer, SSDPSEARCH_ALL) != nullptr) ||
|
||||||
|
(strstr_P(packet_buffer, SSDP_ALL) != nullptr)) {
|
||||||
|
TickerMSearch.attach_ms(response_delay, HueRespondToMSearch);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // USE_EMULATION_HUE
|
||||||
|
|
||||||
|
udp_response_mutex = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
delay(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // USE_EMULATION
|
@ -131,7 +131,7 @@ void WifiConfig(uint8_t type)
|
|||||||
{
|
{
|
||||||
if (!wifi_config_type) {
|
if (!wifi_config_type) {
|
||||||
if ((WIFI_RETRY == type) || (WIFI_WAIT == type)) { return; }
|
if ((WIFI_RETRY == type) || (WIFI_WAIT == type)) { return; }
|
||||||
#if defined(USE_WEBSERVER) && defined(USE_EMULATION)
|
#ifdef USE_EMULATION
|
||||||
UdpDisconnect();
|
UdpDisconnect();
|
||||||
#endif // USE_EMULATION
|
#endif // USE_EMULATION
|
||||||
WiFi.disconnect(); // Solve possible Wifi hangs
|
WiFi.disconnect(); // Solve possible Wifi hangs
|
||||||
@ -211,7 +211,7 @@ void WifiBegin(uint8_t flag, uint8_t channel)
|
|||||||
{
|
{
|
||||||
const char kWifiPhyMode[] = " BGN";
|
const char kWifiPhyMode[] = " BGN";
|
||||||
|
|
||||||
#if defined(USE_WEBSERVER) && defined(USE_EMULATION)
|
#ifdef USE_EMULATION
|
||||||
UdpDisconnect();
|
UdpDisconnect();
|
||||||
#endif // USE_EMULATION
|
#endif // USE_EMULATION
|
||||||
|
|
||||||
@ -565,7 +565,7 @@ void WifiCheck(uint8_t param)
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
WifiSetState(0);
|
WifiSetState(0);
|
||||||
#if defined(USE_WEBSERVER) && defined(USE_EMULATION)
|
#ifdef USE_EMULATION
|
||||||
UdpDisconnect();
|
UdpDisconnect();
|
||||||
#endif // USE_EMULATION
|
#endif // USE_EMULATION
|
||||||
mdns_begun = 0;
|
mdns_begun = 0;
|
||||||
|
@ -493,9 +493,6 @@ void StartWebserver(int type, IPAddress ipweb)
|
|||||||
WebServer->on("/rs", HandleRestoreConfiguration);
|
WebServer->on("/rs", HandleRestoreConfiguration);
|
||||||
WebServer->on("/rt", HandleResetConfiguration);
|
WebServer->on("/rt", HandleResetConfiguration);
|
||||||
WebServer->on("/in", HandleInformation);
|
WebServer->on("/in", HandleInformation);
|
||||||
#ifdef USE_EMULATION
|
|
||||||
HueWemoAddHandlers();
|
|
||||||
#endif // USE_EMULATION
|
|
||||||
XdrvCall(FUNC_WEB_ADD_HANDLER);
|
XdrvCall(FUNC_WEB_ADD_HANDLER);
|
||||||
XsnsCall(FUNC_WEB_ADD_HANDLER);
|
XsnsCall(FUNC_WEB_ADD_HANDLER);
|
||||||
#endif // Not FIRMWARE_MINIMAL
|
#endif // Not FIRMWARE_MINIMAL
|
||||||
@ -1529,12 +1526,20 @@ void HandleOtherConfiguration(void)
|
|||||||
#ifdef USE_EMULATION
|
#ifdef USE_EMULATION
|
||||||
WSContentSend_P(PSTR("<p></p><fieldset><legend><b> " D_EMULATION " </b></legend><p>")); // Keep close to Friendlynames so do not use <br/>
|
WSContentSend_P(PSTR("<p></p><fieldset><legend><b> " D_EMULATION " </b></legend><p>")); // Keep close to Friendlynames so do not use <br/>
|
||||||
for (uint8_t i = 0; i < EMUL_MAX; i++) {
|
for (uint8_t i = 0; i < EMUL_MAX; i++) {
|
||||||
|
#ifndef USE_EMULATION_WEMO
|
||||||
|
if (i == EMUL_WEMO) { i++; }
|
||||||
|
#endif
|
||||||
|
#ifndef USE_EMULATION_HUE
|
||||||
|
if (i == EMUL_HUE) { i++; }
|
||||||
|
#endif
|
||||||
|
if (i < EMUL_MAX) {
|
||||||
WSContentSend_P(PSTR("<input id='r%d' name='b2' type='radio' value='%d'%s><b>%s</b> %s<br/>"), // Different id only used for labels
|
WSContentSend_P(PSTR("<input id='r%d' name='b2' type='radio' value='%d'%s><b>%s</b> %s<br/>"), // Different id only used for labels
|
||||||
i, i,
|
i, i,
|
||||||
(i == Settings.flag2.emulation) ? " checked" : "",
|
(i == Settings.flag2.emulation) ? " checked" : "",
|
||||||
GetTextIndexed(stemp, sizeof(stemp), i, kEmulationOptions),
|
GetTextIndexed(stemp, sizeof(stemp), i, kEmulationOptions),
|
||||||
(i == EMUL_NONE) ? "" : (i == EMUL_WEMO) ? D_SINGLE_DEVICE : D_MULTI_DEVICE);
|
(i == EMUL_NONE) ? "" : (i == EMUL_WEMO) ? D_SINGLE_DEVICE : D_MULTI_DEVICE);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
WSContentSend_P(PSTR("</p></fieldset>"));
|
WSContentSend_P(PSTR("</p></fieldset>"));
|
||||||
#endif // USE_EMULATION
|
#endif // USE_EMULATION
|
||||||
|
|
||||||
@ -2186,10 +2191,12 @@ void HandleNotFound(void)
|
|||||||
if (CaptivePortal()) { return; } // If captive portal redirect instead of displaying the error page.
|
if (CaptivePortal()) { return; } // If captive portal redirect instead of displaying the error page.
|
||||||
|
|
||||||
#ifdef USE_EMULATION
|
#ifdef USE_EMULATION
|
||||||
|
#ifdef USE_EMULATION_HUE
|
||||||
String path = WebServer->uri();
|
String path = WebServer->uri();
|
||||||
if ((EMUL_HUE == Settings.flag2.emulation) && (path.startsWith("/api"))) {
|
if ((EMUL_HUE == Settings.flag2.emulation) && (path.startsWith("/api"))) {
|
||||||
HandleHueApi(&path);
|
HandleHueApi(&path);
|
||||||
} else
|
} else
|
||||||
|
#endif // USE_EMULATION_HUE
|
||||||
#endif // USE_EMULATION
|
#endif // USE_EMULATION
|
||||||
{
|
{
|
||||||
WSContentBegin(404, CT_PLAIN);
|
WSContentBegin(404, CT_PLAIN);
|
||||||
@ -2427,7 +2434,16 @@ bool WebCommand(void)
|
|||||||
}
|
}
|
||||||
#ifdef USE_EMULATION
|
#ifdef USE_EMULATION
|
||||||
else if (CMND_EMULATION == command_code) {
|
else if (CMND_EMULATION == command_code) {
|
||||||
|
#if defined(USE_EMULATION_WEMO) && defined(USE_EMULATION_HUE)
|
||||||
if ((XdrvMailbox.payload >= EMUL_NONE) && (XdrvMailbox.payload < EMUL_MAX)) {
|
if ((XdrvMailbox.payload >= EMUL_NONE) && (XdrvMailbox.payload < EMUL_MAX)) {
|
||||||
|
#else
|
||||||
|
#ifndef USE_EMULATION_WEMO
|
||||||
|
if ((EMUL_NONE == XdrvMailbox.payload) || (EMUL_HUE == XdrvMailbox.payload)) {
|
||||||
|
#endif
|
||||||
|
#ifndef USE_EMULATION_HUE
|
||||||
|
if ((EMUL_NONE == XdrvMailbox.payload) || (EMUL_WEMO == XdrvMailbox.payload)) {
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
Settings.flag2.emulation = XdrvMailbox.payload;
|
Settings.flag2.emulation = XdrvMailbox.payload;
|
||||||
restart_flag = 2;
|
restart_flag = 2;
|
||||||
}
|
}
|
||||||
|
@ -423,7 +423,7 @@ void MqttReconnect(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(USE_WEBSERVER) && defined(USE_EMULATION)
|
#ifdef USE_EMULATION
|
||||||
UdpDisconnect();
|
UdpDisconnect();
|
||||||
#endif // USE_EMULATION
|
#endif // USE_EMULATION
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
xplg_wemohue.ino - wemo and hue support for Sonoff-Tasmota
|
xdrv_20_hue.ino - Philips Hue support for Sonoff-Tasmota
|
||||||
|
|
||||||
Copyright (C) 2019 Heiko Krupp and Theo Arends
|
Copyright (C) 2019 Heiko Krupp and Theo Arends
|
||||||
|
|
||||||
@ -17,93 +17,10 @@
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if defined(USE_WEBSERVER) && defined(USE_EMULATION)
|
#if defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE)
|
||||||
/*********************************************************************************************\
|
|
||||||
* Belkin WeMo and Philips Hue bridge emulation
|
|
||||||
\*********************************************************************************************/
|
|
||||||
|
|
||||||
#define UDP_BUFFER_SIZE 200 // Max UDP buffer size needed for M-SEARCH message
|
|
||||||
#define UDP_MSEARCH_SEND_DELAY 1500 // Delay in ms before M-Search response is send
|
|
||||||
|
|
||||||
#include <Ticker.h>
|
|
||||||
Ticker TickerMSearch;
|
|
||||||
|
|
||||||
IPAddress udp_remote_ip; // M-Search remote IP address
|
|
||||||
uint16_t udp_remote_port; // M-Search remote port
|
|
||||||
|
|
||||||
bool udp_connected = false;
|
|
||||||
bool udp_response_mutex = false; // M-Search response mutex to control re-entry
|
|
||||||
|
|
||||||
/*********************************************************************************************\
|
|
||||||
* UPNP search targets
|
|
||||||
\*********************************************************************************************/
|
|
||||||
|
|
||||||
const char URN_BELKIN_DEVICE[] PROGMEM = "urn:belkin:device:**";
|
|
||||||
const char UPNP_ROOTDEVICE[] PROGMEM = "upnp:rootdevice";
|
|
||||||
const char SSDPSEARCH_ALL[] PROGMEM = "ssdpsearch:all";
|
|
||||||
const char SSDP_ALL[] PROGMEM = "ssdp:all";
|
|
||||||
|
|
||||||
/*********************************************************************************************\
|
|
||||||
* WeMo UPNP support routines
|
|
||||||
\*********************************************************************************************/
|
|
||||||
|
|
||||||
const char WEMO_MSEARCH[] PROGMEM =
|
|
||||||
"HTTP/1.1 200 OK\r\n"
|
|
||||||
"CACHE-CONTROL: max-age=86400\r\n"
|
|
||||||
"DATE: Fri, 15 Apr 2016 04:56:29 GMT\r\n"
|
|
||||||
"EXT:\r\n"
|
|
||||||
"LOCATION: http://%s:80/setup.xml\r\n"
|
|
||||||
"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n"
|
|
||||||
"01-NLS: b9200ebb-736d-4b93-bf03-835149d13983\r\n"
|
|
||||||
"SERVER: Unspecified, UPnP/1.0, Unspecified\r\n"
|
|
||||||
"ST: %s\r\n" // type1 = urn:Belkin:device:**, type2 = upnp:rootdevice
|
|
||||||
"USN: uuid:%s::%s\r\n" // type1 = urn:Belkin:device:**, type2 = upnp:rootdevice
|
|
||||||
"X-User-Agent: redsonic\r\n"
|
|
||||||
"\r\n";
|
|
||||||
|
|
||||||
String WemoSerialnumber(void)
|
|
||||||
{
|
|
||||||
char serial[16];
|
|
||||||
|
|
||||||
snprintf_P(serial, sizeof(serial), PSTR("201612K%08X"), ESP.getChipId());
|
|
||||||
return String(serial);
|
|
||||||
}
|
|
||||||
|
|
||||||
String WemoUuid(void)
|
|
||||||
{
|
|
||||||
char uuid[27];
|
|
||||||
|
|
||||||
snprintf_P(uuid, sizeof(uuid), PSTR("Socket-1_0-%s"), WemoSerialnumber().c_str());
|
|
||||||
return String(uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WemoRespondToMSearch(int echo_type)
|
|
||||||
{
|
|
||||||
char message[TOPSZ];
|
|
||||||
|
|
||||||
TickerMSearch.detach();
|
|
||||||
if (PortUdp.beginPacket(udp_remote_ip, udp_remote_port)) {
|
|
||||||
char type[24];
|
|
||||||
if (1 == echo_type) { // type1 echo 1g & dot 2g
|
|
||||||
strcpy_P(type, URN_BELKIN_DEVICE);
|
|
||||||
} else { // type2 echo 2g (echo, plus, show)
|
|
||||||
strcpy_P(type, UPNP_ROOTDEVICE);
|
|
||||||
}
|
|
||||||
char response[400];
|
|
||||||
snprintf_P(response, sizeof(response), WEMO_MSEARCH, WiFi.localIP().toString().c_str(), type, WemoUuid().c_str(), type);
|
|
||||||
PortUdp.write(response);
|
|
||||||
PortUdp.endPacket();
|
|
||||||
snprintf_P(message, sizeof(message), PSTR(D_RESPONSE_SENT));
|
|
||||||
} else {
|
|
||||||
snprintf_P(message, sizeof(message), PSTR(D_FAILED_TO_SEND_RESPONSE));
|
|
||||||
}
|
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_WEMO " " D_JSON_TYPE " %d, %s " D_TO " %s:%d"),
|
|
||||||
echo_type, message, udp_remote_ip.toString().c_str(), udp_remote_port);
|
|
||||||
|
|
||||||
udp_response_mutex = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*********************************************************************************************\
|
/*********************************************************************************************\
|
||||||
|
* Philips Hue bridge emulation
|
||||||
|
*
|
||||||
* Hue Bridge UPNP support routines
|
* Hue Bridge UPNP support routines
|
||||||
* Need to send 3 response packets with varying ST and USN
|
* Need to send 3 response packets with varying ST and USN
|
||||||
*
|
*
|
||||||
@ -111,6 +28,8 @@ void WemoRespondToMSearch(int echo_type)
|
|||||||
* Philips Lighting is 00:17:88:00:00:00
|
* Philips Lighting is 00:17:88:00:00:00
|
||||||
\*********************************************************************************************/
|
\*********************************************************************************************/
|
||||||
|
|
||||||
|
#define XDRV_20 20
|
||||||
|
|
||||||
const char HUE_RESPONSE[] PROGMEM =
|
const char HUE_RESPONSE[] PROGMEM =
|
||||||
"HTTP/1.1 200 OK\r\n"
|
"HTTP/1.1 200 OK\r\n"
|
||||||
"HOST: 239.255.255.250:1900\r\n"
|
"HOST: 239.255.255.250:1900\r\n"
|
||||||
@ -187,256 +106,6 @@ void HueRespondToMSearch(void)
|
|||||||
udp_response_mutex = false;
|
udp_response_mutex = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*********************************************************************************************\
|
|
||||||
* Belkin WeMo and Philips Hue bridge UDP multicast support
|
|
||||||
\*********************************************************************************************/
|
|
||||||
|
|
||||||
bool UdpDisconnect(void)
|
|
||||||
{
|
|
||||||
if (udp_connected) {
|
|
||||||
PortUdp.flush();
|
|
||||||
WiFiUDP::stopAll();
|
|
||||||
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_MULTICAST_DISABLED));
|
|
||||||
udp_connected = false;
|
|
||||||
}
|
|
||||||
return udp_connected;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool UdpConnect(void)
|
|
||||||
{
|
|
||||||
if (!udp_connected) {
|
|
||||||
// Simple Service Discovery Protocol (SSDP)
|
|
||||||
if (PortUdp.beginMulticast(WiFi.localIP(), IPAddress(239,255,255,250), 1900)) {
|
|
||||||
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_REJOINED));
|
|
||||||
udp_response_mutex = false;
|
|
||||||
udp_connected = true;
|
|
||||||
} else {
|
|
||||||
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_JOIN_FAILED));
|
|
||||||
udp_connected = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return udp_connected;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PollUdp(void)
|
|
||||||
{
|
|
||||||
if (udp_connected) {
|
|
||||||
if (PortUdp.parsePacket()) {
|
|
||||||
char packet_buffer[UDP_BUFFER_SIZE]; // buffer to hold incoming UDP/SSDP packet
|
|
||||||
|
|
||||||
int len = PortUdp.read(packet_buffer, UDP_BUFFER_SIZE -1);
|
|
||||||
packet_buffer[len] = 0;
|
|
||||||
|
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: Packet (%d)"), len);
|
|
||||||
// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("\n%s"), packet_buffer);
|
|
||||||
|
|
||||||
if (devices_present && !udp_response_mutex && (strstr_P(packet_buffer, PSTR("M-SEARCH")) != nullptr)) {
|
|
||||||
udp_response_mutex = true;
|
|
||||||
|
|
||||||
udp_remote_ip = PortUdp.remoteIP();
|
|
||||||
udp_remote_port = PortUdp.remotePort();
|
|
||||||
|
|
||||||
// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: M-SEARCH Packet from %s:%d\n%s"),
|
|
||||||
// udp_remote_ip.toString().c_str(), udp_remote_port, packet_buffer);
|
|
||||||
|
|
||||||
uint32_t response_delay = UDP_MSEARCH_SEND_DELAY + ((millis() &0x7) * 100); // 1500 - 2200 msec
|
|
||||||
|
|
||||||
LowerCase(packet_buffer, packet_buffer);
|
|
||||||
RemoveSpace(packet_buffer);
|
|
||||||
if (EMUL_WEMO == Settings.flag2.emulation) {
|
|
||||||
if (strstr_P(packet_buffer, URN_BELKIN_DEVICE) != nullptr) { // type1 echo dot 2g, echo 1g's
|
|
||||||
TickerMSearch.attach_ms(response_delay, WemoRespondToMSearch, 1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if ((strstr_P(packet_buffer, UPNP_ROOTDEVICE) != nullptr) || // type2 Echo 2g (echo & echo plus)
|
|
||||||
(strstr_P(packet_buffer, SSDPSEARCH_ALL) != nullptr) ||
|
|
||||||
(strstr_P(packet_buffer, SSDP_ALL) != nullptr)) {
|
|
||||||
TickerMSearch.attach_ms(response_delay, WemoRespondToMSearch, 2);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ((strstr_P(packet_buffer, PSTR(":device:basic:1")) != nullptr) ||
|
|
||||||
(strstr_P(packet_buffer, UPNP_ROOTDEVICE) != nullptr) ||
|
|
||||||
(strstr_P(packet_buffer, SSDPSEARCH_ALL) != nullptr) ||
|
|
||||||
(strstr_P(packet_buffer, SSDP_ALL) != nullptr)) {
|
|
||||||
TickerMSearch.attach_ms(response_delay, HueRespondToMSearch);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
udp_response_mutex = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delay(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*********************************************************************************************\
|
|
||||||
* Wemo web server additions
|
|
||||||
\*********************************************************************************************/
|
|
||||||
|
|
||||||
const char WEMO_EVENTSERVICE_XML[] PROGMEM =
|
|
||||||
"<scpd xmlns=\"urn:Belkin:service-1-0\">"
|
|
||||||
"<actionList>"
|
|
||||||
"<action>"
|
|
||||||
"<name>SetBinaryState</name>"
|
|
||||||
"<argumentList>"
|
|
||||||
"<argument>"
|
|
||||||
"<retval/>"
|
|
||||||
"<name>BinaryState</name>"
|
|
||||||
"<relatedStateVariable>BinaryState</relatedStateVariable>"
|
|
||||||
"<direction>in</direction>"
|
|
||||||
"</argument>"
|
|
||||||
"</argumentList>"
|
|
||||||
"</action>"
|
|
||||||
"<action>"
|
|
||||||
"<name>GetBinaryState</name>"
|
|
||||||
"<argumentList>"
|
|
||||||
"<argument>"
|
|
||||||
"<retval/>"
|
|
||||||
"<name>BinaryState</name>"
|
|
||||||
"<relatedStateVariable>BinaryState</relatedStateVariable>"
|
|
||||||
"<direction>out</direction>"
|
|
||||||
"</argument>"
|
|
||||||
"</argumentList>"
|
|
||||||
"</action>"
|
|
||||||
"</actionList>"
|
|
||||||
"<serviceStateTable>"
|
|
||||||
"<stateVariable sendEvents=\"yes\">"
|
|
||||||
"<name>BinaryState</name>"
|
|
||||||
"<dataType>bool</dataType>"
|
|
||||||
"<defaultValue>0</defaultValue>"
|
|
||||||
"</stateVariable>"
|
|
||||||
"<stateVariable sendEvents=\"yes\">"
|
|
||||||
"<name>level</name>"
|
|
||||||
"<dataType>string</dataType>"
|
|
||||||
"<defaultValue>0</defaultValue>"
|
|
||||||
"</stateVariable>"
|
|
||||||
"</serviceStateTable>"
|
|
||||||
"</scpd>\r\n\r\n";
|
|
||||||
|
|
||||||
const char WEMO_METASERVICE_XML[] PROGMEM =
|
|
||||||
"<scpd xmlns=\"urn:Belkin:service-1-0\">"
|
|
||||||
"<specVersion>"
|
|
||||||
"<major>1</major>"
|
|
||||||
"<minor>0</minor>"
|
|
||||||
"</specVersion>"
|
|
||||||
"<actionList>"
|
|
||||||
"<action>"
|
|
||||||
"<name>GetMetaInfo</name>"
|
|
||||||
"<argumentList>"
|
|
||||||
"<retval />"
|
|
||||||
"<name>GetMetaInfo</name>"
|
|
||||||
"<relatedStateVariable>MetaInfo</relatedStateVariable>"
|
|
||||||
"<direction>in</direction>"
|
|
||||||
"</argumentList>"
|
|
||||||
"</action>"
|
|
||||||
"</actionList>"
|
|
||||||
"<serviceStateTable>"
|
|
||||||
"<stateVariable sendEvents=\"yes\">"
|
|
||||||
"<name>MetaInfo</name>"
|
|
||||||
"<dataType>string</dataType>"
|
|
||||||
"<defaultValue>0</defaultValue>"
|
|
||||||
"</stateVariable>"
|
|
||||||
"</serviceStateTable>"
|
|
||||||
"</scpd>\r\n\r\n";
|
|
||||||
|
|
||||||
const char WEMO_RESPONSE_STATE_SOAP[] PROGMEM =
|
|
||||||
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
|
|
||||||
"<s:Body>"
|
|
||||||
"<u:%cetBinaryStateResponse xmlns:u=\"urn:Belkin:service:basicevent:1\">"
|
|
||||||
"<BinaryState>%d</BinaryState>"
|
|
||||||
"</u:%cetBinaryStateResponse>"
|
|
||||||
"</s:Body>"
|
|
||||||
"</s:Envelope>\r\n";
|
|
||||||
|
|
||||||
const char WEMO_SETUP_XML[] PROGMEM =
|
|
||||||
"<?xml version=\"1.0\"?>"
|
|
||||||
"<root xmlns=\"urn:Belkin:device-1-0\">"
|
|
||||||
"<device>"
|
|
||||||
"<deviceType>urn:Belkin:device:controllee:1</deviceType>"
|
|
||||||
"<friendlyName>{x1</friendlyName>"
|
|
||||||
"<manufacturer>Belkin International Inc.</manufacturer>"
|
|
||||||
"<modelName>Socket</modelName>"
|
|
||||||
"<modelNumber>3.1415</modelNumber>"
|
|
||||||
"<UDN>uuid:{x2</UDN>"
|
|
||||||
"<serialNumber>{x3</serialNumber>"
|
|
||||||
"<binaryState>0</binaryState>"
|
|
||||||
"<serviceList>"
|
|
||||||
"<service>"
|
|
||||||
"<serviceType>urn:Belkin:service:basicevent:1</serviceType>"
|
|
||||||
"<serviceId>urn:Belkin:serviceId:basicevent1</serviceId>"
|
|
||||||
"<controlURL>/upnp/control/basicevent1</controlURL>"
|
|
||||||
"<eventSubURL>/upnp/event/basicevent1</eventSubURL>"
|
|
||||||
"<SCPDURL>/eventservice.xml</SCPDURL>"
|
|
||||||
"</service>"
|
|
||||||
"<service>"
|
|
||||||
"<serviceType>urn:Belkin:service:metainfo:1</serviceType>"
|
|
||||||
"<serviceId>urn:Belkin:serviceId:metainfo1</serviceId>"
|
|
||||||
"<controlURL>/upnp/control/metainfo1</controlURL>"
|
|
||||||
"<eventSubURL>/upnp/event/metainfo1</eventSubURL>"
|
|
||||||
"<SCPDURL>/metainfoservice.xml</SCPDURL>"
|
|
||||||
"</service>"
|
|
||||||
"</serviceList>"
|
|
||||||
"</device>"
|
|
||||||
"</root>\r\n";
|
|
||||||
|
|
||||||
/********************************************************************************************/
|
|
||||||
|
|
||||||
void HandleUpnpEvent(void)
|
|
||||||
{
|
|
||||||
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_WEMO_BASIC_EVENT));
|
|
||||||
|
|
||||||
char event[500];
|
|
||||||
strlcpy(event, WebServer->arg(0).c_str(), sizeof(event));
|
|
||||||
|
|
||||||
// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("\n%s"), event);
|
|
||||||
|
|
||||||
//differentiate get and set state
|
|
||||||
char state = 'G';
|
|
||||||
if (strstr_P(event, PSTR("SetBinaryState")) != nullptr) {
|
|
||||||
state = 'S';
|
|
||||||
uint8_t power = POWER_TOGGLE;
|
|
||||||
if (strstr_P(event, PSTR("State>1</Binary")) != nullptr) {
|
|
||||||
power = POWER_ON;
|
|
||||||
}
|
|
||||||
else if (strstr_P(event, PSTR("State>0</Binary")) != nullptr) {
|
|
||||||
power = POWER_OFF;
|
|
||||||
}
|
|
||||||
if (power != POWER_TOGGLE) {
|
|
||||||
uint8_t device = (light_type) ? devices_present : 1; // Select either a configured light or relay1
|
|
||||||
ExecuteCommandPower(device, power, SRC_WEMO);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf_P(event, sizeof(event), WEMO_RESPONSE_STATE_SOAP, state, bitRead(power, devices_present -1), state);
|
|
||||||
WSSend(200, CT_XML, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleUpnpService(void)
|
|
||||||
{
|
|
||||||
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_WEMO_EVENT_SERVICE));
|
|
||||||
|
|
||||||
WSSend(200, CT_PLAIN, FPSTR(WEMO_EVENTSERVICE_XML));
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleUpnpMetaService(void)
|
|
||||||
{
|
|
||||||
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_WEMO_META_SERVICE));
|
|
||||||
|
|
||||||
WSSend(200, CT_PLAIN, FPSTR(WEMO_METASERVICE_XML));
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleUpnpSetupWemo(void)
|
|
||||||
{
|
|
||||||
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_WEMO_SETUP));
|
|
||||||
|
|
||||||
String setup_xml = FPSTR(WEMO_SETUP_XML);
|
|
||||||
setup_xml.replace("{x1", Settings.friendlyname[0]);
|
|
||||||
setup_xml.replace("{x2", WemoUuid());
|
|
||||||
setup_xml.replace("{x3", WemoSerialnumber());
|
|
||||||
WSSend(200, CT_XML, setup_xml);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*********************************************************************************************\
|
/*********************************************************************************************\
|
||||||
* Hue web server additions
|
* Hue web server additions
|
||||||
\*********************************************************************************************/
|
\*********************************************************************************************/
|
||||||
@ -970,19 +639,22 @@ void HandleHueApi(String *path)
|
|||||||
else HueGlobalConfig(path);
|
else HueGlobalConfig(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HueWemoAddHandlers(void)
|
/*********************************************************************************************\
|
||||||
|
* Interface
|
||||||
|
\*********************************************************************************************/
|
||||||
|
|
||||||
|
bool Xdrv20(uint8_t function)
|
||||||
{
|
{
|
||||||
if (devices_present) {
|
bool result = false;
|
||||||
if (EMUL_WEMO == Settings.flag2.emulation) {
|
|
||||||
WebServer->on("/upnp/control/basicevent1", HTTP_POST, HandleUpnpEvent);
|
if (devices_present && (EMUL_HUE == Settings.flag2.emulation)) {
|
||||||
WebServer->on("/eventservice.xml", HandleUpnpService);
|
switch (function) {
|
||||||
WebServer->on("/metainfoservice.xml", HandleUpnpMetaService);
|
case FUNC_WEB_ADD_HANDLER:
|
||||||
WebServer->on("/setup.xml", HandleUpnpSetupWemo);
|
|
||||||
}
|
|
||||||
if (EMUL_HUE == Settings.flag2.emulation) {
|
|
||||||
WebServer->on("/description.xml", HandleUpnpSetupHue);
|
WebServer->on("/description.xml", HandleUpnpSetupHue);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // USE_WEBSERVER && USE_EMULATION
|
#endif // USE_WEBSERVER && USE_EMULATION && USE_EMULATION_HUE
|
271
sonoff/xdrv_21_wemo.ino
Normal file
271
sonoff/xdrv_21_wemo.ino
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
/*
|
||||||
|
xdrv_21_wemo.ino - wemo support for Sonoff-Tasmota
|
||||||
|
|
||||||
|
Copyright (C) 2019 Heiko Krupp and 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined (USE_EMULATION_WEMO)
|
||||||
|
/*********************************************************************************************\
|
||||||
|
* Belkin WeMo emulation
|
||||||
|
\*********************************************************************************************/
|
||||||
|
|
||||||
|
#define XDRV_21 21
|
||||||
|
|
||||||
|
const char WEMO_MSEARCH[] PROGMEM =
|
||||||
|
"HTTP/1.1 200 OK\r\n"
|
||||||
|
"CACHE-CONTROL: max-age=86400\r\n"
|
||||||
|
"DATE: Fri, 15 Apr 2016 04:56:29 GMT\r\n"
|
||||||
|
"EXT:\r\n"
|
||||||
|
"LOCATION: http://%s:80/setup.xml\r\n"
|
||||||
|
"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n"
|
||||||
|
"01-NLS: b9200ebb-736d-4b93-bf03-835149d13983\r\n"
|
||||||
|
"SERVER: Unspecified, UPnP/1.0, Unspecified\r\n"
|
||||||
|
"ST: %s\r\n" // type1 = urn:Belkin:device:**, type2 = upnp:rootdevice
|
||||||
|
"USN: uuid:%s::%s\r\n" // type1 = urn:Belkin:device:**, type2 = upnp:rootdevice
|
||||||
|
"X-User-Agent: redsonic\r\n"
|
||||||
|
"\r\n";
|
||||||
|
|
||||||
|
String WemoSerialnumber(void)
|
||||||
|
{
|
||||||
|
char serial[16];
|
||||||
|
|
||||||
|
snprintf_P(serial, sizeof(serial), PSTR("201612K%08X"), ESP.getChipId());
|
||||||
|
return String(serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
String WemoUuid(void)
|
||||||
|
{
|
||||||
|
char uuid[27];
|
||||||
|
|
||||||
|
snprintf_P(uuid, sizeof(uuid), PSTR("Socket-1_0-%s"), WemoSerialnumber().c_str());
|
||||||
|
return String(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WemoRespondToMSearch(int echo_type)
|
||||||
|
{
|
||||||
|
char message[TOPSZ];
|
||||||
|
|
||||||
|
TickerMSearch.detach();
|
||||||
|
if (PortUdp.beginPacket(udp_remote_ip, udp_remote_port)) {
|
||||||
|
char type[24];
|
||||||
|
if (1 == echo_type) { // type1 echo 1g & dot 2g
|
||||||
|
strcpy_P(type, URN_BELKIN_DEVICE);
|
||||||
|
} else { // type2 echo 2g (echo, plus, show)
|
||||||
|
strcpy_P(type, UPNP_ROOTDEVICE);
|
||||||
|
}
|
||||||
|
char response[400];
|
||||||
|
snprintf_P(response, sizeof(response), WEMO_MSEARCH, WiFi.localIP().toString().c_str(), type, WemoUuid().c_str(), type);
|
||||||
|
PortUdp.write(response);
|
||||||
|
PortUdp.endPacket();
|
||||||
|
snprintf_P(message, sizeof(message), PSTR(D_RESPONSE_SENT));
|
||||||
|
} else {
|
||||||
|
snprintf_P(message, sizeof(message), PSTR(D_FAILED_TO_SEND_RESPONSE));
|
||||||
|
}
|
||||||
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_WEMO " " D_JSON_TYPE " %d, %s " D_TO " %s:%d"),
|
||||||
|
echo_type, message, udp_remote_ip.toString().c_str(), udp_remote_port);
|
||||||
|
|
||||||
|
udp_response_mutex = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************************************************************\
|
||||||
|
* Wemo web server additions
|
||||||
|
\*********************************************************************************************/
|
||||||
|
|
||||||
|
const char WEMO_EVENTSERVICE_XML[] PROGMEM =
|
||||||
|
"<scpd xmlns=\"urn:Belkin:service-1-0\">"
|
||||||
|
"<actionList>"
|
||||||
|
"<action>"
|
||||||
|
"<name>SetBinaryState</name>"
|
||||||
|
"<argumentList>"
|
||||||
|
"<argument>"
|
||||||
|
"<retval/>"
|
||||||
|
"<name>BinaryState</name>"
|
||||||
|
"<relatedStateVariable>BinaryState</relatedStateVariable>"
|
||||||
|
"<direction>in</direction>"
|
||||||
|
"</argument>"
|
||||||
|
"</argumentList>"
|
||||||
|
"</action>"
|
||||||
|
"<action>"
|
||||||
|
"<name>GetBinaryState</name>"
|
||||||
|
"<argumentList>"
|
||||||
|
"<argument>"
|
||||||
|
"<retval/>"
|
||||||
|
"<name>BinaryState</name>"
|
||||||
|
"<relatedStateVariable>BinaryState</relatedStateVariable>"
|
||||||
|
"<direction>out</direction>"
|
||||||
|
"</argument>"
|
||||||
|
"</argumentList>"
|
||||||
|
"</action>"
|
||||||
|
"</actionList>"
|
||||||
|
"<serviceStateTable>"
|
||||||
|
"<stateVariable sendEvents=\"yes\">"
|
||||||
|
"<name>BinaryState</name>"
|
||||||
|
"<dataType>bool</dataType>"
|
||||||
|
"<defaultValue>0</defaultValue>"
|
||||||
|
"</stateVariable>"
|
||||||
|
"<stateVariable sendEvents=\"yes\">"
|
||||||
|
"<name>level</name>"
|
||||||
|
"<dataType>string</dataType>"
|
||||||
|
"<defaultValue>0</defaultValue>"
|
||||||
|
"</stateVariable>"
|
||||||
|
"</serviceStateTable>"
|
||||||
|
"</scpd>\r\n\r\n";
|
||||||
|
|
||||||
|
const char WEMO_METASERVICE_XML[] PROGMEM =
|
||||||
|
"<scpd xmlns=\"urn:Belkin:service-1-0\">"
|
||||||
|
"<specVersion>"
|
||||||
|
"<major>1</major>"
|
||||||
|
"<minor>0</minor>"
|
||||||
|
"</specVersion>"
|
||||||
|
"<actionList>"
|
||||||
|
"<action>"
|
||||||
|
"<name>GetMetaInfo</name>"
|
||||||
|
"<argumentList>"
|
||||||
|
"<retval />"
|
||||||
|
"<name>GetMetaInfo</name>"
|
||||||
|
"<relatedStateVariable>MetaInfo</relatedStateVariable>"
|
||||||
|
"<direction>in</direction>"
|
||||||
|
"</argumentList>"
|
||||||
|
"</action>"
|
||||||
|
"</actionList>"
|
||||||
|
"<serviceStateTable>"
|
||||||
|
"<stateVariable sendEvents=\"yes\">"
|
||||||
|
"<name>MetaInfo</name>"
|
||||||
|
"<dataType>string</dataType>"
|
||||||
|
"<defaultValue>0</defaultValue>"
|
||||||
|
"</stateVariable>"
|
||||||
|
"</serviceStateTable>"
|
||||||
|
"</scpd>\r\n\r\n";
|
||||||
|
|
||||||
|
const char WEMO_RESPONSE_STATE_SOAP[] PROGMEM =
|
||||||
|
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
|
||||||
|
"<s:Body>"
|
||||||
|
"<u:%cetBinaryStateResponse xmlns:u=\"urn:Belkin:service:basicevent:1\">"
|
||||||
|
"<BinaryState>%d</BinaryState>"
|
||||||
|
"</u:%cetBinaryStateResponse>"
|
||||||
|
"</s:Body>"
|
||||||
|
"</s:Envelope>\r\n";
|
||||||
|
|
||||||
|
const char WEMO_SETUP_XML[] PROGMEM =
|
||||||
|
"<?xml version=\"1.0\"?>"
|
||||||
|
"<root xmlns=\"urn:Belkin:device-1-0\">"
|
||||||
|
"<device>"
|
||||||
|
"<deviceType>urn:Belkin:device:controllee:1</deviceType>"
|
||||||
|
"<friendlyName>{x1</friendlyName>"
|
||||||
|
"<manufacturer>Belkin International Inc.</manufacturer>"
|
||||||
|
"<modelName>Socket</modelName>"
|
||||||
|
"<modelNumber>3.1415</modelNumber>"
|
||||||
|
"<UDN>uuid:{x2</UDN>"
|
||||||
|
"<serialNumber>{x3</serialNumber>"
|
||||||
|
"<binaryState>0</binaryState>"
|
||||||
|
"<serviceList>"
|
||||||
|
"<service>"
|
||||||
|
"<serviceType>urn:Belkin:service:basicevent:1</serviceType>"
|
||||||
|
"<serviceId>urn:Belkin:serviceId:basicevent1</serviceId>"
|
||||||
|
"<controlURL>/upnp/control/basicevent1</controlURL>"
|
||||||
|
"<eventSubURL>/upnp/event/basicevent1</eventSubURL>"
|
||||||
|
"<SCPDURL>/eventservice.xml</SCPDURL>"
|
||||||
|
"</service>"
|
||||||
|
"<service>"
|
||||||
|
"<serviceType>urn:Belkin:service:metainfo:1</serviceType>"
|
||||||
|
"<serviceId>urn:Belkin:serviceId:metainfo1</serviceId>"
|
||||||
|
"<controlURL>/upnp/control/metainfo1</controlURL>"
|
||||||
|
"<eventSubURL>/upnp/event/metainfo1</eventSubURL>"
|
||||||
|
"<SCPDURL>/metainfoservice.xml</SCPDURL>"
|
||||||
|
"</service>"
|
||||||
|
"</serviceList>"
|
||||||
|
"</device>"
|
||||||
|
"</root>\r\n";
|
||||||
|
|
||||||
|
/********************************************************************************************/
|
||||||
|
|
||||||
|
void HandleUpnpEvent(void)
|
||||||
|
{
|
||||||
|
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_WEMO_BASIC_EVENT));
|
||||||
|
|
||||||
|
char event[500];
|
||||||
|
strlcpy(event, WebServer->arg(0).c_str(), sizeof(event));
|
||||||
|
|
||||||
|
// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("\n%s"), event);
|
||||||
|
|
||||||
|
//differentiate get and set state
|
||||||
|
char state = 'G';
|
||||||
|
if (strstr_P(event, PSTR("SetBinaryState")) != nullptr) {
|
||||||
|
state = 'S';
|
||||||
|
uint8_t power = POWER_TOGGLE;
|
||||||
|
if (strstr_P(event, PSTR("State>1</Binary")) != nullptr) {
|
||||||
|
power = POWER_ON;
|
||||||
|
}
|
||||||
|
else if (strstr_P(event, PSTR("State>0</Binary")) != nullptr) {
|
||||||
|
power = POWER_OFF;
|
||||||
|
}
|
||||||
|
if (power != POWER_TOGGLE) {
|
||||||
|
uint8_t device = (light_type) ? devices_present : 1; // Select either a configured light or relay1
|
||||||
|
ExecuteCommandPower(device, power, SRC_WEMO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf_P(event, sizeof(event), WEMO_RESPONSE_STATE_SOAP, state, bitRead(power, devices_present -1), state);
|
||||||
|
WSSend(200, CT_XML, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleUpnpService(void)
|
||||||
|
{
|
||||||
|
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_WEMO_EVENT_SERVICE));
|
||||||
|
|
||||||
|
WSSend(200, CT_PLAIN, FPSTR(WEMO_EVENTSERVICE_XML));
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleUpnpMetaService(void)
|
||||||
|
{
|
||||||
|
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_WEMO_META_SERVICE));
|
||||||
|
|
||||||
|
WSSend(200, CT_PLAIN, FPSTR(WEMO_METASERVICE_XML));
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleUpnpSetupWemo(void)
|
||||||
|
{
|
||||||
|
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_WEMO_SETUP));
|
||||||
|
|
||||||
|
String setup_xml = FPSTR(WEMO_SETUP_XML);
|
||||||
|
setup_xml.replace("{x1", Settings.friendlyname[0]);
|
||||||
|
setup_xml.replace("{x2", WemoUuid());
|
||||||
|
setup_xml.replace("{x3", WemoSerialnumber());
|
||||||
|
WSSend(200, CT_XML, setup_xml);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************************************************************\
|
||||||
|
* Interface
|
||||||
|
\*********************************************************************************************/
|
||||||
|
|
||||||
|
bool Xdrv21(uint8_t function)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
if (devices_present && (EMUL_WEMO == Settings.flag2.emulation)) {
|
||||||
|
switch (function) {
|
||||||
|
case FUNC_WEB_ADD_HANDLER:
|
||||||
|
WebServer->on("/upnp/control/basicevent1", HTTP_POST, HandleUpnpEvent);
|
||||||
|
WebServer->on("/eventservice.xml", HandleUpnpService);
|
||||||
|
WebServer->on("/metainfoservice.xml", HandleUpnpMetaService);
|
||||||
|
WebServer->on("/setup.xml", HandleUpnpSetupWemo);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // USE_WEBSERVER && USE_EMULATION && USE_EMULATION_WEMO
|
@ -106,7 +106,9 @@ a_setoption = [[
|
|||||||
"Enable normal sleep instead of dynamic sleep",
|
"Enable normal sleep instead of dynamic sleep",
|
||||||
"Force local operation when button/switch topic is set",
|
"Force local operation when button/switch topic is set",
|
||||||
"Do not use retain flag on HOLD messages",
|
"Do not use retain flag on HOLD messages",
|
||||||
"","","",
|
"Do not scan relay power state at restart",
|
||||||
|
"Use _ instead of - as sensor index separator",
|
||||||
|
"",
|
||||||
"","","","",
|
"","","","",
|
||||||
"","","","",
|
"","","","",
|
||||||
"","","","",
|
"","","","",
|
||||||
@ -116,7 +118,7 @@ a_setoption = [[
|
|||||||
a_features = [[
|
a_features = [[
|
||||||
"","","USE_I2C","USE_SPI",
|
"","","USE_I2C","USE_SPI",
|
||||||
"USE_DISCOVERY","USE_ARDUINO_OTA","USE_MQTT_TLS","USE_WEBSERVER",
|
"USE_DISCOVERY","USE_ARDUINO_OTA","USE_MQTT_TLS","USE_WEBSERVER",
|
||||||
"WEBSERVER_ADVERTISE","USE_EMULATION","MQTT_PUBSUBCLIENT","MQTT_TASMOTAMQTT",
|
"WEBSERVER_ADVERTISE","USE_EMULATION_HUE","MQTT_PUBSUBCLIENT","MQTT_TASMOTAMQTT",
|
||||||
"MQTT_ESPMQTTARDUINO","MQTT_HOST_DISCOVERY","USE_ARILUX_RF","USE_WS2812",
|
"MQTT_ESPMQTTARDUINO","MQTT_HOST_DISCOVERY","USE_ARILUX_RF","USE_WS2812",
|
||||||
"USE_WS2812_DMA","USE_IR_REMOTE","USE_IR_HVAC","USE_IR_RECEIVE",
|
"USE_WS2812_DMA","USE_IR_REMOTE","USE_IR_HVAC","USE_IR_RECEIVE",
|
||||||
"USE_DOMOTICZ","USE_DISPLAY","USE_HOME_ASSISTANT","USE_SERIAL_BRIDGE",
|
"USE_DOMOTICZ","USE_DISPLAY","USE_HOME_ASSISTANT","USE_SERIAL_BRIDGE",
|
||||||
@ -127,8 +129,8 @@ a_features = [[
|
|||||||
"FIRMWARE_KNX_NO_EMULATION","USE_DISPLAY_MODES1TO5","USE_DISPLAY_GRAPH","USE_DISPLAY_LCD",
|
"FIRMWARE_KNX_NO_EMULATION","USE_DISPLAY_MODES1TO5","USE_DISPLAY_GRAPH","USE_DISPLAY_LCD",
|
||||||
"USE_DISPLAY_SSD1306","USE_DISPLAY_MATRIX","USE_DISPLAY_ILI9341","USE_DISPLAY_EPAPER",
|
"USE_DISPLAY_SSD1306","USE_DISPLAY_MATRIX","USE_DISPLAY_ILI9341","USE_DISPLAY_EPAPER",
|
||||||
"USE_DISPLAY_SH1106","USE_MP3_PLAYER","USE_PCA9685","USE_TUYA_DIMMER",
|
"USE_DISPLAY_SH1106","USE_MP3_PLAYER","USE_PCA9685","USE_TUYA_DIMMER",
|
||||||
"USE_RC_SWITCH","USE_ARMTRONIX_DIMMERS","","",
|
"USE_RC_SWITCH","USE_ARMTRONIX_DIMMERS","USE_SM16716","USE_SCRIPT",
|
||||||
"","","","NO_EXTRA_4K_HEAP",
|
"USE_EMULATION_WEMO","","","NO_EXTRA_4K_HEAP",
|
||||||
"VTABLES_IN_IRAM","VTABLES_IN_DRAM","VTABLES_IN_FLASH","PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH",
|
"VTABLES_IN_IRAM","VTABLES_IN_DRAM","VTABLES_IN_FLASH","PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH",
|
||||||
"PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY","PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH","DEBUG_THEO","USE_DEBUG_DRIVER"
|
"PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY","PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH","DEBUG_THEO","USE_DEBUG_DRIVER"
|
||||||
],[
|
],[
|
||||||
|
Loading…
x
Reference in New Issue
Block a user