Thursday, 23 February 2017

Connected by TCP API (The Lightbulbs made by TCPI)


TCPi is no longer making their app or their devices and the cloud service is dead so this means you can get it even cheaper now but I've had issues with their app not working so this is some details on how to talk to their gateway running the newer locked down firmware.

The older firmware has a very nice web interface with no authentication on HTTP you can juse use in your browser it's all graphical and pretty.  You can also ssh into it on port 22 with the username root and password thinkgreen.  The API calls are the same but the old version will accept ANYTHING in token. Both of these access methods are insecure and should not be exposed outside of your LAN.

The device WILL auto update if it has internet access. If you like the web/ssh make sure you keep it on your LAN only.  Once it updates the app may stop working (may not be able to detect the AP), and it may loose authentication every time you close it so you need to press the button to use it which makes their APP pointless aside from setup.  There are 3rd party apps that you can get that work however.

If you want to keep using the OEM app and you are having these problems a small proxy could be written to give the app a valid token each time it starts.

The API should work with the older firmware (where port 80 is open) but you don't need to provide a valid token in your strings.

I have written a python module if anyone is interested contact me, this entry will just detail the raw calls you can make to the device and how you can convince the OEM app it's talking to a gateway when it's actually talking to your web server.

This article is written for developers and experienced IoT hackers that know how to setup a SSL web server and make XML calls via HTTP(s).   You will not find this useful if you aren't at least mildly experienced with these things.

All API calls are XML over HTTP(s) and the gateway will accept GET or POST requests as long as the form data is correct.
There are only 2 fields used:
cmd - The command you are calling
data - All the XML data for the request

You can specify the server IP by hitting the little gear in the lower right hand corner of the app.

The App starts by sending an auth request. All requests are done via HTTPs on port 443.  Again new firmware only. Old firmware uses port 80 standard http.

*** Auth request will be greeted by a 404 message unless the gateway is in auth mode (press the button, blinking lights etc) ***

/gwr/gop.php?cmd=GWRLogin&data=<gip><version>1</version><email>dummy username</email><password>dummypassword</password></gip>

The username and password do not matter. It will only accept them in auth mode... This will return a token.. Make sure you write this down :P

<gip><version>1</version><rc>200</rc><token>YOUR TOKEN GOES HERE</token></gip>

You will need to included the <token>YOUR TOKEN GOES HERE</token> with EVERY subsequent interaction with this server.   As far as I can tell they never expire.

Next up the client requests the following:
/gwr/gop.php?cmd=GatewayGetInfo&data=<gip><version>1</version><token>YOUR TOKEN</token></gip>
And gets back:
<gip><version>1</version><rc>200</rc><gateway><gid>A NUMBER</gid><online>0</online><primary>1</primary><fwversion>3.0.80</fwversion><mac>D4:A9:28:XX:XX:XX</mac><serial>111-1111-11111</serial><lastreboot></lastreboot><lastseen></lastseen><lanip>192.168.XXX.XXX/24</lanip><externalip></externalip><gipserver>tcp.greenwavereality.com</gipserver><type>Apollo3</type><modelno>not set</modelno></gateway></gip>

Online will reflect if it can get to the WAN or not... Mine is blocked at the router... Since their server is down, who cares anyway right?

The next 2 requests aren't supported by my gateway but you need to return the error code so the client doesn't freak out
/gwr/gop.php?cmd=AccountGetDetails&data=<gip><version>1</version><token>TOKEN AGAIN</token></gip>

So give it:
<gip><version>1</version><rc>500</rc></gip>

/gwr/gop.php?cmd=AccountGetPreferences&data=<gip><version>1</version><token>YOU GUESSED IT</token></gip>

And I got this back, and the client seems OK with it...
<html><head><title>400 Bad Request</title></head><body><h1>400 Bad Request</h1><p>Unknown GOP command: AccountGetPreferences</p></body></html>

The following command gets your room list, the easiest way to get the response is to send the command to your gateway...
/gwr/gop.php?cmd=RoomGetCarousel&data=<gip><version>1</version><token>TOKE TOKE</token><fields>name,power,product,class,image,imageurl,control</fields></gip>

The response is super long and will be different for each system... If you can't send this request to your gateway yourself this document isn't for you.... FYI you don't need to do all the prior stuff to run this command, you just need a valid token.

Then this command gets the list of Scenes/Smart Control settings... Mine are default and the list isn't too long so I'll post the response.
/gwr/gop.php?cmd=SceneGetList&data=<gip><version>1</version><token>TOKENS FOR ALL</token><fields>detail,imageurl,bigicon</fields><islocal>1</islocal></gip>

