ניפוי באגים בבית המקומי

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

שילובים של בית חכם מאפשרים ל-Google Assistant לשלוט במכשירים מחוברים בבתים של המשתמשים. כדי ליצור שילוב בין עננים, צריך לספק נקודת קצה של webhook בענן שיכולה לטפל בכוונות (intents) של בית חכם. לדוגמה, כשמשתמש אומר "Ok Google, turn on the lights" (Ok Google, הפעלת האורות), Assistant שולחת את הפקודה לשירותי הענן שלכם כדי לעדכן את מצב המכשיר.

ה-Local Home SDK משפר את השילוב של הפתרון שלכם לבתים חכמים על ידי הוספת מסלול מקומי לשרשור פעולות (fullfillment) כדי לנתב את הכוונות (intents) של הבית החכם ישירות למכשיר Google Home. כך אפשר לשפר את האמינות ולצמצם את זמן האחזור בזמן עיבוד הפקודות של המשתמשים. היא מאפשרת לכתוב ולפרוס אפליקציית השלמה מקומית ב-TypeScript או ב-JavaScript, שמזהה מכשירים ומבצעת פקודות בכל רמקול חכם של Google Home או מסך חכם של Google Nest. לאחר מכן, האפליקציה מתקשרת ישירות עם המכשירים החכמים הקיימים של המשתמשים ברשת האזורית (LAN), באמצעות פרוטוקולים סטנדרטיים קיימים כדי לבצע את הפקודות.

72ffb320986092c.png

ניפוי באגים בשילובים בין עננים הוא שלב קריטי בתהליך היצירה של השילובים באיכות ייצור, אבל הוא מאתגר ודורש זמן רב בלי כלים מועילים וקלים לשימוש לפתרון בעיות ובדיקות. כדי לסייע בניפוי באגים בשילובים של Cloud-to-Cloud, Metrics ו-Logging של Google Cloud Platform‏ (GCP) ו-Test Suite for smart home זמינים כדי לעזור לכם לזהות בעיות בשילובים ולפתור אותן.

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

מה תפַתחו

בסדנת הקוד הזו תלמדו ליצור שרשור פעולות (fulfillment) מקומי לשילובים של Cloud-to-cloud ולקשר אותו ל-Assistant. לאחר מכן תלמדו לנפות באגים באפליקציית Home המקומית באמצעות חבילת בדיקות לבית חכם ולמדדים ולרישום ביומן של פלטפורמת Google Cloud‏ (GCP).

מה תלמדו

  • איך משתמשים במדדים וביומן של GCP כדי לזהות בעיות בסביבת הייצור ולפתור אותן.
  • איך משתמשים ב-Test Suite כדי לזהות בעיות פונקציונליות ובעיות ב-API.
  • איך משתמשים בכלי הפיתוח של Chrome במהלך הפיתוח של אפליקציית Home המקומית.

מה נדרש

2. הפעלת האפליקציה של מכונת הכביסה

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

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

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

$ git clone https://github.com/google-home/smarthome-debug-local.git

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

האפליקציה הבסיסית מכילה ספריות משנה ופונקציות ענן דומות לאלה שבקודלאב הפעלת מילוי מקומי בשילובים של Cloud-to-Cloud. אבל במקום app-start, כאן מופיע app-faulty. נתחיל באפליקציית בית מקומית שפועלת, אבל לא טובה במיוחד.

התחברות ל-Firebase

נשתמש באותו פרויקט שיצרתם בקודלאב הפעלת מילוי מקומי בשילובים של Cloud-to-Cloud, אבל נעביר את הקובצים שהורדתם בקודלאב הזה.

עוברים לספרייה app-faulty ומגדירים את Firebase CLI עם פרויקט השילוב שיצרתם בקודלאב הפעלת מילוי מקומי לשילובים בין עננים:

$ cd app-faulty
$ firebase use <project-id>

פריסה ב-Firebase

עוברים לתיקייה app-faulty/functions ומתקינים את כל הרכיבים התלויים הנדרשים באמצעות npm:

$ cd functions
$ npm install

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

found 5 high severity vulnerabilities
  run `npm audit fix` to fix them, or `npm audit` for details

עוברים לספרייה app-faulty/local/ ומריצים את הפקודות הבאות כדי להוריד את המהדר של TypeScript ולעבד את האפליקציה:

$ cd ../local
$ npm install
$ npm run build

הפקודה הזו מקמפלת את המקור index.ts (TypeScript) ומציבה את התוכן הבא בספרייה app-faulty/public/local-home/:

  • bundle.js – פלט של JavaScript שנוצר על ידי הידור, שכולל את האפליקציה המקומית ואת יחסי התלות.
  • index.html – דף אירוח מקומי שמשמש להצגת האפליקציה לצורך בדיקה במכשיר.

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

$ firebase deploy

זהו הפלט במסוף שאמור להופיע:

...

✔ Deploy complete!

Project Console: https://console.firebase.google.com/project/<project-id>/overview
Hosting URL: https://<projectcd -id>.web.app

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

עדכון HomeGraph

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

fa3c47f293cfe0b7.png

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

2a082ee11d47ad1a.png

3. הפעלת מכונת הכביסה החכמה

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

הפעלת המכשיר

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

$ cd ../../virtual-device
$ npm install
$ npm start -- \
  --deviceId=deviceid123 --projectId=<project-id> \
  --discoveryPortOut=3311 --discoveryPacket=HelloLocalHomeSDK

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

(...): UDP Server listening on 3311
(...): Device listening on port 3388
(...): Report State successful

4. בדיקה של אפליקציית Home המקומית

לשלוח פקודות למכשיר באמצעות פקודות קוליות למכשיר Google Home, כמו:

"Ok Google, turn on my washer"

"Ok Google, start my washer"

"Ok Google, force local"

"Ok Google, stop my washer"

כשתנסו לשלוט במכונה לאחר הרצת הפקודה 'force local', תראו שתופיע התשובה של Google Assistant: "Sorry, it looks like the Faulty Washer isn't available right now" (מצטערים, נראה שמכונת הכביסה הפגומה לא זמינה כרגע).

כלומר, אי אפשר לגשת למכשיר דרך נתיב מקומי. הוא עבד לפני שהפעלת את הפקודה "Ok Google, force local", כי אנחנו עוברים לשימוש בנתיב בענן כשאי אפשר לגשת למכשיר דרך נתיב מקומי. עם זאת, אחרי 'אילוץ מקומי', האפשרות לעבור לנתיב בענן מושבתת.

כדי לברר מה הבעיה, נשתמש בכלים שקיימים לנו: מדדים ורישום ביומן של Google Cloud Platform ‏ (GCP) וכלים למפתחים של Chrome.

5. ניפוי באגים באפליקציית Home המקומית

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

חיבור הכלים למפתחים ב-Chrome

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

  1. חשוב לוודא שקישרתם את מכשיר Google Home למשתמש שיש לו הרשאה לגשת לפרויקט ב-Developer Console.
  2. צריך להפעיל מחדש את מכשיר Google Home כדי לאפשר לו לקבל את כתובת ה-URL של ה-HTML ואת הגדרות הסריקה שהגדרתם במסוף הפיתוח.
  3. פותחים את Chrome במכונה לפיתוח.
  4. פותחים כרטיסייה חדשה ב-Chrome ומזינים chrome://inspect בשדה הכתובת כדי להפעיל את המפקח.

בדף אמור להופיע רשימה של מכשירים, וכתובת ה-URL של האפליקציה אמורה להופיע מתחת לשם של מכשיר Google Home.

567f97789a7d8846.png

הפעלת הכלי

לוחצים על בדיקה מתחת לכתובת ה-URL של האפליקציה כדי להפעיל את הכלים למפתחים של Chrome. בוחרים בכרטיסייה Console ומוודאים שתוכלו לראות את תוכן הכוונה IDENTIFY שנדפסה על ידי אפליקציית TypeScript.

774c460c59f9f84a.png

פלט כזה מציין שהטיפול ב-IDENTIFY הופעל בהצלחה, אבל הערך של verificationId שהוחזר ב-IdentifyResponse לא תואם לאף אחד מהמכשירים ב-HomeGraph. נוסיף כמה יומנים מותאמים אישית כדי לבדוק למה.

הוספת יומנים מותאמים אישית

אמנם מוצגת שגיאה מסוג DEVICE_VERIFICATION_FAILED על ידי Local Home SDK, אבל היא לא עוזרת הרבה במציאת שורש הבעיה. נוסיף כמה יומנים מותאמים אישית כדי לוודא שאנחנו קוראים ומעבדים את נתוני הסריקה בצורה נכונה. חשוב לזכור שאם נדחה את ההבטחה עם שגיאה, הודעת השגיאה נשלחת גם ל-Cloud Logging.

local/index.ts

