שיפור האבטחה של השילובים בין עננים

1. לפני שמתחילים

שילובים בין עננים משתמשים בסוגי מכשירים כדי להודיע ל-Google Assistant באיזו דקדוק צריך להשתמש עם מכשיר מסוים. מאפייני המכשיר מגדירים את היכולות של סוג מכשיר מסוים. מכשיר מקבל בירושה את המצבים של כל מאפיין מכשיר שנוסף לשילוב.

dc8dce0dea87cd5c.png

אתם יכולים לחבר כל מאפיין נתמך לסוג המכשיר שבחרתם כדי להתאים אישית את הפונקציונליות של המכשירים של המשתמשים. אם רוצים להטמיע מאפיינים מותאמים אישית בפעולות שלא זמינים כרגע בסכימת המכשיר, המאפיינים Modes ו-Toggles מאפשרים לשלוט בהגדרות ספציפיות באמצעות שם מותאם אישית שאתם מגדירים.

בנוסף ליכולת הבקרה הבסיסית שמסופקת על ידי סוגים ומאפיינים, ל-Smart Home API יש תכונות נוספות לשיפור חוויית המשתמש. תשובות שגיאה מספקות משוב מפורט מהמשתמשים כשהכוונה לא מצליחה. אימות משני של משתמשים מרחיב את התגובות האלה ומוסיף אבטחה נוספת למאפיין המכשיר שתבחרו. על ידי שליחת תגובות שגיאה ספציפיות לחסימות של אתגרים שהונפקו מ-Assistant, השילוב שלכם מ-Cloud ל-Cloud יכול לדרוש הרשאה נוספת כדי להשלים פקודה.

דרישות מוקדמות

מה תפַתחו

ב-codelab הזה תפרסו שילוב מוכן מראש של בית חכם עם Firebase, ואז תלמדו איך להוסיף מאפיינים לא סטנדרטיים למכונת הכביסה החכמה לגבי גודל הטעינה ומצב טורבו. תטמיעו גם דיווח על שגיאות וחריגים, ותלמדו לאכוף אישור מילולי להפעלת מכונת הכביסה באמצעות אימות משני של המשתמש.

מה תלמדו

  • איך מוסיפים את התכונות Modes ו-Toggles לשילוב
  • איך מדווחים על שגיאות וחריגים
  • איך מפעילים אימות משתמש משני

מה צריך בשביל להצטרף

2. תחילת העבודה

הפעלת בקרת הפעילות בחשבון

כדי להשתמש ב-Google Assistant, אתם צריכים לשתף עם Google נתונים מסוימים של פעילות. ‫Google Assistant זקוקה לנתונים האלה כדי לפעול כראוי, אבל הדרישה לשיתוף נתונים לא ספציפית ל-SDK. כדי לשתף את הנתונים האלה, צריך ליצור חשבון Google אם עדיין אין לכם חשבון. אפשר להשתמש בכל חשבון Google – לא צריך להשתמש בחשבון הפיתוח.

פותחים את דף בקרת הפעילות בחשבון Google שבו רוצים להשתמש עם Assistant.

מוודאים שהמתגים הבאים מופעלים:

  • פעילות באינטרנט ובאפליקציות – בנוסף, חשוב לסמן את תיבת הסימון שמירה של ההיסטוריה והפעילות של Chrome מאתרים, מאפליקציות וממכשירים המשתמשים בשירותי Google.
  • פרטי המכשיר
  • תיעוד קול ואודיו

יצירת פרויקט של שילוב בין עננים

  1. נכנסים אל Developer Console.
  2. לוחצים על Create Project, מזינים שם לפרויקט ולוחצים על Create Project.

שם הפרויקט

בחירת שילוב בין ענן לענן

בדף Project Home ב-Developer Console, בוחרים באפשרות Add cloud-to-cloud integration בקטע Cloud-to-cloud.

הוספת שילוב מענן לענן

התקנת Firebase CLI

ממשק שורת הפקודה (CLI) של Firebase מאפשר להפעיל את אפליקציות האינטרנט באופן מקומי ולפרוס אותן באירוח ב-Firebase.

כדי להתקין את ה-CLI, מריצים את פקודת ה-npm הבאה מהטרמינל:

npm install -g firebase-tools

כדי לוודא שה-CLI הותקן בצורה נכונה, מריצים את הפקודה:

firebase --version

כדי לתת הרשאה ל-Firebase CLI באמצעות חשבון Google, מריצים את הפקודה:

