強化及保護雲端到雲端整合

1. 事前準備

雲端整合功能會使用裝置類型,讓 Google 助理瞭解裝置應使用的文法。裝置特徵會定義裝置類型的功能。裝置會繼承新增至整合的每個裝置特徵狀態。

dc8dce0dea87cd5c.png

您可以將任何支援的特徵連結至所選裝置類型,自訂使用者裝置的功能。如要在動作中實作裝置架構目前未提供的自訂特徵,可以使用「模式」和「切換」特徵,透過您定義的自訂名稱控制特定設定。

除了型別和特徵提供的基本控制功能外,智慧住宅 API 還提供其他功能,可提升使用者體驗。意圖失敗時,錯誤回應會提供詳細的使用者意見回饋。次要使用者驗證會擴充這些回應,並為所選裝置特徵新增額外的安全性。只要將特定錯誤回應傳送至 Google 助理發出的驗證碼,Cloud-to-Cloud 整合功能就能要求額外授權,才能完成指令。

必要條件

建構項目

在本程式碼研究室中,您將使用 Firebase 部署預先建構的智慧型住宅整合功能,然後瞭解如何為智慧型住宅洗衣機新增非標準特徵,以便設定洗衣量和快速模式。您也會實作錯誤和例外狀況回報功能,並瞭解如何透過二次使用者驗證,強制執行口頭確認來開啟洗衣機。

課程內容

  • 如何在整合中加入模式和切換開關特徵
  • 如何回報錯誤和例外狀況
  • 如何啟用雙重使用者驗證

軟硬體需求

2. 開始使用

啟用活動控制項

如要使用 Google 助理,必須與 Google 分享特定活動資料。Google 助理需要這項資料才能正常運作,但分享資料的要求並非 SDK 專屬。如要分享這項資料,請建立 Google 帳戶 (如果還沒有的話)。您可以使用任何 Google 帳戶,不一定要是開發人員帳戶。

開啟要與 Google 助理搭配使用的 Google 帳戶的活動控制項頁面

確認已啟用下列切換鈕:

  • 網路和應用程式活動 - 此外,請務必勾選「包括 Chrome 歷史記錄,以及採用 Google 服務的網站、應用程式和裝置上的活動記錄」核取方塊。
  • 裝置資訊
  • 語音和音訊活動

建立雲端對雲端整合專案

  1. 前往開發人員控制台
  2. 按一下「建立專案」,輸入專案名稱,然後按一下「建立專案」

命名專案

選取「雲端對雲端整合」

在開發人員控制台的「專案首頁」中,選取「雲端對雲端」下方的「新增雲端對雲端整合」

新增雲端對雲端整合

安裝 Firebase CLI

Firebase 指令列介面 (CLI) 可讓您在本地提供網頁應用程式,並將網頁應用程式部署至 Firebase 託管。

如要安裝 CLI,請從終端機執行下列 npm 指令:

npm install -g firebase-tools

如要確認 CLI 是否已正確安裝,請執行下列指令:

firebase --version

執行下列指令,透過 Google 帳戶授權 Firebase CLI:

firebase login

將 Firebase 新增至 Google Home 開發人員控制台專案

方法 1:透過 Firebase 控制台

  1. 前往 Firebase
  2. 按一下「建立 Firebase 專案」
    建立 Firebase 專案
  3. 在「建立專案」畫面中,按一下「將 Firebase 新增到 Google Cloud 專案」
    將 Firebase 新增至 Google Cloud 專案
  4. 在「開始使用」畫面中,選取您剛在 Google Home 開發人員控制台中建立的 Google Cloud 專案,然後按一下「繼續」
    選取 Google Cloud 專案

方法 2:透過 Firebase CLI

firebase projects:addfirebase

選取您剛建立的 Google Home 開發人員控制台專案,然後新增 Firebase。

將 Firebase 新增至 Google Home 開發人員控制台專案後,Firebase 控制台就會顯示該專案。Firebase 專案的專案 ID 會與 Google Home 開發人員控制台專案 ID 一致。

已新增 Cloud 專案

啟用 HomeGraph API

HomeGraph API 可讓您在使用者 Home Graph 中儲存及查詢裝置和裝置狀態。如要使用這項 API,請先開啟 Google Cloud 控制台並啟用 HomeGraph API

在 Google Cloud 控制台中,請務必選取與動作相符的專案 <firebase-project-id>. 接著,在 HomeGraph API 的「API 程式庫」畫面中,按一下「啟用」

ee198858a6eac112.png

3. 執行範例應用程式

您已設定開發環境,現在可以部署範例專案,確認一切設定正確無誤。

取得原始碼

點選下方連結,即可在開發機器上為本程式碼研究室下載範例:

...或者,您也可以從指令列複製 GitHub 存放區:

git clone https://github.com/google-home/smarthome-traits.git

將下載的 ZIP 檔案解壓縮。

關於專案

入門專案包含下列子目錄:

  • public: 前端 UI,可輕鬆控制及監控智慧型洗衣機的狀態。
  • functions: 透過 Cloud Functions for Firebase 和 Firebase 即時資料庫,管理智慧洗衣機的完整雲端服務。

