目次


Watson および IoT Platform サービスを利用してホーム・アシスタント・モバイル・アプリケーションを構築する

Comments

これまで、ホーム・アシスタントを作成するにはかなりの作業が必要になるだけなく、技術的にも難しい場合がありました。けれども今は、IBM Watson と他の補助的クラウド・サービスを利用して、簡単に作成し、新しい技術を取り入れることができるようになっています。

このチュートリアルでは、Watson の力と IBM IoT Platform の単純さを結合して、基本的な電子機器 (照明とカメラ) を制御するホーム・アシスタントを作成する方法を紹介します。実際の家庭用電化製品をインターネットに接続可能にすれば、このフレームワークを拡張して、それらの電化製品を制御できるようになります。このチュートリアルを参考に、皆さん独自のアプリケーションを作成してください。

iOS Swift で開発された、この Home Assistant モバイル・アプリは、キーの入力と音声命令の両方を使って IBM Watson Conversation とやりとりします。つまり、ユーザーインターフェイス・インターフェースでは、メッセージを送信することも、声で命令することもできます。この対話を可能にするために私が利用したのは、Watson Text to Speech サービスと Watson Speech to Text サービスです。Watson Conversation は、音声命令または入力メッセージに基づいてインテントを導き出すようにトレーニングされます。それらのインテントに応じて、Home Assistant アプリケーションはコマンドを IoT Platform サービス経由でデバイスに送信するか、ホーム・ゲートウェイ (Raspberry Pi) に送信します。ホーム・ゲートウェイはデバイスを制御するとともに、デバイスのイベントを、IoT Platform サービスを介して Home Assistant アプリに送信します。このシステムでは、ホーム・ゲートウェイからアップロードされた写真を保管するために、Object Storage サービスも利用しています。

独自のアプリを構築する出発点として、Home Assistant のコードをダウンロードできるようになっています。

アプリケーションのフローを示す図
アプリケーションのフローを示す図

このアプリケーションの基本的なフローは以下のとおりです。

  • (1) Home Assistant ユーザー・インターフェースで、ユーザーが声で命令するか、コマンドを入力します。
  • (2、3) 音声命令は、Watson Speech to Text サービスを利用してテキストに変換されます。ユーザーがコマンドを入力した場合、このステップはスキップされます。
  • (4,5) 変換されたテキストを Watson Conversation に転送し、ユーザーの音声からインテントを導き出します。
  • (6) Home Assistant はインテントを受信すると、コマンドの JSON メッセージを構成して、それを IoT Platform に送信します。
  • (7) コマンド・トピックにサブスクライブしている、ホーム・ゲートウェイ上で稼働する Node-RED アプリケーションが、コマンド・メッセージを受信します。
  • (8a、8b) ホーム・ゲートウェイが指定されたアクション、つまり照明のオン/オフか、写真の撮影を実施します。
  • (8c) ホーム・ゲートウェイが撮った写真を Object Storage にアップロードします。
  • (9) タスクが完了すると、Node-RED アプリケーションがデバイス・イベント (ステータスを含む) を JSON メッセージとして IoT Platform に送信します。
  • (10) デバイス・トピックにサブスクライブしている Home Assistant が、デバイス・イベントを受信します。
  • (10a) Home Assistant が Object Storage から写真をダウンロードして、ユーザー・インターフェース上に表示します。
  • (11、12) Home Assistant はデバイス・イベントからステータス・テキストを抽出し、それを音声に変換するために Watson Text to Speech に転送します。
  • (13) Home Assistant がユーザーに対し、音声を再生します。

以下に、Home Assistant モバイル・アプリケーションのスクリーン・キャプチャーを示します。

モバイル・アプリのスクリーンショット
モバイル・アプリのスクリーンショット

アプリケーションを作成するために必要なもの

Home Assistant のようなアプリケーションを作成するには以下の知識が必要です。

  • iOS 開発環境
  • Swift プログラミング
  • Node-RED 開発環境
  • Watson Developer SDK
  • IoT Platform と MQTT プロトコル

このアプリを作成するために私は以下の開発環境、ハードウェア、Bluemix サービスを利用しました。

開発環境

Bluemix サービス

ハードウェア

  • SD カードと電源アダプターが同梱された Raspberry Pi 3B Starter Kit (および USB ケーブル)
  • Adafruit Nonpixel Diffused 8mm Through-Hole LED - 5 Pack
  • Raspberry Pi 3B/2B/B+ 対応 5 MP ウェブカム・ビデオ・カメラ・モジュール・ボード 1080 p x720 p 高速
  • ジャンパー・ワイヤー (メス ~ メス) ケーブル 1P-1P
  • コンデンサー (0.1µF)
  • 抵抗器 (560 Ω)
  • Ethernet ケーブル (ハードウェアを初めて起動する際にのみ必要)
  • USB キーボード (ハードウェアを初めて起動する際にのみ必要)
  • HDMI ディスプレイ・ケーブル (ハードウェアを初めて起動する際にのみ必要)
  • マウス (ハードウェアを初めて起動する際にのみ必要)
1

Bluemix 上の Watson サービスを準備する

Bluemix カタログから、以下のサービスを選択して作成します。必ず、すべてのサービスの資格情報をすべて記録して保存してください。

  1. Bluemix にログインします。
  2. 「Catalog (カタログ)」をクリックします。
  3. 「Services (サービス)」リストにある「Watson」をクリックします。
  4. 「Conversation」をクリックします。
  5. サービスが作成されたら、「Service credentials (サービス資格情報)」 > 「View credentials (資格情報を表示)」をクリックして、サービス資格情報の名前を記録します。
  6. 「Create (作成)」をクリックします。

Watson Text to Speech サービスと Watson Speech to Text サービスについて、上記の手順を繰り返します。

2

Watson Conversation サービスをインポートする

  1. Watson Conversation サービス・インスタンスを作成した後、「Launch tool (ツールを起動)」をクリックしてサービスを起動します。
  2. 新しい会話の作成を開始するには、「Create (作成)」をクリックし、インテント、エンティティー、ダイアログを定義するという方法、あるいは「Import (インポート)」をクリックして完全なワークスペースをインポートするという方法があります。このチュートリアルではファイルをインポートする方法をとります。
  3. 「Import (インポート)」をクリックし、「workspace-homeassistant.json」(「コードを入手する」からダウンロードしたファイルに含まれています) を選択してから「Import (インポート)」をクリックします。
  4. ワークスペースに戻り、縦に並んだ 3 つのドットをクリックします。これによってメニューが開くので、そこから「View details (詳細を表示)」を選択します。ワークスペース ID を含むワークスペースの詳細がロードされます。
  5. WORKSPACE_ID をコピーします。この ID は、モバイル・アプリケーションから Conversation サービスを利用する際に必要になります。