And you will get back:
<gip><version>1</version><rc>200</rc><enable>1</enable><scene><sid>1</sid><active>1</active><name>Home</name><desc></desc><order>0</order><type>manualcustom</type><icon>images/scene/home.png</icon><device><id>216510933483966411</id><type>D</type><cmd><type>power</type><value>1</value></cmd><cmd><type>level</type><value>100</value></cmd></device><device><id>359879170926881236</id><type>D</type><cmd><type>power</type><value>1</value></cmd><cmd><type>level</type><value>100</value></cmd></device><device><id>360160645903591892</id><type>D</type><cmd><type>power</type><value>1</value></cmd><cmd><type>level</type><value>100</value></cmd></device></scene><scene><sid>2</sid><active>1</active><name>Away</name><desc></desc><order>1</order><type>manualcustom</type><icon>images/scene/away.png</icon><device><id>216510933483966411</id><type>D</type><cmd><type>power</type><value>0</value></cmd><cmd><type>level</type><value>100</value></cmd></device><device><id>359879170926881236</id><type>D</type><cmd><type>power</type><value>0</value></cmd></device></scene><scene><sid>3</sid><active>1</active><name>Night</name><desc></desc><order>2</order><type>manualcustom</type><icon>images/scene/night.png</icon><device><id>216510933483966411</id><type>D</type><cmd><type>power</type><value>0</value></cmd><cmd><type>level</type><value>100</value></cmd></device><device><id>359879170926881236</id><type>D</type><cmd><type>power</type><value>0</value></cmd></device></scene></gip>

Then it tries to get room information:
/gwr/gop.php?cmd=RoomGetInfoAll&data=<gip><version>1</version><token>I'LL TOKE THAT</token><rid>0</rid><fields>name,power,product,class,image,imageurl,control,other</fields></gip>

Again this response is large and unique to your setup/devices, please get your own and look at it, it's fairly self explanitory...

Then the app will try and grab the images of the devices on the current page (you will be at your first room by this point)
/gwr/gop.php?fmt=image&cmd=DeviceGetImage&data=<gip><token>THAT SILLY TOKEN</token><did>Device ID (DID) OF YOUR LIGHT BULB OR FIXTURE</did></gip>

This will return an actual PNG file in the flesh!  Try it yourself!

The app will also call this function while flipping pages, it also has a super long unique response:
/gwr/gop.php?cmd=RoomGetList&data=<gip><version>1</version><token>JRRTOLKEN</token></gip>

The bulb info response can be fetched with the following call:
/gwr/gop.php?cmd=DeviceGetInfo&data=<gip><version>1</version><token>T</token><did>Device ID of the bulb</did><fields>name,power,product,class,image,control,realtype,other,status</fields></gip>

This retuns XML data about the bulb, how it's setup, etc. and even has a BASE 64 encoded ASCII version of the PNG in it!

You can make a bulb go into identify mode where it flashes bright and dim with the following call:
/gwr/gop.php?cmd=DeviceSendCommand&data=<gip><version>1</version><token>T</token><did>Device ID</did><type>identify</type><val>1</val></gip>

Of course the same command with val set to 0 will turn it off.

The response for this as with most commands that don't give you any data is just a 200 as follows: <gip><version>1</version><rc>200</rc></gip>   You will see this a lot.

To find out what bulbs/dids belong to a fixture (a group of bulbs) you can use the following command.
/gwr/gop.php?cmd=DeviceVirtualGetList&data=<gip><version>1</version><token>T</token><did>FIXTURE VIRTUAL DID</did><fields>name,image,imgs,imageurl,realtype</fields></gip>

To add/remove a bulb from a fixture use these commands:
DeviceVirtualDeviceAdd and DeviceVirtualDeviceDelete.  The syntax is the same as the other commands with the initial <did> tag being the DID of the virtual device and then having a <device><did> pair for the bulb to be added or removed.  I don't know if this command supports more than one bulb at a time, I wouldn't push it :P


This again gives a long long long XML response which will contain the <did> and <name> elements of each bulb within that fixture.
Each bulb will be within it's own <device> tag.  Keep in mind you can't directly address a bulb that's linked to a fixture but this can be useful for removing it later.

To rename a lightbulb:
/gwr/gop.php?cmd=DeviceSetInfo&data=<gip><version>1</version><token>T</token><did>DID of the bulb, names aren't used here</did><name>FRIENDLY NAME OF THIS BULB</name><color>THIS IS THE ROOM IT BELONGS TO 0-9</color><other><rcgroup>YOU CAN PUT A CSV OF 1-4 IN HERE TO LINK THIS TO A PHYSICAL REMOTE CONTROL</rcgroup></other></gip>