identifyHandler(request: IntentFlow.IdentifyRequest):
    Promise<IntentFlow.IdentifyResponse> {
  console.log("IDENTIFY intent: " + JSON.stringify(request, null, 2));

  const scanData = request.inputs[0].payload.device.udpScanData;
  if (!scanData) {
    const err = new IntentFlow.HandlerError(request.requestId,
        'invalid_request', 'Invalid scan data');
    return Promise.reject(err);
  }

  // In this codelab, the scan data contains only local device id.
  // Is there something wrong here?
  const localDeviceId = Buffer.from(scanData.data);
  console.log(`IDENTIFY handler: received local device id
      ${localDeviceId}`);

  // Add custom logs
  if (!localDeviceId.toString().match(/^deviceid[0-9]{3}$/gi)) {
    const err = new IntentFlow.HandlerError(request.requestId,
        'invalid_device', 'Invalid device id from scan data ' +
        localDeviceId);
    return Promise.reject(err);
  }

  const response: IntentFlow.IdentifyResponse = {
    intent: Intents.IDENTIFY,
    requestId: request.requestId,
    payload: {
      device: {
        id: 'washer',
        verificationId: localDeviceId.toString(),
      }
    }
  };
  console.log("IDENTIFY response: " + JSON.stringify(response, null, 2));

  return Promise.resolve(response);
}

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

local/index.ts

const localHomeSdk = new App('1.0.1');

אחרי שמוסיפים את היומנים המותאמים אישית, צריך לקמפל שוב את האפליקציה ולפרוס אותה מחדש ב-Firebase.

$ cd ../app-faulty/local
$ npm run build
$ firebase deploy --only hosting

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

ecc56508ebcf9ab.png

גישה ל-Cloud Logging

עכשיו נראה איך משתמשים ב-Cloud Logging כדי למצוא את השגיאות. כדי לגשת ל-Cloud Logging בפרויקט:

  1. נכנסים לדף Projects במסוף Cloud Platform.
  2. בוחרים את הפרויקט של הבית החכם.
  3. בקטע Operations, בוחרים באפשרות Logging > Logs Explorer.

הגישה לנתוני הרישום ביומן מנוהלת באמצעות ניהול זהויות והרשאות גישה (IAM) למשתמשים בפרויקט השילובים. מידע נוסף על תפקידים והרשאות לנתוני יומנים זמין במאמר בקרת גישה ב-Cloud Logging.

שימוש במסננים מתקדמים

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

לוחצים על המתג Show query, והוא אמור להפוך לתיבה Query builder. מזינים jsonPayload.intent="IDENTIFY" בתיבה Query builder ולוחצים על הלחצן Run query.

4c0b9d2828ee2447.png

כתוצאה מכך, תקבלו את כל יומני השגיאות שהושלחו במטפל IDENTIFY. לאחר מכן, מרחיבים את השגיאה האחרונה. ב-handler‏ IDENTIFY תמצאו את הערכים של errorCode ו-debugString שהגדרתם כשדחתם את ההבטחה.

71f2f156c6887496.png

לפי debugString, אפשר לראות שמזהה המכשיר המקומי לא בפורמט הצפוי. אפליקציית Home המקומית מצפה לקבל את מזהה המכשיר המקומי כמחרוזת שמתחילה ב-deviceid ואחריה 3 ספרות, אבל מזהה המכשיר המקומי כאן הוא מחרוזת הקסדצימלית.

תיקון השגיאה

חוזרים לקוד המקור שבו אנחנו מנתחים את מזהה המכשיר המקומי מנתוני הסריקה, ומבחינים שלא סיפקנו את הקידוד במהלך ההמרה של המחרוזת לבייטים. נתוני הסריקה מתקבלים כמחרוזת הקסדצימלית, לכן צריך להעביר את hex כקידוד התווים כשקוראים ל-Buffer.from().

local/index.ts

identifyHandler(request: IntentFlow.IdentifyRequest):
    Promise<IntentFlow.IdentifyResponse> {
  console.log("IDENTIFY intent: " + JSON.stringify(request, null, 2));

  const scanData = request.inputs[0].payload.device.udpScanData;
  if (!scanData) {
    const err = new IntentFlow.HandlerError(request.requestId,
        'invalid_request', 'Invalid scan data');
    return Promise.reject(err);
  }

  // In this codelab, the scan data contains only local device id.
  const localDeviceId = Buffer.from(scanData.data, 'hex');
  console.log(`IDENTIFY handler: received local device id
      ${localDeviceId}`);

  if (!localDeviceId.toString().match(/^deviceid[0-9]{3}$/gi)) {
    const err = new IntentFlow.HandlerError(request.requestId,
      'invalid_device', 'Invalid device id from scan data ' +
      localDeviceId);
    return Promise.reject(err);
  }

  const response: IntentFlow.IdentifyResponse = {
    intent: Intents.IDENTIFY,
    requestId: request.requestId,
    payload: {
      device: {
        id: 'washer',
        verificationId: localDeviceId.toString(),
      }
    }
  };
  console.log("IDENTIFY response: " + JSON.stringify(response, null, 2));

  return Promise.resolve(response);
}

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

local/index.ts

const localHomeSdk = new App('1.0.2');

