MQTT Integration

In addition to HTTP, M2X also supports the MQTT communication protocol.

M2X uses the default 1883 port for MQTT connections, the server listens for connections on the following URL:

mqtt://<YOUR-API-KEY>@api-m2x.att.com

SSL support is also available in port 8883

mqtts://<YOUR-API-KEY>@api-m2x.att.com

Note that in both cases, MQTT requires an API key when connecting to the broker; it must be provided as the username with no password.

Requests

Once the connection is established, the client can start sending API requests by publishing messages to the m2x/<M2X API Key>/requests topic. All messages must be in JSON format with the following structure:

{
  "id": "<Request ID>",
  "method": "<method>",
  "resource": "<resource>",
  "agent": "<user agent>",
  "body": "<request body>"
}

Request ID represents a unique ID used to uniquely identify this request. It is used to link requests and reponses together, as all responses to all requests sent through the same topic will be published in the same response topic as well. Because M2X does not use this information, you can use any value of your choice. However, it is recommended to use unique IDs to help distinguish the requests and responses. E.g. if you are using Ruby, SecureRandom.hex would be a good way of generating request IDs.

The rest of the fields (method, resource and body) have the same meaning as their counterparts in the HTTP API. Notice that the resource path must include the version number prefix.

agent specifies the user agent of the client library. This field is optional: a default value will be provided by MQTT server if missing. However, it is recommended that a meaningful user agent is provided.

Here's an example of a POST request. Notice that the request body is a JSON object itself:

{
  "id": "7261075947a7524cc75a0b7535232e89",
  "method": "POST",
  "resource": "/v2/devices/dcb6806d6d214deb9f409aa57150199c/streams/temperature/values",
  "agent": "M2X-Demo-Client/0.0.1",
  "body": {
    "values": [
      {
        "timestamp": "2014-08-19T13:37:09Z",
        "value": "42"
      }
    ]
  }
}

Responses

The response from the API server will be published (with QoS 0) to the m2x/<M2X API Key>/responses topic with the following format:

{
  "id": "<Request ID>",
  "status": "<status code>",
  "body": "<response body>"
}

Request ID is the same value specified in the request message. This is used to identify each request if multiple requests are being sent at the same time.

status returns the HTTP status code that would have been returned if you used the HTTP API, while body contains the response body returned by the API server. An example of a successful request looks like this:

{
  "id": "e361ebfa51fb5c504764a67bc52228b3",
  "status": 202
}

Below is another example containing a response body:

{
  "id": "696772ca8fba32682da946965fe686ea",
  "status": 200,
  "body": {
    "limit": 100,
    "end": "2014-10-23T07:37:47.635Z",
    "values": [
      {
        "timestamp": "2014-10-23T07:21:26.000Z",
        "value": 9746.0
      },
      {
        "timestamp": "2014-10-23T07:11:21.000Z",
        "value": 8371.0
      },
      {
        "timestamp": "2014-10-23T07:10:59.000Z",
        "value": 1916.0
      },
      {
        "timestamp": "2014-10-23T07:10:06.000Z",
        "value": 119.0
      },
      {
        "timestamp": "2014-10-23T07:06:50.000Z",
        "value": 5631.0
      },
      {
        "timestamp": "2014-10-23T07:02:32.000Z",
        "value": 3431.0
      }
    ]
  }
}

Commands API

If any commands are sent using the Commands API to the device while it is connected, the delivery notification will be delivered via MQTT. Note that the MQTT client must be connected with a Primary Device API key to receive these delivery notifications.

Command delivery notifications received from the API server will be published (with QoS 0) to the m2x/<M2X API Key>/commands topic with the following format:

{
  "url": "https://api-m2x.att.com/v2/devices/65b89448f954f49e42b746d73b385cbb/commands/2015114750b32709e776efe0e4ea8563f20a02",
  "name": "CHECK_UPDATES",
  "data": { "update_server": "https://updates.example.com/" },
  "sent_at": "2015-11-01T10:30:46.508Z",
  "status": "pending"
}

Because command delivery notifications will only be delivered to connected devices, a robust device application design dictates that device should periodically use the Commands API to check for commands it may have missed while offline or during a network partition. Subscribing with QoS > 0 will not be enough to guarantee delivery because the notifications are published with QoS 0 (the only supported QoS level).

After receiving a command, the device application should perform the appropriate action, then use the Commands API to mark it as processed. If for any reason the device does not understand or declines to process the command, it should use the Commands API to mark it as rejected. Note that in either case, the device can convey additional information in the body of the Commands API request to set the response_data for the command delivery.

Here is an example application using the M2X Python MQTT library that handles a few different types of commands. It checks for unprocessed commands on startup, and listens for command delivery notifications to arrive. Whenever a command is received through either mechanism, it marks it as processed or rejected based on whether it was able to process the command.

Errors

There are two different kinds of errors:

a) The request was accepted by the broker, but M2X's API server returned an error, in which case the response looks like this:

{
  "id": "126770a62cf5206cd070af7405b5bf87",
  "status": 401,
  "body": {
    "message": "Invalid API key"
  }
}

b) The request was rejected by the MQTT broker (e.g. badly formatted JSON). In this case the response looks like this:

{
  "id": null,
  "status": -1,
  "error": "A JSON text must at least contain two octets!"
}

Notice that depending on the error, M2X may not even be able to return the corresponding request ID.

Examples

Pushing values

The following Ruby code can be used to push values to M2X via MQTT:

require "json"
require "time"
require "mqtt"
require "securerandom"

key         = "<your M2X API key>"
device_id   = "<device ID>"
stream_name = "<stream name>"
value       = "<value to push>"
broker_url  = "mqtt://#{key}:@api-m2x.att.com"
user_agent  = "M2X-Demo-Client/0.0.1"

MQTT::Client.connect(broker_url) do |client|
  client.subscribe("m2x/#{key}/responses")

  request = {
    id: SecureRandom.hex,
    method: "POST",
    resource: "/v2/devices/#{device_id}/streams/#{stream_name}/values",
    agent: user_agent,
    body: {
      values: [
        { timestamp: Time.now.iso8601, value: value }
      ]
    }
  }

  puts "Pushing value #{value} to stream #{device_id}/#{stream_name}..."

  client.publish("m2x/#{key}/requests", request.to_json)

  response = client.get_packet

  puts JSON.pretty_generate(JSON.parse(response.payload))
end

Reading values

The following Ruby code can be used to read device values from M2X via MQTT:

require "json"
require "mqtt"
require "securerandom"

key         = "<your M2X API key>"
device_id   = "<device ID>"
stream_name = "<stream name>"
broker_url  = "mqtt://#{key}:@api-m2x.att.com"
user_agent  = "M2X-Demo-Client/0.0.1"

MQTT::Client.connect(broker_url) do |client|
  client.subscribe("m2x/#{key}/responses")

  request = {
    id: SecureRandom.hex,
    method: "GET",
    resource: "/v2/devices/#{device_id}/streams/#{stream_name}/values",
    agent: user_agent
  }

  puts "Retrieving values of stream #{device_id}/#{stream_name}..."

  client.publish("m2x/#{key}/requests", request.to_json)

  response = client.get_packet

  puts JSON.pretty_generate(JSON.parse(response.payload))
end