Vinli Developers Blog


The #connectedcar platform for developers


Stream Service

This is a big one!

Since launching Vinli, we've been on a relentless quest to deliver the highest quality, most accurate, real-time-est (is that a word?) vehicle telematics possible. Our new Stream Service provides a whole new way to access vehicle data. You can now establish a WebSocket connection with the Vinli platform that will forward telemetry messages for one or more devices as soon as we receive them.

Now we're approaching the point where we just can't get the photons/electrons moving to your server any quicker. For now, this is as speedy as it's gonna get, but who knows what improvements lay in wait on the hardware side though... ;)

Right now this only supports telemetry messages (what you would get from Telemetry Service), but we'll be adding events and other items in the near future.

The Basics

The Vinli Stream Service provides a WebSocket server that can stream telemetry messages from one or more devices to which an App has access. Apps can start as many sockets as needed and can subscribe to one or more devices on each stream.

For now, we are only sending telemetry messages, but we're planning to add more types of streams soon. Messages published on the stream will match up exactly with the messages retrieved from Telemetry Service through https://telemetry.vin.li/api/v1/devices/<DEVICE_ID>/messages, but you will get them as soon as the device sends them (give or take a few milliseconds for maths). You can see an example at the bottom of this post.

Getting Started

Starting up the stream is as easy as firing up your favorite WebSocket client. In this example we'll be using the "ws" module for Node.js.

You'll need to authenticate your connection in the headers or as part of the URL (assuming you set appId and appSecret to be your App's credentials):

  
const WebSocket = require('ws');

const ws = new WebSocket(`wss://stream.vin.li/api/v1/messages`, null, {  
  headers: {
    Authorization: `Basic ${new Buffer(`${appId}:${appSecret}`).toString('base64')}`
  }
});

// or if your client supports it:
// const ws = new WebSocket(`wss://${appId}:${appSecret}@stream.vin.li/api/v1/messages`);
  

Bearer tokens work just fine, too:

const ws = new WebSocket(`wss://stream.vin.li/api/v1/messages?token=${bearerToken}`);  

Either way, you'll soon have a WebSocket authenticated in the same way as you would any other
service in the Vinli Platform. The server will send messages, both the telemetry messages
and administrative messages, and you will need to handle these events:

ws.on('message', (data) => {  
  console.log(data);
});

Subscribing

Once the connection is established you'll subscribe to devices and start receiving their telemetry messages immediately. (Note that, as with other services, a Bearer token will only allow you to subscribe to devices owned by the user that generated the token. Docs)

Subscriptions are created by sending a message that describes the subject of the subscription:

{
    "type": "sub",
    "subject": {
        "type": "device",
        "id": "b080418e-b82f-43d6-8800-fe580ad1e22a"
    }
}

So in our script:

ws.once('open', () => {  
  ws.send(JSON.stringify({
    type: 'sub',
    subject: {
      type: 'device',
      id: 'b080418e-b82f-43d6-8800-fe580ad1e22a'
  }}));
});

Once you subscribe, you'll get an acknowledgement message:

{
  "type": "sub",
  "subject": {
    "type": "device",
    "id": "b080418e-b82f-43d6-8800-fe580ad1e22a"
  },
  "payload": {
    "ack": true
  }
}

And you will start getting telemetry message for the device:

{
  "type": "pub",
  "subject": {
    "type": "device",
    "id": "b080418e-b82f-43d6-8800-fe580ad1e22a"
  },
  "payload": {
    "id": "110b71e9-1270-4da7-ba53-deac3e31f4e4",
    "timestamp": "2016-02-21T20:35:25.756Z",
    "data": {
      "location": {
        "type": "Point",
        "coordinates": [
          -114.795672,
          37.75432
        ]
      },
      "accel": {
        "maxZ": 6.588877,
        "maxX": 2.145216,
        "maxY": -6.742106,
        "minX": -0.919378,
        "minY": -9.730085,
        "minZ": 3.524283
      },
      "vehicleSpeed": 48,
      "rpm": 3257.75,
      "absoluteThrottleSensorPosition": 31.372549019607842,
      "intakeManifoldPressure": 76,
      "massAirFlow": 29.98,
      "calculatedLoadValue": 80,
      "oxygenSensorLocations": [
        "Bank 1 Sensor 1",
        "Bank 1 Sensor 2"
      ],
      "oxygenSensorVoltage1b": 0.8,
      "shortTermFuelTrim1b": 99.21875
    }
  }
}

Unsubscribing

When you are done with a particular device, you can unsubscribe from a specific device
by sending an message with type unsub:

{
    "type": "unsub",
    "subject": {
        "type": "device",
        "id": "b080418e-b82f-43d6-8800-fe580ad1e22a"
    }
}

Handling Backpressure

As you subscribe to more and more devices, a stream can push a large amount of data through the socket. If your connection or application starts to fall behind, Stream Service will attempt to queue up messages to send. Every 30 seconds, a health message will be published that contains the current queue length as a way for you to see when limits are being approached:

{
  "type": "health",
  "payload": {
    "messagesQueued": 7,
    "messagesAllowed": 1000
  }
}

If messagesQueued reaches the messagesAllowed limit, Vinli will publish an error message and disconnect the client.

{
  "statusCode": 429,
  "payload": {
    "error": "Too Many Requests",
    "message": "max queue size exceeded"
  }
}

Note that each stream has it's own queue, so as you start to approach the limit of a single resource, it's a good idea to start splitting devices among multiple streams.

CLI, of course

The Vinli CLI (starting in v.1.1.0) includes a quick way to get streams up an running,
at least for a single device:

  
> vinli device stream --device=0aef7d48-803a-494d-81e4-78e9a8fef3ff

Creating websocket...  
 - Websocket open.
Subscribing to device 0aef7d48-803a-494d-81e4-78e9a8fef3ff...  
 - Subscription acknowledged (device 0aef7d48-803a-494d-81e4-78e9a8fef3ff)

2016-03-04 12:25:51 -0600 -----------------------------------------------------------------  
 lon: -115.11442     lat: 36.157                    calculatedLoadValue: 49.411764705882355
                                                         fuelLevelInput: 13.72549019607843
                                                                    rpm: 1458.75
                                                           vehicleSpeed: 27
2016-03-04 12:25:54 -0600 -----------------------------------------------------------------  
 lon: -115.1139      lat: 36.15658                  calculatedLoadValue: 50.98039215686274

...