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

程式碼研究室簡介
schedule50 分鐘
subject上次更新時間:2025年3月25日
account_circle作者:Google 員工

1. 事前準備

雲端對雲端整合會使用裝置類型,讓 Google 助理知道應使用哪種語法與裝置互動。裝置特徵定義裝置類型的功能。裝置會繼承新增至整合項目的每個裝置特徵狀態。

dc8dce0dea87cd5c.png

您可以將任何支援的特徵連結至所選的裝置類型,自訂使用者裝置的功能。如果您想在 Action 中實作目前在裝置結構定義中不可用的自訂特徵,則可使用 ModesToggles 特徵,透過您定義的自訂名稱控制特定設定。

除了類型和特徵提供的基本控制功能外,智慧型家居 API 還有其他功能可改善使用者體驗。當意圖無法成功執行時,錯誤回應會提供詳細的使用者意見回饋。次要使用者驗證功能可擴充這些回應,並為您選擇的裝置屬性提供額外安全性。只要將特定錯誤回應傳送至 Google 助理發出的挑戰區塊,您的雲端對雲端整合功能就能要求額外授權來完成指令。

必要條件

建構項目

在本程式碼研究室中,您將部署預先建構的智慧型住宅整合服務 (搭配 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 專案

  1. 前往 Firebase
  2. 按一下「建立專案」,然後輸入專案名稱。
  3. 勾選同意聲明核取方塊,然後按一下「繼續」。如果沒有同意聲明核取方塊,您可以略過這個步驟。
    建立 Firebase 專案
  4. 建立 Firebase 專案後,請找出專案 ID。前往「Project Overview」(專案總覽),然後依序按一下設定圖示 >「Project Settings」(專案設定)
    開啟專案設定
  5. 專案會列在「General」分頁下方。
    一般專案設定

啟用 HomeGraph API

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

在 Google Cloud 控制台中,請務必選取與 Actions 相符的專案 <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 <firebase-project-id>

設定 Firebase 專案

初始化 Firebase 專案。

firebase init

選取 CLI 功能、即時資料庫Functions託管功能 (包含 Firebase 託管)。

? 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 和功能。

系統提示時,請初始化即時資料庫。您可以使用資料庫執行個體的預設位置。

? 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

最後,請設定代管設定,以便使用專案程式碼中的 public 目錄,並使用現有的 index.html 檔案。系統詢問是否要使用 ESLint 時,請選取「否」

? 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,可以透過兩種方法停用:

  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/<firebase-project-id>/overview
Hosting URL: https://<firebase-project-id>.web.app

這個指令會部署網頁應用程式,以及幾個 Cloud Functions for Firebase

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

5845443e94705557.png

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

接下來,您可以使用開發人員控制台,將已部署的雲端服務連結至 Google 助理。

設定開發人員控制台專案

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

新增顯示名稱

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

新增應用程式圖示

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

用戶端 ID

ABC123

用戶端密碼

DEF456

驗證網址

https://us-central1-
.cloudfunctions.net/fakeauth

符記網址

https://us-central1-
.cloudfunctions.net/faketoken

更新帳戶連結網址

在「雲端執行網址」下方,輸入提供智慧型家居意圖執行功能的雲端函式網址。

https://us-central1--cloudfunctions.net/smarthome

新增 Cloud 函式網址

按一下「儲存」儲存專案設定,然後點選「下一步:測試」,啟用專案測試功能。

測試雲端對雲端整合

您現在可以開始實作用於將裝置狀態連結至 Google 助理的 webhook。

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

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

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

ae252220753726f6.png

確認你可以透過 Google Home 應用程式中的語音指令控制洗衣機。你應該也會在雲端執行服務的網頁前端使用者介面中,看到裝置狀態有所變更。

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

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 函式,以便取得儲存在即時資料庫中的狀態。

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

部署完成後,前往網頁 UI,然後點選工具列中的「Refresh」 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 意圖中加入 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 函式,以便取得儲存在即時資料庫中的切換鈕狀態。

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 回報洗衣機是否設為 Turbo 模式。

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」 ae8d3b25777a5e30.png 按鈕,即可在部署完成後觸發要求同步作業。

你現在可以下達指令,將洗衣機設為 Turbo 模式,只要說出以下指令即可:

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

你也可以透過以下方式,確認洗衣機是否已處於 Turbo 模式:

「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 範例