alkalinecoffee / echo_fireplace

A simple ESP8266 script to control a gas fireplace with an Amazon Echo and RF transmitter
10 stars 1 forks source link

Solved: Echo Plus Gen 2, Trailing Zeros, and SkyTech Heartbeat Issue #6

Open stickboy135 opened 5 years ago

stickboy135 commented 5 years ago

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();

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);
    }
  }
}

} 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());

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: Fri, 15 Apr 2016 04:56:29 GMT\r\n"
     "EXT:\r\n"
     "LOCATION: http://" + String(s) + ":80/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";
*/

// Try changing to this if you have problems discovering
// https://github.com/kakopappa/arduino-esp8266-alexa-wemo-switch/issues/26
String response =
  "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"
  "LOCATION: http://" + String(s) + ":80/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: ssdp:all\r\n"
  "USN: uuid:" + persistent_uuid + "::upnp:rootdevice\r\n"
  "X-User-Agent: redsonic\r\n\r\n";

UDP.beginPacket(UDP.remoteIP(), UDP.remotePort());
UDP.write(response.c_str());
UDP.endPacket();

yield(); //Added for Echo Plus Gen2

Serial.println("Response sent !");
Serial.println(response);

}

void startHttpServer() { HTTP.on("/index.html", HTTP_GET, [](){ Serial.println("Got Request index.html ...\n"); HTTP.send(200, "text/plain", "Hello World!"); });

HTTP.on("/upnp/control/basicevent1", HTTP_POST, []() {
  Serial.println("########## Responding to  /upnp/control/basicevent1 ... ##########");

  String request = HTTP.arg(0);
  Serial.print("request:");
  Serial.println(request);

  if(request.indexOf("SetBinaryState") >= 0) {          //Added for Echo Plus Gen2
    if(request.indexOf("<BinaryState>1</BinaryState>") > 0) {
      Serial.println("Got Turn on request");
      turnOnDevice();
    }

    if(request.indexOf("<BinaryState>0</BinaryState>") > 0) {
      Serial.println("Got Turn off request");
      turnOffDevice();
    }
  }

  //Added for Echo Plus Gen2
  if(request.indexOf("GetBinaryState") >= 0) {
    Serial.println("Got binary state request");
    sendSwitchState();
  }

  HTTP.send(200, "text/plain", "");
});

HTTP.on("/eventservice.xml", HTTP_GET, [](){
  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";

  HTTP.send(200, "text/plain", eventservice_xml.c_str());
});

HTTP.on("/setup.xml", HTTP_GET, [](){
  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>" + String(deviceName) + "</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";

    HTTP.send(200, "text/xml", setup_xml.c_str());

    Serial.print("Sending :");
    Serial.println(setup_xml);
});

//Added for Echo Plus Gen2
// openHAB support   
HTTP.on("/on.html", HTTP_GET, [](){
     Serial.println("Got Turn on request");
     HTTP.send(200, "text/plain", "turned on");
     turnOnDevice();
   });

 HTTP.on("/off.html", HTTP_GET, [](){
    Serial.println("Got Turn off request");
    HTTP.send(200, "text/plain", "turned off");
    turnOffDevice();
   });

  HTTP.on("/status.html", HTTP_GET, [](){
    Serial.println("Got status request");

    String statrespone = "0"; 
    if (switchState) {
      statrespone = "1"; 
    }
    HTTP.send(200, "text/plain", statrespone);  

}); 

HTTP.begin();
Serial.println("HTTP Server started ..");

}

// 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()); }