Wemo Emulation

I have made a mains power strip controlled by relays connected to a NodeMCU esp8266 board from this guide:

https://medium.com/@monkeytypewritr/amazon-echo-esp8266-iot-a42076daafa5

It uses Wemo spoofing and it all works like a dream by voice control with Alexa. When Alexa is searching for new devices I get the following data from the serial window of the Arduino IDE:

**Sending :<?xml version="1.0"?><root><device><deviceType>urn:Belkin:device:controllee:1</deviceType><friendlyName>socket 4</friendlyName><manufacturer>Belkin International Inc.</manufacturer><modelName>Emulated Socket</modelName><modelNumber>3.1415</modelNumber><UDN>uuid:Socket-1_0-38323636-4558-4dda-9188-cda0e610346d-8503</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>**

I have the Wemo binding in Openhab2, but it is unable to discover this device. I wondered if it was possible to manually configure this device, and if its is, what the things and items files entries might look like.

WeMo Binding does not support devices reported as „controllee“

That explains it. Thanks for taking the time to let me know. It’s a shame, but at least I can still control them via Alexa.

You are welcome. It´s not a shame, just as it should be. I did not see any device reporting as „controllee“ when writing the binding, just „switch“, „insight“, lightswitch“, „motion“ and a few others.
What capabilities does your device have? Just switching ON/OFF ?
In that case, we could simply extend the switch device.

Yes, just switching ON/OFF.

If this is any help here is the code that handles the callback function:

#include "Switch.h"
#include "CallbackFunction.h"


    
//<<constructor>>
Switch::Switch(){
Serial.println("default constructor called");
}
//Switch::Switch(String alexaInvokeName,unsigned int port){
Switch::Switch(String alexaInvokeName, unsigned int port, CallbackFunction oncb, 
CallbackFunction offcb){
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+"-"+ String(port);
    
device_name = alexaInvokeName;
localPort = port;
onCallback = oncb;
offCallback = offcb;

startWebServer();
}



//<<destructor>>
Switch::~Switch(){/*nothing to destruct*/}

void Switch::serverLoop(){
if (server != NULL) {
    server->handleClient();
    delay(1);
}
}

void Switch::startWebServer(){
server = new ESP8266WebServer(localPort);

server->on("/", [&]() {
handleRoot();
});


server->on("/setup.xml", [&]() {
handleSetupXml();
});

server->on("/upnp/control/basicevent1", [&]() {
handleUpnpControl();
});

server->on("/eventservice.xml", [&]() {
handleEventservice();
});

//server->onNotFound(handleNotFound);
server->begin();

Serial.println("WebServer started on port: ");
Serial.println(localPort);
}

void Switch::handleEventservice(){
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";
      
server->send(200, "text/plain", eventservice_xml.c_str());
}

void Switch::handleUpnpControl(){
Serial.println("########## Responding to  /upnp/control/basicevent1 ... ##########");      

//for (int x=0; x <= HTTP.args(); x++) {
//  Serial.println(HTTP.arg(x));
//}

String request = server->arg(0);      
Serial.print("request:");
Serial.println(request);

if(request.indexOf("<BinaryState>1</BinaryState>") > 0) {
  Serial.println("Got Turn on request");
  onCallback();
}

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

server->send(200, "text/plain", "");
}

void Switch::handleRoot(){
server->send(200, "text/plain", "You should tell Alexa to discover devices");
}

