Directly control WeMo switches (Arduino 2 Photon Code)

Hi community,

i’m Jochen from germany and quite new to the community, but very eager to learn.
I use some WeMo Devices (merely switches and motion sensors) which i already control with Software on Mac/iPhone, Shell Scripts and Apple Scripts and to some extend via IFTTT with my Photon Internet Button.
It actually works fine, but now i try to do it without IFTTT, because of delayed execution of commands.

I have one specific project on my mind for some time, that i like to do with Particle Photon. Originally from Arduino, which requires LAN-Access and I’m not sure how to ‘translate/rewrite’ commands from the original code to photon language.
This is where I wanna learn from the community and you: How do I execute commands to turn on/off my WeMo Switches (see link or code) directly with photon?

void switchON(){
  Serial.println("switchON");
  String data1;
  data1+="<?xml version=\"1.0\" encoding=\"utf-8\"?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body><u:SetBinaryState xmlns:u=\"urn:Belkin:service:basicevent:1\"><BinaryState>1</BinaryState></u:SetBinaryState></s:Body></s:Envelope>"; // Use HTML encoding for comma's
if (client.connect(wemoIP,wemoPort)) {
        client.println("POST /upnp/control/basicevent1 HTTP/1.1");
        client.println("Content-Type: text/xml; charset=utf-8");
        client.println("SOAPACTION: \"urn:Belkin:service:basicevent:1#SetBinaryState\"");
        client.println("Connection: keep-alive");
        client.print("Content-Length: ");
        client.println(data1.length());
        client.println();
        client.print(data1);
        client.println();
    }

I hope, someone has done that already and likes to share some knowledge! :wink:

Code: https://gist.github.com/gTrigonakis/480c1693ffc7a3f31ed3#file-arduinowemocontrol-ino
Project on Arduino: http://www.gtrigonakis.com/blog/

Thank you in advance!

Hi! There is not much to share there. The code just works if you omit everything which has to do with Ethernet. This is done automatically, so in setup and loop just use the WiFi. And you need to use the TCPClient instead of EthernetClient. And add #include <application.h> at the start (it’s the only include you need).

The following works for me:

void switchON()
{

  Serial.println("switchON");
  String data1;
  data1+="<?xml version=\"1.0\" encoding=\"utf-8\"?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body><u:SetBinaryState xmlns:u=\"urn:Belkin:service:basicevent:1\"><BinaryState>1</BinaryState></u:SetBinaryState></s:Body></s:Envelope>"; // Use HTML encoding for comma's
  TCPClient client;
  if (client.connect(wemoIP,wemoPort)) {
        client.println("POST /upnp/control/basicevent1 HTTP/1.1");
        client.println("Content-Type: text/xml; charset=utf-8");
        client.println("SOAPACTION: \"urn:Belkin:service:basicevent:1#SetBinaryState\"");
        client.println("Connection: keep-alive");
        client.print("Content-Length: ");
        client.println(data1.length());
        client.println();
        client.print(data1);
        client.println();
    }

  if (client.connected()) {
     client.stop();
  }
}

Hope that helps!

1 Like

BTW: @JochenK: Thanks for bringing it up, I was looking for something like this, did not find it before :smile:.

1 Like

Thank you very much, Stevie! I appreciate your help. :smile:

Edit: It works! And it’s even faster reacting than i thought!
I will adapt code on my Photon Internet Button and will have a very handy 4/5 Buttons Device! :smiley:

1 Like

Here is also some code which checks the status:

bool getState()
{
    TCPClient client;
    bool state = false;
    if (client.connect(wemoIP, wemoPort))
    {
        String data = "<?xml version=\"1.0\" encoding=\"utf-8\"?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\""
                       "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body><u:GetBinaryState xmlns:u=\""
                       "urn:Belkin:service:basicevent:1\"><BinaryState>1</BinaryState></u:GetBinaryState></s:Body></s:Envelope>";
        client.println("POST /upnp/control/basicevent1 HTTP/1.1");
        client.println("Content-Type: text/xml; charset=utf-8");
        client.println("SOAPACTION: \"urn:Belkin:service:basicevent:1#GetBinaryState\"");
        client.println("Connection: close");
        client.print("Content-Length: ");
        client.println(data.length());
        client.println();
        client.print(data);
        client.println();

        char buffer[512];
        size_t offset = 0;
        while (client.connected() && offset < sizeof(buffer) - 1)
        {
            if (client.available())
            {
                char c = client.read();
                buffer[offset++] = c;
            }
        }
        buffer[offset] = '\0';
        if (strstr(buffer, "<BinaryState>1</BinaryState>"))
            state = true;
    }

    if (client.connected())
        client.stop();
    return state;
}
1 Like

Aww, that was useful to turn OFF/ON devices when their state was unknown (maybe because of: no line of sight or more than one remote) or changed rapidly. Thanks again!

Not so elegant code snippet for toggle-mode:

bool wemostate1;

            wemostate1 = getState();
            if (wemostate1){switchOFF();}
            else {switchON();}

So I know this post is old. I am giving it a try with a Photon module. I am not able to get a TCP connection to the WEMO. Is the port for the TCP connection not 80? That’s what I am trying and the client.connect is failing.

@IOTrav, where are you trying that from?
For HTTP traffic you could try 8080 in case your network is forcing you to go via a Proxy server.

And the default WEMO port is 49153 I think

Hi Scruff,

The Photon and WeMo are on the same LAN so the connection is local(not going through firewall on router or anything).

I am referencing code from here:

Looks like another Particle user got it to work here, but I am not getting the socket to connect. I have verified and re-verified the IP of the WeMo on the LAN so I am sure I have the correct IP for the connection. Here is my current code:

int manualWeMo(String data);
void switchOFF();
void switchON();

byte wemoIP[4] = {192, 168, 1, 61};
int wemoPort = 49153;

void setup() {
    Particle.function("ManualWeMo", manualWeMo);
}

void loop() {

}

int manualWeMo(String data){
    if(data.equalsIgnoreCase("ON")){
        switchON();
        return 1;    
    }
    if(data.equalsIgnoreCase("OFF")){
        switchOFF();
        return 1;
    }
    return 0;
}

void switchON()
{

  Serial.println("switchON");
  String data1;
  data1+="<?xml version=\"1.0\" encoding=\"utf-8\"?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body><u:SetBinaryState xmlns:u=\"urn:Belkin:service:basicevent:1\"><BinaryState>1</BinaryState></u:SetBinaryState></s:Body></s:Envelope>"; // Use HTML encoding for comma's
  TCPClient client;
  if (client.connect(wemoIP,wemoPort)) {
        client.println("POST /upnp/control/basicevent1 HTTP/1.1");
        client.println("Content-Type: text/xml; charset=utf-8");
        client.println("SOAPACTION: \"urn:Belkin:service:basicevent:1#SetBinaryState\"");
        client.println("Connection: keep-alive");
        client.print("Content-Length: ");
        client.println(data1.length());
        client.println();
        client.print(data1);
        client.println();
    }else{
        Serial.println("No Connection");
    }

  if (client.connected()) {
     client.stop();
  }
}

void switchOFF()
{

  Serial.println("switchOFF");
  String data1;
  data1+="<?xml version=\"1.0\" encoding=\"utf-8\"?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body><u:SetBinaryState xmlns:u=\"urn:Belkin:service:basicevent:1\"><BinaryState>0</BinaryState></u:SetBinaryState></s:Body></s:Envelope>"; // Use HTML encoding for comma's
  TCPClient client;
  if (client.connect(wemoIP,wemoPort)) {
        client.println("POST /upnp/control/basicevent1 HTTP/1.1");
        client.println("Content-Type: text/xml; charset=utf-8");
        client.println("SOAPACTION: \"urn:Belkin:service:basicevent:1#SetBinaryState\"");
        client.println("Connection: keep-alive");
        client.print("Content-Length: ");
        client.println(data1.length());
        client.println();
        client.print(data1);
        client.println();
    }else{
        Serial.println("No Connection");
    }

  if (client.connected()) {
     client.stop();
  }
}

Everything looks correct and my functions run but I get No Connection on the serial log which means client.connect failed. Any ideas? Think WeMo changed something?

@IOTrav, you need to define the wemo IP address using the IPAddress type:

IPAddress wemoIP(192, 168, 1, 61);

:wink:

1 Like

AGH! I totally missed that! Thank you. Just any idiot copying and pasting :smile:

Thank you @peekay123 and @ScruffR

Works Perfectly now!

Look forward to a fun Hackster Post on this. Will post here when I put it up.

1 Like

Anyone know if it’s possible to discover WEMO devices on the LAN by listening for UDP broadcast or something? That would be a nice thing to throw in. It would be nice to automatically discover the WeMo on the LAN and determine it’s IP address and perhaps even user friendly name. I’ve been searching around for this but have come up empty thus far.

Looks like WEMO can be discovered through SSDP. Never used that before but saw another community user(@smpickett) was kind enough to submit a Library for it. Looking into that now.

1 Like

Hi @IOTrav! The SSDP library that I wrote only makes the particle device discoverable; it doesn’t listen for other devices. I could investigate this and expand on the original library if there is interest.

1 Like

Hi @smpickett,

After looking at your library for a couple minutes I discovered that. I’m still learning the SSDP protocol and think I have a decent grasp on how it works at this point.

First you send out a UDP packet on IP 239.255.255.250 and port 1900 Then you listen for a UDP packet on that same IP and port number. Using an IP address for sending and receiving that is not the actual IP of the Photon module is what I am trying to wrap my head around right now. I just do not fully understand how that works. Hopefully I will have something together today. When I do I will be sure to share.

@IOTrav, I’ve got a small write-up on the process here if it helps at all http://blog.zerotread.ca/2015/11/27/ssdp-for-the-photon-core/

Thanks @smpickett

So I send the search request on 239.255.255.250 on port 1900 but then when I go to listen for responses I just listen on port 1901 on the Particle Module’s local IP?

Hey everyone. Great news. I have device discovery working in my first demo. This code will discover Belkin WeMo devices on the network.

UDP udp;

IPAddress UpNPIP(239, 255, 255, 250);
int UpNPPort = 1900;

void setup() {
    Particle.function("ManualWeMo", manualWeMo);
    String searchPacket = "M-SEARCH * HTTP/1.1\r\n";
    searchPacket.concat("HOST: 239.255.255.250:1900\r\n");
    searchPacket.concat("MAN: \"ssdp:discover\"\r\n");
    searchPacket.concat("MX: ");
    searchPacket.concat("5");
    searchPacket.concat("\r\n");
    searchPacket.concat("ST: ");
    searchPacket.concat("urn:Belkin:device:controllee:1");
    searchPacket.concat("\r\n");
    searchPacket.concat("\r\n");
    Serial.println("Sending:");
    Serial.print(searchPacket);
    Serial.println();
    udp.begin(1901);
    udp.beginPacket(UpNPIP, UpNPPort);
    udp.write(searchPacket);
    udp.endPacket();
    unsigned long startTime = millis();
    unsigned long tOut = 5000;
    while(millis() < startTime + tOut){
        int packetSize = 0;
        packetSize = udp.parsePacket();
        if(packetSize != 0){
            Serial.println("Device discovered");
            byte packetBuffer[packetSize+1];
            udp.read(packetBuffer, packetSize);
            String deviceData = String((char *)packetBuffer);
            Serial.print(deviceData);
            Serial.println();
        }
    }
}

void loop() {

}

Would definitely be cool to see this in a portable library but I would really have to study the SSDP protocol to properly build it. For now I’m just happy it’s working. Next I’ll parse the XML and get the IP of the WeMo outlet I’m playing with.

Thank you very much for your help @smpickett

5 Likes

@IOTrav, what data to you get back from the WeMo module? Are you hoping to just discover the devices on the network (and therefore their IPs), or do you hope to parse the XML response to see the module capability?
If you don’t mind, I’ll use your example to expand on the library.

1 Like

Hi @smpickett

The module actually does not respond with XML, I was wrong there. It responds basically with an HTTP response. This is what is printed to the serial log:

HTTP/1.1 200 OK
CACHE-CONTROL: max-age=86400
DATE: Wed, 21 Dec 2016 17:18:41 GMT
EXT:
LOCATION: http://192.168.1.12:49153/setup.xml
OPT: "http://schemas.upnp.org/upnp/1/0/"; ns=01
01-NLS: 820301ba-1dd2-11b2-96a4-ac37546accff
SERVER: Unspecified, UPnP/1.0, Unspecified
X-User-Agent: redsonic
ST: urn:Belkin:device:controllee:1
USN: uuid:Socket-1_0-221632K0100977::urn:Belkin:device:controllee:1

You can see in the response there is a Location for the XML. So at that point you could make a request to that URL and get the device’s full information XML and extrapolate additional information about it. It is quite large denoting all services it offers, friendly name, etc. It looks like you did this in your library as well so I won’t bore you.

I am currently only using the Location line to get the IP and Port of the device. I am not worrying about all the information in the XML for now although that would be really cool, just a lot of extra work and honestly right now in my project I do not need that info. Just the IP and port for the device.

Please feel free to use any of the code I posted here to further develop your library. I’m sure future users will find a complete SSDP library very useful. I’m thinking about a complete Library for WeMo myself :wink:

2 Likes