ここで、Conversation サービス内に定義される成果物について説明しておきます。ダイアログは複雑なものではありません。それよりも重点が置かれるのは、各種の Bluemix サービス、モバイル・アプリケーション、そして Raspberry Pi 上で稼働する Node-RED との間の統合です。

Conversation サービスは主に、メッセージまたは音声によるユーザーの対話に基づいてインテントを推定するために利用します。ダイアログは、会話を導くために使用します。

ダイアログ説明
Start (開始)会話を始めるダイアログです。
Greetings (挨拶)ユーザーからの挨拶に対応します。
On Light (照明オン)ユーザーの命令を「On Light」インテントに変換します。
Off Light (照明オフ)ユーザーの命令を「Off Light」インテントに変換します。
Take Picture (写真撮影)ユーザーの命令を「Take a Picture」インテントに変換します。
Else (その他)その他のあらゆる状況に対応します。
会話の開始を示すスクリーン・キャプチャー
会話の開始を示すスクリーン・キャプチャー
3

Bluemix 上の Object Storage サービスを準備する

  1. Bluemix カタログから、「Storage (ストレージ)」 > 「Object Storage」を選択して、インスタンスを作成します。必ず、無料の料金帯を選択してください。
  2. Object Storage を作成した後、サービス資格情報をコピーします。OS_PROJECTIDOS_USERIDOS_USERNAME、および OS_PASSWORD を保存します。これらの情報は、Node-RED アプリケーションとモバイル・アプリケーションから Object Storage に接続するために必要です。
    {
      "auth_url": "https://identity.open.softlayer.com",
      "project": "object_storage_750f36e0_f61b_42c0_9458_0876db9f3e36",
      "projectId": [OS_PROJECTID],
      "region": "dallas",
      "userId": [OS_USERID],
      "username": [OS_USERNAME],
      "password": [OS_PASSWORD],
      "domainId": "508e3dcdff0a4d7eb7246a6852bdcc16",
      "domainName": "1307809",
      "role": "admin"
    }
4

IBM Watson IoT Platform サービスを準備する

IBM Watson IoT Platform サービスを準備するには、IoT Platform に Raspberry Pi を「デバイス」として接続し、モバイル・アプリケーションを「アプリケーション」として接続する必要があります。

Raspberry Pi を「デバイス」として Watson IoT Platform に接続する

  1. IoT Platform サービスのインスタンスを作成するために、Bluemix カタログで「Internet of Things (モノのインターネット)」 > 「Internet of Things Platform」をクリックします。
  2. 「Lite」プラン」を選択してから、「Create (作成)」をクリックします。
  3. Internet of Things Platform のページで「Launch (起動)」をクリックします。
  4. 左側のナビゲーション・バーで、「Devices (デバイス)」をクリックします。
  5. 「Device Type (デバイス・タイプ)」タブを選択し、デバイス・タイプを作成するために「Create Type (タイプの作成)」をクリックします。
  6. 「Create Device Type (デバイス・タイプの作成)」ウィンドウで、「Create device type (デバイス・タイプを作成)」をクリックし、「Name (名前)」および「Description (説明)」フィールドに、名前と説明をそれぞれ指定します。DEV_TYPE 名を保存します。 「Create Device Type (端末タイプの作成)」ウィンドウのスクリーン・キャプチャー
    「Create Device Type (端末タイプの作成)」ウィンドウのスクリーン・キャプチャー
  7. デバイス・タイプが作成されるまで「Next (次へ)」をクリックします。
  8. 左側のナビゲーションで再び「Devices (デバイス)」をクリックし、「Add device (デバイスを追加)」をクリックします。
  9. 「Add Device (デバイスの追加)」ウィンドウで、「Choose Device Type (デバイス・タイプの選択)」ドロップダウン・リストから、作成したデバイスを選択し、「Next (次へ)」をクリックします。 デバイスを追加する画面のスクリーン・キャプチャー
    デバイスを追加する画面のスクリーン・キャプチャー
  10. 「Add Device (デバイスの追加)」 > 「Define Info (情報の定義)」ウィンドウで、デバイス ID とシリアル番号を指定します (これらの情報は、Node-RED 内での接続の際に使用します)。Node-RED 資格情報の構成で使用する DEV_ID を保存します。
  11. 他のすべてのフィールドはデフォルト値のままにして、デバイスが追加されるまで「Next (次へ)」をクリックします。最後に認証トークンが示されます。Node-RED 資格情報の構成で使用する ORG_IDDEV_IDAUTH_TOKEN を保存します。 資格情報を示すスクリーン・キャプチャー
    資格情報を示すスクリーン・キャプチャー

このリンク先の developerWorks レシピから、さらに詳しい情報を入手できます。

モバイル・アプリケーションを IoT Platform に接続する

  1. 左側のナビゲーション・バーで「Apps (アプリケーション)」をクリックして「Apps (アプリケーション)」ページを表示します。
  2. 「Generate API Key (API キーの生成)」をクリックして、モバイル・アプリケーションの API キーを生成します。
  3. 「Generate API Key (API キーの生成)」ウィンドウで、API の役割として「Standard Application (スタンダード・アプリケーション)」を選択します。また、後でモバイル・アプリケーション内で使用できるよう、API_KEYAPP_AUTH_TOKEN を保存します。「Generate (生成)」をクリックします。 「Generate API Key (API キーの生成)」ウィンドウのスクリーン・キャプチャー
    「Generate API Key (API キーの生成)」ウィンドウのスクリーン・キャプチャー
5

Raspberry Pi 2 を準備する

このリンク先の詳しい手順に従って、Raspberry Pi をセットアップできます。私は Raspbian Jessie with Pixel を使用しました。この Raspberry Pi 用 OS には、後で必要になる Node-RED があらかじめインストールされているからです。

デフォルトでは、Wi-Fi 接続はセットアップされていません。したがって、Ethernet ケーブルを使用して Raspberry Pi をルータに接続する必要があります。また、USB キーボードと HDMI ディスプレイ・ケーブルを接続する必要もあります。

  1. Wi-Fi ネットワークに接続するために、右上隅にある Wi-Fi ボタンをクリックします。
  2. 「Pi」 > 「Preferences (設定)」 > 「Raspberry Pi Configuration (Raspberry Pi の構成)」の順に選択して、必要なインターフェースを有効にします。具体的には、キーボードとスクリーンを使わずに Raspberry Pi にアクセスするための SSH および VNC、そして Remote GPIO とカメラを有効にします。 「Raspberry Pi Configuration (Raspberry Pi の構成)」ウィンドウのスクリーン・キャプチャー
    「Raspberry Pi Configuration (Raspberry Pi の構成)」ウィンドウのスクリーン・キャプチャー
  3. 以下の図に示すように、Raspberry Pi に LED とカメラを接続します。
    • GPIO 3V PIN を LED の 5V PIN に接続します。
    • GPIO GND PIN を LED の GND PIN に接続します。
    • GPIO18 PIN をレジスタ (560 Ω) を介して LED の DIN PIN に接続します。
    • コンデンサー (0.1µF) を 5V PIN と GND GPIO 3V PIN に並列接続します。
    • カメラを Raspberry Pi 上のカメラ CSI に接続します。
    Raspberry Pi の配線図
    Raspberry Pi の配線図