void Switch::handleSetupXml(){
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>"+ device_name +"</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";
    
server->send(200, "text/xml", setup_xml.c_str());

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

String Switch::getAlexaInvokeName() {
return device_name;
}

void Switch::respondToSearch(IPAddress& senderIP, unsigned int senderPort) {
Serial.println("");
Serial.print("Sending response to ");
Serial.println(senderIP);
Serial.print("Port : ");
Serial.println(senderPort);

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: Sat, 26 Nov 2016 04:56:29 GMT\r\n"
   "EXT:\r\n"
   "LOCATION: http://" + String(s) + ":" + String(localPort) + "/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(senderIP, senderPort);
UDP.write(response.c_str());
UDP.endPacket();                    

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

Thanks, but this does not help.
I think it might be just a change of one line in WemoDiscoveryParticipant.
It needs to check if modelName equals „Emulated Socket“.
Will try to find some time over the weekend to implement this.

That would be great! Very much appreciated :slight_smile:

I’m in the same situation as Craig.
Using OpenHAB2.2.0
My ESP8266 based device is not even seeing the initial UDP messages from OH to respond to.

When an Echo is searching for Wemo devices it will send a UDP message which includes:
"urn:Belkin:device:**"
I monitored my network and saw nothing like this when OH was searching.

The WeMo Binding is using UPnP discovery, so your device needs to react on SOAP M-Search request.

Thanks Hans.
The device does receive and respond on SOAP M-Search request.
My problem is that the device is “seeing” M-Search requests, but nothing which looks like its coming from the WeMo binding. Nothing which contains:
urn:Belkin:device:

The device code is built around the UPnP conversation given here:

Can you share the structure ofr the initial M-Search request which the Wemo Binding makes please?

Sure, the Binding does a UPnP Root-Device search

            upnpService.getControlPoint().search(new RootDeviceHeader());

the search would result in a message like

M-SEARCH * HTTP/1.1
HOST: 239.255.255.250:1900
MAN: "ssdp:discover"
MX: seconds to delay response
ST: upnp:rootdevice
USER-AGENT: OS/version UPnP/1.1 product/version

The received notifications will then be scanned for the keywork "Belkin"
Please note, the the Binding will not work with devices reporting “Emulated …”. This is not implemented right now and even I already said to implement this, I am not shure if we should do this.

hi hmerk,

my code uses following request and response messages for wemo emulation, please tell me why wemo binding is not finding my device(Amazon Alexa works with my device just fine).

M-Search request:

if(request.indexOf(“M-SEARCH”) >= 0) {
if((request.indexOf(“urn:Belkin:device:**”) > 0) || (request.indexOf(“ssdp:all”) > 0) || (request.indexOf(“upnp:rootdevice”) > 0)) {
Serial.println(“Responding to search request …”);
respondToSearch();

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”;

thanks,
abhinav

Your response is not correct, the USN should follow this syntax:

USN: uuid:Socket-1_0-11111111111::urn:Belkin:device:Socket:1

thanks for replying… i’ll make the changes and see if it works, here is the link to complete emulator code(for esp 8266 mcu), if you want to have a look.

Hi,

I went through the Java code for Wemo binding(https://github.com/openhab/openhab1-addons/blob/master/bundles/binding/org.openhab.binding.wemo/src/main/java/org/openhab/binding/wemo/internal/WemoBinding.java), I couldn’t find the Wemo binding code for OpenHab 2.

the USN check is as follows:

                           if (message != null) {
                                String location = StringUtils.substringBetween(message, "LOCATION: ", "/setup.xml");
                                String udn = StringUtils.substringBetween(message, **_"USN: uuid:", "::urn:Belkin"_**);
                                if (udn != null) {
                                    logger.trace("Save location '{}' for WeMo device with UDN '{}'", location, udn);
                                    wemoConfigMap.put(udn, location);
                                    logger.info("Wemo Device with UDN '{}' discovered", udn);
                                }
                            }

This should work with my response of:

“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”;

Please help,

Regards,
Abhinav

As the WeMo Binding is an ESH Binding, you will find the code here


But I guess you won’t find anythin usefull there, cause it uses JUPnP, so your emulated device needs to be fully hydrated by UPnP and identified as a WeMo device of a certain type (like socket, insight …)

thanks for sharing the code, I didn’t find any explicit ‘M-Search’ or response parsing but I’ll try sending the response suggested by you earlier ‘USN: uuid:Socket-1_0-11111111111:: urn:Belkin:device:Socket:1’.

Hope it works…

That‘s what I wrote, the Binding uses JUPnP, so your emulation needs to fully implement UPnP responses.

Hi hmerk,

Finally I got it working :slight_smile:. I had to make two changes in my emulation code:

  1. Respond to ‘M-Search’ with ‘ST’ as ‘ssdp:all’, this is different from Alexa’s search request.

    if(request.indexOf("M-SEARCH") > -1) {
         Serial.println("Found M-SEARCH request...");
         if((request.indexOf("urn:Belkin:device:**") > 0) || (request.indexOf("ssdp:all") > 0)) {
             Serial.println("Responding to search request ...");
             respondToSearch();
         }
     }
    
  2. Use following in response to M-Search(different to what Alexa is expecting):

    “ST: urn:Belkin:device:Socket:1\r\n”
    “USN: uuid:” + persistent_uuid + “:: urn:Belkin:device:Socket:1\r\n”

Thanks for all your help :slight_smile: