close-circle
Close

REST control over zmote

A project log for zmote

Wi-Fi Universal Remote using the ESP8266

Urmil Parikh 10/25/2015 at 04:570 Comments

NOTE: This log is applicable to older, v1 version of zmote. If you bought a zmote recently (or planning to buy one) and are planning to control through REST APIs, please contact us by email for latest APIs.

zmote is an open-source, Wi-Fi enabled IR blaster / receiver widget that offers full control through simple REST APIs.

We will go through important APIs and learn how to configure a zmote, how to send, receive and analyze IR signals using a zmote, as well as sending IR signals to it remotely, through internet using its REST-to-MQTT bridge.

This project will be useful to any one who wants to
- integrate IR transmission / monitoring features into a home automation project
- analyse IR signals from a remote for hacking or study purpose
- find hidden (discrete) IR codes for a given IR-controlled device
- create a web front-end or a mobile app for IR-controlled devices


Configuring SSID / password

First thing you need to do with a new zmote is to connect it to your Wi-Fi router. Every time zmote is powered ON, it configures itself as Wi-Fi access point (AP) so that one can connect to it (no password required) and configure it for connection with your router.

When connected to zmote through its AP interface, zmote can be accessed at 192.168.4.1 IP.

Let us find out its MAC address, which we will need later:

$ curl http://192.168.4.1/api/wifi/mac

{"ap_mac": "1a-fe-34-f2-e9-58", "sta_mac":"18-fe-34-f2-e9-58"}

Now, let's configure zmote to connect to our Wi-Fi router with SSID router and password sample.

$ curl -X PUT -H 'Content-Type: application/json' -d '{"ssid":"router","password":"sample"}' http://192.168.4.1/18-fe-34-f2-e9-58/api/wifi/connect

{"status":"OK"}

Note that we have used sta_mac from earlier API here. This value is required in many APIs to make sure that our commands reach intended zmote (in case there are dynamic changes in IP by DHCP server).

After above command, zmote will configure itself to connect to router, will restart and get an IP from the DHCP server running on yout router. It may take 15-30 seconds for it to get connected and register itself with zmote.io server.

Registering as a client

zmote supports a special feature of ability to control over internet without any special user authentication. It does that by authenticating client (web browser or mobile app). In order to control a zmote through internet, a client must authenticate with zmote.io server when in the same local network (actually, same public IP as seen by the server) as the zmote.

For example, once zmote is connected to your home router and you visit app.zmote.io from your PC browser, your PC browser will automatically get access to your zmote without any special pairing. Then, even if your PC goes out of home network, let's say it connects through your office network, it will be able to send commands to your zmote through zmote.io server.

This is achieved by a client by first registering itself with v1.zmote.io, which is very simple:

$ curl http://v1.zmote.io/client/register

{"_id": "5629dc538b1a4a0e005323b4", "secret": "I5UBVi42afQ0uIxTPoFcF4SAo1wnMPI4"
}

The _id and secret returned by this API are important and are required in the next step, authentication.

Authentication requires cookies so that authentication token need not be passed manually to APIs that require it. Let's authenticate our client now:

$ curl -c zmote-cookies http://v1.zmote.io/client/auth -X POST -d '{"_id":"5629dc538b1a4a0e005323b4","secret":"I5UBVi42afQ0uIxTPoFcF4SAo1wnMPI4"}' -H 'Content-Type: application/json'

{"extIP":"180.151.117.39"}

Note that we save cookies to a file for future use. The extIP value shows client's public IP as seen by zmote.io server. Any zmotes with the same public IP are now accessible to us.

Discovery of zmote (local or remote access)

Now that we have registered and authenticated our client, let's find out zmotes accessible to us. We will need the cookies we saved in last step.

We refer a zmote as a 'widget' internally:

$ curl -b zmote-cookies http://v1.zmote.io/widgets

[{
    "_id": "5607e785bacb1f187a71a9c2",
    "chipID": "00f2e958",
    "extIP": "180.151.117.39",
    "connected": true,
    "localIP": "192.168.1.7",
    "name": "zmote_f2e958",
}]

Note that this API returns a JSON array with one entry for each zmote we have access to. The reply shows that we have access to only one zmote, having widget id 5607e785bacb1f187a71a9c2. We will need this widget id later, when we want to access our zmote remotely.

Note that it has same external IP as ours and it is connected to zmote.io server right now. So, it is very likely that we will find it in our local network at its reported localIP.

Let's check it out...

$ curl http://192.168.1.7/api/wifi/mac

{"ap_mac": "1a-fe-34-f2-e9-58", "sta_mac":"18-fe-34-f2-e9-58"}

Surely, there it is.

Since it is available locally, we can directly control it without having to go through zmote.io server.

Sending IR codes

zmote requires IR code specification in a specific JSON format, as described in comments below:

{
    // Modulation frequency
    "frequency": 38000,
    
    // Sequence of ON and OFF times, described in number of cycles
    // for the given modulation frequency
    "seq": [76,1064,15,60,15,136,15,2401],

    // Number of entries in 'seq'
    "n": 8,

    // Repeat code specification: [,,]
    //   count: number of times to send repeat code
    //   start: start index in 'seq' for repeat code
    //   end: 1 + end index in 'seq' for repeat code
    "repeat": [0,0,8]
}

Above code is to trigger shutter for a Nikon DSLR camera. We can send it to our zmote as:

$ curl -X POST -H 'Content-Type: application/json' -d '{"frequency":38000,"seq":[76,1064,15,60,15,136,15,2401],"n":8,"repeat":[0,0,8]}' http://192.168.1.7/18-fe-34-f2-e9-58/api/ir/write  {"status":"ok"}

Note that we do not need authentication when accessing a zmote locally, but we need its MAC address.

