[Heroku] Heroku の Xivelyアドオンを Message Broker として使う場合 [MQTT]


## Xivelyとはなんぞや

Heroku には [Xively](https://www.xively.com/) という IoT Platformアドオンがあります。[Xively](https://www.xively.com/)は、MQTT の Message Broker を軸にして、IoTデバイスの管理やルールエンジンによるアラート機能などを持っています。標準で、[Amazon Kinesis](https://aws.amazon.com/jp/kinesis/)や[Salesforce](https://www.salesforce.com/jp/)の外部システム接続機能も持っていて、特にコードを書くことなく、カンタンに IoTを始めることができるアドオンとなっています。

そんな [Xively](https://www.xively.com/) の MQTT Message Broker は、[PubSubメッセージングモデル](https://ja.wikipedia.org/wiki/%E5%87%BA%E7%89%88-%E8%B3%BC%E8%AA%AD%E5%9E%8B%E3%83%A2%E3%83%87%E3%83%AB)になっていますので、プログラムからの呼出方法を理解していれば、IoTだけではなく、アプリ間の非同期メッセージ通信にも利用可能です。今回は、`MQTT`を利用して、[Xively](https://www.xively.com/)でメッセージをやりとりする方法をまとめました。

## 入手・設定すべき情報

Heroku アドオンとして追加した場合も、[Xively](https://www.xively.com/)を単独で利用可能な状態にした場合でも、次の情報を入手する必要があります。※ 一部、Xively へログインした状態でないとアクセスできない箇所があります(*Account IDやデバイス情報*)。

### 入手の必要な情報

| 入手する情報 | 内容 | 入手先 |
|:---|:---|:---|
|Account ID| 利用する契約主を特定する | [Account details](https://app.xively.com/account-details)|
|R1ルート証明書| SSL接続のためのルート証明書 | [GlobalSign Root Certificates](https://support.globalsign.com/customer/portal/articles/1426602-globalsign-root-certificates) |
|Xively Broker URL|Xivelyのメッセージブローカーサーバーのホスト名| [broker.xively.com](broker.xively.com) |
|Xively Broker PORT|Xivelyブローカーサーバーの待受ポート番号| 8883|

ルート証明書は `BASE64` 形式をファイルに保管して、プログラムからアクセスできるようにしておく必要があります。

### 設定が必要な情報

| 設定すべき項目 | 内容 | 設定先 |
|:---|:---|:---|
|デバイスID|IoTデバイスのID|[Xively Devices](https://app.xively.com/devices) |
|デバイスパスワード|IoTデバイスのパスワード|[Xively Devices](https://app.xively.com/devices) |
|チャネル|IoTデバイスが利用するチャネル|[Xively Device Templates](https://app.xively.com/templates/device)|

[Xively](https://www.xively.com/) では、`Publish` する側も `Subscribe` していく側も、定義された IoTデバイス毎の ID/Password と、それらを元に構成される `Topic` 並びに `Channel`名をキーにして、メッセージのやりとりをします。デバイスは、Xively の [Devices](https://app.xively.com/devices) から登録をすると、デバイスごとに ID/Password が発行されます。これらのデバイスは、先に[Xively Device Templates](https://app.xively.com/templates/device)で作成されたテンプレートを元に、デバイスの情報を設定できるようになっています。チャネル情報もテンプレートに作成しておく必要があります。従って、デバイスの構成は、次のとおりとなります。

1. デバイステンプレートの作成 => [Xively Device Templates](https://app.xively.com/templates/device)
2. デバイスの作成 => [Xively Devices](https://app.xively.com/devices)

なお、チャネルは、標準では `_log` だけ定義されています。

## Ruby で書いてみた

実際に MQTTライブラリで、Xively へ Pub/Sub する簡易サンプルを準備してみました。必要な情報は環境変数に保管するようにしましたので、環境変数名、および設定内容は次のとおりです。Heroku のアドオンとして登録した場合には、`XIVELY_BROKER_HOST` `XIVELY_BROKER_PORT` `XIVELY_ACCOUNT_ID`は自動的に設定されていますので、定義は不要です。

```bash:env-values
# Heroku で自動登録される部分
export XIVELY_BROKER_HOST="broker.xively.com"
export XIVELY_BROKER_PORT=8883
export XIVELY_ACCOUNT_ID="ここにXivelyの Account ID情報を入れます"

# Heroku で自動登録されない部分
export XIVELY_CA_FILE="R1ルート証明書ファイルへのパスとファイル名を指定。ex.'certs/gsroot.pem'"
export XIVELY_DEVICE_ID="対象のデバイスID"
export XIVELY_DEVICE_PASS="対象のデバイスパスワード"
export XIVELY_CHANNEL="利用するチャネル名。標準では '_log'"
```

次に Xively への接続と、Pub/Sub をまとめてクラス化してみました。なお、Xively は SSL接続が必須のため、ルート証明書や `ssl=>true` 定義が必須となります。

```Ruby:Xively.rb
require 'mqtt'

class Xively
  # XIVELY Message Broker : sample device #1
  def initialize(id, pass, channel = '_log')
    @xively_account_id  = ENV['XIVELY_ACCOUNT_ID']
    @xively_id          = id
    @xively_channel     = channel

    @client = MQTT::Client.connect(
    host:      ENV['XIVELY_BROKER_HOST'],
    port:      ENV['XIVELY_BROKER_PORT'],
    ssl:       true,
    ca_file:   ENV['XIVELY_CA_FILE'],
    username:  id,
    password:  pass
    )
  end

  def PubXively(message, channel = @xively_channel)
    xively_topic = "xi/blue/v1/#{@xively_account_id}/d/#{@xively_id}/#{channel}"
    @client.publish(xively_topic, message)
  end

  def SubXively(channel = @xively_channel)
    xively_topic = "xi/blue/v1/#{@xively_account_id}/d/#{@xively_id}/#{channel}"
    @client.subscribe(xively_topic)

    topic, message = @client.get
    return [topic, message]
  end

  def Close()
    @client.disconnect()
  end
end
```

`MQTTトピック`は、`Account ID` と `Device ID` と `Channel名` から決定されるので、`publish`/`subscribe`する直前に変数を集めて定義しています。

`PubXively` へ適当にメッセージを送ると、Xively の QUEUE へ登録されます。そのとき、`SubXively` でメッセージの待受をしていると、1つだけ `[topic, message]` の配列形式でメッセージの情報を入手します。`@client.get` 部分をブロック化すると、延々とメッセージを取得し続けるようになり、取得する毎にブロック内の処理を実行するようになります。

このクラスを用いて、実際に `Publish/Subscribe` するサンプルが次の 2つのファイルになります。

```Ruby:publish.rb
#!/usr/bin/env ruby
require './xively.rb'

xively_id      = ENV['XIVELY_DEVICE_ID']
xively_pass    = ENV['XIVELY_DEVICE_PASS']
xively_channel = 'Default Channel 1'
test_message   = "test #{xively_channel}"

mb = Xively.new(xively_id, xively_pass, xively_channel)
mb.PubXively(test_message)
mb.Close
```

```Ruby:subscribe.rb
#!/usr/bin/env ruby
require './xively.rb'

xively_id      = ENV['XIVELY_DEVICE_ID']
xively_pass    = ENV['XIVELY_DEVICE_PASS']
xively_channel = 'Default Channel 1'

mb = Xively.new(xively_id, xively_pass, xively_channel)
p mb.SubXively()
mb.Close
```

今回、デバイスID/Password は環境変数で一つ設定したとおりとなっていますが、インスタンスを作成する毎に別々の ID/Passwordを利用できますので、複数のデバイスも同時に利用できるはずです。たぶん。