Added Alexa support
This commit is contained in:
parent
bdaa8f22b8
commit
255cef8685
3
TODO.txt
3
TODO.txt
@ -1,7 +1,6 @@
|
||||
captive portal for ap
|
||||
alexa support
|
||||
alexa settings
|
||||
ntp bug
|
||||
bri bug
|
||||
simple slide transition
|
||||
additional color picker field
|
||||
implement HSB slider option
|
||||
|
11
readme.md
11
readme.md
@ -16,7 +16,8 @@ Additions for V0.3 (nearly complete!)
|
||||
- Support for power pushbutton
|
||||
- Full OTA software update capability
|
||||
- Password protected OTA page for added security (OTA lock)
|
||||
- NTP and experimental analog clock function
|
||||
- Alexa smart home device server
|
||||
- (not working) NTP and experimental analog clock function
|
||||
|
||||
Compile settings:
|
||||
Board: WeMos D1 mini
|
||||
@ -66,6 +67,14 @@ Add one or multiple of the following parameters after the base url to change val
|
||||
("&I=<0-255>&I2=<0-255>" experimental individual LED range control)
|
||||
|
||||
|
||||
Licensed under the MIT license
|
||||
Uses libraries:
|
||||
ESP8266 Arduino Core
|
||||
WS2812FX by kitesurfer1404 (Aircoookie fork)
|
||||
Timezone library by JChristensen
|
||||
arduino-esp8266-alexa-multiple-wemo-switch by kakopappa
|
||||
|
||||
|
||||
Software update procedure:
|
||||
|
||||
Method 1: Reflash the new update source via USB.
|
||||
|
8
wled00/CallbackFunction.h
Normal file
8
wled00/CallbackFunction.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef CALLBACKFUNCTION_H
|
||||
#define CALLBACKFUNCTION_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
typedef void (*CallbackFunction) ();
|
||||
|
||||
#endif
|
201
wled00/Switch.cpp
Normal file
201
wled00/Switch.cpp
Normal file
@ -0,0 +1,201 @@
|
||||
#include "Switch.h"
|
||||
#include "CallbackFunction.h"
|
||||
|
||||
|
||||
|
||||
//<<constructor>>
|
||||
Switch::Switch(){
|
||||
Serial.println("default constructor called");
|
||||
}
|
||||
//Switch::Switch(String alexaInvokeName,unsigned int port){
|
||||
Switch::Switch(String alexaInvokeName, unsigned int port, CallbackFunction oncb, CallbackFunction offcb){
|
||||
uint32_t chipId = ESP.getChipId();
|
||||
char uuid[64];
|
||||
sprintf_P(uuid, PSTR("38323636-4558-4dda-9188-cda0e6%02x%02x%02x"),
|
||||
(uint16_t) ((chipId >> 16) & 0xff),
|
||||
(uint16_t) ((chipId >> 8) & 0xff),
|
||||
(uint16_t) chipId & 0xff);
|
||||
|
||||
serial = String(uuid);
|
||||
persistent_uuid = "Socket-1_0-" + serial+"-"+ String(port);
|
||||
|
||||
device_name = alexaInvokeName;
|
||||
localPort = port;
|
||||
onCallback = oncb;
|
||||
offCallback = offcb;
|
||||
|
||||
startWebServer();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//<<destructor>>
|
||||
Switch::~Switch(){/*nothing to destruct*/}
|
||||
|
||||
void Switch::serverLoop(){
|
||||
if (server != NULL) {
|
||||
server->handleClient();
|
||||
delay(1);
|
||||
}
|
||||
}
|
||||
|
||||
void Switch::startWebServer(){
|
||||
server = new ESP8266WebServer(localPort);
|
||||
|
||||
server->on("/", [&]() {
|
||||
handleRoot();
|
||||
});
|
||||
|
||||
|
||||
server->on("/setup.xml", [&]() {
|
||||
handleSetupXml();
|
||||
});
|
||||
|
||||
server->on("/upnp/control/basicevent1", [&]() {
|
||||
handleUpnpControl();
|
||||
});
|
||||
|
||||
server->on("/eventservice.xml", [&]() {
|
||||
handleEventservice();
|
||||
});
|
||||
|
||||
//server->onNotFound(handleNotFound);
|
||||
server->begin();
|
||||
|
||||
Serial.println("WebServer started on port: ");
|
||||
Serial.println(localPort);
|
||||
}
|
||||
|
||||
void Switch::handleEventservice(){
|
||||
Serial.println(" ########## Responding to eventservice.xml ... ########\n");
|
||||
|
||||
String eventservice_xml = "<?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>"
|
||||
"<serviceStateTable>"
|
||||
"<stateVariable sendEvents=\"yes\">"
|
||||
"<name>BinaryState</name>"
|
||||
"<dataType>Boolean</dataType>"
|
||||
"<defaultValue>0</defaultValue>"
|
||||
"</stateVariable>"
|
||||
"<stateVariable sendEvents=\"yes\">"
|
||||
"<name>level</name>"
|
||||
"<dataType>string</dataType>"
|
||||
"<defaultValue>0</defaultValue>"
|
||||
"</stateVariable>"
|
||||
"</serviceStateTable>"
|
||||
"</action>"
|
||||
"</scpd>\r\n"
|
||||
"\r\n";
|
||||
|
||||
server->send(200, "text/plain", eventservice_xml.c_str());
|
||||
}
|
||||
|
||||
void Switch::handleUpnpControl(){
|
||||
Serial.println("########## Responding to /upnp/control/basicevent1 ... ##########");
|
||||
|
||||
//for (int x=0; x <= HTTP.args(); x++) {
|
||||
// Serial.println(HTTP.arg(x));
|
||||
//}
|
||||
|
||||
String request = server->arg(0);
|
||||
Serial.print("request:");
|
||||
Serial.println(request);
|
||||
|
||||
if(request.indexOf("<BinaryState>1</BinaryState>") > 0) {
|
||||
Serial.println("Got Turn on request");
|
||||
onCallback();
|
||||
}
|
||||
|
||||
if(request.indexOf("<BinaryState>0</BinaryState>") > 0) {
|
||||
Serial.println("Got Turn off request");
|
||||
offCallback();
|
||||
}
|
||||
|
||||
server->send(200, "text/plain", "");
|
||||
}
|
||||
|
||||
void Switch::handleRoot(){
|
||||
server->send(200, "text/plain", "You should tell Alexa to discover devices");
|
||||
}
|
||||
|
||||
void Switch::handleSetupXml(){
|
||||
Serial.println(" ########## Responding to setup.xml ... ########\n");
|
||||
|
||||
IPAddress localIP = WiFi.localIP();
|
||||
char s[16];
|
||||
sprintf(s, "%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]);
|
||||
|
||||
String setup_xml = "<?xml version=\"1.0\"?>"
|
||||
"<root>"
|
||||
"<device>"
|
||||
"<deviceType>urn:Belkin:device:controllee:1</deviceType>"
|
||||
"<friendlyName>"+ device_name +"</friendlyName>"
|
||||
"<manufacturer>Belkin International Inc.</manufacturer>"
|
||||
"<modelName>Emulated Socket</modelName>"
|
||||
"<modelNumber>3.1415</modelNumber>"
|
||||
"<UDN>uuid:"+ persistent_uuid +"</UDN>"
|
||||
"<serialNumber>221517K0101769</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>"
|
||||
"</serviceList>"
|
||||
"</device>"
|
||||
"</root>\r\n"
|
||||
"\r\n";
|
||||
|
||||
server->send(200, "text/xml", setup_xml.c_str());
|
||||
|
||||
Serial.print("Sending :");
|
||||
Serial.println(setup_xml);
|
||||
}
|
||||
|
||||
String Switch::getAlexaInvokeName() {
|
||||
return device_name;
|
||||
}
|
||||
|
||||
void Switch::respondToSearch(IPAddress& senderIP, unsigned int senderPort) {
|
||||
Serial.println("");
|
||||
Serial.print("Sending response to ");
|
||||
Serial.println(senderIP);
|
||||
Serial.print("Port : ");
|
||||
Serial.println(senderPort);
|
||||
|
||||
IPAddress localIP = WiFi.localIP();
|
||||
char s[16];
|
||||
sprintf(s, "%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]);
|
||||
|
||||
String response =
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"CACHE-CONTROL: max-age=86400\r\n"
|
||||
"DATE: Sat, 26 Nov 2016 04:56:29 GMT\r\n"
|
||||
"EXT:\r\n"
|
||||
"LOCATION: http://" + String(s) + ":" + String(localPort) + "/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: urn:Belkin:device:**\r\n"
|
||||
"USN: uuid:" + persistent_uuid + "::urn:Belkin:device:**\r\n"
|
||||
"X-User-Agent: redsonic\r\n\r\n";
|
||||
|
||||
UDP.beginPacket(senderIP, senderPort);
|
||||
UDP.write(response.c_str());
|
||||
UDP.endPacket();
|
||||
|
||||
Serial.println("Response sent !");
|
||||
}
|
36
wled00/Switch.h
Normal file
36
wled00/Switch.h
Normal file
@ -0,0 +1,36 @@
|
||||
#ifndef SWITCH_H
|
||||
#define SWITCH_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <WiFiUDP.h>
|
||||
#include "CallbackFunction.h"
|
||||
|
||||
|
||||
class Switch {
|
||||
private:
|
||||
ESP8266WebServer *server = NULL;
|
||||
WiFiUDP UDP;
|
||||
String serial;
|
||||
String persistent_uuid;
|
||||
String device_name;
|
||||
unsigned int localPort;
|
||||
CallbackFunction onCallback;
|
||||
CallbackFunction offCallback;
|
||||
|
||||
void startWebServer();
|
||||
void handleEventservice();
|
||||
void handleUpnpControl();
|
||||
void handleRoot();
|
||||
void handleSetupXml();
|
||||
public:
|
||||
Switch();
|
||||
Switch(String alexaInvokeName, unsigned int port, CallbackFunction onCallback, CallbackFunction offCallback);
|
||||
~Switch();
|
||||
String getAlexaInvokeName();
|
||||
void serverLoop();
|
||||
void respondToSearch(IPAddress& senderIP, unsigned int senderPort);
|
||||
};
|
||||
|
||||
#endif
|
86
wled00/UpnpBroadcastResponder.cpp
Normal file
86
wled00/UpnpBroadcastResponder.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
#include "UpnpBroadcastResponder.h"
|
||||
#include "Switch.h"
|
||||
#include <functional>
|
||||
|
||||
// Multicast declarations
|
||||
IPAddress ipMulti(239, 255, 255, 250);
|
||||
const unsigned int portMulti = 1900;
|
||||
char packetBuffer[512];
|
||||
|
||||
#define MAX_SWITCHES 14
|
||||
Switch switches[MAX_SWITCHES] = {};
|
||||
int numOfSwitchs = 0;
|
||||
|
||||
//#define numOfSwitchs (sizeof(switches)/sizeof(Switch)) //array size
|
||||
|
||||
//<<constructor>>
|
||||
UpnpBroadcastResponder::UpnpBroadcastResponder(){
|
||||
|
||||
}
|
||||
|
||||
//<<destructor>>
|
||||
UpnpBroadcastResponder::~UpnpBroadcastResponder(){/*nothing to destruct*/}
|
||||
|
||||
bool UpnpBroadcastResponder::beginUdpMulticast(){
|
||||
boolean state = false;
|
||||
|
||||
Serial.println("Begin multicast ..");
|
||||
|
||||
if(UDP.beginMulticast(WiFi.localIP(), ipMulti, portMulti)) {
|
||||
Serial.print("Udp multicast server started at ");
|
||||
Serial.print(ipMulti);
|
||||
Serial.print(":");
|
||||
Serial.println(portMulti);
|
||||
|
||||
state = true;
|
||||
}
|
||||
else{
|
||||
Serial.println("Connection failed");
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
//Switch *ptrArray;
|
||||
|
||||
void UpnpBroadcastResponder::addDevice(Switch& device) {
|
||||
Serial.print("Adding switch : ");
|
||||
Serial.print(device.getAlexaInvokeName());
|
||||
Serial.print(" index : ");
|
||||
Serial.println(numOfSwitchs);
|
||||
|
||||
switches[numOfSwitchs] = device;
|
||||
numOfSwitchs++;
|
||||
}
|
||||
|
||||
void UpnpBroadcastResponder::serverLoop(){
|
||||
int packetSize = UDP.parsePacket();
|
||||
if (packetSize <= 0)
|
||||
return;
|
||||
|
||||
IPAddress senderIP = UDP.remoteIP();
|
||||
unsigned int senderPort = UDP.remotePort();
|
||||
|
||||
// read the packet into the buffer
|
||||
UDP.read(packetBuffer, packetSize);
|
||||
|
||||
// check if this is a M-SEARCH for WeMo device
|
||||
String request = String((char *)packetBuffer);
|
||||
|
||||
if(request.indexOf('M-SEARCH') > 0) {
|
||||
if(request.indexOf("urn:Belkin:device:**") > 0) {
|
||||
Serial.println("Got UDP Belkin Request..");
|
||||
|
||||
// int arrSize = sizeof(switchs) / sizeof(Switch);
|
||||
|
||||
for(int n = 0; n < numOfSwitchs; n++) {
|
||||
Switch &sw = switches[n];
|
||||
|
||||
if (&sw != NULL) {
|
||||
sw.respondToSearch(senderIP, senderPort);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
20
wled00/UpnpBroadcastResponder.h
Normal file
20
wled00/UpnpBroadcastResponder.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef UPNPBROADCASTRESPONDER_H
|
||||
#define UPNPBROADCASTRESPONDER_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <WiFiUDP.h>
|
||||
#include "Switch.h"
|
||||
|
||||
class UpnpBroadcastResponder {
|
||||
private:
|
||||
WiFiUDP UDP;
|
||||
public:
|
||||
UpnpBroadcastResponder();
|
||||
~UpnpBroadcastResponder();
|
||||
bool beginUdpMulticast();
|
||||
void serverLoop();
|
||||
void addDevice(Switch& device);
|
||||
};
|
||||
|
||||
#endif
|
@ -17,6 +17,9 @@
|
||||
#include <Timezone.h>
|
||||
#include "htmls00.h"
|
||||
#include "htmls01.h"
|
||||
#include "switch.h"
|
||||
#include "UpnpBroadcastResponder.h"
|
||||
#include "CallbackFunction.h"
|
||||
|
||||
//to toggle usb serial debug (un)comment following line
|
||||
#define DEBUG
|
||||
@ -37,7 +40,7 @@
|
||||
* @author Christian Schwinne
|
||||
*/
|
||||
//Hardware-settings (only changeble via code)
|
||||
#define LEDCOUNT 11
|
||||
#define LEDCOUNT 9
|
||||
#define MAXDIRECT 52 //for direct access like arls, should be >= LEDCOUNT
|
||||
uint8_t buttonPin = 0; //needs pull-up
|
||||
uint8_t auxPin = 15; //use e.g. for external relay
|
||||
@ -99,9 +102,14 @@ boolean overlayReverse = true;
|
||||
uint8_t overlaySpeed = 200;
|
||||
boolean useGammaCorrectionBri = true;
|
||||
boolean useGammaCorrectionRGB = true;
|
||||
int arlsOffset = -21; //10: -22 assuming arls52
|
||||
int arlsOffset = -22; //10: -22 assuming arls52
|
||||
boolean realtimeEnabled = true;
|
||||
|
||||
//alexa
|
||||
boolean alexaEnabled = true;
|
||||
String alexaInvocationName = "Schloss";
|
||||
boolean alexaNotify = false;
|
||||
|
||||
double transitionResolution = 0.011;
|
||||
|
||||
//Internal vars
|
||||
@ -153,6 +161,10 @@ uint8_t auxTime = 0;
|
||||
unsigned long auxStartTime;
|
||||
boolean auxActive, auxActiveBefore;
|
||||
|
||||
//alexa
|
||||
Switch *alexa = NULL;
|
||||
UpnpBroadcastResponder upnpBroadcastResponder;
|
||||
|
||||
ESP8266WebServer server(80);
|
||||
ESP8266HTTPUpdateServer httpUpdater;
|
||||
WiFiUDP notifierUdp;
|
||||
@ -220,6 +232,7 @@ void loop() {
|
||||
handleButton();
|
||||
handleNetworkTime();
|
||||
handleOverlays();
|
||||
handleAlexa();
|
||||
strip.service();
|
||||
|
||||
//DEBUG
|
||||
|
@ -305,7 +305,17 @@ boolean handleSet(String req)
|
||||
}
|
||||
}
|
||||
}
|
||||
XML_response();
|
||||
pos = req.indexOf("IN");
|
||||
if (pos < 1)
|
||||
{
|
||||
XML_response();
|
||||
}
|
||||
pos = req.indexOf("NN");
|
||||
if (pos > 0)
|
||||
{
|
||||
colorUpdated(5);
|
||||
return true;
|
||||
}
|
||||
if (effectUpdated)
|
||||
{
|
||||
colorUpdated(6);
|
||||
|
@ -131,6 +131,8 @@ void wledInit()
|
||||
DEBUG_PRINTLN("HTTP server started");
|
||||
// Add service to MDNS
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
//Init alexa service
|
||||
alexaInit();
|
||||
// Initialize NeoPixel Strip
|
||||
strip.init();
|
||||
strip.setMode(effectCurrent);
|
||||
|
@ -35,7 +35,7 @@ void setLedsStandard()
|
||||
|
||||
void colorUpdated(int callMode)
|
||||
{
|
||||
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (no not.) 6: fx changed
|
||||
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (no not.) (NN)6: fx changed
|
||||
if (col[0] == col_it[0] && col[1] == col_it[1] && col[2] == col_it[2] && bri == bri_it)
|
||||
{
|
||||
if (callMode == 6) notify(6);
|
||||
|
44
wled00/wled12_alexa.ino
Normal file
44
wled00/wled12_alexa.ino
Normal file
@ -0,0 +1,44 @@
|
||||
void alexaOn();
|
||||
void alexaOff();
|
||||
|
||||
void alexaInit()
|
||||
{
|
||||
if (alexaEnabled && WiFi.status() == WL_CONNECTED)
|
||||
{
|
||||
upnpBroadcastResponder.beginUdpMulticast();
|
||||
alexa = new Switch(alexaInvocationName, 81, alexaOn, alexaOff);
|
||||
upnpBroadcastResponder.addDevice(*alexa);
|
||||
}
|
||||
}
|
||||
|
||||
void handleAlexa()
|
||||
{
|
||||
if (alexaEnabled && WiFi.status() == WL_CONNECTED)
|
||||
{
|
||||
upnpBroadcastResponder.serverLoop();
|
||||
alexa->serverLoop();
|
||||
}
|
||||
}
|
||||
|
||||
void alexaOn()
|
||||
{
|
||||
if (alexaNotify)
|
||||
{
|
||||
handleSet("win&T=1&IN");
|
||||
} else
|
||||
{
|
||||
handleSet("win&T=1&NN&IN");
|
||||
}
|
||||
}
|
||||
|
||||
void alexaOff()
|
||||
{
|
||||
if (alexaNotify)
|
||||
{
|
||||
handleSet("win&T=0&IN");
|
||||
} else
|
||||
{
|
||||
handleSet("win&T=0&NN&IN");
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user