提供的雲端履行服務包含 index.js 的下列函式:

  • fakeauth:帳戶連結的授權端點
  • faketoken:用於帳戶連結的權杖端點
  • smarthome:智慧型住宅意圖履行端點
  • reportstate:在裝置狀態變更時叫用 Home Graph API
  • requestsync:使用者更新裝置時,不必重新連結帳戶

連結至 Firebase

前往 washer-start 目錄,然後使用整合專案設定 Firebase CLI:

cd washer-start
firebase use <project-id>

設定 Firebase 專案

初始化 Firebase 專案。

firebase init

選取 CLI 功能、「Realtime Database」和「Functions」功能。

? Which Firebase features do you want to set up for this directory? Press Space to select features, then Enter
 to confirm your choices. (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to
 proceed)
>( ) Data Connect: Set up a Firebase Data Connect service
 ( ) Firestore: Configure security rules and indexes files for Firestore
 ( ) Genkit: Setup a new Genkit project with Firebase
 (*) Functions: Configure a Cloud Functions directory and its files
 ( ) App Hosting: Configure an apphosting.yaml file for App Hosting
 ( ) Hosting: Configure files for Firebase Hosting and (optionally) 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
 (*) Realtime Database: Configure a security rules file for Realtime Database and (optionally) provision
default instance
 ( ) Data Connect: Set up a Firebase Data Connect service
 ( ) Firestore: Configure security rules and indexes files for Firestore

這會為專案初始化必要的 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

如果重新初始化專案,系統詢問是否要初始化或覆寫程式碼庫時,請選取「覆寫」

? Would you like to initialize a new codebase, or overwrite an existing one?
Overwrite

設定函式時,請使用預設檔案,並確保不會覆寫專案範例中現有的 index.jspackage.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 時,請選取「否」

? File functions/.gitignore already exists. Overwrite?
No
? Do you want to install dependencies with npm now?
Yes

如果不小心啟用了 ESLint,可以透過以下兩種方法停用:

  1. 使用 GUI 前往專案底下的 ../functions 資料夾,選取隱藏檔案 .eslintrc.js 並刪除。請勿與名稱相似的 .eslintrc.json 混淆。
  2. 使用指令列:
    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

在瀏覽器中開啟主機網址 (https://<firebase-project-id>.web.app),即可查看網頁應用程式。您會看到下列介面:

5845443e94705557.png

這個網頁版 UI 代表第三方平台,可查看或修改裝置狀態。如要開始在資料庫中填入裝置資訊,請按一下「更新」。頁面上不會顯示任何變更,但洗衣機的目前狀態會儲存在資料庫中。

現在請使用開發人員控制台,將部署的雲端服務連結至 Google 助理。

設定開發人員控制台專案

在「開發」分頁中,為互動新增「顯示名稱」。這個名稱會顯示在 Google Home 應用程式中。

新增顯示名稱

在「應用程式品牌宣傳」下方,上傳大小為 144 x 144 像素且名為 .png 的應用程式圖示 png 檔案。

新增應用程式圖示

如要啟用帳戶連結,請使用下列帳戶連結設定:

用戶端 ID

ABC123

用戶端密碼

DEF456

驗證網址

https://us-central1-<project-id>.cloudfunctions.net/fakeauth

權杖網址

https://us-central1-<project-id>.cloudfunctions.net/faketoken

更新帳戶連結網址

在「Cloud fulfillment URL」(雲端服務網址) 下方,輸入雲端函式的網址,該函式會提供智慧住宅意圖的服務。

https://us-central1-<project-id>.cloudfunctions.net/smarthome

新增 Cloud 函式網址

按一下「儲存」儲存專案設定,然後按一下「下一步:測試」,在專案中啟用測試。

測試雲端對雲端整合

現在可以開始實作必要的 Webhook,將裝置狀態與 Google 助理連結。

如要測試雲端對雲端整合,您必須將專案連結至 Google 帳戶。這樣一來,只要登入相同帳戶,就能透過 Google 助理介面和 Google Home 應用程式進行測試。

  1. 在手機上開啟 Google 助理設定。請注意,您登入的帳戶應與控制台中的帳戶相同。
  2. 依序前往「Google 助理」>「設定」>「居家控制」 (位於「Google 助理」下方)。
  3. 按一下右上方的搜尋圖示
  4. 使用 [test] 前置字元搜尋測試應用程式,找出特定測試應用程式。
  5. 選取該項目。Google 助理接著會向服務進行驗證,並傳送 SYNC 要求,請服務提供使用者的裝置清單。

開啟 Google Home 應用程式,確認是否能看到洗衣機裝置。

ae252220753726f6.png

確認你可以在 Google Home 應用程式中使用語音指令控制洗衣機。此外,你應該也會在雲端服務的網頁前端 UI 中看到裝置狀態變更。

現在您已部署基本洗衣機,可以自訂裝置可用的模式。

4. 新增模式

action.devices.traits.Modes 特徵可讓裝置為模式設定任意數量的設定,但一次只能設定一個。您將在洗衣機中新增模式,定義洗衣量大小:小、中或大。

更新 SYNC 回應

您需要在 functions/index.jsSYNC 回應中新增有關新特徵的資訊。這項資料會顯示在 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 回應,回報洗衣機的目前狀態。

將更新後的變更新增至 queryFirebasequeryDevice 函式,以取得儲存在 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,
    },
  };
};

