Orvibo S20 WiFi Mains Socket with Node-RED

orvibo_S20
Orvibo S20 WiFi Smart Socket

For years I used X10 for all my remote controlled sockets but the unreliability eventually drove me to RF based sockets, the Home Easy ones in particular as you can’t beat the price, often available for circa £20 for a pack of 3 with a remote control and easily integrated into Node-RED and the likes with a simple 433MHz transmitter and receiver, the downside being there is no security.

Anyway, there are lots of affordable WiFi controlled sockets on the market now and a common one is the Orvibo S20, £15.99 on Amazon, a bit less from some sellers on ebay or under £11 from Banggood (be sure to select the right model for your country there).

They seem to be pretty well made and are small unobtrusive units. They are designed to work only with their proprietary Android and iPhone apps but there has been several pieces of work done on reverse engineering the protocol and some libraries for various platforms already exist but it looked easy enough to knock something up in Node-RED so that’s what I did as it is the heart of my home automation system these days. This is just simple on/off control here, no point in messing with the built in timers when we have better control than that in Node-RED itself.

Communicating with the socket

First of all the socket needs to be configured to connect to your WiFi network, the easiest way to do this is just to install the Orvibo WiWo app and set it up there, check the socket actually works while you are at it and make a note of the sockets uid. No need to use the app again now, you can uninstall it if you like.

Communication with the socket is done via UDP broadcasts on port 10000 and first of all a packet needs to be sent to register with it, this registration times out after 5 minutes so I’m sending it before every command with a 300ms delay before the actual on/off command, less than this seemed a bit hit an miss and you may need to tweak this.

For simple on/off usage like this the packet format can be simplified to:

Subscribe: 6864001e636c[mac address]202020202020[mac address in little endian]202020202020

On/Off: 686400176463[mac address]20202020202000000000[00 for off, 01 for on]

You can grab a copy of my Node-RED flow to do that here.

Node-RED Flow
Node-RED Flow

I am using an MQTT topics of the format orvibo/0123456789ab where 0123456789ab is the MAC address of the particular socket (grab it from your router or it is also available as the first 12 digits of the uid displayed in the WiWo app) and the payload is just on or off as required. In Node-RED I then remap more readable names like appliances/heater to the relevant socket topic just as I do for the Home Easy and X10 modules.

Here is the code from the Node-RED function block:

// Get mac address from topic and place in array
msg.topic = msg.topic.replace('orvibo/','');
var mac = [];
for (var i = 0; i < msg.topic.length;) {
    mac.push("0x" + msg.topic.charAt(i) + msg.topic.charAt(i+1));
    i = i+2;
}
var padding = [0x20,0x20,0x20,0x20,0x20,0x20];
var uid = mac.concat(padding);
var uidLE = mac.reverse().concat(padding);

// Build subscribe packet
var command = [0x68,0x64,0x00,0x1e,0x63,0x6c];
var subscribe = command.concat(uid).concat(uidLE);

// Build command packet
command = [0x68,0x64,0x00,0x17,0x64,0x63];
if (msg.payload == "on") {
    var data = [0x00,0x00,0x00,0x00,0x01];
} else if (msg.payload == "off") {
    var data = [0x00,0x00,0x00,0x00,0x00];
}
var packet = command.concat(uid).concat(data);

msg = { payload: new Buffer(subscribe) };
var msg2 = { payload: new Buffer(packet) };

return [msg, msg2];

Anything else of interest?

As mentioned communication with the Orvibo sockets is over UDP so there is no guarantee that the packets arrive but it seems reliable so far and it does look like it is possible to query the status of the socket so that is something to look at as well.

It listens on port 10000 for the UDP broacast packets, I checked to see if there were any open TCP ports that might reveal something else but there was nothing open. It does connects to a couple of IP addresses though, 52.28.25.255 (an Amazon AWS instance) on port 10000 which seems to be part of the “cloud” service that allows you to use it from outside your own network as well as a (currently unreachable) Chinese IP 61.164.36.105 on port 123 which is NTP so it could be part of the timer function but who knows what it really is. I don’t like things on my network connecting to unknown Chinese IPs and as I won’t be using the Orvibo app either I’ve blocked both of these IPs on my router and will monitor to see if it tries any others.

 