firebase login

הוספת Firebase לפרויקט ב-Google Home Developer Console

שיטה 1: דרך מסוף Firebase

  1. עוברים אל Firebase.
  2. לוחצים על יצירת פרויקט Firebase.
    יצירת פרויקט Firebase
  3. במסך Create a project (יצירת פרויקט), לוחצים על Add Firebase to Google Cloud project (הוספת Firebase לפרויקט Google Cloud).
    הוספת Firebase לפרויקט ב-Google Cloud
  4. במסך Get started, בוחרים את הפרויקט ב-Google Cloud שיצרתם זה עתה במסוף הפיתוח של Google Home ולוחצים על Continue.
    בחירת פרויקט ב-Google Cloud

שיטה 2: דרך Firebase CLI

firebase projects:addfirebase

בוחרים את הפרויקט ב-Google Home Developer Console שיצרתם כדי להוסיף את Firebase.

כשמוסיפים את Firebase לפרויקט ב-Google Home Developer Console, הוא מופיע במסוף Firebase. מזהה הפרויקט ב-Firebase יהיה זהה למזהה הפרויקט ב-Google Home Developer Console.

הפרויקט ב-Cloud נוסף

הפעלת HomeGraph API

HomeGraph API מאפשר אחסון של מכשירים והמצבים שלהם ב-Home Graph של המשתמש, וגם שליפת נתונים לגביהם. כדי להשתמש ב-API הזה, קודם צריך לפתוח את מסוף Google Cloud ולהפעיל את HomeGraph API.

במסוף Google Cloud, בוחרים את הפרויקט שתואם לפעולות שלכם <firebase-project-id>. ואז, במסך API Library של HomeGraph API, לוחצים על Enable (הפעלה).

ee198858a6eac112.png

3. הפעלת האפליקציה למתחילים

אחרי שמגדירים את סביבת הפיתוח, אפשר לפרוס את פרויקט המתחילים כדי לוודא שהכול מוגדר בצורה תקינה.

קבלת קוד המקור

כדי להוריד את הדוגמה ל-codelab הזה למחשב הפיתוח, לוחצים על הקישור הבא:

…או שאפשר לשכפל את מאגר GitHub משורת הפקודה:

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

מחלצים את קובץ ה-ZIP שהורד.

מידע על הפרויקט

פרויקט המתחיל מכיל את ספריות המשנה הבאות:

  • public: ממשק משתמש קל לשימוש שמאפשר לשלוט במכונת הכביסה החכמה ולעקוב אחרי המצב שלה.
  • functions: שירות ענן מיושם במלואו שמנהל את מכונת הכביסה החכמה באמצעות Cloud Functions for Firebase ו-Firebase Realtime Database.

המימוש בענן שסיפקתם כולל את הפונקציות הבאות ב-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

אם אתם מאתחלים מחדש את הפרויקט, בוחרים באפשרות 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

אם אתם מאתחלים מחדש את הפרויקט, בוחרים באפשרות No כשנשאלים אם אתם רוצים לאתחל או להחליף את functions/.gitignore.

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

אם הפעלתם את ESLint בטעות, יש שתי דרכים להשבית אותו:

  1. בממשק המשתמש הגרפי, עוברים לתיקייה ../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.

