I couldn’t resist to imagine many potential applications and possible integrations with online (cloud) services that are publicly and freely available, like IFTTT; I’m also a big fan of Android Tasker, Node-Red, the MQTT pub/sub protocol, and Raspberry Pi / NodeMCU hooked with sensors and hardware pieces attached on the GPIO pins) that make more connected things, so literally ideas are all over the places in my heads, on papers, in Keep notes…
Like this one that just popped in my mind:
Switch off the main lights in the salon, dim the lights around the sofa and TV, power on the media server box (smartplug) and TV when I sit on the sofa, with a NodeMCU controller and a pressure sensor (FSR)! Or maybe I’d prefer to have all that triggered when I power on the TV, but only in a certain time slot?…
Told you I have crasy ideas ;). Totally got to try to do that BTW!
What I only lack really is time. First, time to try and make some of those idea work (they do eventually with perseverence and I always learn a lot in the process), then time to write about them (here).
I didn’t realize the time it take to write articles people need to write good articles. I usually take notes of what I do, notes that I understand, but publishing those is no value to anyone. You need to structure the ideas, give them some form so the result make sense for the reader, enough at least so they can be inspired in trying also themselves to do, learn and make something that work for them out of what I wrote. Unfortunately due to the lack of time, I don’t enter into too much details, and I assume the reader is tech-savvy enough to read, understand and try by himself. That means some reader gets frustrated because I missed some parts they didn’t know… Sorry about that.
Back to the HS100: there were some (very good) resources publicly available already out there that showed how to interact with an HS100 plug, from shell scripts based on cURL, php and python libraries or Node.js modules, even a Node-Red. But they all had the same problem: they only worked withing the same local network (WiFi/LAN) the plug was connected to. Ugh bummer.
The Kasa App and the Alexa integration were proof that TP-Link was hosting a cloud service, but no API was publicly available yet.
So I started to work on figuring out how the App talked to the TP-Link Cloud endpoint, and started to publish it here. The series got (modestly) popular among the maker and tinkerers ;), and people used the comments to exchange ideas, issues, and come with solutions. So far, I’ve published (not in chronological order)
So far the whole process was rather hacky and manual I reckon, but still one could achieve the goal of switching a smartplug from a remote place (outside of the smartplug local network), like from one’s smartphone (Tasker), or an IFTTT recipe. It already enabled some cool logics.
Soon after having found out how to trigger one TP-Link HS100 smartplug, the most frequently asked question in the comments has been ¿how can I power On/Off multiple devices from ITFFF?
After all it’s a feature that people know from the Kasa App: you define buttons in the app that will put several of you smartplugs in a state you define.
Related to my idea above, a scene would be:
“Time to watch some TV” scene: Switch off the main lights in the salon, dim the lights around the sofa and TV, power on the media server box (smartplug) and TV.
You can define that in Kasa, but what about calling it from IFTTT? You can’t. You can only call one POST call (via Maker), in an IFTTT recipe.
The second to most request in the mind of all is probably related to the TOKEN that will eventually expire sooner of later, making it a hassle to edit all your IFTTT Recipes/Tasker tasks/whatever scripts… manually!
Well, I’m happy to say this was until now!
This week I’ve been working hard on this (it’s the second time I try it, and this time I made it!).
This is what I have done:
All that is already working fine for me, so this is what I’ll talk about in next posts (and I’ll publish the code for everything ) when I get some more time to talk about it. (Believe it or not, it took me more than two hours to write this post)!
So, stay tuned if you are interested. I suggest you follow me on Twitter, and follow this blog, recommend on Disqus (the heart icon).
Ah and well… all this is free, but you might see some interesting (or not) Ads around my content. I won’t mind if you all start clicking everywhere. I’m not suggesting anything… :p
]]>To implement that in Tasker I have used RESTask for Tasker (because I had troubles having the native HTTP Post action work for me):
RESTask for Tasker (Free, Google Play) →
Next create 3 global variables in Tasker (self-explanatory I guess):
Then create a new Task with the following 4 actions:
The first action will be a Variable Set %payload:
Name:%payload To: { "method" :"login", "params" : { "appType" :"Kasa_Android", "cloudPassword" :"%TPLPASS", "cloudUserName" :"%TPLUSER", "terminalUUID" :"%TPLTERM" } }
The second action will be the RESTask call:
Further down we need to set the custom body to %payload:
The third action will be a Javascriptlet with the following code, to extract the token (from the rtres, passed from RESTask) to the %mtoken variable:
var mtoken = JSON.parse(rtres).result.token;
Finally, here the fourth action will just be a Flash action, that will show the token:
Flash [ Text:%mtoken Long:On ]
The whole task should end up looking like this:
You can run the Task and it should flash the token on the screen!! Yay
]]>First you need to generate an UUID v4. You can open https://www.uuidgenerator.net/version4 and pick the one they give you there (no need to create one every time, one is fine, just keep it). This will represent your Client Term ID (like the one of your Kasa App).
Now this is how you can authenticate to the TP Link cloud backend and obtain a token:
We have to do a POST request to https://wap.tplinkcloud.com, with the following payload:
{ "method": "login", "params": { "appType": "Kasa_Android", "cloudUserName": "XXXXX", "cloudPassword": "XXXXX", "terminalUUID": "MY_UUID_v4" } }
In the payload, replace cloudUserName and cloudPassword with your TP-Link (or Kasa) credentials, and the terminalUUID with the one you got above.
Remember to pass the Header Content-Type: application/json as well.
No need to have any extra tool, you can simply try it with Hurl.it from your browser:
What you’ll get should look like this:
{
"error_code": 0,
"result": {
"regTime": "2017-01-06 08:42:35",
"email": "XXXXX",
"token": "YOUR_TOKEN_HERE"
}
}
Isn’t that cool? Of course you can implement that in any language, Shell script with cURL, Python, Node.js,… Your token is response.result.token.
In a next post I’ll show how to do the same with Tasker!
From here you can also have a look at this post: Get the end point URL and HS100 Device ID.
]]>To replay the steps below, be sure to already have:
In the next step, I recommend you use Hurl.it service to test the API calls, it’s the easiest way.
Destination:
{“method”:”passthrough”, “params”: {“deviceId”: “YOUR_DEVICE_ID_HERE”, “requestData”: “{\”count_down\”:{\”get_rules\“:null}}” }}
{“error_code”:0,”result”:{“responseData”:”{\”count_down\” :{\”get_rules\”:{\”rule_list\” :[{\”enable\”:0,\”id\”:\”YOUR_ RULE_ID_HERE \”,\”name\”:\”\”,\“delay\”:1800,\”act\”:0}],\” err_code\”:0}}}”}}
{“method”:”passthrough”, “params”: {“deviceId”: “YOUR_DEVICE_ID_HERE”, “requestData”: “{\”count_down\”:{\”edit_rule\“:{\”name\”:\”\”,\”act\”:0,\” enable\”:1,\”id\”:\”YOUR_RULE_ ID_HERE \”,\”delay\”:1800}}}” }}
Have fun!!
I’ve found out a way to know what is the URL, no more hit and miss needed any more!
The only thing that you’ll need is your TP-Link token (see how to get it). In a terminal, run the following command:
curl -s --request POST "https://wap.tplinkcloud.com?token=YOUR_TOKEN_HERE HTTP/1.1" \
--data '{"method":"getDeviceList"}' \
--header "Content-Type: application/json"
As an alternative, you can use hurl.it, an online version of curl, like shown on the picture below:
As Parameters use simply this:
{"method":"getDeviceList"}
As the name of the method implies, this will list all your HS100 devices (maybe other, please tell me what you see!). You should get something like this:
{ "result" : { "deviceList" : [ { "appServerUrl" : "https://eu-wap.tplinkcloud.com", "isSameRegion" : true, "deviceMac" : "", "status" : 1, "hwId" : "XXXX....", "deviceId" : "XXXX....", "oemId" : "XXXX....", "fwVer" : "1.0.8 Build 151101 Rel.24452", "deviceType" : "IOT.SMARTPLUGSWITCH", "alias" : "My Smart Plug", "fwId" : "BFF24826FBC561803E49379DBE74FD71", "deviceName" : "Wi-Fi Smart Plug", "deviceHwVer" : "1.0", "role" : 0, "deviceModel" : "HS100(EU)" } ] }, "error_code" : 0 }
In the results above, we’ll get a list of all our TPLink devices, with their alias (the custom name we have given it), the deviceId, and the appServerUrl.
The appServerUrl is the one we’ll need to use in further POST requests, to control the plug.
]]>The first thing we need is the ability to control the HS100 smart plug from Tasker on our Android phone. If you haven’t already, be sure to check my preview post where I explain how to create two Tasker Tasks, that will turn our plug On and Off.
For the next step I’ll use the WearTasker: with this app you can choose some of your Tasker tasks and publish them on your watch. They will show as buttons when you open WearTasker on your wrist, and the corresponding task will run when pressed. Quite simple. TBH WearTasker feels like a natural extension of Tasker to Android Wear.
WearTasker - Tasker for Wear (Free+, Google Play) →
You can buy a Pro in-app purchase if you’d like, which unlocks some cool features, but what I use here works with the free version.
So let’s publish both our smart plug tasks. Open Wear Tasker on your phone, and create a new shortcut. Choose your “Power On Plug” task, and name it however you like. You can also select an icon and a color. Then repeat the same operations with the other task:
Back to the app, it should now look like this:
And that’s how it will look on your watch:
Now try to press one of the Task and see how your HS100 smart plug responds!
I love WearTasker. I find it extremely usefull to be able to call Tasker tasks from my watch. Some other tasks I have there are one to power off my home server, one to refresh my Plex library, and one to send a Whatsapp to my wife with my current location and current route ETA.
]]>To quickly recap, this is the command that can change the plug state (depending on the %state variable, 0: switch off, 1: switch on):
curl --request POST "https://eu-wap.tplinkcloud.com/?token=%token HTTP/1.1" \ --data '{"method":"passthrough", "params": {"deviceId": "%deviceId", "requestData": "{\"system\":{\"set_relay_state\":{\"state\":%state}}}" }}' \ --header "Content-Type: application/json"
In Tasker let’s create a new Task called “Switch Plug On ”, then add 3 “Variable Set” actions, to set the value of %deviceId, %token and %state to the right values for you.
Then we’ll use an HTTP Post action, that we’ll configure like this:
Note: Remember to change the URL above to the one that works for you (check the Common Issues section at the end of first post and its comments).
The resulting Tasker Task will look like this:
In text form, it looks like this:
Switch Plug On 💡 (143) A1: Variable Set [ Name:%deviceId To:YOUR_DEVICE_ID_HERE Recurse Variables:Off Do Maths:Off Append:Off ] A2: Variable Set [ Name:%token To:YOUR_TOKEN_ID_HERE Recurse Variables:Off Do Maths:Off Append:Off ] A3: Variable Set [ Name:%state To:1 Recurse Variables:Off Do Maths:Off Append:Off ] A4: HTTP Post [ Server:Port:https://eu-wap.tplinkcloud.com Path:?token=%token Data / File:{"method":"passthrough", "params": {"deviceId": "%deviceId", "requestData": "{\"system\":{\"set_relay_state\":{\"state\":%state}}}" }} Cookies: User Agent: Timeout:30 Content Type:application/json Output File: Trust Any Certificate:Off ]
If you have curl available from the command line on your Android phone, you can alternatively use a Run Shell action, instead of the HTTP Post action.
In this case, the Tasker Task would look like this:
And in text form it would read like this:
Switch Plug On 💡 (143) A1: Variable Set [ Name:%deviceId To:YOUR_DEVICE_ID_HERE Recurse Variables:Off Do Maths:Off Append:Off ] A2: Variable Set [ Name:%token To:YOUR_TOKEN_ID_HERE Recurse Variables:Off Do Maths:Off Append:Off ] A3: Variable Set [ Name:%state To:1 Recurse Variables:Off Do Maths:Off Append:Off ] A4: Run Shell [ Command:curl --request POST "https://eu-wap.tplinkcloud.com/?token=%token HTTP/1.1" --data '{"method":"passthrough", "params": {"deviceId": "%deviceId", "requestData": "{\"system\":{\"set_relay_state\":{\"state\":%state}}}" }}' --header "Content-Type: application/json" Timeout (Seconds):0 Use Root:Off Store Output In: Store Errors In: Store Result In: ]
Once you have the Switch On task working, you can simply clone it in Tasker and change the %state variable to 0, and that will give you the “Switch Off Plug” task!
Do not hesitate to contribute in the comments with any Profile idea you have to play with your TP-Link HS100 smartplug.
You can acquire Tasker from the Play Store, personally I believe it’s really worth the money. You can also download a 7 days free trial from Tasker official website.
In a next post I’ll show how you can control your smart plug from your Android smartwatch, which is super cool!
]]>Please be sure to refer to this post if you are unsure how to obtain the Token and DeviceID that are necessary to control the plug via TP-Link cloud service.
TL;DR: The one-liner below, run in a Linux/Android/Mac shell, will return ON or OFF, indicating what is the state of an HS100 relay:
curl -s --request POST "https://wap.tplinkcloud.com/?token=TOKEN_HERE HTTP/1.1"\ --data '{"method":"passthrough", "params": {"deviceId": "DEVICEID_HERE", "requestData": "{\"system\":{\"get_sysinfo\":null},\"emeter\":{\"get_realtime\":null}}" }}'\ --header "Content-Type: application/json" | grep -q '..relay_state..:1' && echo "ON" || echo "OFF"
I’ve put this line above in a Tasker shell action, and I can perfectly obtain my HS100 state from Tasker now! So cool.
For the one wondering what we can obtain with the get_sysinfo command: run the following line. Here I extract from the Json output the responseData field, which contains the interesting payload, I unescape it with sed, and pretty-format it using json_pp:
curl -s --request POST "https://wap.tplinkcloud.com/?token=TOKEN_HERE HTTP/1.1"\ --data '{"method":"passthrough", "params": {"deviceId": "DEVICEID_HERE", "requestData": "{\"system\":{\"get_sysinfo\":null},\"emeter\":{\"get_realtime\":null}}" }}'\ --header "Content-Type: application/json" | jq ".result.responseData" | sed -e 's#\\\"#"#g; s#^\"##; s#\"$##' | json_pp
The output would be something like this. I’ve masked most of the data for obvious privacy reasons, but you can still get the idea. We see the position, time the device was On, MAC Address, Model, Alias (custom name)…
{
"system" : {
"get_sysinfo" : {
"fwId" : "FWID_HERE",
"relay_state" : 1,
"dev_name" : "Wi-Fi Smart Plug",
"oemId" : "OEMID_HERE",
"on_time" : 451,
"model" : "HS100(EU)",
"icon_hash" : "",
"updating" : 0,
"led_off" : 0,
"err_code" : 0,
"longitude" : LONGITUDE_HERE,
"mac" : "MAC_ADD_HERE",
"hwId" : "HWID_HERE",
"rssi" : -53,
"deviceId" : "DEVICEID_HERE",
"latitude" : LATITUDE_HERE,
"sw_ver" : "1.0.8 Build 151101 Rel.24452",
"alias" : "My Smart Plug",
"type" : "smartplug",
"hw_ver" : "1.0",
"feature" : "TIM",
"active_mode" : "schedule"
}
},
"emeter" : {
"err_msg" : "module not support",
"err_code" : -1
}
}
I guess an HS110 which has some energy consumption colection capability would give more data in the emeter section. Mine is an HS100, and I get “module not support(ed)”. Don’t hesitate to send me some example of an HS110 output energy data, I’ll put them here as well.
We can also extract one particular field’s value using a one-liner like the one below. Here I would extract the value of the relay_state field:
curl -s --request POST "https://wap.tplinkcloud.com/?token=TOKEN_HERE HTTP/1.1"\ --data '{"method":"passthrough", "params": {"deviceId": "DEVICEID_HERE", "requestData": "{\"system\":{\"get_sysinfo\":null},\"emeter\":{\"get_realtime\":null}}" }}'\ --header "Content-Type: application/json" | jq ".result.responseData" | sed -e 's#\\\"#"#g; s#^\"##; s#\"$##' | jq ".system.get_sysinfo.relay_state"]]>
In IFTTT we’ll use the Maker service, which let’s us call a custom HTTP GET/POST request with some Json payload, so that’s exactly what we need here.
Note: If you haven’t already, follow the steps in my previous post, to figure out what your Token and Device ID are.
Then create a new Recipe Applet like you normally would in IFTTT, and choose the “Maker Webhook / Make a Web Request” action service to define a new action.
For the URL, I’ve used:
https://eu-wap.tplinkcloud.com/?token=YOUR_TOKEN_HERE
and the method type is POST.
Note: For the URL: please read this other post, as you might need to use a different URL depending the region where you are from.
The Content Type is application/json, and the payload to switch the plug On should be:
{"method":"passthrough", "params": {"deviceId": "YOUR_DEVICEID_HERE", "requestData": "{\"system\":{\"set_relay_state\":{\"state\":1} } }" } }
If you want to switch the plug Off, just replace the state 1 with state 0.
Notice how I have added extra carriage returns to separate the “}” at the end, otherwise IFTTT was giving me some error.
That’s it! Easy, right?
]]>Kasa for Mobile (Free, Google Play) →
These are already really cool features, but I wanted to be able to do more with it: for example, turn on the plug when I arrive home, and turn it off 5min after I leave home. This is of course out of the scope of the Kasa app features, but I was more thinking about being able to control the plug from a smartphone automation App like Tasker, or a cloud service like IFTTT.
While there are some resources available explaining how to control the TP-Link HS100 plug from another device connected to the same Wifi network, I haven’t been able to find any that would explain how to do it via the TP-Link web service, so this is what I’ll show here.
In this post I’ll show how to change the relay state of the plug via command line from any device connected to Internet, not only from the local Wifi, which can then be called from a script, or from Tasker on Android.
There are basically 3 components involved here:
This is how the Kasa app communicates itself with the TP-Link web service. In this case when the user switches the plug On, the app will send an HTTP POST request (over SSL) like this one:
POST https://eu-wap.tplinkcloud.com/?token=74adcc7e-64f7-47c1-a751-dece6d2f4704&appName=Kasa_Android&termID=c69d10e5-5307-4602-b2c8-eee8f3761238&appVer=1.4.4.607&ospf=Android+6.0.1&netType=wifi&locale=en_US HTTP/1.1 Content-Type: application/json User-Agent: Dalvik/2.1.0 (Linux; U; Android 6.0.1; XXPhoneModelXX) Connection: Keep-Alive Content-Length: 160 Host: eu-wap.tplinkcloud.com { "method":"passthrough", "params":{ "deviceId":"80067AC4FDBD41C54C55896BFA28EAD38A87A5A4", "requestData":"{\"system\":{\"set_relay_state\":{\"state\":1}}}" } }
To which the TP-Link server will respond (when successful):
{ "error_code":0, "result":{ "responseData":"{\"system\":{\"set_relay_state\":{\"err_code\":0}}}" } }
As expected, almost instantly, the plug will switch On.
I have highlighted 4 fields/values in the request above. This values are specific to you and your plug. This is how the TP-Link web service identifies you and which of your plugs you want to switch.
Note: I have changed all the values shown above, to avoid having anyone control my plug by error. I have also formated the Json payloads.
Knowing that, we can now reproduce the same requests, for example using curl, from the command line (even in a Tasker task!). The TP-Link server will have aboslutely no way of telling of the request was originating from the Kasa app or a script.
Now, let see how we can call again the same request from a linux command line using curl. I have removed some parameters, like the User-Agent, and some others in the URL query string, and it still works, so I assume they eventually use them for statistical reasons only:
curl --request POST "https://eu-wap.tplinkcloud.com/?token=74adcc7e-64f7-47c1-a751-dece6d2f4704 HTTP/1.1" \ --data '{"method":"passthrough", "params": {"deviceId": "80067AC4FDBD41C54C55896BFA28EAD38A87A5A4", "requestData": "{\"system\":{\"set_relay_state\":{\"state\":1}}}" }}' \ --header "Content-Type: application/json"
Of course, we need a way to identify the value of this fields. That’s what I’ll cover below.
UPDATE (06/20/2017): this “Data extraction” part is now obsolete. I leave it here for historical purpose. Instead you can now follow the following steps:
OBSOLETE CONTENT STARTS HERE
This is what we’ll need:
First let’s take an Android Backup of the Kasa app:
adb backup -f backup.ab com.tplink.kasa_android
Now let’s unpack the backup with this one-liner:
dd if=backup.ab bs=1 skip=24 | python -c "import zlib,sys;sys.stdout.write(zlib.decompress(sys.stdin.read()))" | tar -xvf -
Now we just need to find the relevant information. I show you how to retrieve the values for each field:
$ sqlite3 db/iot.1.db "select token from accounts;"
74adcc7e-64f7-47c1-a751-dece6d2f4704
$ sqlite3 db/iot.1.db "select deviceAlias,deviceID from devices;"My Smart Plug|80067AC4FDBD41C54C55896BFA28EAD38A87A5A4
$ cat f/INSTALLATION
c69d10e5-5307-4602-b2c8-eee8f3761238
For people on Windows, some users reported in the comments below that you can use Android Backup Extractor (ABE) to extract the backup, instead of dd. See these links:
OBSOLETE CONTENT ENDS HERE
If you follow these steps above you can extract the token and device ID corresponding to your Kasa app and smartplug. You can now control the smart plug from anywhere, without the need to use the Kasa app. For example, you can use the curl expression below, after you replace the values with yours:
curl --request POST "https://eu-wap.tplinkcloud.com/?token=YOUR_TOKEN_HERE HTTP/1.1" \ --data '{"method":"passthrough", "params": {"deviceId": "YOUR_DEVICEID_HERE", "requestData": "{\"system\":{\"set_relay_state\":{\"state\":1}}}" }}' \ --header "Content-Type: application/json"
curl --request POST "https://eu-wap.tplinkcloud.com/?token=YOUR_TOKEN_HERE HTTP/1.1" \ --data '{"method":"passthrough", "params": {"deviceId": "YOUR_DEVICEID_HERE", "requestData": "{\"system\":{\"set_relay_state\":{\"state\":0}}}" }}' \ --header "Content-Type: application/json"
If you have any questions or comment, please, do not hesitate to let me know, using the comments below :).
In future posts I’ll show how to integrate your HS100 smart plug with the cloud Automation service IFTTT (see here), and how to control it from the Android Automation app Tasker or your Smartwatch, as well as how to get the state (On/Off) of the plug.
]]>