The above command can be called with or without any of these params you just need the DID.. You can change the "color" or rcgroup without changing the name, etc.  Just leave the fields out you don't want to change.

Also if you put a bulb into a "color" of 10 or above it will lock up your gateway.

To rename a room you use the following.  Keep in mind if there's no bulb in a room things can get weird.
/gwr/gop.php?cmd=RoomSetInfo&data=<gip><version>1</version><token>T</token><rid>0</rid><name>MY ROOOMMMM</name></gip>
You'll get the usual 200 OK from these guys

You can also delete a device or virtual device with the following call:
/gwr/gop.php?cmd=DeviceDelete&data=<gip><version>1</version><token>T</token><did>BULB/FIXTURE ID TO DELETE</did></gip>

You can create a virtual device/fixture with this call:
/gwr/gop.php?cmd=DeviceVirtualCreate&data=<gip><version>1</version><token>T</token><color>ROOM #</color><name>NAME OF THE FIXTURE</name><image>BASE 64 ENCODED ASCII STRING OF A PNG YOU CAN LEAVE THIS TAG OUT COMPLETELY</image><producttype>159</producttype><device><did>DID of bulb 1</did></device><device><did>DID of bulb 2</did></device></gip>

FYI product type 159 is a virtual fixture and you can have as many <device><did></did></device> items as you want in a fixture (up to whatever the actual software limit is)

You will get back a special 200 message with the DID the gateway assigned your new virtual fixture.  You don't need to remember this though, you can easily get it from the room list.

<gip><version>1</version><rc>200</rc><did>123456781234567890</did></gip>

There is a GWRBatch command but I think it just allows you to run multiple queries within one, I have all but ignored it as it is not important.

The magic auth mode / search mode:
If you have a valid token you can put the device back into "auth" mode so you can add a new client or get a new token... This command is the EXACT SAME function as pushing the button..

/gwr/gop.php?cmd=GatewayInclusionStart&data=<gip><version>1</version><token>T</token></gip>

The unit will accept auth requests as well as learn any new bulbs within range.
If you get a 401 from this your token is no good.

If you want to check on the progress of a scan you can use this command, it will also tell you if it's still in auth mode:
/gwr/gop.php?cmd=GatewayInclusionProgressGet&data=<gip><version>1</version><token>T</token></gip>

The result is this:
<gip><version>1</version><rc>200</rc><gateway><gid>NUMBERS HERE</gid><state>1</state><progress>0</progress><start>0</start><end>0</end></gateway></gip>

I didn't have a bulb to learn with this so I'm not sure what will change if it finds one but the <state> will change from 1 to 0 when it's stopped scanning and left auth mode.

If you want to run a scene use the following call:
/gwr/gop.php?cmd=SceneRun&data=<gip><version>1</version><token>T</token><sid>1</sid></gip>

The SID is the ID of the scene you want to run, If you look up a whole bunch near the beginning the client requested all the Scenes, the SIDs are all listed there.

Scenes can be a little tricky to create so I'll break the code down more here... You will still need all the standard headers <gip><version> <token> when making this call, I have left them off to make this readable.

CMD = SceneCreateEdit  You can also use SceneDelete with the <sid> tag to delete a scene.

<sid>0</sid> # ID of the scene.  If you use 0 here the system will generate you an SID.  If you want to edit an existing entry specify it's SID
<active>1</active> # Enabled. Set to 0 if you want to disable this item
<name>test</name> # Name... Call it whatever you want
<type>manualcustom</type> # manually triggered. Use "schedulecustom" instead if you want to enable time/day schedules
<islocal>1</islocal> # always yes

### SCHEDULES ### SEE ABOVE
<every>0,1,2,3,4,5,6</every> #  0=Sun thru 6=Sat
<starttime>15:07</starttime> # Begin at (sunset, sunrise or mil time)  Zero padded digits please!
<stoptime>02:11</stoptime> # End at (sunset, sunrise or mil time)  Zero padded digits please! (not 1:3 but 01:03)

####################

<icon>tv.png</icon> # See below for a list

