2018-01-09 14:21:37 +01:00
/*
* Alexa Voice On / Off / Brightness Control . Emulates a Philips Hue bridge to Alexa .
*
* This was put together from these two excellent projects :
* https : //github.com/kakopappa/arduino-esp8266-alexa-wemo-switch
* https : //github.com/probonopd/ESP8266HueEmulator
*/
2018-11-01 15:36:13 +01:00
void prepareIds ( ) {
escapedMac = WiFi . macAddress ( ) ;
escapedMac . replace ( " : " , " " ) ;
escapedMac . toLowerCase ( ) ;
}
2018-01-10 00:37:45 +01:00
2018-11-01 15:36:13 +01:00
# ifndef WLED_DISABLE_ALEXA
2017-02-21 23:59:47 +01:00
void alexaInit ( )
{
if ( alexaEnabled & & WiFi . status ( ) = = WL_CONNECTED )
{
2018-11-09 17:00:36 +01:00
alexaUdpConnected = connectUDP ( ) ;
2018-01-07 22:52:48 +01:00
2018-11-09 17:00:36 +01:00
if ( alexaUdpConnected ) alexaInitPages ( ) ;
2017-02-21 23:59:47 +01:00
}
}
void handleAlexa ( )
{
2018-11-09 17:00:36 +01:00
if ( ! alexaEnabled | | WiFi . status ( ) ! = WL_CONNECTED | | ! alexaUdpConnected ) return ;
// if there's data available, read a packet
int packetSize = alexaUDP . parsePacket ( ) ;
if ( packetSize < 1 ) return ;
IPAddress remote = alexaUDP . remoteIP ( ) ;
int len = alexaUDP . read ( obuf , 254 ) ;
if ( len > 0 ) obuf [ len ] = 0 ;
if ( strstr ( obuf , " M-SEARCH " ) > 0 ) {
if ( strstr ( obuf , " upnp:rootdevice " ) > 0 | | strstr ( obuf , " device:basic:1 " ) > 0 ) {
DEBUG_PRINTLN ( " Responding search req... " ) ;
respondToSearch ( ) ;
}
}
2017-02-21 23:59:47 +01:00
}
void alexaOn ( )
{
2018-03-14 00:25:54 +01:00
if ( macroAlexaOn = = 0 )
2017-02-21 23:59:47 +01:00
{
2018-09-15 17:29:01 +02:00
handleSet ( ( notifyAlexa ) ? " win&T=1&IN " : " win&T=1&NN&IN " ) ;
2017-02-21 23:59:47 +01:00
} else
{
2018-03-14 00:25:54 +01:00
applyMacro ( macroAlexaOn ) ;
2017-02-21 23:59:47 +01:00
}
2018-01-07 22:52:48 +01:00
2018-07-22 14:55:10 +02:00
server . send ( 200 , " application/json " , " [{ \" success \" :{ \" /lights/1/state/on \" :true}}] " ) ;
2017-02-21 23:59:47 +01:00
}
void alexaOff ( )
{
2018-03-14 00:25:54 +01:00
if ( macroAlexaOff = = 0 )
2017-02-21 23:59:47 +01:00
{
2018-09-15 17:29:01 +02:00
handleSet ( ( notifyAlexa ) ? " win&T=0&IN " : " win&T=0&NN&IN " ) ;
2017-02-21 23:59:47 +01:00
} else
{
2018-03-14 00:25:54 +01:00
applyMacro ( macroAlexaOff ) ;
2017-02-21 23:59:47 +01:00
}
2018-01-07 22:52:48 +01:00
2018-07-22 14:55:10 +02:00
server . send ( 200 , " application/json " , " [{ \" success \" :{ \" /lights/1/state/on \" :false}}] " ) ;
2017-02-21 23:59:47 +01:00
}
2018-03-14 13:16:28 +01:00
void alexaDim ( byte briL )
2017-12-28 00:37:13 +01:00
{
2018-07-22 14:55:10 +02:00
olen = 0 ;
oappend ( " [{ \" success \" :{ \" /lights/1/state/bri \" : " ) ;
oappendi ( briL ) ;
oappend ( " }}] " ) ;
2018-01-09 11:55:07 +01:00
2018-07-22 14:55:10 +02:00
server . send ( 200 , " application/json " , obuf ) ;
2018-01-09 11:55:07 +01:00
2018-09-15 17:29:01 +02:00
String ct = ( notifyAlexa ) ? " win&IN&A= " : " win&NN&IN&A= " ;
2018-02-20 22:29:48 +01:00
if ( briL < 255 )
{
ct = ct + ( briL + 1 ) ;
} else
{
ct = ct + ( 255 ) ;
}
2017-12-28 00:37:13 +01:00
handleSet ( ct ) ;
}
2018-01-07 22:52:48 +01:00
void respondToSearch ( ) {
2018-11-09 17:00:36 +01:00
DEBUG_PRINTLN ( " " ) ;
DEBUG_PRINT ( " Send resp to " ) ;
DEBUG_PRINTLN ( alexaUDP . remoteIP ( ) ) ;
DEBUG_PRINT ( " Port : " ) ;
DEBUG_PRINTLN ( alexaUDP . remotePort ( ) ) ;
2018-01-07 22:52:48 +01:00
2018-11-09 17:00:36 +01:00
IPAddress localIP = WiFi . localIP ( ) ;
char s [ 16 ] ;
sprintf ( s , " %d.%d.%d.%d " , localIP [ 0 ] , localIP [ 1 ] , localIP [ 2 ] , localIP [ 3 ] ) ;
2018-01-07 22:52:48 +01:00
2018-11-09 17:00:36 +01:00
olen = 0 ;
oappend (
" HTTP/1.1 200 OK \r \n "
" EXT: \r \n "
" CACHE-CONTROL: max-age=100 \r \n " // SSDP_INTERVAL
" LOCATION: http:// " ) ;
oappend ( s ) ;
oappend ( " :80/description.xml \r \n "
" SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/1.17.0 \r \n " // _modelName, _modelNumber
" hue-bridgeid: " ) ;
oappend ( ( char * ) escapedMac . c_str ( ) ) ;
oappend ( " \r \n "
" ST: urn:schemas-upnp-org:device:basic:1 \r \n " // _deviceType
" USN: uuid:2f402f80-da50-11e1-9b23- " ) ;
oappend ( ( char * ) escapedMac . c_str ( ) ) ;
oappend ( " ::upnp:rootdevice \r \n " // _uuid::_deviceType
" \r \n " ) ;
2018-01-07 22:52:48 +01:00
2018-11-09 17:00:36 +01:00
alexaUDP . beginPacket ( alexaUDP . remoteIP ( ) , alexaUDP . remotePort ( ) ) ;
# ifdef ARDUINO_ARCH_ESP32
alexaUDP . write ( ( byte * ) obuf , olen ) ;
# else
alexaUDP . write ( obuf ) ;
# endif
alexaUDP . endPacket ( ) ;
2018-01-07 22:52:48 +01:00
2018-11-09 17:00:36 +01:00
DEBUG_PRINTLN ( " Response sent! " ) ;
2018-01-07 22:52:48 +01:00
}
void alexaInitPages ( ) {
2018-11-09 17:00:36 +01:00
server . on ( " /description.xml " , HTTP_GET , [ ] ( ) {
DEBUG_PRINTLN ( " # Responding to description.xml ... # \n " ) ;
IPAddress localIP = WiFi . localIP ( ) ;
char s [ 16 ] ;
sprintf ( s , " %d.%d.%d.%d " , localIP [ 0 ] , localIP [ 1 ] , localIP [ 2 ] , localIP [ 3 ] ) ;
olen = 0 ;
oappend ( " <?xml version= \" 1.0 \" ?> "
2018-01-07 22:52:48 +01:00
" <root xmlns= \" urn:schemas-upnp-org:device-1-0 \" > "
" <specVersion><major>1</major><minor>0</minor></specVersion> "
2018-07-22 14:55:10 +02:00
" <URLBase>http:// " ) ;
2018-11-09 17:00:36 +01:00
oappend ( s ) ;
oappend ( " :80/</URLBase> "
2018-01-07 22:52:48 +01:00
" <device> "
2018-11-09 17:00:36 +01:00
" <deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType> "
" <friendlyName>Philips hue ( " ) ;
oappend ( s ) ;
oappend ( " )</friendlyName> "
" <manufacturer>Royal Philips Electronics</manufacturer> "
" <manufacturerURL>http://www.philips.com</manufacturerURL> "
" <modelDescription>Philips hue Personal Wireless Lighting</modelDescription> "
" <modelName>Philips hue bridge 2012</modelName> "
" <modelNumber>929000226503</modelNumber> "
" <modelURL>http://www.meethue.com</modelURL> "
" <serialNumber> " ) ;
oappend ( ( char * ) escapedMac . c_str ( ) ) ;
oappend ( " </serialNumber> "
" <UDN>uuid:2f402f80-da50-11e1-9b23- " ) ;
oappend ( ( char * ) escapedMac . c_str ( ) ) ;
oappend ( " </UDN> "
" <presentationURL>index.html</presentationURL> "
" <iconList> "
" <icon> "
" <mimetype>image/png</mimetype> "
" <height>48</height> "
" <width>48</width> "
" <depth>24</depth> "
" <url>hue_logo_0.png</url> "
" </icon> "
" <icon> "
" <mimetype>image/png</mimetype> "
" <height>120</height> "
" <width>120</width> "
" <depth>24</depth> "
" <url>hue_logo_3.png</url> "
" </icon> "
" </iconList> "
2018-01-07 22:52:48 +01:00
" </device> "
2018-07-22 14:55:10 +02:00
" </root> " ) ;
2018-01-07 22:52:48 +01:00
2018-11-09 17:00:36 +01:00
server . send ( 200 , " text/xml " , obuf ) ;
DEBUG_PRINTLN ( " Sending setup_xml " ) ;
} ) ;
2018-01-07 22:52:48 +01:00
2018-11-09 17:00:36 +01:00
// openHAB support
server . on ( " /on.html " , HTTP_GET , [ ] ( ) {
DEBUG_PRINTLN ( " on req " ) ;
server . send ( 200 , " text/plain " , " turned on " ) ;
alexaOn ( ) ;
} ) ;
server . on ( " /off.html " , HTTP_GET , [ ] ( ) {
DEBUG_PRINTLN ( " off req " ) ;
server . send ( 200 , " text/plain " , " turned off " ) ;
alexaOff ( ) ;
} ) ;
server . on ( " /status.html " , HTTP_GET , [ ] ( ) {
DEBUG_PRINTLN ( " Got status request " ) ;
char statrespone [ ] = " 0 " ;
if ( bri > 0 ) {
statrespone [ 0 ] = ' 1 ' ;
}
server . send ( 200 , " text/plain " , statrespone ) ;
} ) ;
2018-01-07 22:52:48 +01:00
}
2018-01-09 11:55:07 +01:00
String boolString ( bool st )
{
return ( st ) ? " true " : " false " ;
}
String briForHue ( int realBri )
{
realBri - - ;
if ( realBri < 0 ) realBri = 0 ;
return String ( realBri ) ;
}
2018-03-14 13:16:28 +01:00
bool handleAlexaApiCall ( String req , String body ) //basic implementation of Philips hue api functions needed for basic Alexa control
2018-01-09 11:55:07 +01:00
{
DEBUG_PRINTLN ( " AlexaApiCall " ) ;
if ( req . indexOf ( " api " ) < 0 ) return false ;
DEBUG_PRINTLN ( " ok " ) ;
if ( body . indexOf ( " devicetype " ) > 0 ) //client wants a hue api username, we dont care and give static
{
DEBUG_PRINTLN ( " devType " ) ;
server . send ( 200 , " application/json " , " [{ \" success \" :{ \" username \" : \" 2WLEDHardQrI3WHYTHoMcXHgEspsM8ZZRpSKtBQr \" }}] " ) ;
return true ;
}
if ( req . indexOf ( " state " ) > 0 ) //client wants to control light
{
DEBUG_PRINTLN ( " ls " ) ;
if ( body . indexOf ( " bri " ) > 0 ) { alexaDim ( body . substring ( body . indexOf ( " bri " ) + 5 ) . toInt ( ) ) ; return true ; }
if ( body . indexOf ( " false " ) > 0 ) { alexaOff ( ) ; return true ; }
alexaOn ( ) ;
return true ;
}
if ( req . indexOf ( " lights/1 " ) > 0 ) //client wants light info
{
DEBUG_PRINTLN ( " l1 " ) ;
2018-07-21 23:21:07 +02:00
server . send ( 200 , " application/json " , " { \" manufacturername \" : \" OpenSource \" , \" modelid \" : \" LST001 \" , \" name \" : \" " + String ( alexaInvocationName ) + " \" , \" state \" :{ \" on \" : " + boolString ( bri ) + " , \" hue \" :0, \" bri \" : " + briForHue ( bri ) + " , \" sat \" :0, \" xy \" :[0.00000,0.00000], \" ct \" :500, \" alert \" : \" none \" , \" effect \" : \" none \" , \" colormode \" : \" hs \" , \" reachable \" :true}, \" swversion \" : \" 0.1 \" , \" type \" : \" Extended color light \" , \" uniqueid \" : \" 2 \" } " ) ;
2018-01-09 11:55:07 +01:00
return true ;
}
if ( req . indexOf ( " lights " ) > 0 ) //client wants all lights
{
DEBUG_PRINTLN ( " lAll " ) ;
2018-07-21 23:21:07 +02:00
server . send ( 200 , " application/json " , " { \" 1 \" :{ \" type \" : \" Extended color light \" , \" manufacturername \" : \" OpenSource \" , \" swversion \" : \" 0.1 \" , \" name \" : \" " + String ( alexaInvocationName ) + " \" , \" uniqueid \" : \" " + WiFi . macAddress ( ) + " -2 \" , \" modelid \" : \" LST001 \" , \" state \" :{ \" on \" : " + boolString ( bri ) + " , \" bri \" : " + briForHue ( bri ) + " , \" xy \" :[0.00000,0.00000], \" colormode \" : \" hs \" , \" effect \" : \" none \" , \" ct \" :500, \" hue \" :0, \" sat \" :0, \" alert \" : \" none \" , \" reachable \" :true}}} " ) ;
2018-01-09 11:55:07 +01:00
return true ;
}
//we dont care about other api commands at this time and send empty JSON
server . send ( 200 , " application/json " , " {} " ) ;
return true ;
}
2018-01-07 22:52:48 +01:00
2018-03-14 13:16:28 +01:00
bool connectUDP ( ) {
bool state = false ;
2018-01-07 22:52:48 +01:00
2018-01-09 14:21:37 +01:00
DEBUG_PRINTLN ( " " ) ;
DEBUG_PRINTLN ( " Con UDP " ) ;
2018-01-09 23:13:29 +01:00
# ifdef ARDUINO_ARCH_ESP32
2018-09-28 23:53:51 +02:00
if ( alexaUDP . beginMulticast ( ipMulti , portMulti ) )
2018-01-09 23:13:29 +01:00
# else
2018-09-28 23:53:51 +02:00
if ( alexaUDP . beginMulticast ( WiFi . localIP ( ) , ipMulti , portMulti ) )
2018-01-09 23:13:29 +01:00
# endif
{
2018-01-09 14:21:37 +01:00
DEBUG_PRINTLN ( " Con success " ) ;
2018-01-07 22:52:48 +01:00
state = true ;
}
else {
2018-01-09 14:21:37 +01:00
DEBUG_PRINTLN ( " Con failed " ) ;
2018-01-07 22:52:48 +01:00
}
return state ;
}
2018-11-09 17:00:36 +01:00
2018-11-01 15:36:13 +01:00
# else
2018-11-09 17:00:36 +01:00
void alexaInit ( ) { }
void handleAlexa ( ) { }
void alexaInitPages ( ) { }
bool handleAlexaApiCall ( String req , String body ) { return false ; }
2018-11-01 15:36:13 +01:00
# endif