Return status of ok indicates that code is accepted and that zmote has started transmitting it.

We may get return status as busy. A busy status indicates that zmote is busy transmitting earlier code and cannot accept a new code right now. That can happen with a long code or a code with many repeats. In such cases, we should wait for some time and then try sending again.

We also may get return status as badformat, which means that the IR code we sent is not in acceptable format.

Sending IR codes remotely

In case when zmote is not available locally, but it is connected to zmote.io server, we can send IR codes to it through the server. Yes, as you guessed, this API requires an authenticated client. It is time to use authentication cookies we saved in a file in earlier steps.

Note that we will also need widget id that we got during zmote discovery.

$ curl -b zmote-cookies -X POST -H 'Content-Type: application/json' -d '{"frequency":38000,"seq":[76,1064,15,60,15,136,15,2401],"n":8,"repeat":[0,0,8]}' http://v1.zmote.io/widgets/5607e785bacb1f187a71a9c2/api/ir/write

{
    "status": "pending",
    "_id": "562a04548b1a4a0e005323b8"
}

Since it may take some time for our command to reach zmote through MQTT, this API returns without waiting for a response from zmote, with a pending status.

We also get a command id, which we can use to query status of our pending request:

$ curl -b zmote-cookies http://v1.zmote.io/widgets/5607e785bacb1f187a71a9c2/command/562a04548b1a4a0e005323b8

{"status":"ok"}

Return status can still be pending or one of ok, badformat or busy as with sending IR codes locally.

Recording IR codes

zmote has an IR receiver (in front), so it can also be used to record IR signals.

First, we need to trigger recording of IR signals:

$ curl http://192.168.1.7/18-fe-34-f2-e9-58/api/ir/trigger

{"status":"ok"}

Then, we need to poll zmote at regular intervals for any received IR signals:

$ curl http://192.168.1.7/18-fe-34-f2-e9-58/api/ir/read

{"status":"ok","trigger":[236,51994,144,1387,8487,4155,567,1547,569,1545,572,486,566,494,570,488,571,488,569,489,568,491,569,489,568,491,570,1544,570,1544,564,1551,571,487,571,487,566,492,569,23969,569,1544,568,1546,568,492,564,494,567,491,565,493,566,492,567,491,568,491,565,493,568,1547,570,1545,571,1543,566,493,567,491,542,516,567]}

In the return message, trigger is a sequence of ON and OFF times, in micro-seconds, recorded by zmote.

Since the IR receiver may catch noise, partial or false signals, this recorded sequence needs to be validated before use. There are many free and paid tools available for such analysis. Server at zmote.io has such a tool accessible online for developers and hackers to use it.

Let's analyse above received sequence. The JSON obtained above API can directly be POSTed to zmote.io server for analysis:

curl http://v1.zmote.io/irp/decode -X POST -H 'Content-Type: application/json' -d '{"trigger":[236,51994,144,1387,8487,4155,567,1547,569,1545,572,486,566,494,570,488,571,488,569,489,568,491,569,489,568,491,570,1544,570,1544,564,1551,571,487,571,487,566,492,569,23969,569,1544,568,1546,568,492,564,494,567,491,565,493,566,492,567,491,568,491,565,493,568,1547,570,1545,571,1543,566,493,567,491,542,516,567]}'

{"protocol":"JVC{2}","device":3,"obc":28,"misc":"no repeat"}

Our analysis shows that the recorded sequence matches JVC protocol with device = 3 and obc (or function) = 28.

It is possible for analyser to not find any valid sequence, in which case, it will return not found error.

It is also possible for analyser to wrongly classify a given sequence. It is recommended to record a sequence two or three times, analyse them independently and only then conclude about its correct protocol / format.

Once we are sure about signal format, we may want to convert it to the format zmote expects and save it so that it can be sent to zmote at a later time whenever required. Server at zmote.io offers an API for that as well:

$ curl http://v1.zmote.io/irp/encode -X POST -H 'Content-Type: application/json' -d '{"protocol":"JVC","device":3,"obc":28}'

{"frequency":37900,"n":36,"repeat":[0,2,36],"seq":[320,159,20,60,20,60,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,60,20,60,20,60,20,20,20,20,20,20,20,899]}

It is recommended to test this code at least once with actual device before saving it for future use.

Note that some IR protocols (e.g. RC5) use a toggle bit, which is to be toggled on successive key presses, to distinguish it from repeat code (long press). For such protocols, there are two IR codes, one with toggle = 0, other with toggle = 1. Such codes can be generated using misc field as follows:

$ curl http://v1.zmote.io/irp/encode -X POST -H 'Content-Type: application/json' -d '{"protocol":"RC5","device":2,"obc":8,"misc":"T=0"}'

{"frequency":36000,"n":22,"repeat":[0,0,22],"seq":[32,32,64,32,32,32,32,32,32,64,64,32,32,32,32,64,64,32,32,32,32,3264]}

$ curl http://v1.zmote.io/irp/encode -X POST -H 'Content-Type: application/json' -d '{"protocol":"RC5","device":2,"obc":8,"misc":"T=1"}'

{"frequency":36000,"n":22,"repeat":[0,0,22],"seq":[32,32,32,32,64,32,32,32,32,64,64,32,32,32,32,64,64,32,32,32,32,3264]}

Conclusion

In this project, we covered most important zmote APIs that a user / hacker / learner may need.

Information provided here can be used to control IR-controlled gadgets from a script, a web page or an automation system.

We also saw how IR codes can be sent remotely when zmote is not available in local network. This feature alone can open up a lot of IoT / home automation applications.

In the end, we saw how IR signals can be captured from a physical IR remote, analysed for correctness and re-sent using zmote. This method is useful for learning more about IR signals and their format, as well as to find out discrete (hidden) IR codes for many devices.

Discussions