פותחים את כתובת ה-URL של האירוח בדפדפן (https://<firebase-project-id>.web.app) כדי לראות את אפליקציית האינטרנט. יוצג לכם הממשק הבא:

5845443e94705557.png

ממשק המשתמש הזה באינטרנט מייצג פלטפורמה של צד שלישי שמאפשרת לראות או לשנות את מצבי המכשיר. כדי להתחיל לאכלס את מסד הנתונים בפרטי המכשיר, לוחצים על עדכון. לא תראו שינויים בדף, אבל המצב הנוכחי של מכונת הכביסה יישמר במסד הנתונים.

עכשיו צריך לחבר את שירות הענן שפרסתם ל-Google Assistant באמצעות Developer Console.

הגדרת הפרויקט ב-Developer Console

בכרטיסייה פיתוח, מוסיפים שם לתצוגה לאינטראקציה. השם הזה יופיע באפליקציית Google Home.

הוספת שם מוצג

בקטע מיתוג האפליקציה, מעלים קובץ png של סמל האפליקציה בגודל 144x144 פיקסלים, עם השם .png.

הוספת סמל אפליקציה

כדי להפעיל קישור לחשבון, משתמשים בהגדרות הבאות של קישור לחשבון:

Client-ID

ABC123

סוד לקוח

DEF456

כתובת אתר להרשאה

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

כתובת URL לטוקן

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

עדכון כתובות URL לקישור חשבונות

בקטע כתובת URL של מילוי הזמנות בענן, מזינים את כתובת ה-URL של פונקציית הענן שמספקת מילוי הזמנות לכוונות של בית חכם.

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

הוספת כתובת URL של פונקציית ענן

לוחצים על שמירה כדי לשמור את הגדרות הפרויקט, ואז לוחצים על הבא: בדיקה כדי להפעיל את הבדיקה בפרויקט.

בדיקת השילוב בין ענן לענן

עכשיו אפשר להתחיל להטמיע את ה-webhook שנדרשים כדי לקשר את מצב המכשיר ל-Assistant.

כדי לבדוק את השילוב בין ענן לענן, צריך לקשר את הפרויקט לחשבון Google. כך אפשר לבצע בדיקות באמצעות ממשקי Google Assistant ואפליקציית Google Home שמחוברים לאותו חשבון.

  1. בטלפון, פותחים את ההגדרות של Google Assistant. שימו לב שאתם צריכים להיות מחוברים לאותו חשבון שדרכו נכנסתם למסוף.
  2. עוברים אל Google Assistant > הגדרות > שליטה בבית (בקטע Assistant).
  3. לוחצים על סמל החיפוש בפינה השמאלית העליונה.
  4. מחפשים את אפליקציית הבדיקה באמצעות הקידומת [test] כדי למצוא את אפליקציית הבדיקה הספציפית.
  5. בוחרים את הפריט. לאחר מכן, Google Assistant תבצע אימות מול השירות שלכם ותשלח בקשת SYNC, שבה היא מבקשת מהשירות שלכם לספק רשימה של מכשירים עבור המשתמש.

פותחים את אפליקציית Google Home ומוודאים שמכונת הכביסה מופיעה ברשימת המכשירים.

ae252220753726f6.png

מוודאים שאפשר לשלוט במכונת הכביסה באמצעות פקודות קוליות באפליקציית Google Home. בנוסף, צריך לראות את שינוי מצב המכשיר בממשק המשתמש של חזית האתר של הענן.

עכשיו, אחרי שפרסתם כלי בסיסי לניקוי המחשב, אתם יכולים להתאים אישית את המצבים שזמינים במכשיר.

4. הוספת אמצעי תחבורה

התכונה action.devices.traits.Modes מאפשרת למכשיר להגדיר מספר שרירותי של הגדרות למצב מסוים, אבל רק אחת מהן יכולה להיות מוגדרת בכל פעם. תוסיפו מצב למכונת הכביסה כדי להגדיר את גודל הכביסה: קטן, בינוני או גדול.

עדכון תגובת הסנכרון

צריך להוסיף מידע על המאפיין החדש לתגובה שלך בטופס SYNC ב-functions/index.js. הנתונים האלה מופיעים במערך 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,
    },
  };
};

עדכון מצב הדוח

לבסוף, מעדכנים את הפונקציה 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 בסרגל הכלים. הפעולה הזו מפעילה סנכרון של הבקשה, כדי ש-Assistant תקבל את נתוני התגובה המעודכנים של SYNC.

bf4f6a866160a982.png

עכשיו אפשר לתת פקודה להגדרת המצב של מכונת הכביסה, למשל:

"Ok Google, set the washer load to large."

בנוסף, אתם יכולים לשאול שאלות לגבי מכונת הכביסה, כמו:

‫"Ok Google, what is the washer load?‎"

5. הוספת מתגים

מאפיין action.devices.traits.Toggles מייצג היבטים בעלי שם של מכשיר שיכולים להיות במצב True או False, למשל אם מכונת הכביסה נמצאת במצב טורבו.

עדכון תגובת הסנכרון

בתגובה של 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,
    },
  };
};

עדכון מצב הדוח

לבסוף, מעדכנים את הפונקציה 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

לוחצים על הלחצן רענון ae8d3b25777a5e30.png בממשק המשתמש האינטרנטי כדי להפעיל בקשת סנכרון אחרי שהפריסה מסתיימת.

עכשיו אפשר להגיד פקודה כדי להגדיר את מכונת הכביסה למצב טורבו:

‫"Ok Google, set the washer to turbo mode" (הפעלת מכונת הכביסה במצב טורבו).