6

Raspberry Pi 2 上の Node-RED を開発する

次は、Raspberry Pi 上の Node-RED 環境を開発して準備する必要があります。

Raspberry Pi 2 上の Node-RED を準備する

  1. ターミナルから ssh を使用して Raspberry Pi に接続し、Node-RED フォルダーにカレント・ディレクトリーを変更します。IP アドレスを調べるには、ルータにログインして調べる必要があります。
    ssh pi@ipaddr-raspberrypi
  2. NeoPixel LED を制御するための node-red-node-pi-neopixel をインストールします。
    curl -sS get.pimoroni.com/unicornhat | bash
    npm install node-red-node-pi-neopixel
  3. カメラを制御するための node-red-contrib-camerapi をインストールします。
    npm install node-red-contrib-camerapi
  4. Bluemix Object Store を制御するための node-red-contrib-objectstore をインストールします。
    npm install node-red-contrib-objectstore

    他の必要なノード (Watson IoT の入力ノードと出力ノード) はプリインストールされています。

  5. 以下のコマンドを実行して、システムの起動時に Node-RED が自動的に起動するように構成します。
    sudo systemctl enable nodered.service
  6. ブラウザーを開き、http://ipaddr-raspberrypi:1880 を指定して Node-RED にアクセスします。パレット内に以下のノードがインストールされているはずです。 NodeRED パレットのスクリーン・キャプチャー
    NodeRED パレットのスクリーン・キャプチャー

LED を制御するフローを開発する

以下の手順を実行する前に、必ず前の手順を完了して Raspberry Pi 内に Node-RED 環境が準備されている状態にしてください。その後、ブラウザーで Node-RED アプリケーションを表示します。

  1. 以下の図に示すようにフローを接続します。このフローは、コマンドを受信し、インテントを決定し、インテント (LED のオン/オフ) を実行し、イベントをモバイル・アプリケーションに送信するという内容になっています。 フローの接続を示す図
    フローの接続を示す図

以下の表で、フローの各部分で制御する内容を説明します。

ノード名ノード・タイプ説明
Light cmdWatson IoT 入力ノードモバイル・アプリケーションからのコマンドを IoT Platform を介して受信するために使用します。フォーマットは iot-2/cmd/[CMD_TYPE]/fmt/json です。ここで、[CMD_TYPE] は「Command (コマンド)」フィールドで定義されます (例: light)。
「Edit Watson IoT node (Watson IoT ノードの編集)」ウィンドウのスクリーン・キャプチャー
「Edit Watson IoT node (Watson IoT ノードの編集)」ウィンドウのスクリーン・キャプチャー
このノードは、raspberrypi 構成オブジェクト内に定義されている資格情報を使用します。
Decide Intent関数ノードペイロードからインテントを取り、ペイロードを再フォーマット化して LED オン/オフのダイアログに変換します。
action = msg.payload.action
object = msg.payload.object
intent = msg.payload.intent
if (intent == "OnLight") {
    msg.payload = "#ffffff"
    return [msg, null]
} else if (intent == "OffLight") {
    msg.payload = "#000000"
    return [null, msg];
}
return msg;
NeoPixel LEDrpi neopixels ノードこのノードは以下のように構成されています。
「Edit rpi-neopixels node (rpi-neopixels ノードの編集)」ウィンドウのスクリーン・キャプチャー
「Edit rpi-neopixels node (rpi-neopixels ノードの編集)」ウィンドウのスクリーン・キャプチャー
Format Status On関数ノードステータス「on」メッセージを JSON 内で構成します。
var jsonObj = { "dev":"light", "status": "on"};
msg.payload = JSON.stringify(jsonObj)
return msg;
Format Status Off関数ノードステータス「off」メッセージを JSON 内で構成します。
var jsonObj = { "dev":"light", "status": "off"};
msg.payload = JSON.stringify(jsonObj)
return msg;
Light EventWatson IoT 出力ノードモバイル・アプリケーションからのデバイス・イベントを IoT Platform を介して送信するために使用します。フォーマットは iot-2/type/[DEV_TYPE]/id/[DEV_ID]/evt/[EVT_TYPE]/fmt/json です。
  • [DEV_TYPE] は、IoT Platform サービス内で定義されています。
  • [DEV_ID] は、IoT Platform サービス内で定義されています。
  • [EVT_TYPE] は、「Event type (イベント・タイプ)」フィールド内で構成します (例: light)
「Edit Watson IoT node (Watson IoT ノードの編集)」ウィンドウのスクリーン・キャプチャー
「Edit Watson IoT node (Watson IoT ノードの編集)」ウィンドウのスクリーン・キャプチャー
raspberrypiwiotp-credentials のための構成オブジェクトLED の On および Off 関数をテストするために使用する Inject ノードがあります。右側の「Debug (デバッグ)」ウィンドウにメッセージ・ペイロードを送信するには、Debug ノードを使用します。 「Edit wiotp-credentials node (wiotp-credentials ノードの編集)」ウィンドウのスクリーン・キャプチャー
「Edit wiotp-credentials node (wiotp-credentials ノードの編集)」ウィンドウのスクリーン・キャプチャー

IoT Platform から取得して、Watson IoT ノードで使用する資格情報構成オブジェクトは以下のとおりです。
  • Organization (組織):ORG_ID
  • Server Name (サーバー名):ORG_ID.messaging.internetofthings.ibmcloud.com
  • Device Type (デバイス・タイプ): DEV_TYPE
  • Device ID (端末 ID):DEV_ID
  • Auth Token (認証トークン):AUTH_TOKEN
  • Name (名前): raspberrypi

写真を撮って Object Storage にアップロードするフローを開発する

以下の手順を実行する前に、必ず Raspberry Pi 内に Node-RED 環境を準備する手順を完了してください。その後、ブラウザーで Node-RED アプリケーションを表示します。

  1. 以下の図に示すようにフローを接続します。このフローは、写真撮影のコマンドを受信して、写真を撮った後、写真を Object Storage にアップロードし、イベントを生成します。 写真を撮るフローを示すスクリーン・キャプチャー
    写真を撮るフローを示すスクリーン・キャプチャー

