1. 始める前に
スマートホーム アクションはデバイスタイプを使用して、デバイスで使用する文法を Google アシスタントに伝えます。これらのデバイスタイプの機能を定義するのがデバイス トレイトです。デバイスは、アクションに追加された各デバイス トレイトの状態を継承します。
ユーザーのデバイスの機能をカスタマイズするには、サポートされているトレイトを特定のデバイスタイプに関連付けます。現在デバイス スキーマで使用できないカスタム トレイトをアクションに実装したい場合は、Modes トレイトと Toggles トレイトを使用して特定の設定を許可します。カスタム名を定義します。
Smart Home API には、タイプやトレイトが提供する基本的なコントロール機能に加えて、ユーザー エクスペリエンスを向上させるための追加機能も用意されています。インテント レスポンスが正常に完了しなかった場合には、エラー レスポンスによって詳細なユーザー フィードバックが提供されます。二次的なユーザー確認では、このような応答を拡張し、選択したデバイス トレイトのセキュリティを強化します。アシスタントが発行したチャレンジ ブロックに特定のエラー レスポンスを送信することにより、スマートホーム アクションでコマンドを実行するための追加の承認を要求することも可能です。
前提条件
- スマートホーム アクションの作成に関するデベロッパー ガイド
- スマートホーム洗濯機の Codelab
- デバイスのタイプとトレイトに関するデベロッパー ガイド
作成するアプリの概要
この Codelab では、事前構築済みのスマートホームと Firebase の統合をデプロイし、負荷サイズとターボモードの標準外のトレイトをスマートホーム洗濯機に追加する方法を学びます。また、エラーと例外の報告を実装し、二次的なユーザー確認を使用して洗濯機の電源を切るために口頭での確認応答を行う方法を学びます。
学習内容
- Modes トレイトと Toggles トレイトを追加する
- エラーと例外を報告できるようにする
- 予備のユーザー確認を適用する方法
必要なもの
- Google Chrome などのウェブブラウザ
- Google Home アプリがインストールされている iOS デバイスまたは Android デバイス
- Node.js バージョン 10.16 以降
- Google アカウント
- 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
HomeGraph API を有効にする
HomeGraph API を使用すると、ユーザーのホームグラフ内のデバイスとその状態を保存し、クエリできます。この API を使用するには、まず Google Cloud コンソールを開き、HomeGraph API を有効にする必要があります。
Google Cloud Console でアクションの <project-id>.
に一致するプロジェクトを選択し、HomeGraph API の [API ライブラリ] 画面で [有効にする]をクリックします。
3. スターター アプリを実行する
開発環境の設定が完了したので、スターター プロジェクトをデプロイし、すべてが正しく設定されていることを確認しましょう。
ソースコードを取得する
下のリンクをクリックして、この Codelab のサンプルを開発マシンにダウンロードします。
または、コマンドラインから GitHub リポジトリのクローンを作成することもできます。
git clone https://github.com/google-home/smarthome-traits.git
ダウンロードした zip ファイルを解凍します。
プロジェクトについて
スターター プロジェクトには、以下のサブディレクトリが含まれています。
public:
スマート洗濯機の状態を簡単に制御、モニタリングするためのフロントエンド UI。functions:
Cloud Functions for Firebase と Firebase Realtime Database を使用してスマート洗濯機を管理する、実装が完了したクラウド サービス。
提供されるクラウド フルフィルメントの index.js
には、以下の関数が含まれています。
fakeauth
: アカウントのリンク用の認可エンドポイントfaketoken
: アカウント リンク用のトークン エンドポイントsmarthome
: スマートホーム インテント フルフィルメント エンドポイントreportstate
: デバイスの状態が変化したときに Home Graph API を呼び出すrequestsync
: アカウントを再リンクすることなく、ユーザーのデバイスを更新できます。
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 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] をクリックして、テストが有効になっていることを確認します。
Google アシスタントにリンクする
スマートホーム アクションをテストするには、プロジェクトを Google アカウントにリンクする必要があります。これにより、同じアカウントにログインしている Google アシスタント画面と Google Home アプリでテストできるようになります。
- スマートフォンで Google アシスタントの設定を開きます。なお、コンソールと同じアカウントでログインする必要があります。
- [Google アシスタント] > [設定] > [スマートホーム]([アシスタント] の下)に移動します。
- 右上にある検索アイコンをクリックします。
- [test] 接頭辞を使用してテストアプリを検索し、特定のテストアプリを見つけます。
- そのアイテムを選択します。Google アシスタントがサービスで認証を行い、
SYNC
リクエストを送信してデバイスのリストをユーザーに提供するようサービスに依頼します。
Google Home アプリを開いて、洗濯機デバイスが表示されることを確認します。
Google Home アプリで、音声コマンドを使用して洗濯機を操作できることを確認します。また、クラウド フルフィルメントのフロントエンド ウェブ UI で、デバイスの状態の変化を確認します。
これで、基本的な洗濯機デバイスをデプロイできました。次は、デバイスで使用できるモードをカスタマイズします。
4. モードを追加する
action.devices.traits.Modes
トレイトを使用すると、デバイスは 1 つのモードに対して任意の数の設定を持つことができ、一度に設定できるのは 1 つだけです。ここでは、洗濯量を定義するモードを追加し、値として「small」(小)、「medium」(中)、「large」(大)を設定します。
SYNC レスポンスを更新する
新しいトレイトを追加するには、トレイトに関する情報を functions/index.js
の SYNC
レスポンスに追加する必要があります。追加した情報は、次のコード スニペットに示すように traits
配列と attributes
オブジェクトに表示されます。
index.js
app.onSync(body => {
return {
requestId: 'ff36a3cc-ec34-11e6-b1a0-64510650abcf',
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',
// Add Modes trait
'action.devices.traits.Modes',
],
name: { ... },
deviceInfo: { ... },
attributes: {
pausable: true,
//Add availableModes
availableModes: [{
name: 'load',
name_values: [{
name_synonym: ['load'],
lang: 'en',
}],
settings: [{
setting_name: 'small',
setting_values: [{
setting_synonym: ['small'],
lang: 'en',
}]
}, {
setting_name: 'medium',
setting_values: [{
setting_synonym: ['medium'],
lang: 'en',
}]
}, {
setting_name: 'large',
setting_values: [{
setting_synonym: ['large'],
lang: 'en',
}]
}],
ordered: true,
}],
},
}],
},
};
});
新しい EXECUTE インテント コマンドを追加する
次のコード スニペットに示すように、EXECUTE
インテントに action.devices.commands.SetModes
コマンドを追加します。
index.js
const updateDevice = async (execution,deviceId) => {
const {params,command} = execution;
let state, 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 = {isRunning: params.start};
ref = firebaseRef.child(deviceId).child('StartStop');
break;
case 'action.devices.commands.PauseUnpause':
state = {isPaused: params.pause};
ref = firebaseRef.child(deviceId).child('StartStop');
Break;
// Add SetModes command
case 'action.devices.commands.SetModes':
state = {load: params.updateModeSettings.load};
ref = firebaseRef.child(deviceId).child('Modes');
break;
}
QUERY レスポンスを更新する
次に、QUERY
レスポンスを更新して洗濯機の現在の状態を取得します。
更新された変更を queryFirebase
関数と queryDevice
関数に追加して、Realtime Database に保存されている状態を取得します。
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,
// Add Modes snapshot
load: snapshotVal.Modes.load,
};
}
const queryDevice = async (deviceId) => {
const data = await queryFirebase(deviceId);
return {
on: data.on,
isPaused: data.isPaused,
isRunning: data.isRunning,
currentRunCycle: [{ ... }],
currentTotalRemainingTime: 1212,
currentCycleRemainingTime: 301,
// Add currentModeSettings
currentModeSettings: {
load: data.load,
},
};
};
Report State を更新する
最後に reportstate
関数を更新し、現在の洗濯物量の設定がホームグラフに報告されるようにします。
index.js
const requestBody = {
requestId: 'ff36a3cc', /* Any unique ID */
agentUserId: USER_ID,
payload: {
devices: {
states: {
/* Report the current state of your washer */
[context.params.deviceId]: {
on: snapshot.OnOff.on,
isPaused: snapshot.StartStop.isPaused,
isRunning: snapshot.StartStop.isRunning,
// Add currentModeSettings
currentModeSettings: {
load: snapshot.Modes.load,
},
},
},
},
},
};
Firebase にデプロイする
次のコマンドを実行して、更新したアクションをデプロイします。
firebase deploy --only functions
デプロイが完了したら、ウェブ UI に移動してツールバーの更新ボタン をクリックします。これにより Request Sync がトリガーされ、更新された SYNC
レスポンス データがアシスタントに送信されます。
洗濯機のモードは、次のようなコマンドで設定できます。
「OK Google, 洗濯量を大にセットして。」
次のように、洗濯量を確認することもできます。
「OK Google, 洗濯量はどれくらい?」
5. 切り替えを追加する
action.devices.traits.Toggles
トレイトは、デバイスの状態を true または false で示す名前付きアスペクトを表します。たとえば、洗濯機がターボモードになっているかどうかを示すことができます。
SYNC レスポンスを更新する
SYNC
レスポンスで、新しいデバイス トレイトに関する情報を追加する必要があります。追加した情報は、次のコード スニペットに示すように traits
配列と attributes
オブジェクトに表示されます。
index.js
app.onSync(body => {
return {
requestId: 'ff36a3cc-ec34-11e6-b1a0-64510650abcf',
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',
'action.devices.traits.Modes',
// Add Toggles trait
'action.devices.traits.Toggles',
],
name: { ... },
deviceInfo: { ... },
attributes: {
pausable: true,
availableModes: [{
name: 'load',
name_values: [{
name_synonym: ['load'],
lang: 'en'
}],
settings: [{ ... }],
ordered: true,
}],
//Add availableToggles
availableToggles: [{
name: 'Turbo',
name_values: [{
name_synonym: ['turbo'],
lang: 'en',
}],
}],
},
}],
},
};
});
新しい EXECUTE インテント コマンドを追加する
次のコード スニペットに示すように、EXECUTE
インテントに action.devices.commands.SetToggles
コマンドを追加します。
index.js
const updateDevice = async (execution,deviceId) => {
const {params,command} = execution;
let state, 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 = {isRunning: params.start};
ref = firebaseRef.child(deviceId).child('StartStop');
break;
case 'action.devices.commands.PauseUnpause':
state = {isPaused: params.pause};
ref = firebaseRef.child(deviceId).child('StartStop');
break;
case 'action.devices.commands.SetModes':
state = {load: params.updateModeSettings.load};
ref = firebaseRef.child(deviceId).child('Modes');
break;
// Add SetToggles command
case 'action.devices.commands.SetToggles':
state = {Turbo: params.updateToggleSettings.Turbo};
ref = firebaseRef.child(deviceId).child('Toggles');
break;
}
QUERY レスポンスを更新する
最後に QUERY
レスポンスを更新して、洗濯機のターボモードを報告できるようにする必要があります。更新された変更を queryFirebase
関数と queryDevice
関数に追加して、Realtime Database に保存されているトグルの状態を取得します。
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,
load: snapshotVal.Modes.load,
// Add Toggles snapshot
Turbo: snapshotVal.Toggles.Turbo,
};
}
const queryDevice = async (deviceId) => {
const data = queryFirebase(deviceId);
return {
on: data.on,
isPaused: data.isPaused,
isRunning: data.isRunning,
currentRunCycle: [{ ... }],
currentTotalRemainingTime: 1212,
currentCycleRemainingTime: 301,
currentModeSettings: {
load: data.load,
},
// Add currentToggleSettings
currentToggleSettings: {
Turbo: data.Turbo,
},
};
};
Report State を更新する
最後に reportstate
関数を更新し、洗濯機がターボモードに設定されているかどうかがホームグラフに報告されるようにします。
index.js
const requestBody = {
requestId: 'ff36a3cc', /* Any unique ID */
agentUserId: USER_ID,
payload: {
devices: {
states: {
/* Report the current state of your washer */
[context.params.deviceId]: {
on: snapshot.OnOff.on,
isPaused: snapshot.StartStop.isPaused,
isRunning: snapshot.StartStop.isRunning,
currentModeSettings: {
load: snapshot.Modes.load,
},
// Add currentToggleSettings
currentToggleSettings: {
Turbo: snapshot.Toggles.Turbo,
},
},
},
},
},
};
Firebase にデプロイする
次のコマンドを実行して、更新した関数をデプロイします。
firebase deploy --only functions
デプロイが完了したら、ウェブ UI の [Refresh](更新) ボタンをクリックして Request Sync をトリガーします。
これで、次のようなコマンドにより洗濯機をターボモードに設定できるようになりました。
「OK Google, 洗濯機のターボをオンにして。」
次のようなコマンドで、洗濯機がターボモードになっているかどうかを確認することもできます。
「OK Google, 洗濯機はターボモードになってる?」
6. エラーと例外の報告
スマートホーム アクションのエラー処理を使用すると、EXECUTE
レスポンスや QUERY
レスポンスが失敗したことをユーザーに報告できます。スマート デバイスやアクションの操作中に適切に通知することで、ユーザー エクスペリエンスを向上させることができます。
EXECUTE
リクエストや QUERY
リクエストが失敗したときには、必ずアクションからエラーコードを返すようにしてください。たとえば、ユーザーが蓋を開けたまま洗濯機を始動しようとしたときにエラーを返す場合は、EXECUTE
レスポンスを次のコード スニペットのようにします。
{
"requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
"payload": {
"commands": [
{
"ids": [
"456"
],
"status": "ERROR",
"errorCode": "deviceLidOpen"
}
]
}
}
これで、ユーザーが蓋を開けたまま洗濯機を始動しようとすると、アシスタントは次のように応答します。
「洗濯機の蓋が開いています。閉じてからもう一度試してください。」
例外はエラーと似ていますが、コマンドに関連するアラートを示すためのもので、コマンドの実行をブロックする場合としない場合があります。例外では、StatusReport
トレイトを使用して関連情報(電池残量、状態の変化など)を提供できます。実行をブロックしない例外コードの場合は SUCCESS
ステータスが返され、実行をブロックする例外コードの場合は EXCEPTIONS
ステータスが返されます。
次のコード スニペットに、例外を含むレスポンスの例を示します。
{
"requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
"payload": {
"commands": [{
"ids": ["123"],
"status": "SUCCESS",
"states": {
"online": true,
"isPaused": false,
"isRunning": false,
"exceptionCode": "runCycleFinished"
}
}]
}
}
アシスタントは次のように応答します。
「洗濯は終了しています。」
洗濯機からのエラーを報告できるようにするには、functions/index.js
を開いてエラークラスの定義を追加します。次のコード スニペットを参照してください。
index.js
app.onQuery(async (body) => {...});
// Add SmartHome error handling
class SmartHomeError extends Error {
constructor(errorCode, message) {
super(message);
this.name = this.constructor.name;
this.errorCode = errorCode;
}
}
EXECUTE レスポンスを次のように変更し、エラーコードとエラー ステータスが返されるようにします。
index.js
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( ... )
//Add error response handling
.catch((error) => {
functions.logger.error('EXECUTE', device.id, error);
result.ids.push(device.id);
if(error instanceof SmartHomeError) {
result.status = 'ERROR';
result.errorCode = error.errorCode;
}
})
);
}
}
}
これで、報告したエラーコードを Google アシスタントからユーザーに知らせることができるようになりました。次のセクションでは、具体的な例に沿って説明します。
7. 予備のユーザー確認を追加する
ソフトウェア アップデートやロックの解除など、保護が必要なモードや特定の承認済みユーザー グループに限定する必要があるモードがデバイスにある場合、二次的なユーザー確認をアクションに実装する必要があります。
二次ユーザー認証は、すべてのデバイスタイプとデバイスのトレイトに実装できます。これにより、セキュリティ チャレンジを毎回行うか、特定の条件を満たす必要があるかをカスタマイズできます。
サポートされている 3 種類のチャレンジがあります。
No
challenge
- 認証チャレンジを使用しないリクエストとレスポンス(デフォルト)ackNeeded
- 明示的な確認を必要とする 2 次的なユーザー確認(はい / いいえ)pinNeeded
- 個人識別番号(PIN)を必要とする 2 回目のユーザー確認
この Codelab では、洗濯機をオンにするコマンドに ackNeeded
チャレンジを追加し、2 次的な確認チャレンジが失敗した場合にエラーを返す機能を追加します。
functions/index.js
を開き、次のコード スニペットに示すように、エラーコードとチャレンジ タイプを返すエラークラスの定義を追加します。
index.js
class SmartHomeError extends Error { ... }
// Add secondary user verification error handling
class ChallengeNeededError extends SmartHomeError {
/**
* Create a new ChallengeNeededError
* @param {string} suvType secondary user verification challenge type
*/
constructor(suvType) {
super('challengeNeeded', suvType);
this.suvType = suvType;
}
}
EXECUTE レスポンスを次のように変更し、challengeNeeded
エラーが返されるようにします。
index.js
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( ... )
.catch((error) => {
functions.logger.error('EXECUTE', device.id, error);
result.ids.push(device.id);
if(error instanceof SmartHomeError) {
result.status = 'ERROR';
result.errorCode = error.errorCode;
//Add error response handling
if(error instanceof ChallengeNeededError) {
result.challengeNeeded = {
type: error.suvType
};
}
}
})
);
}
}
}
最後に updateDevice
を変更し、洗濯機の電源をオンまたはオフにする際に、明示的な承認が必要となるようにします。
index.js
const updateDevice = async (execution,deviceId) => {
const {challenge,params,command} = execution; //Add secondary user challenge
let state, ref;
switch (command) {
case 'action.devices.commands.OnOff':
//Add secondary user verification challenge
if (!challenge || !challenge.ack) {
throw new ChallengeNeededError('ackNeeded');
}
state = {on: params.on};
ref = firebaseRef.child(deviceId).child('OnOff');
break;
...
}
return ref.update(state)
.then(() => state);
};
Firebase にデプロイする
次のコマンドを実行して、更新した関数をデプロイします。
firebase deploy --only functions
デプロイが完了した後は、洗濯機の電源をオンまたはオフに切り替えようとすると、アシスタントから次のように承認を求められるようになります。
あなた: 「OK Google, 洗濯機をオンにして。」
アシスタント: 「洗濯機をオンにしてもよろしいですか?」
あなた: 「はい。」
Firebase ログを開くと、二次的なユーザー確認フローの各ステップでの詳細なレスポンスを確認することもできます。
8. 完了
これで、Modes
トレイトと Toggles
トレイトを通じてスマートホーム アクションの機能を拡張し、二次的なユーザー確認によって実行を保護しました。
その他の情報
さらに詳しく学びたい方は、以下のことをお試しください。
- デバイスにローカル実行機能を追加します。
- 別の種類の二次ユーザー確認チャレンジを使用して、デバイスの状態を変更します。
RunCycle
トレイトの QUERY レスポンスに変更を加えて動的に更新する。- こちらの GitHub サンプルを確認する。