### DEVICES SECTION ### You can have any number of <device> items in here ####
<device><id>DID of the bulb you want to control</id> # DID of the bulb.
<type>D</type> # D here tells the gateway the above ID is a DEVICE and not a ROOM .Virtual devices/fixtures are D type as well.
<cmd><type>power</type><value>0</value></cmd></device> # This device is off, see below for dimming options
<device><id>3</id> # DID of second device in this case room/color #
<type>R</type> # This tells the gateway your above item is a ROOM and not a DEVICE
<cmd><type>power</type><value>1</value></cmd><cmd><type>level</type><value>80</value></cmd></device>   # This turns the device ON and sets the dim level to 80%

Here is a full list of icons:
tv.png - A TV
heart.png - Heart
sensor.png - Like an EKG
zroom_00.png - Black Room
zroom_01.png - Green Room
zroom_02.png - Blue Room
zroom_03.png - Red Room
zroom_04.png - Yellow Room
zroom_05.png - Purple Room
zroom_06.png - Orange Room
zroom_07.png - Aqua Room
zroom_08.png - Pink Room
zroom_09.png - Gray Room
clock.png - A clock
away.png - Person running away
light.png - Light bulb
lamp.png - Floor lamp
fan_cool.png - Blue fan
fan_heat.png - Red fan
star.png - Star
vacation.png - Plane
home.png - House
night.png - Partial Moon
target.png - Gun sight
washing_machine.png - Washing machine
music.png - Music note
dim.png - Contrast symbol
off_to_work.png - Briefcase
rainy.png - Umbrella
e_car.png - Car
tree.png - Xmas tree
thermostat.png - Thermometer
coffee.png - Mug
bolt.png - Lightning
eat.png - fork and knife

And finally the stuff you can find almost anywhere on how to get status, switch and dim bulbs/fixtures:

Status:
cmd is GWRBatch data is as follows to get multiple data:
<gwrcmds><gwrcmd><gcmd>RoomGetCarousel</gcmd><gdata><gip>\n<version>1</version><token>1234567890</token><fields>name,control,power,product,class,realtype,status</fields></gip></gdata></gwrcmd><gwrcmd><gcmd>UserGetListDefaultRooms</gcmd><gdata><gip><version>1</version><token>1234567890</token></gip></gdata></gwrcmd><gwrcmd><gcmd>UserGetListDefaultColors</gcmd><gdata><gip><version>1</version>\n<token>THIS TOKEN MATTERS!</token></gip></gdata></gwrcmd></gwrcmds>

This will return a huge amount of data about the rooms, colors, and devices. I suggest you use an XML parser library to deal with the output.

This will allow you to tell what bulbs/fixtures are part of your system and if they are on or off, what their dim level is and if they are marked as offline by the <offline>1</offline> tag which means the gateway cannot currently talk to it (out of range or switched off)

To Switch a bulb on or off use the cmd DeviceSendCommand with the <did> of the bulb/fixture and <value> of 1 or 0 for on or off.
The dim comand is exactly the same except you would add <type>level</type> to your data field and your value would be from 0% to 100%.

I think that covers everything the app can do. This should help you get going with this wacky and weird interface.  If this helped you please drop a comment below and if you have any questions please comment or contact me and I will gladly answer them.

Bonus:
Here is a simple Python script you can put on a web server that support SSL (made for Windows). It will proxy your app and give it a valid token you can specify.

It's slower than talking directly to the gateway but if your app is broken this will make it work at least...

The blog has removed my tabs I put spaces in, hopefully it still works.

#!c:\python27\python.exe -W ignore


import cgi, sys, urllib2, urllib, msvcrt, os


# Put your valid token & gateway IP in here and don't change anything else.

token = "YOURTOKENHERE"
gateway = "192.168.xx.xxx"

###############################################################################


# Header is always HTML even when it's an image -.-
print "content-type: text/html\n"

form = cgi.FieldStorage()

image = 0

# read all the keys and put them into a urlencoded var
data = ""
for key in form.keys():
 if key == "fmt" and form[key].value == "image": image = 1 # if they requested an image

data += urllib.urlencode({key : form[key].value}) + "&"

# chop off last extra &
data = data[:-1]



url = 'https://%s/gwr/gop.php' % gateway



# Give the users token for the login request, this is really the ONLY thing this script does -.-
if str(form).find('GWRLogin') != -1:
 print "<gip><version>1</version><rc>200</rc><token>%s</token></gip>" % token
 sys.exit(0)



try:
# All non login requests will be sent to the gateway
 content = urllib2.urlopen(url=url, data=data).read()

# If it's a raw image we need the output format to be binary
 if image == 1:
  msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)

# Output the data from the gateway to the client
 sys.stdout.write(content)


except:
 pass  



No comments:

Post a Comment