כדי לתמוך במימוש הזמנות בחנויות מקומיות, צריך ליצור אפליקציה שתטפל בכוונות האלה של בית חכם:
-
IDENTIFY
: תומך באיתור של מכשירים חכמים שאפשר לשלוט בהם באופן מקומי. ה-intent handler מחלץ נתונים שהמכשיר החכם מחזיר במהלך הגילוי ושולח אותם בתגובה ל-Google. -
EXECUTE
: תומך בהרצת פקודות. -
QUERY
: תומך בשאילתות לגבי מצב המכשיר. -
REACHABLE_DEVICES
: (אופציונלי) תומך בגילוי של מכשירי קצה מקומיים שאפשר לשלוט בהם, שנמצאים מאחורי רכזת (או מכשיר גישור).
האפליקציה הזו פועלת במכשירי Google Home או Google Nest של המשתמש ומחברת את המכשיר החכם אל Assistant. אפשר ליצור את האפליקציה באמצעות TypeScript (מומלץ) או JavaScript.
מומלץ להשתמש ב-TypeScript כי אפשר להשתמש בקשירות כדי לוודא באופן סטטי שהנתונים שהאפליקציה מחזירה תואמים לסוגים שהפלטפורמה מצפה להם.
פרטים נוספים על ה-API זמינים במאמרי העזרה בנושא Local Home SDK API.
בדוגמאות הקוד הבאות אפשר לראות איך מאתחלים את אפליקציית ההזמנות מחנויות מקומיות ומצרפים את הפונקציות לטיפול בבקשות.
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 לקוד ולכל התלויות שלו.
משתמשים בכלי לאתחול פרויקטים של אפליקציית ההפעלה של הזמנות מקומיות כדי לאתחל את מבנה הפרויקט המתאים עם הגדרות ה-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 בספרייה חדשה שתכיל את פרויקט האפליקציה של מימוש הזמנות מקומי.
TypeScript עם הגדרת חבילת 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 עם הגדרת חבילה של Rollup:
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
במהלך הפיתוח, הסקריפט הזה מציג את חבילות האפליקציות שלכם באופן מקומי בסביבות זמן הריצה של Chrome ו-Node.js.
הטמעה של ה-handler IDENTIFY
ה-handler IDENTIFY
יופעל כשמכשיר Google Home או Google Nest יופעל מחדש ויזהה מכשירים מקומיים לא מאומתים (כולל מכשירי קצה שמחוברים לרכזת). פלטפורמת Local Home תסרוק את המכשירים המקומיים באמצעות פרטי הגדרת הסריקה שציינתם קודם, ותפעיל את הפונקציה IDENTIFY
handler עם תוצאות הסריקה.
IdentifyRequest
מהפלטפורמה Local Home מכיל את נתוני הסריקה של מופע 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 לוודא שקיים מכשיר רכזת, פועלים לפי ההוראות הבאות עבור IDENTIFY
ה-handler:
- אם בתגובה של
SYNC
מופיעים מזהים של מכשירי קצה מקומיים שמחוברים ל-hub, צריך להגדיר אתisProxy
כ-true
ב-IdentifyResponsePayload
. - אם בתגובה של
SYNC
לא מופיע המכשיר שלכם, צריך להגדיר אתisLocalOnly
כ-true
ב-IdentifyResponsePayload
. - השדה
device.id
מכיל את מזהה המכשיר המקומי של מכשיר ה-Hub עצמו.
הטמעה של רכיב ה-handler REACHABLE_DEVICES (שילובים של רכזות בלבד)
Google שולחת את REACHABLE_DEVICES
כדי לאשר אילו מכשירי קצה אפשר לשלוט בהם באופן מקומי. הכוונה הזו מופעלת בכל פעם ש-Google מריצה סריקה לגילוי (פעם בדקה בערך), כל עוד המרכז מזוהה כמחובר לאינטרנט.
מטמיעים את רכיב ה-handler REACHABLE_DEVICES
באופן דומה לרכיב ה-handler IDENTIFY
, אבל רכיב ה-handler צריך לאסוף מזהי מכשירים נוספים שאפשר להגיע אליהם באמצעות שרת ה-proxy המקומי (כלומר, מכשיר ה-hub). השדה
device.verificationId
מכיל את מזהה המכשיר המקומי של מכשיר קצה
שמחובר לרכזת.
ReachableDevicesRequest
מפלטפורמת Local Home מכיל מופע של
LocalIdentifiedDevice
.
באמצעות המופע הזה, אפשר לקבל את מזהה מכשיר ה-proxy וגם נתונים מתוצאות הסריקה.
ה-handler של REACHABLE_DEVICES
צריך להחזיר אובייקט ReachableDevicesPayload
שכולל אובייקט devices
שמכיל מערך של ערכי verificationId
שמייצגים את מכשירי הקצה שהרכזת שולטת בהם. הערכים של verificationId
צריכים להיות זהים לאחד מהערכים של otherDeviceIds
בתגובה SYNC
.
בקטע הקוד הבא מוצג אופן יצירת REACHABLE_DEVICES
handler.
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 מעבירה את אותה מטען ייעודי (payload) של קלט לפונקציית ה-handler של EXECUTE
כמו לפונקציית ה-intent של EXECUTE
למילוי ההזמנות בענן. באופן דומה, הפונקציה לטיפול בבקשות EXECUTE
מחזירה נתוני פלט באותו פורמט כמו העיבוד של כוונת המשתמש 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 מעבירה את אותה מטען ייעודי (payload) של בקשה לפונקציית הטיפול QUERY, כמו במקרה של כוונת QUERY
למילוי הזמנות בענן. באופן דומה, הפונקציה QUERY
handler מחזירה נתונים באותו פורמט כמו העיבוד של כוונת המשתמש QUERY
.
שליחת פקודות למכשירים שמחוברים למרכזייה
כדי לשלוט במכשירי קצה שנמצאים מאחורי רכזת, יכול להיות שתצטרכו לספק מידע נוסף במטען הייעודי (payload) של הפקודה הספציפית לפרוטוקול שנשלחת לרכזת, כדי שהרכזת תוכל לזהות לאיזה מכשיר מיועדת הפקודה. במקרים מסוימים אפשר להסיק את הערך הזה ישירות מהערך של device.id
, אבל אם זה לא המצב, צריך לכלול את הנתונים הנוספים האלה כחלק מהשדה customData
.
אם יצרתם את האפליקציה באמצעות TypeScript, חשוב לזכור לקמפל את האפליקציה ל-JavaScript. אתם יכולים להשתמש במערכת המודולים שתבחרו כדי לכתוב את הקוד. מוודאים שהיעד נתמך בדפדפן Chrome.