I was able to get this working with my Echo Plus Gen 2 and my Skytech 1001TH_A fireplace remote. Here is what is addressed:
1) Able to discover and avoid the 'function not available' error after discovery
2) The original script was adding a zero to the end of the on and off codes, which is now addressed
3) Turns out my Skytech has a safety mechanism whereby a heartbeat signal is sent every 15 minutes. There is a 'fireplace is on' heartbeat and 'fireplace is off' heartbeat. This is important since if I turn on the fireplace with Echo, the fireplace will send an 'off' heartbeat (since the remote still thinks the fireplace is off) within 15 minutes and turn off the fireplace. I solved this by figuring out the the heartbeat codes using the same method described in the video, and then added code to send those codes (based on fireplace state) every X minutes. The only downside is that you must remove batteries from the remote since only once device can control the fireplace at a time due to the heartbeat codes. Note -before adding this code, I tried just removing batteries from the remote, but after 2 hours of not receiving the heartbeat, the fireplace will turn off, so this approach is necessary.
Here is my code:
include
include
include
include
include
void prepareIds();
boolean connectWifi();
boolean connectUDP();
void startHttpServer();
void turnOnDevice();
void turnOffDevice();
void sendSwitchState(); //Added for Echo Plus Gen2
const char* ssid = "*";
const char password = "";
const char deviceName = "Fireplace";
const int ledPin = 16;
const char onCode = "00000000101100010001110110001111";
const char onHeartbeatCode = "00000000101100010001110110000111";
const char offCode = "00000000101100010001110110001000";
const char* offHeartbeatCode = "00000000101100010001110110000000";
const int transmitPin = 12;
const int heartbeatMinutes = 10; //Time between heartbeat checks from fireplace remote to receiver in minutes (remote is 15 min)
const int pulseLength = 390;
boolean switchState = false; //Added for Echo Plus Gen2
static const RCSwitch::Protocol customprotocol = { 390, { 0, 31 }, { 1, 3 }, { 3, 1 }, false }; //Added to address trailing zero and timing issue. First paramater should be same as pulsewidth
RCSwitch mySwitch = RCSwitch();
unsigned int localPort = 1900; // local port to listen on
WiFiUDP UDP;
boolean udpConnected = false;
IPAddress ipMulti(239, 255, 255, 250);
unsigned int portMulti = 1900; // local port to listen on
ESP8266WebServer HTTP(80);
boolean wifiConnected = false;
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,
// only proceed if wifi connection successful
if(wifiConnected){
udpConnected = connectUDP();
if (udpConnected){
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, HIGH);
mySwitch.enableTransmit(transmitPin);
mySwitch.setProtocol(customprotocol); //Added to address trailing zero and timing issue
mySwitch.setPulseLength(pulseLength);
startHttpServer();
}
}
}
void loop() {
HTTP.handleClient();
delay(1);
// if there's data available, read a packet
// check if the WiFi and UDP connections were successful
if(wifiConnected){
if(udpConnected){
// if there’s data available, read a packet
int packetSize = UDP.parsePacket();
if(packetSize) {
Serial.println("");
Serial.print("Received packet of size ");
Serial.println(packetSize);
Serial.print("From ");
IPAddress remote = UDP.remoteIP();
for (int i =0; i < 4; i++) {
Serial.print(remote[i], DEC);
if (i < 3) {
Serial.print(".");
}
}
Serial.print(", port ");
Serial.println(UDP.remotePort());
int len = UDP.read(packetBuffer, 255);
if (len > 0) {
packetBuffer[len] = 0;
}
String request = packetBuffer;
// Issue https://github.com/kakopappa/arduino-esp8266-alexa-wemo-switch/issues/24 fix
if(request.indexOf("M-SEARCH") >= 0) {
// Issue https://github.com/kakopappa/arduino-esp8266-alexa-multiple-wemo-switch/issues/22 fix
//if(request.indexOf("urn:Belkin:device:**") > 0) {
if((request.indexOf("urn:Belkin:device:**") > 0) || (request.indexOf("ssdp:all") > 0) || (request.indexOf("upnp:rootdevice") > 0)) {
Serial.println("Responding to search request ...");
Serial.println(request); //For debug
respondToSearch();
}
}
}
delay(10);
//Send heartbeat signal when fireplace is on or off at heartbeatMinutes interval
const unsigned long heartbeatTime = heartbeatMinutes * 60 * 1000UL;
static unsigned long lastSampleTime = 0 - heartbeatTime; // initialize such that a reading is due the first time through loop()
unsigned long now = millis();
if (now - lastSampleTime >= heartbeatTime)
{
lastSampleTime += heartbeatTime;
if(switchState)
{
mySwitch.send(onHeartbeatCode);
}
else
{
mySwitch.send(offHeartbeatCode);
}
}
}
// connect to wifi – returns true if successful or false if not
boolean connectWifi(){
boolean state = true;
int i = 0;
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println("");
Serial.println("Connecting to WiFi");
// Wait for connection
Serial.print("Connecting ...");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
if (i > 10){
state = false;
break;
}
i++;
}
if (state){
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
else {
Serial.println("");
Serial.println("Connection failed.");
}
return state;
}
boolean connectUDP(){
boolean state = false;
Serial.println("");
Serial.println("Connecting to UDP");
I was able to get this working with my Echo Plus Gen 2 and my Skytech 1001TH_A fireplace remote. Here is what is addressed:
1) Able to discover and avoid the 'function not available' error after discovery 2) The original script was adding a zero to the end of the on and off codes, which is now addressed 3) Turns out my Skytech has a safety mechanism whereby a heartbeat signal is sent every 15 minutes. There is a 'fireplace is on' heartbeat and 'fireplace is off' heartbeat. This is important since if I turn on the fireplace with Echo, the fireplace will send an 'off' heartbeat (since the remote still thinks the fireplace is off) within 15 minutes and turn off the fireplace. I solved this by figuring out the the heartbeat codes using the same method described in the video, and then added code to send those codes (based on fireplace state) every X minutes. The only downside is that you must remove batteries from the remote since only once device can control the fireplace at a time due to the heartbeat codes. Note -before adding this code, I tried just removing batteries from the remote, but after 2 hours of not receiving the heartbeat, the fireplace will turn off, so this approach is necessary.
Here is my code:
include
include
include
include
include
void prepareIds(); boolean connectWifi(); boolean connectUDP(); void startHttpServer(); void turnOnDevice(); void turnOffDevice(); void sendSwitchState(); //Added for Echo Plus Gen2
const char* ssid = "*"; const char password = "";
const char deviceName = "Fireplace"; const int ledPin = 16; const char onCode = "00000000101100010001110110001111"; const char onHeartbeatCode = "00000000101100010001110110000111"; const char offCode = "00000000101100010001110110001000"; const char* offHeartbeatCode = "00000000101100010001110110000000"; const int transmitPin = 12; const int heartbeatMinutes = 10; //Time between heartbeat checks from fireplace remote to receiver in minutes (remote is 15 min) const int pulseLength = 390; boolean switchState = false; //Added for Echo Plus Gen2 static const RCSwitch::Protocol customprotocol = { 390, { 0, 31 }, { 1, 3 }, { 3, 1 }, false }; //Added to address trailing zero and timing issue. First paramater should be same as pulsewidth
RCSwitch mySwitch = RCSwitch();
unsigned int localPort = 1900; // local port to listen on
WiFiUDP UDP; boolean udpConnected = false; IPAddress ipMulti(239, 255, 255, 250); unsigned int portMulti = 1900; // local port to listen on
ESP8266WebServer HTTP(80);
boolean wifiConnected = false;
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,
String serial; String persistent_uuid;
boolean cannotConnectToWifi = false;
void setup() { Serial.begin(115200);
prepareIds();
// Initialise wifi connection wifiConnected = connectWifi();
// only proceed if wifi connection successful if(wifiConnected){ udpConnected = connectUDP();
} }
void loop() {
HTTP.handleClient(); delay(1);
// if there's data available, read a packet // check if the WiFi and UDP connections were successful if(wifiConnected){ if(udpConnected){ // if there’s data available, read a packet int packetSize = UDP.parsePacket();
} else { // Turn on/off to indicate cannot connect .. Serial.println("Cannot Connect ..."); Serial.println("wifiConnected:"); Serial.println(wifiConnected); Serial.println("udpConnected:"); Serial.println(udpConnected); } }
void prepareIds() { 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; }
void respondToSearch() { Serial.println(""); Serial.print("Sending response to "); Serial.println(UDP.remoteIP()); Serial.print("Port : "); Serial.println(UDP.remotePort());
}
void startHttpServer() { HTTP.on("/index.html", HTTP_GET, [](){ Serial.println("Got Request index.html ...\n"); HTTP.send(200, "text/plain", "Hello World!"); });
}
// connect to wifi – returns true if successful or false if not boolean connectWifi(){ boolean state = true; int i = 0;
WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); Serial.println(""); Serial.println("Connecting to WiFi");
// Wait for connection Serial.print("Connecting ..."); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); if (i > 10){ state = false; break; } i++; }
if (state){ Serial.println(""); Serial.print("Connected to "); Serial.println(ssid); Serial.print("IP address: "); Serial.println(WiFi.localIP()); } else { Serial.println(""); Serial.println("Connection failed."); }
return state; }
boolean connectUDP(){ boolean state = false;
Serial.println(""); Serial.println("Connecting to UDP");
if(UDP.beginMulticast(WiFi.localIP(), ipMulti, portMulti)) { Serial.println("Connection successful"); state = true; } else{ Serial.println("Connection failed"); }
return state; }
void turnOnDevice() { digitalWrite(ledPin, false); mySwitch.send(onCode); switchState = true;
//Added for Echo Plus Gen2 String body = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
"<u:SetBinaryStateResponse xmlns:u=\"urn:Belkin:service:basicevent:1\">\r\n"
"1 \r\n"
"</u:SetBinaryStateResponse>\r\n"
"</s:Body> </s:Envelope>";
HTTP.send(200, "text/xml", body.c_str());
Serial.print("Sending :"); Serial.println(body);
}
void turnOffDevice() { digitalWrite(ledPin, true); mySwitch.send(offCode); switchState = false;
//Added for Echo Plus Gen2 String body = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
"<u:SetBinaryStateResponse xmlns:u=\"urn:Belkin:service:basicevent:1\">\r\n"
"0 \r\n"
"</u:SetBinaryStateResponse>\r\n"
"</s:Body> </s:Envelope>";
HTTP.send(200, "text/xml", body.c_str());
Serial.print("Sending :"); Serial.println(body);
}
//Added for Echo Plus Gen2 void sendSwitchState() {
String body = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
"<u:GetBinaryStateResponse xmlns:u=\"urn:Belkin:service:basicevent:1\">\r\n"
"";
body += (switchState ? "1" : "0");
body += "\r\n" "</u:GetBinaryStateResponse>\r\n" "</s:Body> </s:Envelope>\r\n";
HTTP.send(200, "text/xml", body.c_str()); }