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

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

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

dc8dce0dea87cd5c.png

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

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

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

מה תפַתחו

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

מה תלמדו

  • איך מוסיפים את המאפיינים 'מצבים' ו'מתגים' לשילוב
  • איך מדווחים על שגיאות ועל חריגות
  • איך מפעילים אימות משתמש משני

מה נדרש

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

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

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

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

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

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

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

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

שם הפרויקט

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

בדף Project Home במסוף הפיתוח, בוחרים באפשרות 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

  1. עוברים אל Firebase.
  2. לוחצים על Create a project ומזינים את שם הפרויקט.
  3. מסמנים את התיבה של ההסכם ולוחצים על המשך. אם אין תיבת סימון של הסכמה, אפשר לדלג על השלב הזה.
    יצירת פרויקט Firebase
  4. אחרי שיוצרים את פרויקט Firebase, מחפשים את מזהה הפרויקט. עוברים אל סקירה כללית של הפרויקט ולוחצים על סמל ההגדרות > הגדרות הפרויקט.
    פתיחת הגדרות הפרויקט
  5. הפרויקט מופיע בכרטיסייה General.
    הגדרות כלליות של הפרויקט

הפעלת HomeGraph API

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

במסוף Google Cloud, חשוב לבחור את הפרויקט שתואם ל-Actions שלכם <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: נקודת קצה (endpoint) להשלמת כוונה בבית חכם
  • reportstate: קריאה ל-Home Graph API כשמתרחשים שינויים במצב המכשיר
  • requestsync: מאפשרת עדכונים של מכשירי המשתמשים בלי צורך לקשר מחדש את החשבון

התחברות ל-Firebase

עוברים לספרייה washer-start ומגדירים את ה-CLI של Firebase עם פרויקט השילוב:

cd washer-start
firebase use <firebase-project-id>

הגדרת פרויקט Firebase

איך מפעילים פרויקט Firebase.

firebase init

בוחרים את התכונות של CLI: Realtime Database,‏ Functions והתכונה Hosting שכוללת את Firebase 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

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

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

לבסוף, מגדירים את הגדרות האירוח כך שישתמשו בספרייה public בקוד הפרויקט, ומשתמשים בקובץ index.html הקיים. כשתתבקשו להשתמש ב-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 הופעל בטעות, יש שתי שיטות להשבתה שלו:

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

הפקודה הזו פורסת אפליקציית אינטרנט, יחד עם כמה Cloud Functions for Firebase.

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

5845443e94705557.png

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

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

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

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

הוספת שם מוצג

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

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

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

Client-ID

ABC123

סוד לקוח

DEF456

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

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

כתובת URL לטוקן

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

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

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

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

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

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

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

עכשיו אפשר להתחיל להטמיע את ה-webhooks הנדרשים כדי לקשר את מצב המכשיר ל-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 כדי לדווח על הגדרת העומס הנוכחית של מכונת הכביסה לתרשים הבית.

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 כדי לדווח לתרשים הבית אם מכונת הכביסה מוגדרת למצב טורבו.

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

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

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

"Ok Google, turn on turbo for the washer"

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

"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)

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

מידע נוסף

ריכזנו כאן כמה רעיונות שתוכלו ליישם כדי להעמיק את הניתוח:

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