designing an api for the internet of things
TRANSCRIPT
The Hub
DevicesDevicesDevices
Hub
Cloud Components
DevicesDevicesDevices
Hub
ClientsClientsClients
ClientsClientsClients Local Network
Data Center
Internet
The Bridge
DevicesDevicesDevices
Bridge
Cloud Components
DevicesDevicesDevices
Bridge
ClientsClientsClients
Local Network
Data Center
Internet
The IP-Connected Device
Device
Cloud Components
Device
ClientsClientsClients
Local Network
Data Center
Internet
Reaching the Edge
DevicesDevicesDevices
Zetta
Zetta
DevicesDevicesDevices
Zetta
ClientsClientsClients
ClientsClientsClients Local Network
Data Center
Internet
The only thing we control
Desired Architectural Properties
• Decoupling of implementations
• Independent evolution
• Scale
• Load balancing
• Centralize complexity, distribute simplicity
• Reduce latency
• Support event streams
Architectural Constraints• Client-server, stateless, cacheable, layered
system, uniform interface, etc.
Wait, isn’t that REST?
How streams are done over HTTP today
• WebHooks
• Chunked Transfer-Encoding
• HTTP Pipelining
• Long Polling
• Server Sent Events
XXXXX
Connection info is in the action parameters or link URI
• mqtt://example.org:8863/topic
• stomp+ws://example.org/topic
• amqp://example.org/topic • { “href”: “ws://example.org”, “protocol”: “custom” }
Model resources as finite state machines
LED.prototype.init = function(config) { config .type('led') .state('off') .when('off', { allow: ['turn-on'] }) .when('on', { allow: ['turn-off'] }) .map('turn-on', this.turnOn) .map('turn-off', this.turnOff); };
Transitions are represented as affordances in the message.
"actions": [ { "name": "turn-on", "method": "POST", "href": “...”, "fields": [ { "name": "action", "type": "hidden", "value": "turn-on" } ] } ]
We need to consume an object stream.
links: [ { title: “pulse“, rel: [“http://rxapi.org/object-stream“], href: “ws://...” } ]
We need to consume an object stream.
» wscat -c ws://... connected (press CTRL+C to quit) < {"topic":"heartbeat/1/pulse","timestamp":1411757412119,"data":61} < {"topic":"heartbeat/1/pulse","timestamp":1411757412620,"data":65} < {"topic":"heartbeat/1/pulse","timestamp":1411757413121,"data":69} < {"topic":"heartbeat/1/pulse","timestamp":1411757413622,"data":71} < {"topic":"heartbeat/1/pulse","timestamp":1411757414124,"data":66} < {"topic":"heartbeat/1/pulse","timestamp":1411757414626,"data":64} < {"topic":"heartbeat/1/pulse","timestamp":1411757415128,"data":68} < {"topic":"heartbeat/1/pulse","timestamp":1411757415629,"data":63} < {"topic":"heartbeat/1/pulse","timestamp":1411757416130,"data":65} < {"topic":"heartbeat/1/pulse","timestamp":1411757416631,"data":63}
Monitor resource events before taking action.
links: [ { title: “logs“, rel: [ “monitor“, “http://rxapi.org/object-stream“ ], href: “ws://...” } ]
Monitor resource events before taking action.
» wscat -c ws://... connected (press CTRL+C to quit) < {"topic":"led/2/logs","timestamp":1411809676901,"transition":"turn-on","input":[],"properties":{"id":"2","type":"led","name":null,"state":"on"}} < {"topic":"led/2/logs","timestamp":1411809677548,"transition":"turn-off","input":[],"properties":{"id":"2","type":"led","name":null,"state":"off"}} < {"topic":"led/2/logs","timestamp":1411809678483,"transition":"turn-on","input":[],"properties":{"id":"2","type":"led","name":null,"state":"on"}} < {"topic":"led/2/logs","timestamp":1411809679126,"transition":"turn-off","input":[],"properties":{"id":"2","type":"led","name":null,"state":"off"}}
We also need binary.
links: [ { rel: [“http://rxapi.org/binary-stream“], href: “ws://...“, type: “video/mpeg“, } ]
We also need binary.
» wscat -c ws://... connected (press CTRL+C to quit) Q`rCEDˌDIp=`"3ss79:Yk}{` 5e\`>9%J[K89\z^> 8X Gp;W̮qXF h1{%O;7<Biڞ.r r5 vyeK[X@ƯDRmT:uLz[�V .vK",,u!N8 ֝=LQ=s~ӆ`]{E6[ WȝM]3Uo|&n Њmo('/K>2칩e,l{%`:<rtN|SXu曆psFJS*s#AB]ý0Rt_`uS}vX3ctA>kJfuΎ^L๖ V"u8GU*ڹ
Reactive Clientsvar siren = require('siren');
siren() .load('http://zetta-cloud-2.herokuapp.com') .link('http://rels.zettajs.io/peer', 'Detroit') .entity(function(e) { return e.properties.type === 'sound'; }) .link('http://rels.zettajs.io/object-stream', 'level') .monitor() .subscribe(console.log);
Reactive Clientsvar zrx = require('zrx');
zrx() .load('http://zetta-cloud-2.herokuapp.com') .server('Detroit') .device(function(d) { return d.type === 'sound'; }) .stream('level') .subscribe(console.log);
Reactive Stream Control Protocol
• Consumer controlled streams
• Runs over WebSockets
• Multiplex streams
What can you do with streams?
• Monitoring values over time
• Distributed data processing
• Event sourcing
• Batches
// TODO:
• Extract reactive hypermedia framework from Zetta internals.
• Produce a clean specification and documentation.
• Experiment with multiple languages/platform implementations.
Additional Topics• Managing device identity
• Security along the entire stack
• Working in an occasionally connected environment
• Working with highly constrained devices
• Enterprise IoT
Hit me up!• [email protected]
• https://twitter.com/kevinswiber
• https://linkedin.com/in/kevinswiber
• https://github.com/kevinswiber