הטמעה של האפליקציה המקומית למילוי הזמנות

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

  • IDENTIFY: תמיכה במציאת מכשירים חכמים שנשלטים באופן מקומי. ה-handler של הכוונות מחלץ את הנתונים שהמכשיר החכם מחזיר במהלך הגילוי, ושולח אותם בתגובה ל-Google.
  • EXECUTE: תמיכה בביצוע פקודות.
  • QUERY: תמיכה בשליחת שאילתות על מצב המכשיר.
  • REACHABLE_DEVICES: (אופציונלי) תמיכה בגילוי של מכשירי קצה שניתן לשלוט בהם באופן מקומי מאחורי מכשיר עם רכזת (או גשר).

האפליקציה הזו פועלת במכשירי Google Home או Google Nest של המשתמשים, ומחברת את המכשיר החכם ל-Assistant. ניתן ליצור את האפליקציה באמצעות TypeScript (מועדף) או JavaScript.

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

מידע נוסף על ה-API זמין במאמר חומר עזר בנושא Local Home SDK API.

בקטעי הקוד הבאים אפשר לראות איך אפשר לאתחל את אפליקציית מילוי ההזמנות המקומית ולצרף את ה-handlers שלכם.

עצמאי
import App = smarthome.App;
const localHomeApp: App = new App("1.0.0");
localHomeApp
  .onIdentify(identifyHandler)
  .onExecute(executeHandler)
  .listen()
  .then(() => {
    console.log("Ready");
  });
מרכז הפעילות
import App = smarthome.App;
const localHomeApp: App = new App("1.0.0");
localHomeApp
  .onIdentify(identifyHandler)
  .onReachableDevices(reachableDevicesHandler)
  .onExecute(executeHandler)
  .listen()
  .then(() => {
    console.log("Ready");
  });

יוצרים פרויקט

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

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

תבניות לפרויקט

כדי לבחור את הגדרות ה-bundler, מריצים את הפקודה npm init כפי שמוצג בדוגמאות הבאות:

ללא

TypeScript ללא הגדרת Bundler:

npm init @google/local-home-app project-directory/ --bundler none

מבנה הפרויקט:

project-directory/
├── node_modules/
├── package.json
├── .gitignore
├── index.ts
├── test.ts
├── tsconfig.json
├── tslint.json
└── serve.js

מחליפים את project-directory בספרייה חדשה שתכיל את פרויקט האפליקציה המקומית למילוי הזמנות.

Webpack

TypeScript עם תצורת Bundles של webpack:

npm init @google/local-home-app project-directory/ --bundler webpack

מבנה הפרויקט:

project-directory/
├── node_modules/
├── package.json
├── .gitignore
├── index.ts
├── test.ts
├── tsconfig.json
├── tslint.json
├── webpack.config.web.js
├── webpack.config.node.js
└── serve.js

מחליפים את project-directory בספרייה חדשה שתכיל את פרויקט האפליקציה המקומית למילוי הזמנות.

רשימה כללית

TypeScript עם תצורת בונטר (bundler) על:

npm init @google/local-home-app project-directory/ --bundler rollup

מבנה הפרויקט:

project-directory/
├── node_modules/
├── package.json
├── .gitignore
├── index.ts
├── test.ts
├── tsconfig.json
├── tslint.json
├── rollup.config.js
└── serve.js

מחליפים את project-directory בספרייה חדשה שתכיל את פרויקט האפליקציה המקומית למילוי הזמנות.

חבילה

TypeScript עם תצורת חבילה של Parcel:

npm init @google/local-home-app project-directory/ --bundler parcel

מבנה הפרויקט:

project-directory/
├── node_modules/
├── package.json
├── .gitignore
├── index.ts
├── test.ts
├── tsconfig.json
├── tslint.json
└── serve.js

מחליפים את project-directory בספרייה חדשה שתכיל את פרויקט האפליקציה המקומית למילוי הזמנות.

ביצוע משימות נפוצות ברמת הפרויקט

הפרויקט שנוצר תומך בסקריפטים הבאים של npm:

חבילה
cd project-directory/
npm run build

הסקריפט הזה מבצע הידור של מקור TypeScript ומקבץ את האפליקציה עם יחסי התלות שלו לסביבת זמן הריצה של Chrome בספריית המשנה dist/web ובסביבת זמן הריצה של Node.js בספריית המשנה dist/node.

אימות
cd project-directory/
npm run lint
npm run compile
npm test

הסקריפט מאמת את התחביר של קוד ה-TypeScript שלך, מהדר אותו בלי להפיק פלט בספריית המשנה dist/ ומפעיל בדיקות אוטומטיות מ-test.ts.

הצגה
cd project-directory/
npm run start

במהלך הפיתוח, הסקריפט הזה מציג את קובצי ה-App Bundle לסביבות זמן הריצה של Chrome ו-Node.js באופן מקומי.

הטמעת ה-handler של IDENTIFY

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

הקוד IdentifyRequest מפלטפורמת הבית המקומי מכיל את נתוני הסריקה של מכונה LocalIdentifiedDevice. רק מופע אחד של device יאוכלס, על סמך הגדרות הסריקה שגילתה את המכשיר.

אם תוצאות הסריקה תואמות למכשיר שלכם, ה-handler של IDENTIFY אמור להחזיר אובייקט IdentifyResponsePayload שכולל אובייקט device עם מטא-נתונים לבית החכם (כמו הסוגים, התכונות ומצב הדוח).

Google יוצרת שיוך מכשיר אם verificationId מהתגובה IDENTIFY תואם לאחד מערכי otherDeviceIds שהחזירו בתגובה SYNC.

דוגמה

בקטעי הקוד הבאים מוסבר איך ליצור גורמי handler של IDENTIFY לשילובים עצמאיים של מכשירים ורכזות, בהתאמה.

עצמאי
const identifyHandler = (request: IntentFlow.IdentifyRequest):
  IntentFlow.IdentifyResponse => {

    // Obtain scan data from protocol defined in your scan config
    const device = request.inputs[0].payload.device;
    if (device.udpScanData === undefined) {
      throw Error("Missing discovery response");
    }
    const scanData = device.udpScanData.data;

    // Decode scan data to obtain metadata about local device
    const verificationId = "local-device-id";

    // Return a response
    const response: IntentFlow.IdentifyResponse = {
      intent: Intents.IDENTIFY,
      requestId: request.requestId,
      payload: {
        device: {
          id: device.id || "",
          verificationId, // Must match otherDeviceIds in SYNC response
        },
      },
    };
    return response;
  };
מרכז הפעילות
const identifyHandler = (request: IntentFlow.IdentifyRequest):
  IntentFlow.IdentifyResponse => {

    // Obtain scan data from protocol defined in your scan config
    const device = request.inputs[0].payload.device;
    if (device.udpScanData === undefined) {
      throw Error("Missing discovery response");
    }
    const scanData = device.udpScanData.data;

    // Decode scan data to obtain metadata about local device
    const proxyDeviceId = "local-hub-id";

    // Return a response
    const response: IntentFlow.IdentifyResponse = {
      intent: Intents.IDENTIFY,
      requestId: request.requestId,
      payload: {
        device: {
          id: proxyDeviceId,
          isProxy: true,     // Device can control other local devices
          isLocalOnly: true, // Device not present in `SYNC` response
        },
      },
    };
    return response;
  };

זיהוי מכשירים מאחורי רכזת

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

כדי לאפשר ל-Google לוודא שיש מכשיר Hub, פועלים לפי ההוראות הבאות ב-handler של IDENTIFY:

  • אם התשובה של SYNC מדווחת על המזהים של מכשירי קצה מקומיים שמחוברים למרכז, צריך להגדיר את isProxy בתור true ב-IdentifyResponsePayload.
  • אם התשובה של SYNC לא מדווחת על המכשיר המרכזי שלכם, צריך להגדיר את הערך isLocalOnly בתור true ב-IdentifyResponsePayload.
  • השדה device.id מכיל את מזהה המכשיר המקומי של מכשיר המרכז עצמו.

יישום הגורם המטפל REACHABLE_ מכשיר (שילובים של Hub בלבד)

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

ה-handler של REACHABLE_DEVICES מוטמע באופן דומה ל-handler של IDENTIFY, אלא שה-handler צריך לאסוף מזהי מכשירים נוספים שאליו אפשר להגיע דרך שרת ה-proxy המקומי (כלומר, המכשיר של ה-Hub). השדה device.verificationId מכיל את מזהה המכשיר המקומי של מכשיר קצה שמחובר לרכזת.

הערך ReachableDevicesRequest מפלטפורמת הבית המקומי מכיל מופע של LocalIdentifiedDevice. באמצעות המכונה הזו, תוכלו לקבל את מזהה המכשיר של שרת ה-Proxy וכן נתונים מתוצאות הסריקה.

ה-handler של REACHABLE_DEVICES צריך להחזיר אובייקט ReachableDevicesPayload שכולל אובייקט devices שמכיל מערך של ערכי verificationId שמייצגים את מכשירי הקצה שה-Hub שולט בהם. הערכים של verificationId חייבים להתאים לאחד מהערכים של otherDeviceIds מהתגובה SYNC.

בקטע הקוד הבא מוסבר איך ליצור את ה-handler של REACHABLE_DEVICES.

