2017-01-28 13:41:01 +00:00
/*
2018-01-05 11:26:19 +00:00
xplg_wemohue . ino - wemo and hue support for Sonoff - Tasmota
2017-05-13 12:02:10 +01:00
2017-12-22 13:55:24 +00:00
Copyright ( C ) 2018 Heiko Krupp and Theo Arends
2017-05-13 12:02:10 +01:00
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/>.
2017-01-28 13:41:01 +00:00
*/
2018-09-08 07:49:08 +01:00
# define min(a,b) ((a)<(b)?(a):(b))
# define max(a,b) ((a)>(b)?(a):(b))
2018-04-10 10:45:53 +01:00
# if defined(USE_WEBSERVER) && defined(USE_EMULATION)
2017-07-15 14:07:30 +01:00
/*********************************************************************************************\
* Belkin WeMo and Philips Hue bridge emulation
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2018-01-05 11:26:19 +00:00
# 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
2017-01-28 13:41:01 +00:00
2018-01-05 11:26:19 +00:00
# include <Ticker.h>
Ticker TickerMSearch ;
2017-01-28 13:41:01 +00:00
2017-10-18 17:22:34 +01:00
boolean udp_connected = false ;
2017-01-28 13:41:01 +00:00
2018-01-05 11:26:19 +00:00
char packet_buffer [ UDP_BUFFER_SIZE ] ; // buffer to hold incoming UDP packet
IPAddress ipMulticast ( 239 , 255 , 255 , 250 ) ; // Simple Service Discovery Protocol (SSDP)
uint32_t port_multicast = 1900 ; // Multicast address and port
bool udp_response_mutex = false ; // M-Search response mutex to control re-entry
IPAddress udp_remote_ip ; // M-Search remote IP address
uint16_t udp_remote_port ; // M-Search remote port
2017-01-28 13:41:01 +00:00
/*********************************************************************************************\
* WeMo UPNP support routines
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2017-07-15 14:07:30 +01:00
2017-01-28 13:41:01 +00:00
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 "
2017-10-25 13:27:30 +01:00
" LOCATION: http://{r1:80/setup.xml \r \n "
2017-01-28 13:41:01 +00:00
" 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 "
2018-01-02 15:08:27 +00:00
" ST: {r3 \r \n " // type1 = urn:Belkin:device:**, type2 = upnp:rootdevice
" USN: uuid:{r2::{r3 \r \n " // type1 = urn:Belkin:device:**, type2 = upnp:rootdevice
2017-01-28 13:41:01 +00:00
" X-User-Agent: redsonic \r \n "
" \r \n " ;
2017-10-18 17:22:34 +01:00
String WemoSerialnumber ( )
2017-01-28 13:41:01 +00:00
{
2017-02-04 16:09:54 +00:00
char serial [ 16 ] ;
2017-09-02 13:37:02 +01:00
2017-02-04 16:09:54 +00:00
snprintf_P ( serial , sizeof ( serial ) , PSTR ( " 201612K%08X " ) , ESP . getChipId ( ) ) ;
2017-01-28 13:41:01 +00:00
return String ( serial ) ;
}
2017-10-18 17:22:34 +01:00
String WemoUuid ( )
2017-01-28 13:41:01 +00:00
{
2017-02-04 16:09:54 +00:00
char uuid [ 27 ] ;
2017-09-02 13:37:02 +01:00
2017-10-18 17:22:34 +01:00
snprintf_P ( uuid , sizeof ( uuid ) , PSTR ( " Socket-1_0-%s " ) , WemoSerialnumber ( ) . c_str ( ) ) ;
2017-01-28 13:41:01 +00:00
return String ( uuid ) ;
}
2018-01-05 11:26:19 +00:00
void WemoRespondToMSearch ( int echo_type )
2017-01-28 13:41:01 +00:00
{
2017-04-25 17:24:42 +01:00
char message [ TOPSZ ] ;
2017-01-28 13:41:01 +00:00
2018-01-05 11:26:19 +00:00
TickerMSearch . detach ( ) ;
if ( PortUdp . beginPacket ( udp_remote_ip , udp_remote_port ) ) {
2017-01-28 13:41:01 +00:00
String response = FPSTR ( WEMO_MSEARCH ) ;
2017-10-25 13:27:30 +01:00
response . replace ( " {r1 " , WiFi . localIP ( ) . toString ( ) ) ;
response . replace ( " {r2 " , WemoUuid ( ) ) ;
2018-01-02 15:08:27 +00:00
if ( 1 = = echo_type ) { // type1 echo 1g & dot 2g
response . replace ( " {r3 " , F ( " urn:Belkin:device:** " ) ) ;
} else { // type2 echo 2g (echo, plus, show)
response . replace ( " {r3 " , F ( " upnp:rootdevice " ) ) ;
}
2017-10-18 17:22:34 +01:00
PortUdp . write ( response . c_str ( ) ) ;
PortUdp . endPacket ( ) ;
2017-09-02 13:37:02 +01:00
snprintf_P ( message , sizeof ( message ) , PSTR ( D_RESPONSE_SENT ) ) ;
2017-01-28 13:41:01 +00:00
} else {
2017-09-02 13:37:02 +01:00
snprintf_P ( message , sizeof ( message ) , PSTR ( D_FAILED_TO_SEND_RESPONSE ) ) ;
2017-01-28 13:41:01 +00:00
}
2018-01-02 15:08:27 +00:00
snprintf_P ( log_data , sizeof ( log_data ) , PSTR ( D_LOG_UPNP D_WEMO " " D_JSON_TYPE " %d, %s " D_TO " %s:%d " ) ,
2018-01-05 11:26:19 +00:00
echo_type , message , udp_remote_ip . toString ( ) . c_str ( ) , udp_remote_port ) ;
2017-10-18 17:22:34 +01:00
AddLog ( LOG_LEVEL_DEBUG ) ;
2018-01-05 11:26:19 +00:00
udp_response_mutex = false ;
2017-01-28 13:41:01 +00:00
}
/*********************************************************************************************\
* Hue Bridge UPNP support routines
* Need to send 3 response packets with varying ST and USN
2017-09-02 13:37:02 +01:00
*
2017-07-15 14:07:30 +01:00
* Using Espressif Inc Mac Address of 5 C : CF : 7F : 00 : 00 : 00
* Philips Lighting is 00 : 17 : 88 : 00 : 00 : 00
2017-01-28 13:41:01 +00:00
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2017-07-15 14:07:30 +01:00
2017-01-28 13:41:01 +00:00
const char HUE_RESPONSE [ ] PROGMEM =
2017-07-15 14:07:30 +01:00
" HTTP/1.1 200 OK \r \n "
2017-01-28 13:41:01 +00:00
" HOST: 239.255.255.250:1900 \r \n "
" CACHE-CONTROL: max-age=100 \r \n "
" EXT: \r \n "
2017-10-25 13:27:30 +01:00
" LOCATION: http://{r1:80/description.xml \r \n "
2017-09-09 14:36:30 +01:00
" SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.17.0 \r \n "
2017-10-25 13:27:30 +01:00
" hue-bridgeid: {r2 \r \n " ;
2017-09-02 13:37:02 +01:00
const char HUE_ST1 [ ] PROGMEM =
2017-07-15 14:07:30 +01:00
" ST: upnp:rootdevice \r \n "
2017-10-25 13:27:30 +01:00
" USN: uuid:{r3::upnp:rootdevice \r \n "
2017-01-28 13:41:01 +00:00
" \r \n " ;
const char HUE_ST2 [ ] PROGMEM =
2017-10-25 13:27:30 +01:00
" ST: uuid:{r3 \r \n "
" USN: uuid:{r3 \r \n "
2017-01-28 13:41:01 +00:00
" \r \n " ;
const char HUE_ST3 [ ] PROGMEM =
2017-09-09 14:36:30 +01:00
" ST: urn:schemas-upnp-org:device:basic:1 \r \n "
2017-10-25 13:27:30 +01:00
" USN: uuid:{r3 \r \n "
2017-07-15 14:07:30 +01:00
" \r \n " ;
2017-09-02 13:37:02 +01:00
2017-10-18 17:22:34 +01:00
String HueBridgeId ( )
2017-01-28 13:41:01 +00:00
{
2017-07-15 14:07:30 +01:00
String temp = WiFi . macAddress ( ) ;
temp . replace ( " : " , " " ) ;
String bridgeid = temp . substring ( 0 , 6 ) + " FFFE " + temp . substring ( 6 ) ;
return bridgeid ; // 5CCF7FFFFE139F3D
}
2017-10-18 17:22:34 +01:00
String HueSerialnumber ( )
2017-07-15 14:07:30 +01:00
{
String serial = WiFi . macAddress ( ) ;
serial . replace ( " : " , " " ) ;
serial . toLowerCase ( ) ;
return serial ; // 5ccf7f139f3d
2017-01-28 13:41:01 +00:00
}
2017-10-18 17:22:34 +01:00
String HueUuid ( )
2017-01-28 13:41:01 +00:00
{
2017-07-15 14:07:30 +01:00
String uuid = F ( " f6543a06-da50-11ba-8d8f- " ) ;
2017-10-18 17:22:34 +01:00
uuid + = HueSerialnumber ( ) ;
2017-07-15 14:07:30 +01:00
return uuid ; // f6543a06-da50-11ba-8d8f-5ccf7f139f3d
2017-01-28 13:41:01 +00:00
}
2017-10-18 17:22:34 +01:00
void HueRespondToMSearch ( )
2017-01-28 13:41:01 +00:00
{
2017-04-25 17:24:42 +01:00
char message [ TOPSZ ] ;
2017-01-28 13:41:01 +00:00
2018-01-05 11:26:19 +00:00
TickerMSearch . detach ( ) ;
if ( PortUdp . beginPacket ( udp_remote_ip , udp_remote_port ) ) {
2017-07-15 14:07:30 +01:00
String response1 = FPSTR ( HUE_RESPONSE ) ;
2017-10-25 13:27:30 +01:00
response1 . replace ( " {r1 " , WiFi . localIP ( ) . toString ( ) ) ;
response1 . replace ( " {r2 " , HueBridgeId ( ) ) ;
2017-07-15 14:07:30 +01:00
String response = response1 ;
response + = FPSTR ( HUE_ST1 ) ;
2017-10-25 13:27:30 +01:00
response . replace ( " {r3 " , HueUuid ( ) ) ;
2017-10-18 17:22:34 +01:00
PortUdp . write ( response . c_str ( ) ) ;
PortUdp . endPacket ( ) ;
2017-07-15 14:07:30 +01:00
response = response1 ;
response + = FPSTR ( HUE_ST2 ) ;
2017-10-25 13:27:30 +01:00
response . replace ( " {r3 " , HueUuid ( ) ) ;
2017-10-18 17:22:34 +01:00
PortUdp . write ( response . c_str ( ) ) ;
PortUdp . endPacket ( ) ;
2017-07-15 14:07:30 +01:00
response = response1 ;
response + = FPSTR ( HUE_ST3 ) ;
2017-10-25 13:27:30 +01:00
response . replace ( " {r3 " , HueUuid ( ) ) ;
2017-10-18 17:22:34 +01:00
PortUdp . write ( response . c_str ( ) ) ;
PortUdp . endPacket ( ) ;
2017-01-28 13:41:01 +00:00
2017-09-02 13:37:02 +01:00
snprintf_P ( message , sizeof ( message ) , PSTR ( D_3_RESPONSE_PACKETS_SENT ) ) ;
2017-01-28 13:41:01 +00:00
} else {
2017-09-02 13:37:02 +01:00
snprintf_P ( message , sizeof ( message ) , PSTR ( D_FAILED_TO_SEND_RESPONSE ) ) ;
2017-01-28 13:41:01 +00:00
}
2017-09-13 13:19:34 +01:00
snprintf_P ( log_data , sizeof ( log_data ) , PSTR ( D_LOG_UPNP D_HUE " %s " D_TO " %s:%d " ) ,
2018-01-05 11:26:19 +00:00
message , udp_remote_ip . toString ( ) . c_str ( ) , udp_remote_port ) ;
2017-10-18 17:22:34 +01:00
AddLog ( LOG_LEVEL_DEBUG ) ;
2018-01-05 11:26:19 +00:00
udp_response_mutex = false ;
2017-01-28 13:41:01 +00:00
}
2017-07-15 14:07:30 +01:00
/*********************************************************************************************\
* Belkin WeMo and Philips Hue bridge UDP multicast support
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2017-01-28 13:41:01 +00:00
2017-10-18 17:22:34 +01:00
boolean UdpDisconnect ( )
2017-01-28 13:41:01 +00:00
{
2017-10-18 17:22:34 +01:00
if ( udp_connected ) {
2017-01-28 13:41:01 +00:00
WiFiUDP : : stopAll ( ) ;
2017-10-18 17:22:34 +01:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_UPNP D_MULTICAST_DISABLED ) ) ;
udp_connected = false ;
2017-01-28 13:41:01 +00:00
}
2017-10-18 17:22:34 +01:00
return udp_connected ;
2017-01-28 13:41:01 +00:00
}
2017-10-18 17:22:34 +01:00
boolean UdpConnect ( )
2017-01-28 13:41:01 +00:00
{
2017-10-18 17:22:34 +01:00
if ( ! udp_connected ) {
if ( PortUdp . beginMulticast ( WiFi . localIP ( ) , ipMulticast , port_multicast ) ) {
AddLog_P ( LOG_LEVEL_INFO , PSTR ( D_LOG_UPNP D_MULTICAST_REJOINED ) ) ;
2018-01-05 11:26:19 +00:00
udp_response_mutex = false ;
2017-10-18 17:22:34 +01:00
udp_connected = true ;
2017-01-28 13:41:01 +00:00
} else {
2017-10-18 17:22:34 +01:00
AddLog_P ( LOG_LEVEL_INFO , PSTR ( D_LOG_UPNP D_MULTICAST_JOIN_FAILED ) ) ;
udp_connected = false ;
2017-01-28 13:41:01 +00:00
}
}
2017-10-18 17:22:34 +01:00
return udp_connected ;
2017-01-28 13:41:01 +00:00
}
2017-10-18 17:22:34 +01:00
void PollUdp ( )
2017-01-28 13:41:01 +00:00
{
2018-01-05 11:26:19 +00:00
if ( udp_connected & & ! udp_response_mutex ) {
2017-10-18 17:22:34 +01:00
if ( PortUdp . parsePacket ( ) ) {
int len = PortUdp . read ( packet_buffer , UDP_BUFFER_SIZE - 1 ) ;
2017-04-25 17:24:42 +01:00
if ( len > 0 ) {
2017-10-18 17:22:34 +01:00
packet_buffer [ len ] = 0 ;
2017-04-25 17:24:42 +01:00
}
2017-10-18 17:22:34 +01:00
String request = packet_buffer ;
2017-05-11 16:47:34 +01:00
2017-10-18 17:22:34 +01:00
// AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: Packet received"));
// AddLog_P(LOG_LEVEL_DEBUG_MORE, packet_buffer);
2017-05-11 16:47:34 +01:00
2017-01-28 13:41:01 +00:00
if ( request . indexOf ( " M-SEARCH " ) > = 0 ) {
2017-07-15 14:07:30 +01:00
request . toLowerCase ( ) ;
request . replace ( " " , " " ) ;
2017-10-18 17:22:34 +01:00
// AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: M-SEARCH Packet received"));
// AddLog_P(LOG_LEVEL_DEBUG_MORE, request.c_str());
2017-07-15 14:07:30 +01:00
2018-01-05 11:26:19 +00:00
udp_remote_ip = PortUdp . remoteIP ( ) ;
udp_remote_port = PortUdp . remotePort ( ) ;
2018-01-02 15:08:27 +00:00
if ( EMUL_WEMO = = Settings . flag2 . emulation ) {
if ( request . indexOf ( F ( " urn:belkin:device:** " ) ) > 0 ) { // type1 echo dot 2g, echo 1g's
2018-01-05 11:26:19 +00:00
udp_response_mutex = true ;
TickerMSearch . attach_ms ( UDP_MSEARCH_SEND_DELAY , WemoRespondToMSearch , 1 ) ;
2018-01-02 15:08:27 +00:00
}
else if ( ( request . indexOf ( F ( " upnp:rootdevice " ) ) > 0 ) | | // type2 Echo 2g (echo & echo plus)
( request . indexOf ( F ( " ssdpsearch:all " ) ) > 0 ) | |
( request . indexOf ( F ( " ssdp:all " ) ) > 0 ) ) {
2018-01-05 11:26:19 +00:00
udp_response_mutex = true ;
TickerMSearch . attach_ms ( UDP_MSEARCH_SEND_DELAY , WemoRespondToMSearch , 2 ) ;
2018-01-02 15:08:27 +00:00
}
2017-01-28 13:41:01 +00:00
}
2017-11-11 11:33:30 +00:00
else if ( ( EMUL_HUE = = Settings . flag2 . emulation ) & &
2017-12-30 11:47:19 +00:00
( ( request . indexOf ( F ( " urn:schemas-upnp-org:device:basic:1 " ) ) > 0 ) | |
( request . indexOf ( F ( " upnp:rootdevice " ) ) > 0 ) | |
( request . indexOf ( F ( " ssdpsearch:all " ) ) > 0 ) | |
( request . indexOf ( F ( " ssdp:all " ) ) > 0 ) ) ) {
2018-01-05 11:26:19 +00:00
udp_response_mutex = true ;
TickerMSearch . attach_ms ( UDP_MSEARCH_SEND_DELAY , HueRespondToMSearch ) ;
2017-01-28 13:41:01 +00:00
}
}
}
}
}
2017-02-21 17:14:33 +00:00
/*********************************************************************************************\
2017-07-15 14:07:30 +01:00
* Wemo web server additions
2017-02-21 17:14:33 +00:00
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2017-07-15 14:07:30 +01:00
2017-02-21 17:14:33 +00:00
const char WEMO_EVENTSERVICE_XML [ ] PROGMEM =
2017-12-27 13:10:55 +00:00
" <scpd xmlns= \" urn:Belkin:service-1-0 \" > "
" <actionList> "
" <action> "
" <name>SetBinaryState</name> "
" <argumentList> "
" <argument> "
2017-12-30 11:47:19 +00:00
" <retval/> "
2017-12-27 13:10:55 +00:00
" <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>Boolean</dataType> "
" <defaultValue>0</defaultValue> "
" </stateVariable> "
2017-12-30 11:47:19 +00:00
" <stateVariable sendEvents= \" yes \" > "
" <name>level</name> "
" <dataType>string</dataType> "
" <defaultValue>0</defaultValue> "
" </stateVariable> "
2017-12-27 13:10:55 +00:00
" </serviceStateTable> "
2018-01-02 15:08:27 +00:00
" </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 " ;
2017-12-25 16:41:12 +00:00
const char WEMO_RESPONSE_STATE_SOAP [ ] PROGMEM =
2017-12-28 12:43:51 +00:00
" <s:Envelope xmlns:s= \" http://schemas.xmlsoap.org/soap/envelope/ \" s:encodingStyle= \" http://schemas.xmlsoap.org/soap/encoding/ \" > "
2017-12-25 16:41:12 +00:00
" <s:Body> "
2017-12-28 12:43:51 +00:00
" <u:SetBinaryStateResponse xmlns:u= \" urn:Belkin:service:basicevent:1 \" > "
2017-12-25 16:41:12 +00:00
" <BinaryState>{x1</BinaryState> "
2017-12-28 12:43:51 +00:00
" </u:SetBinaryStateResponse> "
2017-12-25 16:41:12 +00:00
" </s:Body> "
2017-12-30 11:47:19 +00:00
" </s:Envelope> \r \n " ;
2017-12-25 16:41:12 +00:00
2017-02-21 17:14:33 +00:00
const char WEMO_SETUP_XML [ ] PROGMEM =
" <?xml version= \" 1.0 \" ?> "
2017-12-28 12:43:51 +00:00
" <root xmlns= \" urn:Belkin:device-1-0 \" > "
2017-02-21 17:14:33 +00:00
" <device> "
" <deviceType>urn:Belkin:device:controllee:1</deviceType> "
2017-10-25 13:27:30 +01:00
" <friendlyName>{x1</friendlyName> "
2017-02-21 17:14:33 +00:00
" <manufacturer>Belkin International Inc.</manufacturer> "
2017-12-30 11:47:19 +00:00
" <modelName>Socket</modelName> "
2017-02-21 17:14:33 +00:00
" <modelNumber>3.1415</modelNumber> "
2017-10-25 13:27:30 +01:00
" <UDN>uuid:{x2</UDN> "
" <serialNumber>{x3</serialNumber> "
2017-02-21 17:14:33 +00:00
" <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> "
2018-01-02 15:08:27 +00:00
" <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> "
2017-02-21 17:14:33 +00:00
" </serviceList> "
" </device> "
2017-12-30 11:47:19 +00:00
" </root> \r \n " ;
2017-09-02 13:37:02 +01:00
2017-07-15 14:07:30 +01:00
/********************************************************************************************/
2017-10-18 17:22:34 +01:00
void HandleUpnpEvent ( )
2017-07-15 14:07:30 +01:00
{
2017-10-18 17:22:34 +01:00
AddLog_P ( LOG_LEVEL_DEBUG , S_LOG_HTTP , PSTR ( D_WEMO_BASIC_EVENT ) ) ;
2017-12-28 12:43:51 +00:00
2017-10-18 17:22:34 +01:00
String request = WebServer - > arg ( 0 ) ;
2017-12-25 16:41:12 +00:00
String state_xml = FPSTR ( WEMO_RESPONSE_STATE_SOAP ) ;
//differentiate get and set state
2017-12-17 15:01:30 +00:00
if ( request . indexOf ( F ( " SetBinaryState " ) ) > 0 ) {
2018-09-02 13:26:00 +01:00
uint8_t power = POWER_TOGGLE ;
2017-12-17 15:01:30 +00:00
if ( request . indexOf ( F ( " State>1</Binary " ) ) > 0 ) {
2018-09-02 13:46:06 +01:00
power = POWER_ON ;
2017-12-17 15:01:30 +00:00
}
2017-12-25 16:41:12 +00:00
else if ( request . indexOf ( F ( " State>0</Binary " ) ) > 0 ) {
2018-09-02 13:26:00 +01:00
power = POWER_OFF ;
2017-12-17 15:01:30 +00:00
}
2018-09-02 13:26:00 +01:00
if ( power ! = POWER_TOGGLE ) {
uint8_t device = ( light_type ) ? devices_present : 1 ; // Select either a configured light or relay1
ExecuteCommandPower ( device , power , SRC_WEMO ) ;
}
2017-07-15 14:07:30 +01:00
}
2017-12-25 16:41:12 +00:00
else if ( request . indexOf ( F ( " GetBinaryState " ) ) > 0 ) {
2017-12-28 12:43:51 +00:00
state_xml . replace ( F ( " Set " ) , F ( " Get " ) ) ;
2017-12-25 16:41:12 +00:00
}
state_xml . replace ( " {x1 " , String ( bitRead ( power , devices_present - 1 ) ) ) ;
WebServer - > send ( 200 , FPSTR ( HDR_CTYPE_XML ) , state_xml ) ;
2017-07-15 14:07:30 +01:00
}
2017-10-18 17:22:34 +01:00
void HandleUpnpService ( )
2017-07-15 14:07:30 +01:00
{
2017-10-18 17:22:34 +01:00
AddLog_P ( LOG_LEVEL_DEBUG , S_LOG_HTTP , PSTR ( D_WEMO_EVENT_SERVICE ) ) ;
2017-12-28 12:43:51 +00:00
2017-10-18 17:22:34 +01:00
WebServer - > send ( 200 , FPSTR ( HDR_CTYPE_PLAIN ) , FPSTR ( WEMO_EVENTSERVICE_XML ) ) ;
2017-07-15 14:07:30 +01:00
}
2018-01-02 15:08:27 +00:00
void HandleUpnpMetaService ( )
{
AddLog_P ( LOG_LEVEL_DEBUG , S_LOG_HTTP , PSTR ( D_WEMO_META_SERVICE ) ) ;
WebServer - > send ( 200 , FPSTR ( HDR_CTYPE_PLAIN ) , FPSTR ( WEMO_METASERVICE_XML ) ) ;
}
2017-10-18 17:22:34 +01:00
void HandleUpnpSetupWemo ( )
2017-07-15 14:07:30 +01:00
{
2017-10-18 17:22:34 +01:00
AddLog_P ( LOG_LEVEL_DEBUG , S_LOG_HTTP , PSTR ( D_WEMO_SETUP ) ) ;
2017-07-15 14:07:30 +01:00
String setup_xml = FPSTR ( WEMO_SETUP_XML ) ;
2017-10-25 13:27:30 +01:00
setup_xml . replace ( " {x1 " , Settings . friendlyname [ 0 ] ) ;
setup_xml . replace ( " {x2 " , WemoUuid ( ) ) ;
setup_xml . replace ( " {x3 " , WemoSerialnumber ( ) ) ;
2017-10-18 17:22:34 +01:00
WebServer - > send ( 200 , FPSTR ( HDR_CTYPE_XML ) , setup_xml ) ;
2017-07-15 14:07:30 +01:00
}
/*********************************************************************************************\
* Hue web server additions
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2017-02-21 17:14:33 +00:00
const char HUE_DESCRIPTION_XML [ ] PROGMEM =
" <?xml version= \" 1.0 \" ?> "
" <root xmlns= \" urn:schemas-upnp-org:device-1-0 \" > "
" <specVersion> "
2017-07-15 14:07:30 +01:00
" <major>1</major> "
" <minor>0</minor> "
2017-02-21 17:14:33 +00:00
" </specVersion> "
2017-10-25 13:27:30 +01:00
// "<URLBase>http://{x1/</URLBase>"
" <URLBase>http://{x1:80/</URLBase> "
2017-02-21 17:14:33 +00:00
" <device> "
2017-07-15 14:07:30 +01:00
" <deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType> "
2017-10-25 13:27:30 +01:00
" <friendlyName>Amazon-Echo-HA-Bridge ({x1)</friendlyName> "
// "<friendlyName>Philips hue ({x1)</friendlyName>"
2017-07-15 14:07:30 +01:00
" <manufacturer>Royal Philips Electronics</manufacturer> "
" <modelDescription>Philips hue Personal Wireless Lighting</modelDescription> "
" <modelName>Philips hue bridge 2012</modelName> "
" <modelNumber>929000226503</modelNumber> "
2017-10-25 13:27:30 +01:00
" <serialNumber>{x3</serialNumber> "
" <UDN>uuid:{x2</UDN> "
2017-02-21 17:14:33 +00:00
" </device> "
" </root> \r \n "
" \r \n " ;
2018-01-30 13:14:55 +00:00
const char HUE_LIGHTS_STATUS_JSON [ ] PROGMEM =
2018-01-30 13:50:38 +00:00
" { \" on \" :{state}, "
2017-07-15 14:07:30 +01:00
" \" bri \" :{b}, "
" \" hue \" :{h}, "
" \" sat \" :{s}, "
" \" xy \" :[0.5, 0.5], "
2018-09-08 07:49:08 +01:00
" \" ct \" :{t}, "
2017-07-15 14:07:30 +01:00
" \" alert \" : \" none \" , "
" \" effect \" : \" none \" , "
2018-09-08 07:49:08 +01:00
" \" colormode \" : \" {m} \" , "
2018-01-30 13:14:55 +00:00
" \" reachable \" :true} " ;
const char HUE_LIGHTS_STATUS_JSON2 [ ] PROGMEM =
" , \" type \" : \" Extended color light \" , "
2017-10-25 13:27:30 +01:00
" \" name \" : \" {j1 \" , "
2017-06-01 16:37:30 +01:00
" \" modelid \" : \" LCT007 \" , "
2017-10-25 13:27:30 +01:00
" \" uniqueid \" : \" {j2 \" , "
2018-01-30 13:14:55 +00:00
" \" swversion \" : \" 5.50.1.19085 \" } " ;
2017-07-15 14:07:30 +01:00
const char HUE_GROUP0_STATUS_JSON [ ] PROGMEM =
" { \" name \" : \" Group 0 \" , "
2017-10-25 13:27:30 +01:00
" \" lights \" :[{l1], "
2017-07-15 14:07:30 +01:00
" \" type \" : \" LightGroup \" , "
2018-01-30 13:50:38 +00:00
" \" action \" : " ;
2017-07-15 14:07:30 +01:00
// "\"scene\":\"none\",";
2017-10-18 17:22:34 +01:00
const char HueConfigResponse_JSON [ ] PROGMEM =
2017-02-21 17:14:33 +00:00
" { \" name \" : \" Philips hue \" , "
2017-10-25 13:27:30 +01:00
" \" mac \" : \" {ma \" , "
2017-02-21 17:14:33 +00:00
" \" dhcp \" :true, "
2017-10-25 13:27:30 +01:00
" \" ipaddress \" : \" {ip \" , "
" \" netmask \" : \" {ms \" , "
" \" gateway \" : \" {gw \" , "
2017-07-15 14:07:30 +01:00
" \" proxyaddress \" : \" none \" , "
2017-02-21 17:14:33 +00:00
" \" proxyport \" :0, "
2017-10-25 13:27:30 +01:00
" \" bridgeid \" : \" {br \" , "
" \" UTC \" : \" {dt \" , "
" \" whitelist \" :{ \" {id \" :{ "
" \" last use date \" : \" {dt \" , "
" \" create date \" : \" {dt \" , "
2017-02-21 17:14:33 +00:00
" \" name \" : \" Remote \" }}, "
2018-05-20 17:01:12 +01:00
" \" swversion \" : \" 01041302 \" , "
2017-09-09 14:36:30 +01:00
" \" apiversion \" : \" 1.17.0 \" , "
2017-02-21 17:14:33 +00:00
" \" swupdate \" :{ \" updatestate \" :0, \" url \" : \" \" , \" text \" : \" \" , \" notify \" : false}, "
" \" linkbutton \" :false, "
" \" portalservices \" :false "
" } " ;
2017-07-15 14:07:30 +01:00
const char HUE_LIGHT_RESPONSE_JSON [ ] PROGMEM =
2017-10-25 13:27:30 +01:00
" { \" success \" :{ \" /lights/{id/state/{cm \" :{re}} " ;
2017-02-21 17:14:33 +00:00
const char HUE_ERROR_JSON [ ] PROGMEM =
" [{ \" error \" :{ \" type \" :901, \" address \" : \" / \" , \" description \" : \" Internal Error \" }}] " ;
2017-07-15 14:07:30 +01:00
/********************************************************************************************/
2017-02-21 17:14:33 +00:00
2017-10-18 17:22:34 +01:00
String GetHueDeviceId ( uint8_t id )
2017-02-21 17:14:33 +00:00
{
2017-07-15 14:07:30 +01:00
String deviceid = WiFi . macAddress ( ) + F ( " :00:11- " ) + String ( id ) ;
deviceid . toLowerCase ( ) ;
return deviceid ; // 5c:cf:7f:13:9f:3d:00:11-1
2017-02-21 17:14:33 +00:00
}
2017-10-18 17:22:34 +01:00
String GetHueUserId ( )
2017-02-21 17:14:33 +00:00
{
2017-07-15 14:07:30 +01:00
char userid [ 7 ] ;
2017-02-21 17:14:33 +00:00
2017-07-15 14:07:30 +01:00
snprintf_P ( userid , sizeof ( userid ) , PSTR ( " %03x " ) , ESP . getChipId ( ) ) ;
return String ( userid ) ;
2017-02-21 17:14:33 +00:00
}
2017-10-18 17:22:34 +01:00
void HandleUpnpSetupHue ( )
2017-02-21 17:14:33 +00:00
{
2017-10-18 17:22:34 +01:00
AddLog_P ( LOG_LEVEL_DEBUG , S_LOG_HTTP , PSTR ( D_HUE_BRIDGE_SETUP ) ) ;
2017-02-21 17:14:33 +00:00
String description_xml = FPSTR ( HUE_DESCRIPTION_XML ) ;
2017-10-25 13:27:30 +01:00
description_xml . replace ( " {x1 " , WiFi . localIP ( ) . toString ( ) ) ;
description_xml . replace ( " {x2 " , HueUuid ( ) ) ;
description_xml . replace ( " {x3 " , HueSerialnumber ( ) ) ;
2017-10-18 17:22:34 +01:00
WebServer - > send ( 200 , FPSTR ( HDR_CTYPE_XML ) , description_xml ) ;
2017-02-21 17:14:33 +00:00
}
2017-10-18 17:22:34 +01:00
void HueNotImplemented ( String * path )
2017-02-21 17:14:33 +00:00
{
2017-09-13 13:19:34 +01:00
snprintf_P ( log_data , sizeof ( log_data ) , PSTR ( D_LOG_HTTP D_HUE_API_NOT_IMPLEMENTED " (%s) " ) , path - > c_str ( ) ) ;
2017-10-18 17:22:34 +01:00
AddLog ( LOG_LEVEL_DEBUG_MORE ) ;
2017-07-15 14:07:30 +01:00
2017-10-18 17:22:34 +01:00
WebServer - > send ( 200 , FPSTR ( HDR_CTYPE_JSON ) , " {} " ) ;
2017-02-21 17:14:33 +00:00
}
2017-10-18 17:22:34 +01:00
void HueConfigResponse ( String * response )
2017-02-21 17:14:33 +00:00
{
2017-10-18 17:22:34 +01:00
* response + = FPSTR ( HueConfigResponse_JSON ) ;
2017-10-25 13:27:30 +01:00
response - > replace ( " {ma " , WiFi . macAddress ( ) ) ;
response - > replace ( " {ip " , WiFi . localIP ( ) . toString ( ) ) ;
response - > replace ( " {ms " , WiFi . subnetMask ( ) . toString ( ) ) ;
response - > replace ( " {gw " , WiFi . gatewayIP ( ) . toString ( ) ) ;
response - > replace ( " {br " , HueBridgeId ( ) ) ;
2018-02-17 13:09:39 +00:00
response - > replace ( " {dt " , GetDateAndTime ( DT_UTC ) ) ;
2017-10-25 13:27:30 +01:00
response - > replace ( " {id " , GetHueUserId ( ) ) ;
2017-07-15 14:07:30 +01:00
}
2017-10-18 17:22:34 +01:00
void HueConfig ( String * path )
2017-07-15 14:07:30 +01:00
{
String response = " " ;
2017-10-18 17:22:34 +01:00
HueConfigResponse ( & response ) ;
WebServer - > send ( 200 , FPSTR ( HDR_CTYPE_JSON ) , response ) ;
2017-07-15 14:07:30 +01:00
}
2018-09-08 07:49:08 +01:00
bool g_gotct = false ;
2018-01-30 13:14:55 +00:00
void HueLightStatus1 ( byte device , String * response )
2017-07-15 14:07:30 +01:00
{
2018-01-30 13:14:55 +00:00
float hue = 0 ;
float sat = 0 ;
float bri = 0 ;
2018-09-08 07:49:08 +01:00
uint16_t ct = 500 ;
2017-09-02 13:37:02 +01:00
2017-10-18 17:22:34 +01:00
if ( light_type ) {
2018-09-08 07:49:08 +01:00
LightGetHsb ( & hue , & sat , & bri , g_gotct ) ;
ct = LightGetColorTemp ( ) ;
2017-07-15 14:07:30 +01:00
}
2018-01-30 13:14:55 +00:00
* response + = FPSTR ( HUE_LIGHTS_STATUS_JSON ) ;
response - > replace ( " {state} " , ( power & ( 1 < < ( device - 1 ) ) ) ? " true " : " false " ) ;
response - > replace ( " {h} " , String ( ( uint16_t ) ( 65535.0f * hue ) ) ) ;
response - > replace ( " {s} " , String ( ( uint8_t ) ( 254.0f * sat ) ) ) ;
response - > replace ( " {b} " , String ( ( uint8_t ) ( 254.0f * bri ) ) ) ;
2018-09-08 07:49:08 +01:00
response - > replace ( " {t} " , String ( ct ) ) ;
response - > replace ( " {m} " , g_gotct ? " ct " : " hs " ) ;
2018-01-30 13:14:55 +00:00
}
void HueLightStatus2 ( byte device , String * response )
{
* response + = FPSTR ( HUE_LIGHTS_STATUS_JSON2 ) ;
response - > replace ( " {j1 " , Settings . friendlyname [ device - 1 ] ) ;
response - > replace ( " {j2 " , GetHueDeviceId ( device ) ) ;
2017-02-21 17:14:33 +00:00
}
2017-10-18 17:22:34 +01:00
void HueGlobalConfig ( String * path )
2017-02-21 17:14:33 +00:00
{
String response ;
2017-10-18 17:22:34 +01:00
uint8_t maxhue = ( devices_present > MAX_FRIENDLYNAMES ) ? MAX_FRIENDLYNAMES : devices_present ;
2017-02-21 17:14:33 +00:00
2017-09-26 14:10:58 +01:00
path - > remove ( 0 , 1 ) ; // cut leading / to get <id>
2017-07-15 14:07:30 +01:00
response = F ( " { \" lights \" :{ \" " ) ;
2017-10-12 10:29:40 +01:00
for ( uint8_t i = 1 ; i < = maxhue ; i + + ) {
2017-02-21 17:14:33 +00:00
response + = i ;
2018-01-30 13:50:38 +00:00
response + = F ( " \" :{ \" state \" : " ) ;
2018-01-30 13:14:55 +00:00
HueLightStatus1 ( i , & response ) ;
HueLightStatus2 ( i , & response ) ;
2017-10-12 10:29:40 +01:00
if ( i < maxhue ) {
2017-07-15 14:07:30 +01:00
response + = " , \" " ;
2017-02-21 17:14:33 +00:00
}
}
response + = F ( " }, \" groups \" :{}, \" schedules \" :{}, \" config \" : " ) ;
2017-10-18 17:22:34 +01:00
HueConfigResponse ( & response ) ;
2017-02-21 17:14:33 +00:00
response + = " } " ;
2017-10-18 17:22:34 +01:00
WebServer - > send ( 200 , FPSTR ( HDR_CTYPE_JSON ) , response ) ;
2017-02-21 17:14:33 +00:00
}
2017-10-18 17:22:34 +01:00
void HueAuthentication ( String * path )
2017-02-21 17:14:33 +00:00
{
char response [ 38 ] ;
2017-09-02 13:37:02 +01:00
2017-10-18 17:22:34 +01:00
snprintf_P ( response , sizeof ( response ) , PSTR ( " [{ \" success \" :{ \" username \" : \" %s \" }}] " ) , GetHueUserId ( ) . c_str ( ) ) ;
WebServer - > send ( 200 , FPSTR ( HDR_CTYPE_JSON ) , response ) ;
2017-02-21 17:14:33 +00:00
}
2017-10-18 17:22:34 +01:00
void HueLights ( String * path )
2017-02-21 17:14:33 +00:00
{
/*
* http : //sonoff/api/username/lights/1/state?1={"on":true,"hue":56100,"sat":254,"bri":254,"alert":"none","transitiontime":40}
*/
2017-07-15 14:07:30 +01:00
String response ;
2017-02-21 17:14:33 +00:00
uint8_t device = 1 ;
2017-07-15 14:07:30 +01:00
uint16_t tmp = 0 ;
float bri = 0 ;
float hue = 0 ;
float sat = 0 ;
2017-08-16 16:05:36 +01:00
uint16_t ct = 0 ;
2017-07-15 14:07:30 +01:00
bool resp = false ;
2017-02-21 17:14:33 +00:00
bool on = false ;
bool change = false ;
2017-10-18 17:22:34 +01:00
uint8_t maxhue = ( devices_present > MAX_FRIENDLYNAMES ) ? MAX_FRIENDLYNAMES : devices_present ;
2017-02-21 17:14:33 +00:00
2017-09-26 14:10:58 +01:00
path - > remove ( 0 , path - > indexOf ( " /lights " ) ) ; // Remove until /lights
if ( path - > endsWith ( " /lights " ) ) { // Got /lights
2017-02-21 17:14:33 +00:00
response = " { \" " ;
2017-10-12 10:29:40 +01:00
for ( uint8_t i = 1 ; i < = maxhue ; i + + ) {
2017-02-21 17:14:33 +00:00
response + = i ;
2018-01-30 13:50:38 +00:00
response + = F ( " \" :{ \" state \" : " ) ;
2018-01-30 13:14:55 +00:00
HueLightStatus1 ( i , & response ) ;
HueLightStatus2 ( i , & response ) ;
2017-10-12 10:29:40 +01:00
if ( i < maxhue ) {
2017-07-15 14:07:30 +01:00
response + = " , \" " ;
2017-02-21 17:14:33 +00:00
}
}
response + = " } " ;
2017-10-18 17:22:34 +01:00
WebServer - > send ( 200 , FPSTR ( HDR_CTYPE_JSON ) , response ) ;
2017-02-21 17:14:33 +00:00
}
2017-09-26 14:10:58 +01:00
else if ( path - > endsWith ( " /state " ) ) { // Got ID/state
path - > remove ( 0 , 8 ) ; // Remove /lights/
path - > remove ( path - > indexOf ( " /state " ) ) ; // Remove /state
2017-02-21 17:14:33 +00:00
device = atoi ( path - > c_str ( ) ) ;
2017-10-12 10:29:40 +01:00
if ( ( device < 1 ) | | ( device > maxhue ) ) {
2017-04-25 17:24:42 +01:00
device = 1 ;
}
2018-10-24 20:54:16 +01:00
if ( WebServer - > args ( ) ) {
2017-07-15 14:07:30 +01:00
response = " [ " ;
2017-02-21 17:14:33 +00:00
StaticJsonBuffer < 400 > jsonBuffer ;
2018-10-28 10:17:41 +00:00
JsonObject & hue_json = jsonBuffer . parseObject ( WebServer - > arg ( ( WebServer - > args ( ) ) - 1 ) ) ;
2017-02-21 17:14:33 +00:00
if ( hue_json . containsKey ( " on " ) ) {
2017-09-02 13:37:02 +01:00
2017-07-15 14:07:30 +01:00
response + = FPSTR ( HUE_LIGHT_RESPONSE_JSON ) ;
2017-10-25 13:27:30 +01:00
response . replace ( " {id " , String ( device ) ) ;
response . replace ( " {cm " , " on " ) ;
2017-09-02 13:37:02 +01:00
2017-02-21 17:14:33 +00:00
on = hue_json [ " on " ] ;
switch ( on )
{
2018-05-28 14:52:42 +01:00
case false : ExecuteCommandPower ( device , POWER_OFF , SRC_HUE ) ;
2017-10-25 13:27:30 +01:00
response . replace ( " {re " , " false " ) ;
2017-02-21 17:14:33 +00:00
break ;
2018-05-28 14:52:42 +01:00
case true : ExecuteCommandPower ( device , POWER_ON , SRC_HUE ) ;
2017-10-25 13:27:30 +01:00
response . replace ( " {re " , " true " ) ;
2017-02-21 17:14:33 +00:00
break ;
2017-10-25 13:27:30 +01:00
default : response . replace ( " {re " , ( power & ( 1 < < ( device - 1 ) ) ) ? " true " : " false " ) ;
2017-02-21 17:14:33 +00:00
break ;
}
2017-07-15 14:07:30 +01:00
resp = true ;
2017-02-21 17:14:33 +00:00
}
2017-09-02 13:37:02 +01:00
2017-10-18 17:22:34 +01:00
if ( light_type ) {
2018-09-08 07:49:08 +01:00
LightGetHsb ( & hue , & sat , & bri , g_gotct ) ;
2017-07-15 14:07:30 +01:00
}
2018-09-08 07:49:08 +01:00
if ( hue_json . containsKey ( " bri " ) ) { // Brightness is a scale from 1 (the minimum the light is capable of) to 254 (the maximum). Note: a brightness of 1 is not off.
2017-02-21 17:14:33 +00:00
tmp = hue_json [ " bri " ] ;
2018-09-08 07:49:08 +01:00
tmp = max ( tmp , 1 ) ;
tmp = min ( tmp , 254 ) ;
2017-07-15 14:07:30 +01:00
bri = ( float ) tmp / 254.0f ;
if ( resp ) {
response + = " , " ;
}
2017-02-21 17:14:33 +00:00
response + = FPSTR ( HUE_LIGHT_RESPONSE_JSON ) ;
2017-10-25 13:27:30 +01:00
response . replace ( " {id " , String ( device ) ) ;
response . replace ( " {cm " , " bri " ) ;
response . replace ( " {re " , String ( tmp ) ) ;
2017-07-15 14:07:30 +01:00
resp = true ;
2017-02-21 17:14:33 +00:00
change = true ;
}
2018-09-08 07:49:08 +01:00
if ( hue_json . containsKey ( " hue " ) ) { // The hue value is a wrapping value between 0 and 65535. Both 0 and 65535 are red, 25500 is green and 46920 is blue.
2017-02-21 17:14:33 +00:00
tmp = hue_json [ " hue " ] ;
2017-07-15 14:07:30 +01:00
hue = ( float ) tmp / 65535.0f ;
if ( resp ) {
response + = " , " ;
}
2017-02-21 17:14:33 +00:00
response + = FPSTR ( HUE_LIGHT_RESPONSE_JSON ) ;
2017-10-25 13:27:30 +01:00
response . replace ( " {id " , String ( device ) ) ;
response . replace ( " {cm " , " hue " ) ;
response . replace ( " {re " , String ( tmp ) ) ;
2018-09-08 07:49:08 +01:00
g_gotct = false ;
2017-07-15 14:07:30 +01:00
resp = true ;
2017-02-21 17:14:33 +00:00
change = true ;
}
2018-09-08 07:49:08 +01:00
if ( hue_json . containsKey ( " sat " ) ) { // Saturation of the light. 254 is the most saturated (colored) and 0 is the least saturated (white).
2017-02-21 17:14:33 +00:00
tmp = hue_json [ " sat " ] ;
2018-09-08 07:49:08 +01:00
tmp = max ( tmp , 0 ) ;
tmp = min ( tmp , 254 ) ;
2017-07-15 14:07:30 +01:00
sat = ( float ) tmp / 254.0f ;
if ( resp ) {
response + = " , " ;
}
2017-02-21 17:14:33 +00:00
response + = FPSTR ( HUE_LIGHT_RESPONSE_JSON ) ;
2017-10-25 13:27:30 +01:00
response . replace ( " {id " , String ( device ) ) ;
response . replace ( " {cm " , " sat " ) ;
response . replace ( " {re " , String ( tmp ) ) ;
2018-09-08 07:49:08 +01:00
g_gotct = false ;
resp = true ;
2017-02-21 17:14:33 +00:00
change = true ;
}
2017-09-26 14:10:58 +01:00
if ( hue_json . containsKey ( " ct " ) ) { // Color temperature 153 (Cold) to 500 (Warm)
2017-08-16 16:05:36 +01:00
ct = hue_json [ " ct " ] ;
2017-08-15 21:09:52 +01:00
if ( resp ) {
response + = " , " ;
}
response + = FPSTR ( HUE_LIGHT_RESPONSE_JSON ) ;
2017-10-25 13:27:30 +01:00
response . replace ( " {id " , String ( device ) ) ;
response . replace ( " {cm " , " ct " ) ;
response . replace ( " {re " , String ( ct ) ) ;
2018-09-08 07:49:08 +01:00
g_gotct = true ;
2017-08-15 21:09:52 +01:00
change = true ;
}
2017-07-15 14:07:30 +01:00
if ( change ) {
2017-10-18 17:22:34 +01:00
if ( light_type ) {
2018-09-08 07:49:08 +01:00
LightSetHsb ( hue , sat , bri , ct , g_gotct ) ;
2017-07-15 14:07:30 +01:00
}
2017-04-25 17:24:42 +01:00
change = false ;
2017-02-21 17:14:33 +00:00
}
response + = " ] " ;
2017-07-15 14:07:30 +01:00
if ( 2 = = response . length ( ) ) {
response = FPSTR ( HUE_ERROR_JSON ) ;
}
2017-09-02 13:37:02 +01:00
}
2017-02-21 17:14:33 +00:00
else {
2017-07-15 14:07:30 +01:00
response = FPSTR ( HUE_ERROR_JSON ) ;
2017-02-21 17:14:33 +00:00
}
2017-08-15 21:09:52 +01:00
2017-10-18 17:22:34 +01:00
WebServer - > send ( 200 , FPSTR ( HDR_CTYPE_JSON ) , response ) ;
2017-02-21 17:14:33 +00:00
}
2017-09-26 14:10:58 +01:00
else if ( path - > indexOf ( " /lights/ " ) > = 0 ) { // Got /lights/ID
path - > remove ( 0 , 8 ) ; // Remove /lights/
2017-02-21 17:14:33 +00:00
device = atoi ( path - > c_str ( ) ) ;
2017-10-12 10:29:40 +01:00
if ( ( device < 1 ) | | ( device > maxhue ) ) {
2017-04-25 17:24:42 +01:00
device = 1 ;
}
2018-01-30 13:50:38 +00:00
response + = F ( " { \" state \" : " ) ;
2018-01-30 13:14:55 +00:00
HueLightStatus1 ( device , & response ) ;
HueLightStatus2 ( device , & response ) ;
2017-10-18 17:22:34 +01:00
WebServer - > send ( 200 , FPSTR ( HDR_CTYPE_JSON ) , response ) ;
2017-07-15 14:07:30 +01:00
}
else {
2017-10-18 17:22:34 +01:00
WebServer - > send ( 406 , FPSTR ( HDR_CTYPE_JSON ) , " {} " ) ;
2017-07-15 14:07:30 +01:00
}
}
2017-10-18 17:22:34 +01:00
void HueGroups ( String * path )
2017-07-15 14:07:30 +01:00
{
2017-09-02 13:37:02 +01:00
/*
2017-08-15 21:09:52 +01:00
* http : //sonoff/api/username/groups?1={"name":"Woonkamer","lights":[],"type":"Room","class":"Living room"})
*/
2017-07-15 14:07:30 +01:00
String response = " {} " ;
2017-10-18 17:22:34 +01:00
uint8_t maxhue = ( devices_present > MAX_FRIENDLYNAMES ) ? MAX_FRIENDLYNAMES : devices_present ;
2017-09-02 13:37:02 +01:00
2017-07-15 14:07:30 +01:00
if ( path - > endsWith ( " /0 " ) ) {
response = FPSTR ( HUE_GROUP0_STATUS_JSON ) ;
String lights = F ( " \" 1 \" " ) ;
2017-10-12 10:29:40 +01:00
for ( uint8_t i = 2 ; i < = maxhue ; i + + ) {
2017-07-15 14:07:30 +01:00
lights + = " , \" " + String ( i ) + " \" " ;
2017-02-21 17:14:33 +00:00
}
2017-10-25 13:27:30 +01:00
response . replace ( " {l1 " , lights ) ;
2018-01-30 13:14:55 +00:00
HueLightStatus1 ( 1 , & response ) ;
response + = F ( " } " ) ;
2017-02-21 17:14:33 +00:00
}
2017-09-02 13:37:02 +01:00
2017-10-18 17:22:34 +01:00
WebServer - > send ( 200 , FPSTR ( HDR_CTYPE_JSON ) , response ) ;
2017-02-21 17:14:33 +00:00
}
2017-10-18 17:22:34 +01:00
void HandleHueApi ( String * path )
2017-02-21 17:14:33 +00:00
{
/* HUE API uses /api/<userid>/<command> syntax. The userid is created by the echo device and
* on original HUE the pressed button allows for creation of this user . We simply ignore the
2017-09-02 13:37:02 +01:00
* user part and allow every caller as with Web or WeMo .
2017-02-21 17:14:33 +00:00
*
* ( c ) Heiko Krupp , 2017
2018-10-28 15:16:18 +00:00
*
* Hue URL
* http : //sonoff/api/username/lights/1/state with post data {"on":true,"hue":56100,"sat":254,"bri":254,"alert":"none","transitiontime":40}
* is converted by webserver to
* http : //sonoff/api/username/lights/1/state with arg plain={"on":true,"hue":56100,"sat":254,"bri":254,"alert":"none","transitiontime":40}
2017-02-21 17:14:33 +00:00
*/
2017-09-02 13:37:02 +01:00
2017-02-21 17:14:33 +00:00
uint8_t args = 0 ;
2017-07-15 14:07:30 +01:00
path - > remove ( 0 , 4 ) ; // remove /api
uint16_t apilen = path - > length ( ) ;
2017-09-13 13:19:34 +01:00
snprintf_P ( log_data , sizeof ( log_data ) , PSTR ( D_LOG_HTTP D_HUE_API " (%s) " ) , path - > c_str ( ) ) ;
2017-10-18 17:22:34 +01:00
AddLog ( LOG_LEVEL_DEBUG_MORE ) ; // HTP: Hue API (//lights/1/state)
for ( args = 0 ; args < WebServer - > args ( ) ; args + + ) {
String json = WebServer - > arg ( args ) ;
2017-09-13 13:19:34 +01:00
snprintf_P ( log_data , sizeof ( log_data ) , PSTR ( D_LOG_HTTP D_HUE_POST_ARGS " (%s) " ) , json . c_str ( ) ) ;
2017-10-18 17:22:34 +01:00
AddLog ( LOG_LEVEL_DEBUG_MORE ) ; // HTP: Hue POST args ({"on":false})
2017-02-21 17:14:33 +00:00
}
2017-09-02 13:37:02 +01:00
2017-09-26 14:10:58 +01:00
if ( path - > endsWith ( " /invalid/ " ) ) { } // Just ignore
2017-10-18 17:22:34 +01:00
else if ( ! apilen ) HueAuthentication ( path ) ; // New HUE App setup
else if ( path - > endsWith ( " / " ) ) HueAuthentication ( path ) ; // New HUE App setup
else if ( path - > endsWith ( " /config " ) ) HueConfig ( path ) ;
else if ( path - > indexOf ( " /lights " ) > = 0 ) HueLights ( path ) ;
else if ( path - > indexOf ( " /groups " ) > = 0 ) HueGroups ( path ) ;
else if ( path - > endsWith ( " /schedules " ) ) HueNotImplemented ( path ) ;
else if ( path - > endsWith ( " /sensors " ) ) HueNotImplemented ( path ) ;
else if ( path - > endsWith ( " /scenes " ) ) HueNotImplemented ( path ) ;
else if ( path - > endsWith ( " /rules " ) ) HueNotImplemented ( path ) ;
else HueGlobalConfig ( path ) ;
2017-02-21 17:14:33 +00:00
}
2018-10-10 21:21:44 +01:00
void HueWemoAddHandlers ( )
{
if ( EMUL_WEMO = = Settings . flag2 . emulation ) {
WebServer - > on ( " /upnp/control/basicevent1 " , HTTP_POST , HandleUpnpEvent ) ;
WebServer - > on ( " /eventservice.xml " , HandleUpnpService ) ;
WebServer - > on ( " /metainfoservice.xml " , HandleUpnpMetaService ) ;
WebServer - > on ( " /setup.xml " , HandleUpnpSetupWemo ) ;
}
if ( EMUL_HUE = = Settings . flag2 . emulation ) {
WebServer - > on ( " /description.xml " , HandleUpnpSetupHue ) ;
}
}
2018-04-10 10:45:53 +01:00
# endif // USE_WEBSERVER && USE_EMULATION