更新報表狀態

最後,更新 reportstate 函式,向 Home Graph 回報洗衣機目前的負載設定。

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

部署完成後,請前往網頁使用者介面,然後點選工具列中的「重新整理」 ae8d3b25777a5e30.png 按鈕。這會觸發「要求同步」,讓 Google 助理接收更新後的 SYNC 回應資料。

bf4f6a866160a982.png

現在你可以下達指令來設定洗衣機模式,例如:

「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 回應,回報洗衣機的渦輪模式。將更新後的變更新增至 queryFirebasequeryDevice 函式,以取得儲存在 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,
    },
  };
};

更新報表狀態

最後,更新 reportstate 函式,向 Home Graph 回報洗衣機是否設為快速模式。

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 中點選「重新整理」 ae8d3b25777a5e30.png 按鈕,觸發「要求同步」作業。

你現在可以說出指令,將洗衣機設為快速模式:

「Ok Google,將洗衣機設為快速模式。」

你也可以詢問洗衣機是否已處於快速模式:

「Ok Google,洗衣機是否處於快速模式?」

6. 回報錯誤和例外狀況

在雲端對雲端整合中處理錯誤,可讓您在問題導致 EXECUTEQUERY 回應失敗時,向使用者回報。使用者與智慧型裝置和整合服務互動時,通知可提供更優質的體驗。

只要 EXECUTEQUERY 要求失敗,整合功能就應傳回錯誤代碼。舉例來說,如果使用者嘗試在洗衣機蓋子打開時啟動洗衣機,您想擲回錯誤,則 EXECUTE 回應會如以下程式碼片段所示:

{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "commands": [
      {
        "ids": [
          "456"
        ],
        "status": "ERROR",
        "errorCode": "deviceLidOpen"
      }
    ]
  }
}

現在,當使用者要求啟動洗衣機時,Google 助理會回應:

洗衣機的蓋子沒蓋上。請關閉該視窗,然後再試一次。」

例外狀況與錯誤類似,但會指出警示與指令相關聯,可能或可能不會阻礙成功執行。例外狀況可以使用 StatusReport 特徵提供相關資訊,例如電池電量或最近的狀態變化。非阻斷例外狀況代碼會連同 SUCCESS 狀態一併傳回,而阻斷例外狀況代碼則會連同 EXCEPTIONS 狀態一併傳回。

以下程式碼片段顯示含有例外狀況的回應範例:

{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "commands": [{
      "ids": ["123"],
      "status": "SUCCESS",
      "states": {
        "online": true,
        "isPaused": false,
        "isRunning": false,
        "exceptionCode": "runCycleFinished"
      }
    }]
  }
}

Google 助理會回應:

「洗衣機已停止運轉。」

如要為洗衣機新增錯誤回報功能,請開啟 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;
  }
}

更新執行回應,傳回錯誤代碼和錯誤狀態:

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(
        updateDevice(execution, device.id)
        .then((data) => {
          ...
        })
        //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. 新增雙重使用者驗證

如果裝置有任何需要保護的模式,或應僅限特定授權使用者群組使用,例如軟體更新或解除鎖定,您應在整合中實作次要使用者驗證

您可以在所有裝置類型和特徵上導入次要使用者驗證,並自訂安全驗證問題的出現時機,例如每次都出現,或是符合特定條件時才出現。

支援的驗證類型有三種

  • No challenge:不使用驗證挑戰的要求和回應 (這是預設行為)
  • ackNeeded—需要明確確認 (是或否) 的第二種使用者驗證
  • pinNeeded—需要個人識別碼 (PIN 碼) 的次要使用者驗證

在本程式碼研究室中,請在開啟洗衣機的指令中新增 ackNeeded 挑戰,並新增功能,以便在次要驗證挑戰失敗時傳回錯誤。

開啟 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;
  }
}

您還需要更新執行回應,傳回 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(

        updateDevice(execution, device.id)
        .then((data) => {
          ...
        })
        .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

部署更新後的程式碼後,要求 Google 助理開啟或關閉洗衣機時,你必須口頭確認動作,如下所示:

你: 「Ok Google,開啟洗衣機。」

Google 助理: 「確定要啟動洗衣機嗎?」

你: 「是。」

您也可以開啟 Firebase 記錄,查看次要使用者驗證流程每個步驟的詳細回應。

289dbe48f4bb8106.png

8. 恭喜

674c4f4392e98c1.png

恭喜!您透過 ModesToggles 特徵擴充了雲端整合功能,並透過次要使用者驗證確保執行安全。

瞭解詳情

以下提供幾種做法,協助您深入瞭解:

  • 在裝置上新增本機執行功能。
  • 使用其他次要使用者驗證挑戰類型修改裝置狀態。
  • 更新 RunCycle 特徵 QUERY 回應,以動態更新。
  • 請參閱這個 GitHub 範例