מרכז הפעילות
const reachableDevicesHandler = (request: IntentFlow.ReachableDevicesRequest):
  IntentFlow.ReachableDevicesResponse => {

    // Reference to the local proxy device
    const proxyDeviceId = request.inputs[0].payload.device.id;

    // Gather additional device ids reachable by local proxy device
    // ...

    const reachableDevices = [
      // Each verificationId must match one of the otherDeviceIds
      // in the SYNC response
      { verificationId: "local-device-id-1" },
      { verificationId: "local-device-id-2" },
    ];

    // Return a response
    const response: IntentFlow.ReachableDevicesResponse = {
      intent: Intents.REACHABLE_DEVICES,
      requestId: request.requestId,
      payload: {
        devices: reachableDevices,
      },
    };
    return response;
  };

הטמעת ה-handler של EXECUTE

ה-handler של EXECUTE באפליקציה מעבד פקודות של משתמשים ומשתמש ב-Local Home SDK כדי לגשת למכשירים החכמים באמצעות פרוטוקול קיים.

פלטפורמת Local Home מעבירה לפונקציית ה-handler של EXECUTE את אותו מטען ייעודי (payload) של הפונקציה EXECUTE למילוי הבקשה בענן. באופן דומה, ה-handler של EXECUTE מחזיר נתוני פלט באותו פורמט של העיבוד של ה-Intent EXECUTE. כדי ליצור תשובות בקלות, תוכלו להשתמש במחלקה Execute.Response.Builder שמוצעת ב-Local Home SDK.

לאפליקציה שלך אין גישה ישירה לכתובת ה-IP של המכשיר. במקום זאת, השתמשו בממשק CommandRequest כדי ליצור פקודות שמבוססות על אחד מהפרוטוקולים האלה: UDP, TCP או HTTP. לאחר מכן, מפעילים את הפונקציה deviceManager.send() כדי לשלוח את הפקודות.

כשמטרגטים פקודות למכשירים, צריך להשתמש במזהה המכשיר (ובפרמטרים מהשדה customData, אם הוא כלול) מהתגובה SYNC כדי לתקשר עם המכשיר.

דוגמה

קטע הקוד הבא מראה איך ליצור את ה-handler של EXECUTE.

עצמאי/מרכזי
const executeHandler = (request: IntentFlow.ExecuteRequest):
  Promise<IntentFlow.ExecuteResponse> => {

    // Extract command(s) and device target(s) from request
    const command = request.inputs[0].payload.commands[0];
    const execution = command.execution[0];

    const response = new Execute.Response.Builder()
      .setRequestId(request.requestId);

    const result = command.devices.map((device) => {
      // Target id of the device provided in the SYNC response
      const deviceId = device.id;
      // Metadata for the device provided in the SYNC response
      // Use customData to provide additional required execution parameters
      const customData: any = device.customData;

      // Convert execution command into payload for local device
      let devicePayload: string;
      // ...

      // Construct a local device command over TCP
      const deviceCommand = new DataFlow.TcpRequestData();
      deviceCommand.requestId = request.requestId;
      deviceCommand.deviceId = deviceId;
      deviceCommand.data = devicePayload;
      deviceCommand.port = customData.port;
      deviceCommand.operation = Constants.TcpOperation.WRITE;

      // Send command to the local device
      return localHomeApp.getDeviceManager()
        .send(deviceCommand)
        .then((result) => {
          response.setSuccessState(result.deviceId, state);
        })
        .catch((err: IntentFlow.HandlerError) => {
          err.errorCode = err.errorCode || IntentFlow.ErrorCode.INVALID_REQUEST;
          response.setErrorState(device.id, err.errorCode);
        });
    });

    // Respond once all commands complete
    return Promise.all(result)
      .then(() => response.build());
  };

יישום ה-handler של QUERY

ה-handler של QUERY באפליקציה מעבד בקשות של משתמשים ומשתמש ב-Local Home SDK כדי לדווח על מצב המכשירים החכמים.

פלטפורמת Local Home מעבירה לפונקציית ה-handler 'QUERY' את אותו מטען ייעודי (payload) של הבקשה, כמו גם עבור הכוונה QUERY למילוי הבקשה בענן. באופן דומה, ה-handler של QUERY מחזיר נתונים בפורמט זהה לזה של עיבוד ה-Intent QUERY.

שליחת פקודות למכשירים מאחורי רכזת

כדי לשלוט במכשירי קצה מאחורי רכזת, ייתכן שיהיה צורך לספק מידע נוסף במטען הייעודי (payload) הספציפי לפרוטוקול שנשלח למרכז כדי שהמרכז יוכל לזהות לאיזה מכשיר הפקודה מיועדת. במקרים מסוימים אפשר להסיק זאת ישירות מהערך של device.id, אבל אם זה לא המצב, צריך לכלול את הנתונים הנוספים האלה כחלק מהשדה customData.

אם יצרתם את האפליקציה באמצעות TypeScript, זכרו להדר את האפליקציה ל-JavaScript. תוכל להשתמש במערכת המודול שתבחר כדי לכתוב את הקוד שלך. חשוב לוודא שדפדפן Chrome תומך ביעד.