Directly control WeMo switches (Arduino 2 Photon Code)

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

I am using your code and it works great! (thanks) I wonder if you also know how the code would look if you had more than one (1) WEMO device, and I would like to get the friendly name as well.
Michael

1 Like

Hi @mgcurtis,

Glad the code was useful for you! Took a little futzing to get it working so I’m glad it saved you a little time.

You are definitely right that I didn’t write it to support multiple WEMOs on the same LAN. I believe that in order to support that you will need to parse the setup.xml file to get information on the devices including friendly name.

Once you get that HTTP response from the WEMO you will see a LOCATION line. This line gives you the IP address of the device but also the url for the device’s setup.xml If you make a request to that address using something like the HTTP Client you can get all that XML information. I did not dig too deep on the community here but I bet someone has an XML parser class which could be used to extrapolate device specific information from the XML like the Friendly name.

Hopefully I will have time in the near future to proceed further on this project. I would like to build a WEMO class library for Photon which will discover devices and let you instantiate objects for all discovered devices. I think that would be useful for many Particle users and it could be easily ported to Arduino I’m sure.

I thought I should touch back with some follow-up. It turns out that:
searchPacket.concat(“urn:Belkin:device:controllee:1”); will only return outlet types,
searchPacket.concat(“urn:Belkin:device:lightswitch:1”); only returns light switch types, but
searchPacket.concat(“urn:Belkin:device:1”);
will return all Belkin devices.

3 Likes

Good to know @mgcurtis Thanks for sharing!