אפשר גם לשאול אם מכונת הכביסה כבר במצב טורבו:

"Ok Google, is my washer in turbo mode?"

6. דיווח על שגיאות וחריגות

טיפול בשגיאות בשילוב בין ענן לענן מאפשר לכם לדווח למשתמשים על בעיות שגורמות לתגובות EXECUTE ו-QUERY להיכשל. ההתראות יוצרות חוויית משתמש חיובית יותר למשתמשים כשהם מבצעים אינטראקציה עם המכשיר החכם והשילוב.

בכל פעם שבקשת EXECUTE או QUERY נכשלת, השילוב צריך להחזיר קוד שגיאה. לדוגמה, אם רוצים להציג שגיאה כשמשתמש מנסה להפעיל את מכונת הכביסה כשהמכסה פתוח, התגובה של EXECUTE תיראה כמו קטע הקוד הבא:

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

עכשיו, כשמשתמש מבקש להפעיל את מכונת הכביסה, ה-Assistant משיב:

"המכסה של מכונת הכביסה פתוח. צריך לסגור אותה ולנסות שוב".

חריגים דומים לשגיאות, אבל הם מציינים מתי התראה משויכת לפקודה, שיכול להיות שהיא תחסום את ההפעלה המוצלחת ויכול להיות שלא. חריגה יכולה לספק מידע קשור באמצעות מאפיין StatusReport, כמו רמת הסוללה או שינוי מצב אחרון. קודי חריגה שאינם חוסמים מוחזרים עם הסטטוס SUCCESS, וקודי חריגה חוסמים מוחזרים עם הסטטוס EXCEPTIONS.

בדוגמה הבאה מוצגת תגובה עם חריגה:

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

‫Assistant מגיבה באמירה:

"המכונה סיימה את הפעולה".

כדי להוסיף דיווח על שגיאות למכונת הכביסה, פותחים את 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;
          }
        })
      );
    }
  }
}

‫Assistant יכולה עכשיו לספר למשתמשים שלכם על כל קוד שגיאה שאתם מדווחים עליו. דוגמה ספציפית מופיעה בקטע הבא.

7. הוספת אימות משתמש משני

אם למכשיר שלכם יש מצבים שצריך לאבטח או להגביל לקבוצה מסוימת של משתמשים מורשים, כמו עדכון תוכנה או ביטול נעילה, כדאי להטמיע אימות משני של משתמש בשילוב.

אתם יכולים להטמיע אימות משתמש משני בכל סוגי המכשירים והמאפיינים, ולהתאים אישית את האופן שבו אתגר האבטחה מתרחש – בכל פעם או רק כשמתקיימים קריטריונים ספציפיים.

יש שלושה סוגים נתמכים של אתגרים:

  • No challenge – בקשה ותגובה שלא משתמשות באתגר אימות (זו התנהגות ברירת המחדל)
  • ackNeeded—אימות משתמש משני שדורש אישור מפורש (כן או לא)
  • pinNeeded—אימות משני של משתמש שדורש קוד אימות (PIN)

ב-codelab הזה, מוסיפים 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

אחרי שמטמיעים את הקוד המעודכן, צריך לאשר את הפעולה באופן מילולי כשמבקשים מ-Assistant להפעיל או להשבית את מכונת הכביסה, כמו בדוגמה הבאה:

אתם: "Ok Google, turn on the washer" (הפעל את מכונת הכביסה).

ה-Assistant: "Are you sure you want to turn on the washer?‎"

אתם: "כן".

אפשר גם לראות תגובה מפורטת לכל שלב בתהליך האימות של המשתמש המשני על ידי פתיחת היומנים של Firebase.

289dbe48f4bb8106.png

8. מזל טוב

674c4f4392e98c1.png

מעולה! הרחבתם את התכונות של שילובים בין עננים באמצעות המאפיינים Modes ו-Toggles, ואבטחתם את ההפעלה שלהם באמצעות אימות משני של המשתמש.

מידע נוסף

הנה כמה רעיונות להעמקה:

  • להוסיף למכשירים שלכם יכולות של הרצה מקומית.
  • כדי לשנות את מצב המכשיר, צריך להשתמש בסוג אחר של אתגר אימות משתמש משני.
  • מעדכנים את התגובה של מאפיין RunCycle שאילתת החיפוש כדי שהעדכון יהיה דינמי.
  • אפשר לעיין בדוגמה הזו ב-GitHub.