19 thoughts on “Orvibo S20 WiFi Mains Socket with Node-RED

  1. Hi, I’ve recently purchased a S20 and have been hunting around for reverse engineering info.

    I was going to implement a node red “node” for the S20 and hence found your blog!

    I might go a step further and encapsulate all the nitty gritty UDP comms into a custom node, we’ll see 🙂

    The main reason I added this comment is re: the Chinese IP address the socket is attempting to contact on port 123, this will be a NTP time sync request and is probably best left open if you want the socket to keep accurate time without you having to intervene.

    Cheers,

    Andy.

  2. Thanks for the information I’ve been trying to sort out node red and thes sockets for a while but not being very good at writing the functions etc. I just can’t get it to work.
    With your help I’ve now got the sockets working from contrib-ui in node red.

    Combine that with pushbullet and I should be able to control them from outside my network

  3. @AndyW Yeah my first thought was to use the node.js lib to create a node but didn’t seem necessary once simple on/off control boiled down to just this. The NTP I mentioned in the post, doesn’t bother me if the time is off as I’m not using the built in timer.

    @Phil Glad it was of use. Would be nice if these companies provided some sort of documented API, would make their products so much more popular for little extra effort. Less fun for us working it out though I suppose 😀

  4. @AndyW – The timer commands shouldn’t be needed just use node red to trigger based on time or even use Pete Scargill’s Bigtimer node.

    I’m struggling to get the status reported back from the sockets so I can , it nearly works…….. sometimes the fun can fade 😉

  5. Phil, I’ve been working on this too, I can get the response back from the socket OK but really I am looking at a Node-RED method to send a UDP packet to it and retry it if the response isn’t received.

    To get the response from the socket when the state changes (be it from Node-RED, the WiWo app or pressing the button on the socket) I’ve done the following:

    Delete the port number 10000 from the UDP out node in my example in the post above.

    In the function node change the msg and msg2 outputs to (adding port no.) :

    msg = { payload: new Buffer(subscribe), port: 10000 };
    var msg2 = { payload: new Buffer(packet), port: 10000 };

    This stops the out node from binding to port 10000 so you can create a UDP in node on port 10000 to receive the response from the socket, I’ve fed that response into a function node with:

    var reply = msg.payload.toString("hex");

    if (reply.substr(0, 12) == "686400177366"){
    var mac = reply.substr(12, 12);
    var state = reply.substr(45, 1);

    var msg = { Payload: mac + ':' + state };
    return msg;

    }

    Which (as a test) will return a message with the mac address of the socket and the state the socket was switched to. What I’m struggling with at the moment is a Node-RED way to send the initial UDP packet and then retry if the correct response is not received within a time limit.

  6. Nathan,
    I modified your function to send a discovery message, it triggers a message, or two! from the socket ending in 0 or 1 to indicate the status, to test I inject the trigger orvibo\mac addr.

    I’m having some problem with the socket I’m testing with not being consistent with its replies though
    [code]
    // Read my blog post about this here: https://nathan.chantrell.net/20160101/orvibo-s20-wifi-mains-socket-with-node-red

    // Get mac address from topic and place in array
    msg.topic = msg.topic.replace('orvibo/','');
    var mac = [];
    for (var i = 0; i < msg.topic.length;) {
    mac.push("0x" + msg.topic.charAt(i) + msg.topic.charAt(i+1));
    i = i+2;
    }
    var padding = [0x20,0x20,0x20,0x20,0x20,0x20];
    var uid = mac.concat(padding);
    var uidLE = mac.reverse().concat(padding);

    // Build discover packet
    var command = [0x68,0x64,0x00,0x12,0x71,0x67];
    var discover = command.concat(uid)//.concat(uidLE);
    var packet = command.concat(uid);

    msg = { payload: new Buffer(discover) };

    return msg;
    [/code]

  7. Brilliant Nathan, just what I’ve been looking for / trying too do myself.
    Thanks a million. Works brilliantly.

  8. These have been integrated into Home-Assistant fairly well, there may be some clues in there for you to use.

    cheers,

  9. ** Off topic! **
    Hi Nathan, I’ve been reading your blog entries for a while and today I got the time to fire-up my new Pi-3 with Node-Red and start to play with my ancient X10 system. The ‘intelligence’ for it runs in a perl script I wrote, linking up to the mochad driver. I can get X10 events in Node-Red but I’m not sure where to go from there to fire events, etc. I saw you have an X10 tab in Node-Red; would you be prepared to write a blog post on it and share the flows?

  10. Hi Simon, All I was doing to control X10 modules was using the exec node with heyu (http://www.heyu.org/) for basic on/off control by just sending a payload of “on A1” etc. Just a stop gap while I phased X10 out in favour of RF controlled units.

    I still have some of the X10 RF wall switches and PIRs in use, for those I use a 433MHz receiver on an Arduino clone which relays the signal over my RFM12B network back to Node-RED as mentioned in this post: https://nathan.chantrell.net/20140329/x10-and-home-easy-with-node-red/
    That wouldn’t make sense if you didn’t already have RFM12B kit in use though.

  11. I see that Itead.cc are now selling ‘S20’ sockets based on the ESP8266 which should open them up for reloading with decent software. Has anyone pulled one apart yet? I’ve ordered a couple in the hope they can be reflashed easily.

  12. Hello, I am working on the same subject. But i don’t understand what You are using the MQTT node for? Should i place the mac address in there to receive state back?

  13. Hi Nathan (and Phil),

    I can’t get the S20’s to report their status.

    Nathan, from your explanation I made the changes to the function and UDP out node; then dropped in a UDP in node listening on port 10000, running into a function node with your extra script and then outputting to a debug node… but nothing ever appears.

    if you have 2 minutes please could you just let me now if I’m doing that right so that I’m more confident I’m not blundering around in the dark?

    cheers
    Don

  14. Hi,
    This is the code in my function, a UDP listen node feeds the function node and I have three sockets and depending on the MAC address one of the three outputs is fed with the status.
    I’ve no got my sockets working with the Amazon ECHO.

    var mac = msg.payload.toString(‘hex’).substr(14, 12);
    var command = msg.payload.toString(‘hex’).substr(0, 12);
    var IP = msg.ip;

    if(command== “686400177366”){

    if(mac==”xxxxxxxxxxxx”){
    var Garage = msg.payload.toString(“hex”).substr(45, 1);
    if(Garage == 1){
    Garage=”on”;
    context.global.Garage=”on”;
    }else if(Garage == 0){
    Garage = “off”;
    context.global.Garage=”off”;
    }
    mac=”RPi Socket”;
    var msg = {payload: Garage};
    return [msg, null, null];
    }
    if(mac==”xxxxxxxxxxxx”){
    var xstate = msg.payload.toString(“hex”).substr(45, 1);
    if(xstate == 1){
    xstate=”on”;
    context.global.xstate=”on”;
    }else if(xstate == 0){
    xstate = “off”;
    context.global.xstate=”off”;
    }
    mac=”Xbox Socket”;
    var msg2 = {payload: xstate};
    return [null,msg2, null];
    }

    if(mac==”xxxxxxxxxxxx”){
    var Socket3 = msg.payload.toString(“hex”).substr(45, 1);
    if(Socket3 == 1){
    Socket3=”on”;
    context.global.Socket3=”on”;
    }else if(Socket3 == 0){
    Socket3 = “off”;
    context.global.Socket3=”off”;
    }
    mac=”Socket 3″;
    var msg3 = {payload: Socket3};
    return [null, null, msg3];
    }

    }

  15. Ok, so I think I understand what that’s doing but even before i swap in my own variables i’m getting a “SyntaxError: Unexpected token ILLEGAL” from the function node after copying and pasting your text. Any ideas? Not to worry if not, I’ll keep fiddling. Don

  16. Do,
    Not sure what the issue might be, I copied and pasted the code, has the code pasted correctly nyou r function? and have you set your function outputs to three or removed some of the if statements?

    Phil

Leave a Reply

Your email address will not be published. Required fields are marked *

Notify me of followup comments via e-mail. You can also subscribe without commenting.