1. 始める前に
モノのインターネット(IoT)のデベロッパーは、ユーザーが Google Home アプリのタップ操作やアシスタントの音声コマンドを通じてデバイスを操作できるようにするスマートホーム アクションを構築できます。
スマートホーム アクションは、ホームグラフに基づいて家とそのデバイスに関するコンテキスト データを提供し、家の論理マップを作成します。このコンテキストにより、アシスタントはユーザーのリクエストを屋内の位置と関連付けて、より自然な形で理解できるようになります。たとえば、ホームグラフには、「さまざまなメーカーの複数のデバイス(エアコン、照明、扇風機、掃除機など)が置かれたリビングルーム」というコンセプトを保存できます。
前提条件
- スマートホーム アクションを作成するデベロッパー ガイド
作成するアプリの概要
この Codelab では、仮想スマート洗濯機を管理するクラウド サービスを公開し、スマートホーム アクションを構築してアシスタントにリンクします。
演習内容
- スマートホーム クラウド サービスをデプロイする方法
- サービスをアシスタントにリンクする方法
- デバイスの状態変化を Google に公開する方法
必要なもの
- Google Chrome などのウェブブラウザ
- Google Home アプリがインストールされている iOS デバイスまたは Android デバイス
- Node.js バージョン 10.16 以降
- Google Cloud 請求先アカウント
2. はじめに
アクティビティ管理を有効にする
Google アシスタントを使用するには、特定のアクティビティ データを Google と共有する必要があります。このデータは Google アシスタントが適切に機能するために必要となります。ただし、データの共有は SDK に固有の要件ではありません。このデータを共有するには、Google アカウントを作成します(まだ作成していない場合)。Google アカウントは任意のものを使用できます。デベロッパー アカウントである必要はありません。
アシスタントで使用する Google アカウントの [アクティビティ管理] ページを開きます。
以下の切り替えスイッチが有効になっていることを確認します。
- [ウェブとアプリのアクティビティ] - これに加えて、[Chrome の履歴と Google サービスを使用するサイト、アプリ、デバイスでのアクティビティを含める] チェックボックスがオンになっていることも確認してください。
- [デバイス情報]
- [音声アクティビティ]
Actions プロジェクトを作成する
- Actions on Google デベロッパー コンソールに移動します。
- [New Project](新しいプロジェクト)をクリックし、プロジェクトの名前を入力して [CREATE PROJECT](プロジェクトを作成)をクリックします。
スマートホーム アプリを選択する
Actions Console の概要画面で [Smart Home](スマートホーム)を選択します。
[Smart home] エクスペリエンス カードを選択し、[Start Building] をクリックするとプロジェクト コンソールが表示されます。
Firebase CLI をインストールする
Firebase コマンドライン インターフェース(CLI)を使用すると、ウェブアプリをローカルで提供し Firebase Hosting にデプロイできます。
CLI をインストールするには、ターミナルから次の npm コマンドを実行します。
npm install -g firebase-tools
CLI が正しくインストールされたことを確認するには、次のコマンドを実行します。
firebase --version
次のコマンドを実行して、Google アカウントで Firebase CLI を承認します。
firebase login
3. スターター アプリを実行する
開発環境の設定が完了したので、スターター プロジェクトをデプロイし、すべてが正しく設定されていることを確認しましょう。
ソースコードを取得する
下のリンクをクリックして、この Codelab のサンプルを開発マシンにダウンロードします。
または、コマンドラインから GitHub リポジトリのクローンを作成することもできます。
git clone https://github.com/google-home/smarthome-washer.git
プロジェクトについて
スターター プロジェクトには、以下のサブディレクトリが含まれています。
public:
スマート洗濯機の状態を簡単に制御、モニタリングするためのフロントエンド UI。functions:
Cloud Functions for Firebase と Firebase Realtime Database を使用してスマート洗濯機を管理する、実装が完了したクラウド サービス。
Firebase に接続する
washer-start
ディレクトリに移動し、Actions プロジェクトに Firebase CLI を設定します。
cd washer-start firebase use <project-id>
Firebase プロジェクトを設定する
Firebase プロジェクトを初期化します。
firebase init
CLI 機能、[Realtime Database]、[Functions]、Firebase Hosting を含む [Hosting] 機能を選択します。
? Which Firebase CLI features do you want to set up for this directory? Press Space to select features, then Enter to confirm your choices. ❯◉ Realtime Database: Configure a security rules file for Realtime Database and (optionally) provision default instance ◯ Firestore: Configure security rules and indexes files for Firestore ◉ Functions: Configure a Cloud Functions directory and its files ◉ Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys ◯ Hosting: Set up GitHub Action deploys ◯ Storage: Configure a security rules file for Cloud Storage ◯ Emulators: Set up local emulators for Firebase products ◯ Remote Config: Configure a template file for Remote Config ◯ Extensions: Set up an empty Extensions manifest
これにより、プロジェクトに必要な API と機能が初期化されます。
プロンプトが表示されたら、Realtime Database を初期化します。データベース インスタンスのデフォルトの場所を使用できます。
? It seems like you haven't initialized Realtime Database in your project yet. Do you want to set it up? Yes ? Please choose the location for your default Realtime Database instance: us-central1
スターター プロジェクトのコードを使用しているため、セキュリティ ルールのデフォルト ファイルを選択します。既存のデータベース ルール ファイルを上書きしないよう注意してください。
? File database.rules.json already exists. Do you want to overwrite it with the Realtime Database Security Rules for <project-ID>-default-rtdb from the Firebase Console? No
プロジェクトを再初期化する場合、コードベースを初期化するか上書きするかを尋ねられたら、[Overwrite] を選択します。
? Would you like to initialize a new codebase, or overwrite an existing one? Overwrite
関数を構成するときは、デフォルト ファイルを使用する必要があります。また、プロジェクト サンプル内の既存の index.js ファイルと package.json ファイルを上書きしないでください。
? What language would you like to use to write Cloud Functions? JavaScript ? Do you want to use ESLint to catch probable bugs and enforce style? No ? File functions/package.json already exists. Overwrite? No ? File functions/index.js already exists. Overwrite? No
プロジェクトを再初期化する場合、functions/.gitignore を初期化するか上書きするかを尋ねられたら [No] を選択します。
? File functions/.gitignore already exists. Overwrite? No
? Do you want to install dependencies with npm now? Yes
最後に、プロジェクト コードの public
ディレクトリと既存の index.html ファイルを使用するように Hosting 設定を構成します。ESLint の使用を尋ねられたら、[No] を選択します。
? What do you want to use as your public directory? public ? Configure as a single-page app (rewrite all urls to /index.html)? Yes ? Set up automatic builds and deploys with GitHub? No ? File public/index.html already exists. Overwrite? No
ESLint が誤って有効になっていた場合は、次の 2 つの方法で無効にできます。
- GUI を使用して、プロジェクトの
../functions
フォルダに移動し、隠しファイル.eslintrc.js
を選択して削除します。類似した名前の.eslintrc.json
と混同しないでください。 - コマンドラインを使用する場合:
cd functions rm .eslintrc.js
正しく完全な Firebase 構成になるようにするには、firebase.json
ファイルを washer-done
ディレクトリから washer-start
ディレクトリにコピーし、washer-start
内のファイルを上書きします。
washer-start
ディレクトリで:
cp -vp ../washer-done/firebase.json .
Firebase にデプロイする
これで依存関係のインストールとプロジェクトの設定が完了し、アプリを実行する準備が整いました。
firebase deploy
コンソールに次のような出力が表示されます。
... ✔ Deploy complete! Project Console: https://console.firebase.google.com/project/<project-id>/overview Hosting URL: https://<project-id>.web.app
このコマンドは、ウェブアプリと複数の Cloud Functions for Firebase をデプロイします。
ブラウザで Hosting URL(https://<project-id>.web.app
)を開き、ウェブアプリを表示します。次のようなインターフェースが表示されます。
このウェブ UI は、デバイスの状態を表示したり変更したりするためのサードパーティ プラットフォームを表したものです。データベースへのデバイス情報の入力を開始するには、[UPDATE](更新)をクリックします。ページの表示は変化しませんが、洗濯機の現在の状態がデータベースに保存されます。
次に、Actions Console を使用して、デプロイしたクラウド サービスを Google アシスタントに接続します。
Actions Console プロジェクトを構成する
[Overview](概要)> [Build your Action](アクションの構築)で、[Add Action(s)](アクションを追加)を選択します。スマートホーム インテントのフルフィルメントを提供する Cloud Functions の URL を入力し、[Save](保存)をクリックします。
https://us-central1-<project-id>.cloudfunctions.net/smarthome
[Develop] > [Invocation](呼び出し)タブで、アクションの [Display Name](表示名)を追加して [Save] をクリックします。この名前は Google Home アプリに表示されます。
アカウント リンクを有効にするには、左側のナビゲーションで [Develop] > [Account linking](アカウント リンク)を選択します。以下の値でアカウント リンクを設定します。
クライアント ID |
|
クライアント シークレット |
|
認証 URL |
|
トークンの URL |
|
[Save] をクリックしてアカウント リンクの設定を保存し、[Test](テスト)をクリックしてプロジェクトでのテストを有効にします。
[Simulator](シミュレータ)にリダイレクトされます。[Test now enabled] が表示されない場合は、[Reset Test] をクリックして、テストが有効になっていることを確認します。
これで、デバイスの状態とアシスタントをリンクするために必要な Webhook の実装を開始できるようになりました。
4. 洗濯機を作成する
アクションの設定が完了したら、次はデバイスを追加してデータを送信します。クラウド サービスでは、以下のインテントを処理する必要があります。
SYNC
インテント。ユーザーがリンク済みのデバイスについてアシスタントが問い合わせたときに発生します。これは、ユーザーがアカウントをリンクしたときにサービスに送信されます。ユーザーのすべてのデバイスとその機能を格納した JSON ペイロードで応答する必要があります。QUERY
インテント。デバイスの現在の状態をアシスタントが問い合わせたときに発生します。リクエストされた各デバイスの状態を格納した JSON ペイロードで応答する必要があります。EXECUTE
インテント。アシスタントがユーザーに代わってデバイスを制御するときに発生します。リクエストされた各デバイスの実行状態を格納した JSON ペイロードで応答する必要があります。DISCONNECT
インテントは、ユーザーがアシスタントからアカウントのリンクを解除したときに発生します。このユーザーのデバイスのイベントをアシスタントに送信するのを停止する必要があります。
以降のセクションでは、すでにデプロイした関数を、これらのインテントを処理できるように更新します。
SYNC レスポンスを更新する
functions/index.js
を開きます。このファイルには、アシスタントからのリクエストに応答するコードが含まれています。
SYNC
インテントを処理するには、デバイスのメタデータと機能を返す必要があります。onSync
配列の JSON を更新して、デバイス情報と、洗濯機の推奨トレイトを含めます。
index.js
app.onSync((body) => {
return {
requestId: body.requestId,
payload: {
agentUserId: USER_ID,
devices: [{
id: 'washer',
type: 'action.devices.types.WASHER',
traits: [
'action.devices.traits.OnOff',
'action.devices.traits.StartStop',
'action.devices.traits.RunCycle',
],
name: {
defaultNames: ['My Washer'],
name: 'Washer',
nicknames: ['Washer'],
},
deviceInfo: {
manufacturer: 'Acme Co',
model: 'acme-washer',
hwVersion: '1.0',
swVersion: '1.0.1',
},
willReportState: true,
attributes: {
pausable: true,
},
}],
},
};
});
Firebase にデプロイする
Firebase CLI を使用して、更新したクラウド フルフィルメントをデプロイします。
firebase deploy --only functions
Google アシスタントにリンクする
スマートホーム アクションをテストするには、プロジェクトを Google アカウントにリンクする必要があります。これにより、同じアカウントにログインしている Google アシスタント画面と Google Home アプリでテストできるようになります。
- スマートフォンで Google アシスタントの設定を開きます。なお、コンソールと同じアカウントでログインする必要があります。
- [Google アシスタント] > [設定] > [スマートホーム]([アシスタント] の下)に移動します。
- 右上にある検索アイコンをクリックします。
- [test] 接頭辞を使用してテストアプリを検索し、特定のテストアプリを見つけます。
- そのアイテムを選択します。Google アシスタントがサービスで認証を行い、
SYNC
リクエストを送信してデバイスのリストをユーザーに提供するようサービスに依頼します。
Google Home アプリを開いて、洗濯機デバイスが表示されることを確認します。
5. コマンドとクエリを処理する
クラウド サービスが洗濯機デバイスを Google に正しく報告できたので、次は、デバイスの状態をリクエストしてコマンドを送信する機能を追加します。
QUERY インテントを処理する
QUERY
インテントにはデバイスのセットが含まれます。デバイスごとに現在の状態を返す必要があります。
functions/index.js
で、インテント リクエストに含まれる対象デバイスのリストを処理するよう QUERY
ハンドラを編集します。
index.js
app.onQuery(async (body) => {
const {requestId} = body;
const payload = {
devices: {},
};
const queryPromises = [];
const intent = body.inputs[0];
for (const device of intent.payload.devices) {
const deviceId = device.id;
queryPromises.push(queryDevice(deviceId)
.then((data) => {
// Add response to device payload
payload.devices[deviceId] = data;
}
));
}
// Wait for all promises to resolve
await Promise.all(queryPromises);
return {
requestId: requestId,
payload: payload,
};
});
リクエストに含まれるデバイスごとに、Realtime Database に保存されている現在の状態を返します。洗濯機の状態データを返すように queryFirebase
関数と queryDevice
関数を更新します。
index.js
const queryFirebase = async (deviceId) => {
const snapshot = await firebaseRef.child(deviceId).once('value');
const snapshotVal = snapshot.val();
return {
on: snapshotVal.OnOff.on,
isPaused: snapshotVal.StartStop.isPaused,
isRunning: snapshotVal.StartStop.isRunning,
};
};
const queryDevice = async (deviceId) => {
const data = await queryFirebase(deviceId);
return {
on: data.on,
isPaused: data.isPaused,
isRunning: data.isRunning,
currentRunCycle: [{
currentCycle: 'rinse',
nextCycle: 'spin',
lang: 'en',
}],
currentTotalRemainingTime: 1212,
currentCycleRemainingTime: 301,
};
};
EXECUTE インテントを処理する
EXECUTE
インテントは、デバイスの状態を更新するコマンドを処理します。レスポンスが返すのは、各コマンドのステータス(SUCCESS
、ERROR
、PENDING
など)と更新後のデバイスの状態です。
functions/index.js
で、更新が必要なトレイトのリストと各コマンドの対象デバイスのセットを処理するよう EXECUTE
ハンドラを編集します。
index.js
app.onExecute(async (body) => {
const {requestId} = body;
// Execution results are grouped by status
const result = {
ids: [],
status: 'SUCCESS',
states: {
online: true,
},
};
const executePromises = [];
const intent = body.inputs[0];
for (const command of intent.payload.commands) {
for (const device of command.devices) {
for (const execution of command.execution) {
executePromises.push(
updateDevice(execution, device.id)
.then((data) => {
result.ids.push(device.id);
Object.assign(result.states, data);
})
.catch(() => functions.logger.error('EXECUTE', device.id)));
}
}
}
await Promise.all(executePromises);
return {
requestId: requestId,
payload: {
commands: [result],
},
};
});
コマンドと対象デバイスごとに、リクエストされたトレイトに対応する Realtime Database の値を更新します。該当する Firebase 参照を更新して更新後のデバイスの状態を返すように updateDevice
関数を変更します。
index.js
const updateDevice = async (execution, deviceId) => {
const {params, command} = execution;
let state; let ref;
switch (command) {
case 'action.devices.commands.OnOff':
state = {on: params.on};
ref = firebaseRef.child(deviceId).child('OnOff');
break;
case 'action.devices.commands.StartStop':
state = params.start
? {isRunning: true, isPaused: false}
: {isRunning: false, isPaused: false};
ref = firebaseRef.child(deviceId).child('StartStop');
break;
case 'action.devices.commands.PauseUnpause':
const data = await queryDevice(deviceId);
state = (data.isPaused === false && data.isRunning === false)
? {isRunning: false, isPaused: false}
: {isRunning: !params.pause, isPaused: params.pause};
ref = firebaseRef.child(deviceId).child('StartStop');
break;
}
return ref.update(state)
.then(() => state);
};
6. アクションをテストする
3 つのインテントをすべて実装したら、アクションで洗濯機を制御できるかどうかをテストします。
Firebase にデプロイする
Firebase CLI を使用して、更新したクラウド フルフィルメントをデプロイします。
firebase deploy --only functions
洗濯機をテストする
スマートフォンで以下の音声コマンドを試して、値が変化することを確認します。
「OK Google, 洗濯機をオンにして。」
「OK Google, 洗濯機を一時停止して。」
「OK Google, 洗濯機を止めて。」
また、洗濯機の現在の状態を確認することもできます。
「OK Google, 洗濯機はオンになってる?」
「OK Google, 洗濯機は動いてる?」
「OK Google, 洗濯機の今のステップを教えて」
これらのクエリやコマンドは、Firebase コンソールの [Functions] セクションにある関数の下に表示されるログで確認できます。Firebase ログの詳細については、ログの書き込みと表示をご覧ください。
これらのクエリとコマンドは、Google Cloud コンソールで [ロギング] > [ログ エクスプローラ。Google Cloud Logging の詳細については、Cloud Logging を使用してイベントログにアクセスするをご覧ください。
7. 変更について Google に報告する
以上でクラウド サービスとスマートホーム インテントの統合がすべて完了し、洗濯機デバイスの現在の状態の制御とクエリの実行ができるようになりました。ただし、サービスがアシスタントにイベント情報(デバイスのプレゼンスや状態の変化など)を自動的に送信する手段がまだ実装されていません。
Request Sync を使用すると、ユーザーがデバイスを追加または削除したときや、デバイスの機能が変更されたときに、新しい同期リクエストをトリガーできます。Report State を使用して、ユーザーがデバイスの状態を物理的に変更(照明のスイッチをオンにするなど)したり、別のサービスを使用して状態を変更したりした場合に、クラウド サービスでデバイスの状態をホームグラフに事前に送信できます。
このセクションでは、フロントエンドのウェブアプリからこれらのメソッドを呼び出すためのコードを追加します。
HomeGraph API を有効にする
HomeGraph API を使用すると、ユーザーのホームグラフ内のデバイスとその状態を保存し、クエリできます。この API を使用するには、まず Google Cloud コンソールを開き、HomeGraph API を有効にする必要があります。
Google Cloud Console でアクションの <project-id>.
に一致するプロジェクトを選択し、HomeGraph API の [API ライブラリ] 画面で [有効にする]をクリックします。
Report State を有効にする
Realtime Database への書き込みによって、スターター プロジェクトの reportstate
関数がトリガーされます。データベースに書き込まれたデータをキャプチャして Report State 経由でホームグラフに送信するよう、functions/index.js
の reportstate
関数を更新します。
index.js
exports.reportstate = functions.database.ref('{deviceId}').onWrite(
async (change, context) => {
functions.logger.info('Firebase write event triggered Report State');
const snapshot = change.after.val();
const requestBody = {
requestId: 'ff36a3cc', /* Any unique ID */
agentUserId: USER_ID,
payload: {
devices: {
states: {
/* Report the current state of our washer */
[context.params.deviceId]: {
on: snapshot.OnOff.on,
isPaused: snapshot.StartStop.isPaused,
isRunning: snapshot.StartStop.isRunning,
},
},
},
},
};
const res = await homegraph.devices.reportStateAndNotification({
requestBody,
});
functions.logger.info('Report state response:', res.status, res.data);
});
Request Sync を有効にする
フロントエンド ウェブ UI のアイコンを更新すると、スターター プロジェクトの requestsync
関数がトリガーされます。functions/index.js
の requestsync
関数に、HomeGraph API の呼び出しを実装します。
index.js
exports.requestsync = functions.https.onRequest(async (request, response) => {
response.set('Access-Control-Allow-Origin', '*');
functions.logger.info(`Request SYNC for user ${USER_ID}`);
try {
const res = await homegraph.devices.requestSync({
requestBody: {
agentUserId: USER_ID,
},
});
functions.logger.info('Request sync response:', res.status, res.data);
response.json(res.data);
} catch (err) {
functions.logger.error(err);
response.status(500).send(`Error requesting sync: ${err}`);
}
});
Firebase にデプロイする
Firebase CLI を使用して、更新したコードをデプロイします。
firebase deploy --only functions
実装をテストする
ウェブ UI で [更新 ] ボタン をクリックし、Firebase コンソールのログに同期リクエストが表示されていることを確認します。
次に、フロントエンド ウェブ UI で洗濯機の属性を調整し、[Update] をクリックします。状態の変化が Google に報告されていることを Firebase コンソールのログで確認します。
8. 完了
以上で、スマートホーム アクションを使用するデバイス クラウド サービスとアシスタントの統合が完了しました。
さらに詳しく
さらに詳しく学びたい方は、以下のことをお試しください。
- デバイスにモードと切り替えを追加する。
- サポートされているトレイトをデバイスに追加する。
- スマートホームのローカル実行について確認する。
- 詳しくは、GitHub サンプルをご覧ください。
また、アクションをユーザーに公開するための認定プロセスなど、アクションのテストと送信の詳細もご確認ください。