以下の表で、フロー制御の各部の内容を説明します。

ノード名ノード・タイプ説明
Camera CmdWatson IoT 入力ノードモバイル・アプリケーションからのコマンドを IoT Platform から受信するために使用します。フォーマットは iot-2/cmd/[CMD_TYPE]/fmt/json です。ここで、[CMD_TYPE] は「Command (コマンド)」フィールドで定義されます (例: camera)。
「Edit Watson IoT node (Watson IoT ノードの編集)」ウィンドウのスクリーン・キャプチャー
「Edit Watson IoT node (Watson IoT ノードの編集)」ウィンドウのスクリーン・キャプチャー
このノードは、raspberrypi 構成オブジェクト内に定義されている資格情報を使用します。
Take Photocamerapi 写真撮影ノードRaspberry Pi カメラを使用して写真を撮ります。ファイル・モード「Generate」により、ファイルがフォルダー内に作成されます。ファイル名、フォルダー名、フォーマットは、それぞれ msg.filename、msg.filepath、msg.fileformat 内で指定されます。
「Edit camerapi-takephoto node (camerapi-takephoto ノードの編集)」ウィンドウのスクリーン・キャプチャー
「Edit camerapi-takephoto node (camerapi-takephoto ノードの編集)」ウィンドウのスクリーン・キャプチャー
ObjectStorage Uploados-put ノードキャプチャーされたイメージ・ファイルを取得し、そのファイルを Bluemix 内の Object Storage サービスにアップロードします。コマンドが、入力メッセージ内 (msg.filename、msg.filepath、msg.fileformat) で指定されたファイルを選択し、そのファイルをこのノード内で指定されたコンテナーにアップロードします。アップロードが正常に完了すると、URL を格納した msg.url と、オブジェクト名を格納した msg.objectname を返します。サービス資格情報については、Object Storage 構成から取得します。
「Edit os-put node (os-put ノードの編集)」ウィンドウのスクリーン・キャプチャー
「Edit os-put node (os-put ノードの編集)」ウィンドウのスクリーン・キャプチャー
Format Event関数ノードURL、オブジェクト名、コンテナー名を含めたイベント・メッセージを JSON で構成し、モバイル・アプリケーションがファイルをどこからダウンロードすればよいかを把握できるようにします。
var json = { "url": msg.url, 
"objectname": msg.objectname, 
"containername": "visual-recognition-images"};
msg.payload = JSON.stringify(json);
return msg;
Camera EventWatson IoT 出力ノードモバイル・アプリケーションからのデバイス・イベントを IoT Platform を介して送信するために使用します。フォーマットは iot-2/type/[DEV_TYPE]/id/[DEV_ID]/evt/[EVT_TYPE]/fmt/json です。
  • [DEV_TYPE] は、IoT Platform サービス内で定義されています。
  • [DEV_ID] は、IoT Platform サービス内で定義されています。
  • [EVT_TYPE] は、「Event type (イベント・タイプ)」フィールド内で構成します (例: camera)
「Edit Watson IoT node (Watson IoT ノードの編集)」ウィンドウのスクリーン・キャプチャー
「Edit Watson IoT node (Watson IoT ノードの編集)」ウィンドウのスクリーン・キャプチャー
'none'os-config ノードObject Storage サービスへのログイン資格情報を定義します。以下の値は、前に保存した Object Storage 資格情報から取得します。
  • Configuration Information (構成情報): API Based
  • Region (地域): Dallas
  • Tenant Id (テナント ID): OS_PROJECTID
  • User Id (ユーザー ID): OS_USERID
  • User Name (ユーザー名): OS_USERNAME
  • Password (パスワード): OS_PASSWORD
「Edit os-config node (os-config ノードの編集)」ウィンドウのスクリーン・キャプチャー
「Edit os-config node (os-config ノードの編集)」ウィンドウのスクリーン・キャプチャー
7

モバイル・アプリケーションを開発する

このアプリはチャット・メッセージング・インターフェースと似ているため、JSQMessagesViewController という名前のよく使われている iOS ユーザー・インターフェースを使用します。流用できる同様のサンプル・コードもあります。そのコードは Objective C で作成されていますが、参考としては役立ちます。

ユーザー・インターフェースを開発する

Xcode プロジェクトを準備する

  1. シングル・ビュー・アプリケーションを作成するために、Xcode で「File (ファイル)」 > 「Project (プロジェクト)」を選択します。「Single View Application (シングル・ビュー・アプリケーション)」を選択し、「Product Name (プロジェクト名)」として Home Assistant を指定します。
  2. プロジェクト・フォルダーにカレント・ディレクトリーを変更して、CocoaPods を初期化します。これにより、Podfile が生成されます。
    pod init
  3. 生成された Podfile に以下の行を追加して、JSQMessagesViewController ウィジェットがインストールされるようにします。
    pod 'JSQMessagesViewController'
  4. 以下のコマンドを実行して JSQMessagesViewController 依存関係をインストールします。Xcode ワークスペースが生成されます。プロジェクト (*.xcodeproj) ではなく、ワークスペース (*.wcworkspace) を使用して Xcode を再オープンします。
    pod install
  5. ViewController.swift ファイル内で、JSQMessagesViewController モジュールをインポートし、ViewController クラスが JSQMessagesViewController を継承するように変更します。チャット・メッセージを保持するためのメッセージの配列を宣言します。
    import JSQMessagesViewController
    class ViewController: JSQMessagesViewController {
      var messages = [JSQMessage]()
    }
  6. 最後に、Info.plist 内に以下のコードを入力して、マイクを有効にします。
    <key>NSMicrophoneUsageDescription</key>
      <string>Need microphone to talk to Watson</string><

ユーザー・インターフェースをセットアップして拡張機能を作成する

