Amazon Alexa

How to turn On/Off TV or Air Conditioner via Alexa

It is very easy to control TVs, Air conditioners or any Audio devices with Amazon Alexa using IRDevKit. In this tutorial we will learn how to emulate a smart home device named "TV" respond to On/Off requests from Amazon Alexa using WeMos D1 Mini (ESP8266) development board. You can use another device as well.

Solution 1:

Signup for Sinric account. Install the Amazon Alexa Skill. Create a smart home device. Connect your ESP to Sinric platform. Detail instructions are on Sinric home page

Solution 2:

Step 1

You must learn the On/Off IR signal from your remote. Here's the example code to do that. Once you have learned On and Off button IR codes, you are ready to move to Step 2.

Step 2

Now you are ready to integrate with Alexa. When you ask Alexa to find connected devices, it will send a UPnP broadcast your WiFi to detect any smart home devices. Now will emulate a smart home device named "TV" and respond to On/Off requests from Amazon Alexa.

Here's the full example


#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <WiFiUdp.h>
#include <functional>
#include <SoftwareSerial.h>

void prepareIds();
boolean connectWifi();
boolean connectUDP();
void startHttpServer();
void turnOnTv();
void turnOffTv();

const char* ssid = "Aruna"; //TODO: replace wifi-name
const char* password = "********"; //TODO: replace wifi-password
String device_name = "tv"; //TODO: Change to your device name
char* turnOnIrCode = "152,35,181,140, ....."; //TODO: Replace your IR code
char* turnOffIrCode = "152,35,181,140, ....."; //TODO: Replace your IR code

WiFiUDP UDP;
boolean udpConnected = false;
IPAddress ipMulti(239, 255, 255, 250);
unsigned int portMulti = 1900;      // local port to listen on
unsigned int localPort = 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;
SoftwareSerial mySerial(D1, D2); // RX, TX

void setup() {
  Serial.begin(115200);
  
  prepareIds();
  
  // Initialise wifi connection
  wifiConnected = connectWifi();

  // only proceed if wifi connection successful
  if(wifiConnected){
    udpConnected = connectUDP();
    
    if (udpConnected){
      // initialise pins if needed 
      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 theres data available, read a packet
      int packetSize = UDP.parsePacket();
      
      if(packetSize) {
        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;
        //Serial.println("Request:");
        //Serial.println(request);
         
        if(request.indexOf('M-SEARCH') > 0) {
            if(request.indexOf("urn:Belkin:device:**") > 0) {
                Serial.println("Responding to search request ...");
                respondToSearch();
            }
        }
      }
        
      delay(10);
    }
  }
}

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

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

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

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

      //for (int x=0; x <= HTTP.args(); x++) {
      //  Serial.println(HTTP.arg(x));
      //}
  
      String request = HTTP.arg(0);      
      
      if(request.indexOf("1") > 0) {
          Serial.println("Got Turn on request");
          turnOnTv();
      }

      if(request.indexOf("0") > 0) {
          Serial.println("Got Turn off request");
          turnOffTv();
      }
      
      HTTP.send(200, "text/plain", "");
    });

    HTTP.on("/eventservice.xml", HTTP_GET, [](){
      Serial.println(" ########## Responding to eventservice.xml ... ########\n");
      String eventservice_xml = ""
            ""
              ""
                "SetBinaryState"
                ""
                  ""
                    ""
                    "BinaryState"
                    "BinaryState"
                    "in"
                  ""
                ""
                 ""
                  ""
                    "BinaryState"
                    "Boolean"
                    "0"
                  ""
                  ""
                    "level"
                    "string"
                    "0"
                  ""
                ""
              ""
            "\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 = ""
            ""
             ""
                "urn:Belkin:device:controllee:1"
                ""+ device_name +""
                "Belkin International Inc."
                "Emulated Socket"
                "3.1415"
                "uuid:"+ persistent_uuid +""
                "221517K0101769"
                "0"
                ""
                  ""
                      "urn:Belkin:service:basicevent:1"
                      "urn:Belkin:serviceId:basicevent1"
                      "/upnp/control/basicevent1"
                      "/upnp/event/basicevent1"
                      "/eventservice.xml"
                  ""
              "" 
              ""
            "\r\n"
            "\r\n";
            
        HTTP.send(200, "text/xml", setup_xml.c_str());
        
        Serial.print("Sending :");
        Serial.println(setup_xml);
    });
    
    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 turnOnTv() {
  // Send turn On 
  uint8_t irArray[512];
  unsigned int idx = 1;
  char *str;

  irArray[0] = 227; // Sending ir command
  
  // Convert string to array
  while ((str = strtok_r(turnOnIrCode, ",", &turnOnIrCode)) != NULL) {
    uint8_t ir = (uint8_t)atoi(str + '\0');
    irArray[idx++] = ir;
    yield();
  }
  
  // Write the array to Serial port.
  mySerial.write((uint8_t*)irArray, idx);

}

void turnOffTv() {
  // Send turn off
  uint8_t irArray[512];
  unsigned int idx = 1;
  char *str;

  irArray[0] = 227; // Sending ir command
  
  // Convert string to array
  while ((str = strtok_r(turnOffIrCode, ",", &turnOffIrCode)) != NULL) {
    uint8_t ir = (uint8_t)atoi(str + '\0');
    irArray[idx++] = ir;
    yield();
  }
  
  // Write the array to Serial port.
  mySerial.write((uint8_t*)irArray, idx);
}