אחרי שתתקנו את השגיאה, תצטרכו לקמפל את האפליקציה ולפרוס אותה מחדש ב-Firebase. ב-app-faulty/local, מריצים את הפקודה:

$ npm run build
$ firebase deploy --only hosting

בדיקת התיקון

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

c8456f7b5f77f894.png

עכשיו אפשר לנסות לשלוח שוב פקודות למכשיר.

"Ok Google, force local"

"Ok Google, stop my washer"

"Ok Google, turn on my washer"

...

"Ok Google, force default"

6. הרצת חבילת בדיקות לבית חכם

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

הרצת חבילה לבדיקות לבית חכם

כדי לבדוק את השילוב של Cloud-to-cloud באמצעות Test Suite:

  1. בדפדפן האינטרנט, פותחים את חבילת הבדיקה לבית חכם.
  2. נכנסים לחשבון Google באמצעות הלחצן בפינה השמאלית העליונה. כך מערכת ערכות הבדיקה יכולה לשלוח את הפקודות ישירות ל-Google Assistant.
  3. בשדה Project ID מזינים את מזהה הפרויקט של השילוב בין עננים. לאחר מכן לוחצים על הבא כדי להמשיך.
  4. בשלב Test Settings, המכונה הפגומה אמורה להופיע בקטע Devices and Trais.
  5. משביתים את האפשרות Test Request Sync (בדיקת סנכרון הבקשות) כי לאפליקציית מכונת הכביסה לדוגמה אין ממשק משתמש להוספה, להסרה או לשינוי השם של מכונת הכביסה. במערכת ייצור, צריך להפעיל את Request Sync בכל פעם שהמשתמש מוסיף, מסיר או משנה את השם של מכשיר.
  6. משאירים את האפשרות Local Home SDK מופעלת, כי אנחנו הולכים לבדוק גם נתיבים מקומיים וגם נתיבים בענן.
  7. לוחצים על השלב הבא: סביבת הבדיקה כדי להתחיל להריץ את הבדיקה.

67433d9190fa770e.png

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

d1ebd5cfae2a2a47.png

ניתוח הודעת השגיאה

כדאי לבדוק לעומק את הודעות השגיאה בתרחישי הבדיקה שנכשלו. הם מאפשרים לכם לדעת מה המצב הצפוי לבדיקה הזו ומה המצב בפועל. במקרה הזה, עבור 'השהיית מכונת הכביסה', המצב הצפוי הוא isPaused: true, אבל המצב בפועל הוא isPaused: false. באופן דומה, עבור 'השהיית מכונת הכביסה', המצב הצפוי הוא isPaused: true, אבל המצב בפועל הוא isPaused: false.

6bfd3acef9c16b84.png

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

זיהוי ותיקון השגיאה

נמצא את קוד המקור שבו אפליקציית Home המקומית שולחת את פקודת ההפעלה למכשיר. getDataCommand() היא הפונקציה שנקראת על ידי executeHandler() כדי להגדיר את payload בפקודת הביצוע שנשלחת למכשיר.

local/index.ts

getDataForCommand(command: string, params: IWasherParams): unknown {
    switch (command) {
        case 'action.devices.commands.OnOff':
            return {
                on: params.on ? true : false
            };
        case 'action.devices.commands.StartStop':
            return {
                isRunning: params.start ? true : false
            };
        case 'action.devices.commands.PauseUnpause':
            return {
                // Is there something wrong here?
                isPaused: params.pause ? false : true
            };
        default:
            console.error('Unknown command', command);
            return {};
    }
}

אנחנו אכן מגדירים את isPause במצב הפוך. צריך להגדיר אותו לערך true כש-params.pause הוא true, ולערך false במקרים אחרים. אז בואו נתקן את זה.

local/index.ts

getDataForCommand(command: string, params: IWasherParams): unknown {
    switch (command) {
        case 'action.devices.commands.OnOff':
            return {
                on: params.on ? true : false
            };
        case 'action.devices.commands.StartStop':
            return {
                isRunning: params.start ? true : false
            };
        case 'action.devices.commands.PauseUnpause':
            return {
                isPaused: params.pause ? true : false
            };
        default:
            console.error('Unknown command', command);
            return {};
    }
}

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

local/index.ts

const localHomeSdk = new App('1.0.3');

חשוב לזכור לקמפל את האפליקציה שוב ולפרוס אותה מחדש ב-Firebase. ב-app-faulty/local, מריצים את הפקודה:

$ npm run build
$ firebase deploy --only hosting

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

בדיקת התיקון

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

b7fc8c5d3c727d8d.png

7. מזל טוב

764dbc83b95782a.png

מעולה! סיימתם ללמוד איך לפתור בעיות באפליקציית Home מקומית באמצעות חבילת הבדיקה לבית חכם ול-Cloud Logging.

מידע נוסף

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

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