ユーザー・インターフェースをセットアップして拡張機能を作成するには、以下の手順に従います。

  1. UI 関連のロジックのすべてを Swift 拡張機能として格納する UIExt.swift という名前のファイルを作成します。次に拡張機能を 1 つ宣言します。
    extension ViewController {
    }
  2. 以下の処理を行う SetupUI() 関数を作成します。
    • titlesenderIdsenderDisplayName を初期化します。
    • マイク・ボタンを作成し、touchDown および touchUpInside イベントをコールバック関数に登録します。
    • メニュー項目を登録します (コールバック関数に合成します)。
    func setupUI() {
        self.title = "Watson Chat"
        self.senderId = UIDevice.current.identifierForVendor?.uuidString
        self.senderDisplayName = UIDevice.current.identifierForVendor?.uuidString
        
        JSQMessagesCollectionViewCell.registerMenuAction(#selector(synthesize(sender:)))
        
        // Create mic button
        let microphoneImage = UIImage(named:"microphone")!
        let microphoneButton = UIButton(type: .custom)
        microphoneButton.setImage(microphoneImage, for: .normal)
        microphoneButton.imageView?.contentMode = UIViewContentMode.scaleAspectFit
        self.inputToolbar.contentView.leftBarButtonItem = microphoneButton
        
        // Add press and release mic button
        microphoneButton.addTarget(self, action:#selector(didPressMicrophoneButton), for: .touchDown)
        microphoneButton.addTarget(self, action:#selector(didReleaseMicrophoneButton), for: .touchUpInside)
        
        setAudioPortToSpeaker()
      }
  3. マイク・アイコンをアセットとして追加します。「Assets.xcassets」をクリックし、アイコン・ファイルをワークスペースにドラックして画像セットを作成します。
押されていない状態のマイク・アイコンを示す図
押されていない状態のマイク・アイコンを示す図
押された状態のマイク・アイコンを示す図
押された状態のマイク・アイコンを示す図

ユーザー・インターフェースのハンドラー関数を実装する

UIExt.swift ファイル内で以下の作業を行います。

  1. マイク・ボタンの押下イベントを処理する以下の関数を追加します。このコードにより、ユーザーがアクションを命令するためにこのボタンを押すと、Watson Speech to Text サービスへのストリーム配信が開始されます。
    func didPressMicrophoneButton(sender: UIButton) {
        let microphonePressedImage = UIImage(named:"microphone_pressed")!
        sender.setImage(microphonePressedImage, for: .normal)
        AudioServicesPlayAlertSound(SystemSoundID(kSystemSoundID_Vibrate))
        // Clear the input text
        self.inputToolbar.contentView.textView.text = ""
        // speech-to-text startStreaming
        sttStartStreaming()
      }
  2. マイク・ボタンのリリース・イベントを処理する以下の関数を追加します。この関数によって、Watson Speech to Text サービスへのストリーム配信が停止されます。
    func didReleaseMicrophoneButton(sender: UIButton){
        let microphoneImage = UIImage(named:"microphone")!
        sender.setImage(microphoneImage, for: .normal)
        // speech-to-text stop streaming
        self.sttStopStreaming()
      }
  3. 送信ボタンの押下イベントを処理する didPressSend() 関数をオーバーライドします。以下のコードはメッセージをメッセージ配列の末尾に追加してから、Watson Conversation サービスにリクエストを送信し、そのリクエストに対するレスポンスを受け取ります。
    override func didPressSend(
    _ button: UIButton!, 
    withMessageText text: String!, 
    senderId: String!, 
    senderDisplayName: String!, 
    date: Date!) {
        send(text)
      }

UI コールバック関数をオーバーライドする

UIExt.swift ファイル内で以下の関数を追加します。

  1. コールバック collectionView | numberOfItemsInSection 関数をオーバーライドします。この関数は、配列に含まれるメッセージの数を返す関数です。
    override func collectionView(_ collectionView: UICollectionView, 
        numberOfItemsInSection section: Int) -> Int {
        return self.messages.count
      }
  2. コールバック collectionView | cellForItemAt 関数をオーバーライドします。この関数は、インデックス・パスでの特定のセルのテキストを設定して色をレンダリングする関数です。
      override func collectionView(_ collectionView: UICollectionView, 
        cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = super.collectionView(collectionView, cellForItemAt: indexPath) as! JSQMessagesCollectionViewCell
        let message = self.messages[indexPath.item]
        if !message.isMediaMessage {
          if message.senderId == self.senderId {
            cell.textView.textColor = UIColor(R: 0x72, G: 0x9B, B: 0x79)
          } else {
            cell.textView.textColor = UIColor(R: 0x47, G: 0x5B, B: 0x63)
          }
          let attributes : [String:AnyObject] = 
              [NSForegroundColorAttributeName:cell.textView.textColor!, NSUnderlineStyleAttributeName: 1 as AnyObject]
          cell.textView.linkTextAttributes = attributes
        }
        return cell
      }
  3. コールバック collectionView | messageBubbleImageDataForItemAt 関数をオーバーライドします。この関数は、インデックス・パスでの特定のセルの吹き出しが着信したものか、発信したものかどうかを返す関数です。
    override func collectionView(_ collectionView: JSQMessagesCollectionView!, 
        messageBubbleImageDataForItemAt indexPath: IndexPath!) -> JSQMessageBubbleImageDataSource! {
        let data = messages[indexPath.row]
        switch(data.senderId) {
        case self.senderId:
          return self.outgoingBubble
        default:
          return self.incomingBubble
        }
      }
  4. コールバック collectionView | messageBubbleImageDataForItemAt 関数をオーバーライドします。この関数は、インデックス・パスで特定のセルの吹き出しが着信したものか、発信したものかどうかを返す関数です。
    override func collectionView(_ collectionView: JSQMessagesCollectionView!, 
        attributedTextForMessageBubbleTopLabelAt indexPath: IndexPath!) -> NSAttributedString! {
        if let message = firstMessage(at: indexPath) {
          let paragraphStyle = NSMutableParagraphStyle()
          paragraphStyle.alignment = NSTextAlignment.left
          let attrs = [
            NSParagraphStyleAttributeName: paragraphStyle,
            NSBaselineOffsetAttributeName: NSNumber(value: 0),
            NSForegroundColorAttributeName: UIColor(R: 0x1e, G: 0x90, B: 0xff)
          ]
          return NSAttributedString(string: message.senderDisplayName, attributes: attrs)
        } else {
          return nil
        }
      }
  5. コールバック collectionView | heightForMessageBubbleTopLabelAt 関数をオーバーライドします。この関数は、テキスト・ラベルの高さを返す関数です。
    override func collectionView(_ collectionView: JSQMessagesCollectionView!, 
      layout collectionViewLayout: JSQMessagesCollectionViewFlowLayout!, 
      heightForMessageBubbleTopLabelAt indexPath: IndexPath!) -> CGFloat {
        if let _ = firstMessage(at: indexPath) {
          return kJSQMessagesCollectionViewCellLabelHeightDefault
        } else {
          return 0.0
        }
      }

ユーティリティー関数

最後に、同じく UIExt.swift ファイル内で以下の関数を追加します。

  1. send() 関数は、音の再生、メッセージ配列への送信メッセージの追加、finishSendingMessage() 関数の呼び出しを行い、Watson Conversation サービスとのやりとりを処理する conversationRequestResponse() 関数を呼び出します。
    func send(_ text: String) {
        setAudioPortToSpeaker()
        JSQSystemSoundPlayer.jsq_playMessageSentSound()
        let message = JSQMessage(senderId: self.senderId, senderDisplayName: self.senderDisplayName, date: Date(), text: text)    
        self.messages.append(message!)
        self.finishSendingMessage(animated: true)
        self.conversationRequestResponse(text)
      }
  2. firstMessage() 関数は、送信者が変わると、その送信者からの最初のメッセージを返して、テキスト吹き出しの先頭に送信者を示します。
    func firstMessage(at: IndexPath) -> JSQMessage! {
        let message = self.messages[at.item]
        if message.senderId == self.senderId {
          return nil
        }
        if at.item - 1 > 0 {
          let previousMessage = self.messages[at.item-1]
          if previousMessage.senderId == message.senderId {
            return nil
          }
        }
        return message
      }
  3. didReceiveConversationResponse() 関数は、Watson Conversation がレスポンスを返すと呼び出されます。この関数は、音を再生して、メッセージ配列にメッセージを追加し、センテンスを合成するためにレスポンスを Watson Text to Speech サービスにディスパッチします。そして最後に finishReceiveMessage() 関数を呼び出します。
    func didReceiveConversationResponse(_ response: [String]) {
        let sentence = re,sponse.joined(separator: " ")
        if sentence == "" { return }
        setAudioPortToSpeaker()
        JSQSystemSoundPlayer.jsq_playMessageReceivedSound()
        let message = JSQMessage(senderId: "Home Assistant", senderDisplayName: "Home Assistant", date: Date(), text: sentence)
        self.messages.append(message!)
        
        DispatchQueue.main.async {
          // text-to-speech synthesize
          self.ttsSynthesize(sentence)
          self.reloadMessagesView()
          self.finishReceivingMessage(animated: true)
        }
      }

完全な実装については、UIExt.swift ファイル (「コードを入手する」からダウンロードしたファイルに含まれています) を参照してください。

Watson サービスとのインターフェースをとる

次は、Watson サービスとのインターフェースを実装します。

Watson SDK を準備する

  1. Watson SDK をインストールします。それには Carthage を使用する必要があります。Carthage をインストールする 1 つの方法として、Homebrew (MacOS 用のパッケージ・マネージャー) を使用します。
  2. Homebrew をインストールします。
    /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
  3. Carthage をインストールします。
    brew update
    brew install carthage
  4. プロジェクト・フォルダー内で Cartfile ファイルを作成して、そこに以下の行を追加します。
    github "https://github.com/watson-developer-cloud/swift-sdk"
  5. Swift SDK フレームワークをインストールします。
    carthage update --platform iOS
  6. 以下のようなエラーを受け取る場合があります。
    Module compiled with Swift 3.1 cannot be imported in Swift 3.0.2

    その場合は、以下のコマンドを使用してバイナリーを再コンパイルします。
    carthage update --platform iOS --no-use-binaries
  7. Swift SDK フレームワークをプロジェクトに追加するために、「General (一般)」 > 「Linked Frameworks (リンクされたフレームワーク)」 > 「Libraries (ライブラリー)」の順に選択し「+」アイコンをクリックします。 「General (一般)」タブが表示されたプロジェクト・ウィンドウのスクリーン・キャプチャー
    「General (一般)」タブが表示されたプロジェクト・ウィンドウのスクリーン・キャプチャー
  8. 「Add Other (その他の追加)」をクリックし、Carthage/Build/iOS フォルダーにナビゲートします。TextToSpeechV1.framework、SpeechToTextV1.framework、ConversationV1.framework、および RestKit.framework の各フレームワークを選択します。 フレームワークとライブラリーを追加するウィンドウのスクリーン・キャプチャー
    フレームワークとライブラリーを追加するウィンドウのスクリーン・キャプチャー

    この作業が完了すると、以下のフレームワークが追加された状態になります。

    「Linked Frameworks and Libraries (リンクされたフレームワークとライブラリー)」ウィンドウのスクリーン・キャプチャー
    「Linked Frameworks and Libraries (リンクされたフレームワークとライブラリー)」ウィンドウのスクリーン・キャプチャー
  9. 上記のフレームワークをアプリケーションにコピーし、実行時にアクセスできるようにします。「Build Phases (ビルド・フェーズ)」タブを表示し、「+」アイコンをクリックして、「New Run Script Phase (新規実行スクリプト・フェーズ)」を選択します。 「Build Phases (ビルド・フェーズ)」タブが表示されたプロジェクト・ウィンドウのスクリーン・キャプチャー
    「Build Phases (ビルド・フェーズ)」タブが表示されたプロジェクト・ウィンドウのスクリーン・キャプチャー
  10. 「Run Script (実行スクリプト)」で、以下を指定します。
    /usr/local/bin/carthage copy-frameworks
    「Run Script (実行スクリプト)」ウィンドウのスクリーン・キャプチャー
    「Run Script (実行スクリプト)」ウィンドウのスクリーン・キャプチャー
  11. 「+」アイコンをクリックして、以下の入力ファイルを指定します。
    $(SRCROOT)/Carthage/Build/iOS/TextToSpeechV1.framework
    $(SRCROOT)/Carthage/Build/iOS/SpeechToTextV1.framework
    $(SRCROOT)/Carthage/Build/iOS/ConversationV1.framework
    $(SRCROOT)/Carthage/Build/iOS/RestKit.framew
    入力ファイルを示すスクリーン・キャプチャー
    入力ファイルを示すスクリーン・キャプチャー
  12. 最後に、Watson IoT Platform に接続するためのソース・コードとして、Info.plist file 内に以下の行を追加します。
    <key>NSAppTransportSecurity</key>
    <dict>
      <key>NSExceptionDomains</key>
      <dict>
        <key>watsonplatform.net</key>
        <dict>
          <key>NSTemporaryExceptionRequiresForwardSecrecy</key>
          <false/>
          <key>NSIncludesSubdomains</key>
          <true/>
          <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
          <true/>
          <key>NSTemporaryExceptionMinimumTLSVersion</key>
           <string>TLSv1.0</string>
        </dict>
      </dict>
    </dict>

初期化

  1. WatsonExt.swift という名前のファイルを作成します。このファイルに、Watson Text to Speech、Speech to Text、および Conversation サービスとインターフェースをとるためのコードを格納します。ViewController の拡張機能を宣言します。
  2. Watson Text to Speech サービスを初期化するために、Bluemix サービス資格情報からユーザー名とパスワードを取得します。
    textToSpeech = TextToSpeech(username: Credentials.TextToSpeechUsername,
                                password: Credentials.TextToSpeechPassword)
  3. Watson Speech to Text サービスを初期化するために、Bluemix サービス資格情報からユーザー名とパスワードを取得します。
    speechToTextSession = SpeechToTextSession(username: Credentials.SpeechToTextUsername,
                                              password: Credentials.SpeechToTextPassword)
  4. 音声テキスト変換の結果を処理するコールバックを追加します。
    speechToTextSession?.onResults = onResults
  5. 最後に、Watson Conversation サービスを初期化するために、Bluemix サービス資格情報からユーザー名とパスワードを取得します。さらに、WORKSPACE_ID も必要です。最初の接続時に、Conversation サービスは conversation_start ダイアログを定義し、このダイアログから (通常は挨拶として構成された) 初期レスポンスを返します。そのため、メッセージを追加して Text to Speech を使用してテキストを合成するために didReceiveConversationResponse() 関数が呼び出されます。このコンテキストが保存されて、以降のやりとりに使用されます。
    conversation = Conversation(username: Credentials.ConversationUsername,
                                password: Credentials.ConversationPassword,
                                version: "2017-03-12")
        let failure = { (error: Error) in print(error) }
        conversation?.message(withWorkspace: Credentials.ConversationWorkspaceID, failure: failure) {
          response in
          print("output.text: \(response.output.text)")
          self.didReceiveConversationResponse(response.output.text)
          self.context = response.context
        }

Watson Speech to Text サービス

Speech to Text サービスに接続するには、sttStartStreaming() 関数を使用します。この関数によって、ストリーミング・リクエストを開始できます。また、この関数はマイク入力も開始します。この関数が呼び出されるタイミングは、ユーザーがマイク・ボタンを押したときです。

func sttStartStreaming() {
    // define settings
    var settings = RecognitionSettings(contentType: .opus)
    settings.continuous = true
    settings.interimResults = true

    self.speechToTextSession?.connect()
    self.speechToTextSession?.startRequest(settings: settings)
    self.speechToTextSession?.startMicrophone()
  }

Speech to Text サービスを停止するには、sttStopStreaming() 関数を使用します。この関数は、マイクの入力も停止します。この関数が呼び出されるタイミングは、ユーザーがマイク・ボタンを離したときです。

func sttStopStreaming() {
    self.speechToTextSession?.stopMicrophone()
    self.speechToTextSession?.stopRequest()
    // No need to disconnect -- the connection will timeout if the microphone
    // is not used again within 30 seconds. This avoids the overhead of
    // connecting and disconnecting the session with every press of the
    // microphone button.
    //self.speechToTextSession?.disconnect()
    //self.speechToText?.stopRecognizeMicrophone()
  }

onResults() コールバック関数は、Speech to Text から返された最善のトランスクリプト結果に基づいてテキスト・ビュー・ウィジェットを更新します。

func onResults(results: SpeechRecognitionResults) {
    self.inputToolbar.contentView.textView.text = results.bestTranscript
    self.inputToolbar.toggleSendButtonEnabled()
  }

Watson Text to Speech サービス

ttsSynthesize() 関数は、センテンスを基に、Text to Speech サービスを利用して音声を合成するために使用されます。Text to Speech サービスが合成された音声データを再生します。

func ttsSynthesize(_ sentence: String) {
    // Synthesize the text
    let failure = { (error: Error) in print(error) }
    self.textToSpeech?.synthesize(sentence, voice: SynthesisVoice.gb_Kate.rawValue, failure: failure) { data in
      self.audioPlayer = try! AVAudioPlayer(data: data)
      self.audioPlayer.prepareToPlay()
      self.audioPlayer.play()
    }
  }

Watson Conversation サービス

conversationRequestResponse() 関数は、Conversation サービスとのやりとりを処理します。この関数は Conversation サービスにリクエストを (テキストで) 送信し、Conversation サービスからレスポンスを受け取った後、レスポンス・テキストを処理するために didReceiveConversationResponse() 関数を呼び出します。そして最後に issueCommand() 関数を呼び出します。

func conversationRequestResponse(_ text: String) {
    let failure = { (error: Error) in print(error) }
    let request = MessageRequest(text: text, context: self.context)
    self.conversation?.message(withWorkspace: Credentials.ConversationWorkspaceID,
                               request: request,
                               failure: failure) {
    response in
      print(response.output.text)
      self.didReceiveConversationResponse(response.output.text)
      self.context = response.context
      // issue command based on intents and entities
      print("appl_action: \(response.context.json["appl_action"])")
      self.issueCommand(intents: response.intents, entities: response.entities)
    }
  }

issueCommand() 関数は Conversation サービスから返されたインテントを解釈し、該当するコマンドを Watson IoT Platform サービス経由で Raspberry Pi に送信します。

func issueCommand(intents: [Intent], entities: [Entity]) {
    
    for intent in intents {
      print("intent: \(intent.intent), confidence: \(intent.confidence) ")
    }
    for entity in entities {
      print("entity: \(entity.entity), value: \(entity.value)")
    }
    
    for intent in intents {
      if intent.confidence > 0.9 {
        switch intent.intent {
        case "OnLight":
          let command = Command(action: "On", object: "Light", intent: intent.intent)
          sendToDevice(command, subtopic: "light")
        case "OffLight":
          let command = Command(action: "Off", object: "Light", intent: intent.intent)
            sendToDevice(command, subtopic: "light")
        case "TakePicture":
          let command = Command(action: "Take", object: "Picture", intent: intent.intent)
          sendToDevice(command, subtopic: "camera")
        default:
          print("No such command")
          return
        }
      }
    }
  }

IBM Watson IoT Platform サービスとインターフェースをとる

このセクションでは、IBM Watson IoT Platform サービスとのインターフェースをセットアップする方法を説明します。

Xcode プロジェクトを準備する

  1. Podfile に以下のコードを追加して、CocoaMQTT がインストールされるようにします。MQTT というプロトコルを使用する CocoaMQTT は、Watson IoT Platform サービスとやりとりするのに役立つ Swift SDK を提供します。また、メッセージを解析して JSON メッセージにフォーマット化する SwiftyJSON も追加します。
    pod 'CocoaMQTT'
    pod 'SwiftyJSON'
  2. 以下のコマンドを実行して CocoaMQTT および SwiftyJSON 依存関係をインストールします。
    pod install

初期化

  1. MQTT 接続はビュー・レベルではなくアプリケーション・レベルで処理する必要があるため、AppDelegate.swift ファイル内でグローバル変数を宣言します。MQTT は、ホスト名、ポート、クライアント ID で初期化されます。
    let mqttClient = CocoaMQTT(clientID: Credentials.WatsonIOTClientID, 
    host: Credentials.WatsonIOTHost, port: UInt16(Credentials.WatsonIOTPort))
    • ホスト名: [ORG_ID].messaging.internetofthings.ibmcloud.com
    • ポート: 1883
    • クライアント ID: a:[ORG_ID]:[API_KEY]
  2. MqttExt.swift という名前のファイルを作成します。このファイルに、Watson IoT Platform サービスとインターフェースをとるためのコードを格納します。ViewController の拡張機能を宣言し、CocoaMQTTDelegate を実装します。
    extension ViewController : CocoaMQTTDelegate {
    }
  3. ユーザー名を [API_KEY]、パスワードを [AUTH_TOKEN] で初期化し、自身を委任先として設定します。
    mqttClient.username = Credentials.WatsonIOTUsername
    mqttClient.password = Credentials.WatsonIOTPassword
    mqttClient.keepAlive = 60
    mqttClient.delegate = self
  4. アプリケーションがアクティブになった時点で接続します。
    func applicationDidBecomeActive(_ application: UIApplication) {
        mqttClient.connect()
      }
  5. アプリケーションがバックグラウンドに入った時点で接続を切断します。
    func applicationDidEnterBackground(_ application: UIApplication) {
        mqttClient.disconnect()
       }

MQTT コールバックを実装する

  1. 接続成功イベントを受信するために、以下のコールバックを実装します。接続が確立されたら、イベント・トピックにサブスクライブするための呼び出しを行います。トピックのフォーマットは iot-2/type/[DEV_TYPE]/id/[DEV_ID]/evt/+/fmt/json です。ここで、+ はワイルドカードです。
    func mqtt(_ mqtt: CocoaMQTT, didConnect host: String, port: Int) {
        mqttClient.subscribe(eventTopic)
    }
  2. デバイスからのイベントを処理する didReceiveMessage() コールバック関数を実装します。この関数はカメラからステータスを受信した場合、Object Storage サービスから該当する写真をダウンロードします。照明からのステータスを受信した場合は、会話にメッセージを追加します。
    func mqtt(_ mqtt: CocoaMQTT, didReceiveMessage message: CocoaMQTTMessage, id: UInt16) {
        var json : JSON = JSON.null
        if let message = message.string {
          json = JSON.init(parseJSON: message)
        } else {
          return  // do nothing
        }
        let cameraTopic = "iot-2/type/\(Credentials.DevType)/id/\(Credentials.DevId)/evt/camera/fmt/json"
        let lightTopic = "iot-2/type/\(Credentials.DevType)/id/\(Credentials.DevId)/evt/light/fmt/json"
        switch message.topic {
        case cameraTopic:
          //ObjectStorage
          if let objectname = json["d"]["objectname"].string {
            if let containername = json["d"]["containername"].string {
              self.downloadPictureFromObjectStorage(containername: containername, objectname: objectname)
            }
          }
        case lightTopic:
          if let status = json["d"]["status"].string {
            switch status {
            case "on":
              self.didReceiveConversationResponse(["Light is on"])
            case "off":
              self.didReceiveConversationResponse(["Light is off"])
            default:
              break
            }
          }
        default:
          break
        }
      }

コマンドをデバイスに送信する

  1. コマンドをデバイスに送信するために、以下の関数を実装します。トピックのフォーマットは iot-2/type/[DEV_TYPE]/id/[DEV_ID]/cmd/[light|camera]/fmt/json です。
    func sendToDevice(_ command: Command, subtopic: String) {
        if let json = command.toJSON() {
          let topic = "iot-2/type/\(Credentials.DevType)/id/\(Credentials.DevId)/cmd/\(subtopic)/fmt/json"
          let message = CocoaMQTTMessage(topic: topic, string: json)
          print("publish message \(json)")
          mqttClient.publish(message)
        }
      }

Object Storage サービスから画像を受信する

Object Storage サービスから画像を受信するには、Xcode プロジェクトを準備してから初期化する必要があります。

Xcode プロジェクトを準備する

  1. Podfile に以下のコードを追加して、BluemixObjectStorage フレームワークがインストールされるようにします。
    pod 'BluemixObjectStorage'
  2. 以下のコマンドを実行して BluemixObjectStorage 依存関係をインストールします。
    pod install

初期化

  1. ObjectStorageExt.swift という名前のファイルを作成します。このファイルに、Object Storage サービスとのインターフェースをとり、ViewController の拡張機能を宣言するコードを格納します。
    extension ViewController {
    }
  2. 前に作成した [OS_PROJECTID][OS_USERNAME]、および [OS_PASSWORD] で Object Storage サービスを初期化します。
    self.objectStorage = ObjectStorage(projectId: Credentials.ObjectStorageProjectId)
    
        objectStorage.connect(userId: Credentials.ObjectStorageUserId,
                           password: Credentials.ObjectStoragePassword,
                           region: ObjectStorage.Region.Dallas) {
                              error in
                              if let error = error {
                                print("objectstorage connect error :: \(error)")
                              } else {
                                print("objectstorage connect success")
                              }
        }
  3. Node-RED アプリケーションによって撮影されてアップロードされた写真をダウンロードする downloadPictureFromObjectStorage() 関数を実装します。
    func downloadPictureFromObjectStorage(containername: String, objectname: String) {
        self.objectStorage.retrieve(container: containername) {
          error, container in
          if let error = error {
            print("retrieve container error :: \(error)")
          } else if let container = container {
            container.retrieve(object: objectname) {
              error, object in
              if let error = error {
                print("retrieve object error :: \(error)")
              } else if let object = object {
                print("retrieve object success :: \(object.name)")
                guard let data = object.data else {
                  return
                }
                if let image = UIImage(data: data) {
                  self.addPicture(image)
                  self.didReceiveConversationResponse(["Picture taken"])
                }
              } else {
                print("retrieve object exception")
              }
            }
          } else {
            print("retrieve container exception")
          }
        }
      }

まとめ

このチュートリアルでは、モバイル・アプリケーションで Watson Conversation、Text to Speech、および Speech to Text サービスを利用してユーザーの命令を理解する方法を説明しました。ユーザーの命令から解釈したコマンドを使って、Watson IoT Platform サービスを介してデバイスを制御するという仕組みです。さらに、このモバイル・アプリからコマンドを受信し、イベントをモバイル・アプリケーションに送信する Raspberry Pi を、ホーム・ゲートウェイとして統合する方法を説明し、最後に Object Storage サービスを利用して画像を保管する方法も説明しました。

このチュートリアルをレビューして協力してくれた Chung Kit Chan に感謝します。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Mobile development
ArticleID=1051085
ArticleTitle=Watson および IoT Platform サービスを利用してホーム・アシスタント・モバイル・アプリケーションを構築する
publish-date=10262017