I have bought Orvibo S20 (picture) smart socket. You can find them on Amazon, eBay and probably many other online shops. The socket comes with an app for Android and iOS that is used to control the socket. Unfortunately, the app is proprietary and also not available for normal computers. I wanted to have some free software solution. Also, the original app didn’t work too reliably, and of course you can’t fix it without having a code. Again, this shows why it is important to have free software…
After searching a bit I found some code on GitHub written for Ninja Blocks. It also came with some reverse engineering data (I will refer to this as the original reverse engineering later and will assume that you briefly looked at it). That file might look slightly scary initially if you are not used to that kind of stuff but actually everything is quite simple. It seems that the socket can be controlled by simply sending UDP packets over the network and listening for replies. You can even try to play a bit with netcat but of course it is not too convenient. So I decided to write my own program using Qt 5. It provides a lot of convenient functions in its QtNetwork module and also it would make it easy to write GUI if I ever decide to do so in the future. Also, I’m familiar with Qt because it is used by KDE Applications. Another reason for choosing Qt was it’s support for sockets and slots and I expected them to be useful.
As you can see in the reverse engineering file, there are commands to discover the socket, subscribe to it (which is required before doing anything else), power it on/off and read some tables (Socket Data contains information about the socket and Timing Data stores when to turn the socket on or off). They all follow similar pattern. You have to send
Magic Key+ message length + command id + rest of the message
where Magic Key is 68 64 (hexadecimals) and is used to distinguish these UDP packets from any other packets that are send over UDP port 10000. Every time you send a message the socket replies with another message confirming the action of the first message. Or the socket doesn’t reply. We are using UDP protocol for networking. So there is no guarantee that message is received and later I had to write some code to make sure packets are received. Hence, I implemented message queue and resend every command until I get a proper reply before sending another command. This finally made my program more reliable.
Writing Socket Data
I quickly managed to get some basic stuff working (for example powering it on and off) and soon I implemented most of the commands from the file with reversed engineered commands (reading Timing Table is still not completed but shouldn’t be too hard). Since I wanted to do more than that, I started Wireshark and analyzed a few more packets. I quickly learned how to write Socket Data Table too. Apparently, you send command very similar to what you receive when you request Socket Data but with different Command ID (74 6d instead of 72 74 in hexadecimals).
So to write Socket Data I send the following packet
Magic Key + message length + 74 6d + mac + mac padding + 00 00 00 00 + AA 00 BB + recordLength + record;
record = 01 00 /* record number = 1*/ + versionID + mac + mac padding + reversed mac + mac padding + remote password + socket name + icon + hardwareVersion + firmwareVersion + wifiFirmwareVersion + port + staticServerIP + port + domainServerName + localIP + localGatewayIP + localNetMask + dhcpNode + discoverable + timeZoneSet + timezone + countdownStatus + countdown + 00 (repeated twelve times) + 30 (repeated 30 times, note that hex 30 corresponds to 0 in ASCII);
countdownStatus is 00 ff when countdown is disabled and 01 00 when countdown is enabled.
AA 00 BB is actually table number and version flag. E.g. 04 00 01. 4 stands for table number (Socket Data is Table number 4) I don’t completely understand what is version flag, so if you know please tell me in the comments.
Then socket replies with:
Magic Key + message length + 74 6d + mac + mac padding + 01 00 00 00 00;
Now just send an already documented Socket Table packet (see: http://pastebin.com/LfUhsbcS) to update your variables.
Writing Timing Data
Writing Timing Data is exactly the same (even Command ID is still 74 6d) as writing Socket Data but you must specify 03 a a table number
Magic Key + message length + 74 6d + mac + mac padding + 00 00 00 00 + AA 00 BB + record1Length + record 1+ record2Length + record2 + record3Length + record3 + …;
record again contains the same data as what socket sends when you request timing data.
Initial pairing of the socket
The authors of the original Ninja Blocks orvibo-allone driver assume that socket was already paired using the proprietary Android/iOS application. Their original reverse engineering also contains no information how to do that. I expected that this might be a bit tricky to do because unpaired socket is not connected to the router and you have to somehow transmit your wifi configuration into the socket. I think there are at least two ways to pair that proprietary Android/iOS app implements. If you press the socket button for a few second it switches to a rapidly blinking red led mode. Then long press it again and it switches to rapidly blinking blue led and the socket creates an unencrypted wifi network (it was called WiWo-S20).
Then I created the wifi network with the same name on my laptop and tricked the proprietary app into believing that it is the socket’s wifi network. I was able to intercept the following message on UDP port 48899 (everything is in ASCII in the section, not in hex):
So apparently, Orvibo S20 has HF-LPB100 Wifi chip inside. This chip can be controlled by the AT+ commands (you can find them online but I will write a brief summary here) and I was able to do initial socket configuration!
- Switch S20 to rapidly blinking blue led more. Connect your computer to WiWo-S20 network.
- Send “HF-A11ASSISTHREAD” on UDP port 48899 to the broadcast address (don’t include ” in the message).
- S20 will reply with “IP address,MAC Address,Hostname”. The socket always replies to the same port as the source port of your message.
- Acknowledge that you got the previous message by sending “+ok”.
- Send “AT+WSSSID=ssid\r” where you replace ssid with your WiFi network name. \r is the carriage return (CR) symbol.
- The socket will reply with “+ok\n\n” (\n in this case is carriage return + line feed) if everything is correct or “+ERR\n\n” if something is wrong.
- Send your Wifi security settings: “AT+WSKEY=WPA2PSK,AES,PASSWORD\r”. The socket will again reply with “+ok\n\n”.
- Switch HF-A11 chip to station mode by sending “AT+WMODE=STA\r”. Again, wait for “+ok\n\n”
- Reboot your socket with “AT+Z\r”.
- Connect your computer back to your router. Wait until the socket boots. Now you can find it using normal discovery packet on port 10000, then change socket name, timezone, etc. with Write Socket Data packet…
I did some investigation and it seems that we need to send a slightly modified Table Data and Socket Data packets immediately after pairing to set them to default values. More information will be published later.
Another way to pair the socket from the rapidly blinking red mode. It is slighly less reliable than this method but on the other hand does not require you to disconnect from your wireless. Actually, it doesn’t require your computer to have any wireless at all.
Unfortunately, it seems that either way WPA encryption key is transferred in an insecure way, i.e. the socket is not able to use any public key cryptography. Slightly safer way to do it manually is to first change your WPA key to something temporary and pair the socket. Then use AT+ commands to change WPA password to the real one and change your router’s WPA password back.
Also, note that this socket doesn’t support WPA Enterprise. So if you would like to use it at home then create two Wifi networks: WPA-PSK for the socket and WPA-EAP for everything else (OpenWrt can do this easily).
I released all code under the GNU General Public License version 3, so the code is freely available to everybody. Git repository is available at:
https://git.stikonas.eu/andrius/s20 (feel free to create an account here and fork the code)
I compiled windows binaries. There is also a very limited GUI that does not yet have all the features of console app but if you prefer GUI then it might be useful.
If you find this work useful then tips are very welcome. You can send Bitcoin tips to bc1qe2dfqjwgse5v6cl6rhtk352ru90t0hnve45f2c.
I was able to buy the second socket which already resulted in improved multiple socket support. Thanks for the donations!
Bugs are tracked in the Gitlab issue tracker.
Go code by Grayda (basic support for S20 but also supports Orvibo AllOne devices)
PHP code by Fernano Silva (supports most features of S20). See also his technical data file.