From e326a2bfe452219a4a662a5775234ed8d5635ac0 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Tue, 9 Jan 2018 11:55:07 +0100 Subject: [PATCH] Alexa Brightness Control now working (server now emulates Philips hue to Alexa instead of Belkin Wemo) (you can now say "Alexa, turn my light to 75%") --- wled00/wled00.ino | 4 +- wled00/wled05_init.ino | 4 + wled00/wled12_alexa.ino | 184 ++++++++++++++++------------------------ 3 files changed, 80 insertions(+), 112 deletions(-) diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 2af260b3..3aa0a80c 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -22,7 +22,7 @@ #include "WS2812FX.h" //version in format yymmddb (b = daily build) -#define VERSION 1712310 +#define VERSION 1801091 //AP and OTA default passwords (change them!) String appass = "wled1234"; @@ -40,7 +40,7 @@ String otapass = "wledota"; //#define USEFS //to toggle usb serial debug (un)comment following line -//#define DEBUG +#define DEBUG //Hardware-settings (only changeble via code) #define LEDCOUNT 255 //maximum, exact count set-able via settings diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index df3db00b..43b10ab5 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -156,7 +156,11 @@ void wledInit() } //called when the url is not defined here, ajax-in; get-settings server.onNotFound([](){ + DEBUG_PRINTLN("Not-Found HTTP call:"); + DEBUG_PRINTLN("URI: " + server.uri()); + DEBUG_PRINTLN("Body: " + server.arg(0)); if(!handleSet(server.uri())){ + if(!handleAlexaApiCall(server.uri(),server.arg(0))) server.send(404, "text/plain", "FileNotFound"); } }); diff --git a/wled00/wled12_alexa.ino b/wled00/wled12_alexa.ino index 907cc79c..b104b628 100644 --- a/wled00/wled12_alexa.ino +++ b/wled00/wled12_alexa.ino @@ -30,8 +30,8 @@ void handleAlexa() String request = packetBuffer; if(request.indexOf("M-SEARCH") >= 0) { - if((request.indexOf("urn:Belkin:device:**") > 0) || (request.indexOf("ssdp:all") > 0) || (request.indexOf("upnp:rootdevice") > 0)) { - Serial.println("Responding to search request ..."); + if(request.indexOf("upnp:rootdevice") > 0) { + Serial.println("Responding search req..."); respondToSearch(); } } @@ -50,12 +50,7 @@ void alexaOn() applyMacro(alexaOnMacro); } - String body = - "\r\n" - "\r\n" - "1\r\n" - "\r\n" - " "; + String body = "[{\"success\":{\"/lights/1/state/on\":true}}]"; server.send(200, "text/xml", body.c_str()); @@ -73,23 +68,22 @@ void alexaOff() applyMacro(alexaOffMacro); } - String body = - "\r\n" - "\r\n" - "0\r\n" - "\r\n" - " "; + String body = "[{\"success\":{\"/lights/1/state/on\":false}}]"; - server.send(200, "text/xml", body.c_str()); + server.send(200, "application/json", body.c_str()); - Serial.print("Sending :"); + Serial.print("Sending:"); Serial.println(body); } -void alexaDim(uint8_t bri) +void alexaDim(uint8_t briL) { + String body = "[{\"success\":{\"/lights/1/state/bri\":"+ String(briL) +"}}]"; + + server.send(200, "application/json", body.c_str()); + String ct = (alexaNotify)?"win&IN&A=":"win&NN&IN&A="; - ct = ct + bri; + ct = ct + (briL+1); handleSet(ct); } @@ -101,7 +95,7 @@ void prepareIds() { void respondToSearch() { Serial.println(""); - Serial.print("Sending response to "); + Serial.print("Send resp to "); Serial.println(UDP.remoteIP()); Serial.print("Port : "); Serial.println(UDP.remotePort()); @@ -113,7 +107,7 @@ void respondToSearch() { String response = "HTTP/1.1 200 OK\r\n" "EXT:\r\n" - "CACHE-CONTROL: max-age=%u\r\n" // SSDP_INTERVAL + "CACHE-CONTROL: max-age=100\r\n" // SSDP_INTERVAL "LOCATION: http://"+ String(s) +":80/description.xml\r\n" "SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/1.17.0\r\n" // _modelName, _modelNumber "hue-bridgeid: "+ escapedMac +"\r\n" @@ -125,85 +119,13 @@ void respondToSearch() { UDP.write(response.c_str()); UDP.endPacket(); - Serial.println("Response sent !"); + Serial.println("Response sent!"); } void alexaInitPages() { - server.on("/upnp/control/basicevent1", HTTP_POST, []() { - Serial.println("########## Responding to /upnp/control/basicevent1 ... ##########"); - - String request = server.arg(0); - Serial.print("request:"); - Serial.println(request); - - if(request.indexOf("SetBinaryState") >= 0) { - if(request.indexOf("1") >= 0) { - Serial.println("Got Turn on request"); - alexaOn(); - } - - if(request.indexOf("0") >= 0) { - Serial.println("Got Turn off request"); - alexaOff(); - } - } - - if(request.indexOf("GetBinaryState") >= 0) { - Serial.println("Got binary state request"); - sendState(); - } - - server.send(200, "text/plain", ""); - }); - - server.on("/eventservice.xml", HTTP_GET, [](){ - Serial.println(" ########## Responding to eventservice.xml ... ########\n"); - - String eventservice_xml = "" - "" - "" - "SetBinaryState" - "" - "" - "" - "BinaryState" - "BinaryState" - "in" - "" - "" - "" - "" - "GetBinaryState" - "" - "" - "" - "BinaryState" - "BinaryState" - "out" - "" - "" - "" - "" - "" - "" - "BinaryState" - "Boolean" - "0" - "" - "" - "level" - "string" - "0" - "" - "" - "\r\n" - "\r\n"; - - server.send(200, "text/plain", eventservice_xml.c_str()); - }); server.on("/description.xml", HTTP_GET, [](){ - Serial.println(" ########## Responding to setup.xml ... ########\n"); + Serial.println(" # Responding to description.xml ... #\n"); IPAddress localIP = WiFi.localIP(); char s[16]; @@ -275,6 +197,64 @@ void alexaInitPages() { }); } +String boolString(bool st) +{ + return (st)?"true":"false"; +} + +String briForHue(int realBri) +{ + realBri--; + if (realBri < 0) realBri = 0; + return String(realBri); +} + +boolean handleAlexaApiCall(String req, String body) //basic implementation of Philips hue api functions needed for basic Alexa control +{ + 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"); + server.send(200, "application/json", "{\"manufacturername\":\"OpenSource\",\"modelid\":\"LST001\",\"name\":\""+ 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\"}"); + + return true; + } + if (req.indexOf("lights/2") > 0) //client wants pointless light info + { + DEBUG_PRINTLN("l2"); + server.send(200, "application/json", "{\"manufacturername\":\"OpenSource\",\"modelid\":\"LST001\",\"name\":\""+ 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\":\"3\"}"); + + return true; + } + if (req.indexOf("lights") > 0) //client wants all lights + { + DEBUG_PRINTLN("lAll"); + server.send(200, "application/json", "{\"1\":{\"type\":\"Extended color light\",\"manufacturername\":\"OpenSource\",\"swversion\":\"0.1\",\"name\":\""+ alexaInvocationName +"\",\"uniqueid\":\""+ WiFi.macAddress() +"-1\",\"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}},\"1\":{\"type\":\"Extended color light\",\"manufacturername\":\"OpenSource\",\"swversion\":\"0.1\",\"name\":\""+ 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}}}"); + + return true; + } + + //we dont care about other api commands at this time and send empty JSON + server.send(200, "application/json", "{}"); + return true; +} boolean connectUDP(){ boolean state = false; @@ -293,19 +273,3 @@ boolean connectUDP(){ return state; } -void sendState() { - - String body = - "\r\n" - "\r\n" - ""; - - body += ((bri>0) ? "1" : "0"); - - body += "\r\n" - "\r\n" - " \r\n"; - - server.send(200, "text/xml", body.c_str()); -} -