1. לפני שמתחילים
מפתחים של Internet of Things (IoT) יכולים ליצור פעולות בבית חכם שמאפשרות למשתמשים לשלוט במכשירים שלהם באמצעות פקדי מגע באפליקציית Google Home ופקודות קוליות עם Assistant.
פעולות בבית החכם מסתמכות על תרשים הבית כדי לספק נתונים לפי הקשר לגבי הבית והמכשירים שבו, וכך ליצור מפה לוגית של הבית. ההקשר הזה מספק ל-Assistant הבנה טבעית יותר של הבקשות של המשתמש ביחס למיקום שלו בבית. לדוגמה, ב-Home Graph הקונספט של סלון שמכיל כמה סוגים של מכשירים מיצרנים שונים, כמו תרמוסטט, מנורה, מאוורר ושואב אבק.
דרישות מוקדמות
- מדריך למפתחים בנושא יצירת פעולה בבית חכם
מה תפַתחו
בשיעור ה-Codelab הזה, תפרסמו שירות ענן שמנהל מכונת כביסה חכמה וירטואלית, ולאחר מכן ניצור פעולת בית חכם ולחבר אותה ל-Assistant.
מה תלמדו
- איך לפרוס שירות ענן לבית חכם
- איך לחבר את השירות ל-Assistant
- איך לפרסם ב-Google שינויים במצב המכשיר
מה הדרישות כדי להצטרף לתוכנית?
- דפדפן אינטרנט, למשל Google Chrome
- מכשיר iOS או Android שאפליקציית Google Home מותקנת בהם
- Node.js בגרסה 10.16 ואילך
- חשבון לחיוב ב-Google Cloud
2. תחילת העבודה
הפעלה של בקרת הפעילות בחשבון
כדי להשתמש ב-Google Assistant, עליכם לשתף עם Google נתוני פעילות מסוימים. הנתונים האלה נחוצים ל-Google Assistant כדי לפעול כראוי, אבל הדרישה לשיתוף נתונים אינה ספציפית ל-SDK. כדי לשתף את הנתונים האלה, צריך ליצור חשבון Google אם עדיין אין לכם חשבון. אפשר להשתמש בכל חשבון Google — זה לא חייב להיות חשבון הפיתוח שלך.
פותחים את הדף 'בקרת הפעילות בחשבון' בחשבון Google שבו רוצים להשתמש עם Assistant.
צריך לוודא שמתגי החלפת המצב הבאים מופעלים:
- פעילות באינטרנט ובאפליקציות – בנוסף, חשוב לסמן את התיבה ההגדרה הזו כוללת את ההיסטוריה והפעילות של Chrome מאתרים, מאפליקציות וממכשירים המשתמשים בשירותי Google.
- פרטי מכשיר
- תיעוד קול ואודיו
יצירת פרויקט פעולות
- עוברים אל Actions on Google Developer Console.
- לוחצים על New Project (פרויקט חדש), נותנים שם לפרויקט ולוחצים על CREATE PROJECT.
בחירת האפליקציה 'בית חכם'
במסך הסקירה הכללית במסוף Actions, בוחרים באפשרות בית חכם.
בוחרים בכרטיס החוויה של בית חכם, לוחצים על התחלת הבנייה ואז תועברו למסוף הפרויקט.
התקנת ה-CLI של Firebase
ממשק שורת הפקודה (CLI) של Firebase מאפשר להציג את אפליקציות האינטרנט באופן מקומי ולפרוס את אפליקציית האינטרנט באירוח ב-Firebase.
כדי להתקין את ה-CLI, מריצים את פקודת ה-npm הבאה מהטרמינל:
npm install -g firebase-tools
כדי לוודא שה-CLI הותקן כהלכה, מריצים את:
firebase --version
כדי לאשר את ה-CLI של Firebase באמצעות חשבון Google, מריצים את הפקודה:
firebase login
3. הפעלת האפליקציה למתחילים
עכשיו, אחרי שהגדרתם את סביבת הפיתוח, אפשר לפרוס את הפרויקט למתחילים כדי לוודא שהכול מוגדר כמו שצריך.
קבלת קוד המקור
כדי להוריד את הדוגמה של ה-Codelab הזה במחשב הפיתוח, לוחצים על הקישור הבא:
או שאפשר לשכפל את המאגר של GitHub משורת הפקודה:
git clone https://github.com/google-home/smarthome-washer.git
מידע על הפרויקט
הפרויקט למתחילים מכיל את ספריות המשנה הבאות:
public:
ממשק משתמש חזיתי שמאפשר לך לשלוט בקלות במצב של מכונת הכביסה החכמה ולפקח עליה.functions:
שירות ענן שהוטמע באופן מלא שמנהל את מכונת הכביסה החכמה באמצעות Cloud Functions for Firebase ומסד נתונים בזמן אמת ב-Firebase.
קישור ל-Firebase
עוברים לספרייה washer-start
ומגדירים את ה-CLI של Firebase בפרויקט הפעולות:
cd washer-start firebase use <project-id>
הגדרת פרויקט Firebase
מפעילים פרויקט Firebase.
firebase init
בוחרים את תכונות ה-CLI, מסד נתונים בזמן אמת, פונקציות ואת התכונה אירוח שכוללת אירוח ב-Firebase.
? 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 הנחוצים לפרויקט שלכם.
כשתוצג הבקשה, מפעילים מסד נתונים בזמן אמת. אפשר להשתמש במיקום ברירת המחדל של המופע של מסד הנתונים.
? 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
מאחר שבחרת להשתמש בקוד הפרויקט למתחילים, עליך לבחור את קובץ ברירת המחדל לכללי האבטחה ולוודא שלא מחליפים את קובץ הכללים הקיים של מסד הנתונים.
? What file should be used for Realtime Database Security Rules? Database.rules.json ? 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
באופן דומה, כשמופיעה השאלה אם רוצים לאתחל או להחליף קוד בסיס, בוחרים באפשרות החלפה.
? 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 ? File functions/package.json already exists. Overwrite? No ? File functions/index.js already exists. Overwrite? No ? Do you want to install dependencies with npm now? Yes
לבסוף, צריך להגדיר את הגדרת האירוח לשימוש בספרייה public
בקוד הפרויקט, ולהשתמש בקובץ index.html הקיים. כשמתבקשים להשתמש ב-ESLint, בוחרים באפשרות לא.
? What do you want to use as your public directory? public ? Do you want to use ESLint to catch probable bugs and enforce style? No ? 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 הופעל בטעות, יש שתי שיטות להשבית אותו:
- באמצעות ה-GUI, עוברים לתיקייה
../functions
במסגרת הפרויקט, בוחרים את הקובץ המוסתר.eslintrc.js
ומוחקים אותו. אל תטעה עם השם.eslintrc.json
, בעל שם דומה. - באמצעות שורת הפקודה:
cd functions rm .eslintrc.js
כדי לוודא שהגדרת Firebase נכונה ומלאה, צריך להעתיק את הקובץ firebase.json
מהספרייה app-done
לספרייה app-start
ולהחליף את הקובץ שנמצא ב-app-start
.
בספרייה app-start
:
cp -vp ../app-done/firebase.json .
פריסה ב-Firebase
עוברים לתיקייה functions
ומתקינים את כל יחסי התלות הנדרשים באמצעות npm.
cd functions npm install
עכשיו, לאחר שהתקנתם את יחסי התלות והגדרתם את הפרויקט, אתם מוכנים להפעיל את האפליקציה בפעם הראשונה.
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://<project-id>.web.app
) כדי להציג את אפליקציית האינטרנט. יוצג הממשק הבא:
ממשק המשתמש באינטרנט מייצג פלטפורמה של צד שלישי שמאפשרת להציג או לשנות מצבי מכשיר. כדי להתחיל לאכלס את מסד הנתונים במידע מהמכשירים שלך, לוחצים על עדכון. לא תראו שינויים בדף, אבל המצב הנוכחי של מכונת הכביסה יאוחסן במסד הנתונים.
עכשיו צריך לחבר את שירות הענן שפרסת ל-Google Assistant באמצעות Actions Console.
הגדרת הפרויקט ב-Actions Console
בקטע סקירה כללית > בניית הפעולה, בוחרים באפשרות הוספת פעולות. מזינים את כתובת ה-URL של הפונקציה ב-Cloud Functions שמספקת מילוי הזמנות עבור ה-Intents של הבית החכם, ולוחצים על שמירה.
https://us-central1-<project-id>.cloudfunctions.net/smarthome
בכרטיסייה פיתוח > הפעלה, מוסיפים שם תצוגה לפעולה ולוחצים על שמירה. השם הזה יופיע באפליקציית Google Home.
כדי להפעיל את קישור החשבונות, יש לבחור באפשרות פיתוח > קישור חשבונות בתפריט הניווט הימני. אפשר להשתמש בהגדרות הבאות של קישור החשבונות:
Client ID |
|
סוד לקוח |
|
כתובת אתר להרשאה |
|
כתובת ה-URL של האסימון |
|
לוחצים על שמירה כדי לשמור את הגדרת הקישור של החשבון, ואז לוחצים על בדיקה כדי להפעיל את הבדיקה בפרויקט.
המערכת תפנה אתכם אל הסימולטור. אם האפשרות הבדיקה מופעלת לא מוצגת, לוחצים על איפוס הבדיקה כדי לוודא שהבדיקה מופעלת.
עכשיו אפשר להתחיל להטמיע את ה-webhooks הנחוצים כדי לחבר את מצב המכשיר ל-Assistant.
4. יצירת מכונת כביסה
אחרי שהגדרת את הפעולה, אפשר להוסיף מכשירים ולשלוח נתונים. שירות הענן שלכם צריך לטפל בכוונות הבאות:
- Intent מסוג
SYNC
מתבצע כש-Assistant רוצה לדעת אילו מכשירים חובר למשתמש. המזהה הזה נשלח לשירות שלכם כשהמשתמש מקשר חשבון. עליך להגיב באמצעות מטען ייעודי (payload) של JSON של כל מכשירי המשתמש והיכולות שלהם. - Intent מסוג
QUERY
מתבצע כש-Assistant רוצה לדעת מה המצב הנוכחי של המכשיר או הסטטוס שלו. עליך להגיב באמצעות מטען ייעודי (payload) של JSON ולציין את המצב של כל מכשיר מבוקש. - Intent מסוג
EXECUTE
מתרחש כש-Assistant רוצה לשלוט במכשיר בשמו של משתמש. עליך להגיב באמצעות מטען ייעודי (payload) של JSON ולציין את סטטוס הביצוע של כל מכשיר מבוקש. - Intent מסוג
DISCONNECT
מתרחש כשהמשתמש מבטל את הקישור של החשבון שלו ל-Assistant. אין לשלוח ל-Assistant אירועים מהמכשירים של המשתמש הזה.
בקטעים הבאים נעדכן את הפונקציות שכבר פרסתם כדי לטפל באובייקטים האלה.
עדכון תגובת הסנכרון
פותחים את functions/index.js
, שמכיל את הקוד שצריך להגיב לבקשות מ-Assistant.
יהיה עליך לטפל ב-SYNC
Intent על ידי החזרת המטא-נתונים והיכולות של המכשיר. צריך לעדכן את קובץ ה-JSON במערך onSync
כך שיכלול את פרטי המכשיר ואת התכונות המומלצות למכונת כביסה.
index.js
app.onSync((body) => {
return {
requestId: body.requestId,
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',
],
name: {
defaultNames: ['My Washer'],
name: 'Washer',
nicknames: ['Washer'],
},
deviceInfo: {
manufacturer: 'Acme Co',
model: 'acme-washer',
hwVersion: '1.0',
swVersion: '1.0.1',
},
willReportState: true,
attributes: {
pausable: true,
},
}],
},
};
});
פריסה ב-Firebase
פורסים את מילוי הבקשה המעודכן בענן באמצעות ה-CLI של Firebase:
firebase deploy --only functions
קישור ל-Google Assistant
כדי לבדוק את הפעולה בבית החכם, עליך לקשר את הפרויקט לחשבון Google. כך ניתן לבצע בדיקות בפלטפורמות של Google Assistant ובאפליקציית Google Home, שמחוברים לאותו חשבון.
- בטלפון, פותחים את ההגדרות של Google Assistant. שימו לב שעליכם להיות מחוברים עם אותו חשבון כמו במסוף.
- עוברים אל Google Assistant > הגדרות > בית חכם (בקטע Assistant).
- לוחצים על סמל החיפוש בפינה השמאלית העליונה.
- מחפשים את אפליקציית הבדיקה באמצעות הקידומת [test] כדי למצוא את אפליקציית הבדיקה הספציפית.
- בוחרים את הפריט הרצוי. לאחר מכן, Google Assistant תבצע אימות מול השירות שלך ותשלח בקשת
SYNC
, כדי לבקש מהשירות לספק רשימת מכשירים עבור המשתמש.
פותחים את אפליקציית Google Home ומוודאים שאתם רואים את מכונת הכביסה.
5. טיפול בפקודות ובשאילתות
עכשיו, כששירות הענן מדווח ל-Google כמו שצריך על מכונת הכביסה, אתם צריכים להוסיף אפשרות לבקש את מצב המכשיר ולשלוח פקודות.
טיפול ב-Intent QUERY
QUERY
Intent כולל קבוצה של מכשירים. עבור כל מכשיר, עליך להגיב ולציין את המצב הנוכחי שלו.
ב-functions/index.js
, יש לערוך את ה-handler של QUERY
כך שיעבד את רשימת מכשירי היעד שנכללים בבקשת ה-Intent.
index.js
app.onQuery(async (body) => {
const {requestId} = body;
const payload = {
devices: {},
};
const queryPromises = [];
const intent = body.inputs[0];
for (const device of intent.payload.devices) {
const deviceId = device.id;
queryPromises.push(queryDevice(deviceId)
.then((data) => {
// Add response to device payload
payload.devices[deviceId] = data;
}
));
}
// Wait for all promises to resolve
await Promise.all(queryPromises);
return {
requestId: requestId,
payload: payload,
};
});
לכל מכשיר שנכלל בבקשה, מחזירים את המצב הנוכחי שנשמר במסד הנתונים בזמן אמת. צריך לעדכן את הפונקציות queryFirebase
ו-queryDevice
כדי להחזיר את נתוני המצב של מכונת הכביסה.
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,
};
};
const queryDevice = async (deviceId) => {
const data = await queryFirebase(deviceId);
return {
on: data.on,
isPaused: data.isPaused,
isRunning: data.isRunning,
currentRunCycle: [{
currentCycle: 'rinse',
nextCycle: 'spin',
lang: 'en',
}],
currentTotalRemainingTime: 1212,
currentCycleRemainingTime: 301,
};
};
טיפול ב-Intent ההפעלה
ל-EXECUTE
Intent יש פקודות לעדכון מצב המכשיר. התשובה מחזירה את הסטטוס של כל פקודה – לדוגמה, SUCCESS
, ERROR
או PENDING
– ואת מצב המכשיר החדש.
ב-functions/index.js
, צריך לערוך את ה-handler של EXECUTE
כך שיעבד את רשימת התכונות שצריך לעדכן ואת קבוצת מכשירי היעד לכל פקודה:
index.js
app.onExecute(async (body) => {
const {requestId} = body;
// Execution results are grouped by status
const result = {
ids: [],
status: 'SUCCESS',
states: {
online: true,
},
};
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) => {
result.ids.push(device.id);
Object.assign(result.states, data);
})
.catch(() => functions.logger.error('EXECUTE', device.id)));
}
}
}
await Promise.all(executePromises);
return {
requestId: requestId,
payload: {
commands: [result],
},
};
});
עבור כל פקודה ומכשיר יעד, מעדכנים במסד הנתונים בזמן אמת את הערכים שתואמים לתכונה המבוקשת. משנים את הפונקציה updateDevice
כדי לעדכן את ההפניה המתאימה של Firebase ולהחזיר את מצב המכשיר המעודכן.
index.js
const updateDevice = async (execution, deviceId) => {
const {params, command} = execution;
let state; let 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;
}
return ref.update(state)
.then(() => state);
};
6. בדיקת הפעולה
אחרי שמטמיעים את כל שלוש האובייקטים מסוג Intent, אפשר לבדוק שהפעולה שולטת במכונת הכביסה.
פריסה ב-Firebase
פורסים את מילוי הבקשה המעודכן בענן באמצעות ה-CLI של Firebase:
firebase deploy --only functions
בדיקת מכונת הכביסה
עכשיו אפשר לראות את השינוי בערך כשמנסים לבצע את אחת מהפקודות הקוליות הבאות בטלפון:
"Ok Google, turn on my Waher".
"Ok Google, paused my washer."
"Ok Google, stop my washer"
אפשר גם לשאול שאלות כדי לראות מה המצב הנוכחי של מכונת הכביסה.
"Ok Google, is my Waher on?"
"Ok Google, is my Waher running?"
"Ok Google, what bike is my Waher on?"
אתם יכולים להציג את השאילתות והפקודות האלה ביומנים שמופיעים מתחת לפונקציה בקטע Functions (פונקציות) במסוף Firebase. מידע נוסף על יומני Firebase זמין במאמר כתיבה והצגה של יומנים.
אפשר למצוא את השאילתות והפקודות האלה גם ב-Google Cloud Console בקטע Logging > Logs Explorer. מידע נוסף על התחברות ב-Google Cloud זמין במאמר גישה ליומני אירועים באמצעות Cloud Logging.
7. דיווח על עדכונים ל-Google
שילבת את שירות הענן שלך באופן מלא עם כוונות הבית החכם, כדי לאפשר למשתמשים לשלוט במצב הנוכחי של המכשירים שלהם ולשלוח שאילתות לגביהם. עם זאת, במסגרת ההטמעה עדיין לא תהיה לשירות שלכם אפשרות לשלוח ל-Assistant מידע על אירועים באופן יזום, כמו שינויים בנוכחות או במצב של המכשיר.
בעזרת בקשת סנכרון, אפשר להפעיל בקשת סנכרון חדשה כשמשתמשים מוסיפים או מסירים מכשירים, או כשיכולות המכשיר שלהם משתנות. באמצעות האפשרות Report State (מצב הדוח), שירות הענן יכול לשלוח באופן יזום את מצב המכשיר לתרשים הבית, כשמשתמשים משנים פיזית את מצב המכשיר – למשל, אם מפעילים מתג תאורה – או משנים את המצב באמצעות שירות אחר.
בקטע הזה תוסיפו קוד להפעלת השיטות האלה מאפליקציית האינטרנט של ממשק הקצה.
הפעלת ה-API של HomeGraph
Home Graph API מאפשר אחסון והרצת שאילתות לגבי מכשירים והמצבים שלהם בתוך תרשים הבית של המשתמש. כדי להשתמש ב-API הזה, קודם צריך לפתוח את מסוף Google Cloud ולהפעיל את HomeGraph API.
במסוף Google Cloud, בוחרים את הפרויקט שתואם לפעולות <project-id>.
. לאחר מכן, במסך 'ספריית API' של HomeGraph API, לוחצים על הפעלה.
הפעלה של מצב הדוח
כותבת למסד הנתונים בזמן אמת את הפונקציה reportstate
בפרויקט לתחילת פעולה. יש לעדכן את הפונקציה reportstate
ב-functions/index.js
כדי לתעד את הנתונים שנכתבו במסד הנתונים ולפרסם אותם בתרשים הבית באמצעות מצב הדוח.
index.js
exports.reportstate = functions.database.ref('{deviceId}').onWrite(
async (change, context) => {
functions.logger.info('Firebase write event triggered Report State');
const snapshot = change.after.val();
const requestBody = {
requestId: 'ff36a3cc', /* Any unique ID */
agentUserId: USER_ID,
payload: {
devices: {
states: {
/* Report the current state of our washer */
[context.params.deviceId]: {
on: snapshot.OnOff.on,
isPaused: snapshot.StartStop.isPaused,
isRunning: snapshot.StartStop.isRunning,
},
},
},
},
};
const res = await homegraph.devices.reportStateAndNotification({
requestBody,
});
functions.logger.info('Report state response:', res.status, res.data);
});
הפעלה של בקשת סנכרון
רענון הסמל בממשק המשתמש באינטרנט של ממשק הקצה מפעיל את הפונקציה requestsync
בפרויקט לתחילת פעולה. צריך להטמיע את הפונקציה requestsync
ב-functions/index.js
כדי להפעיל את HomeGraph API.
index.js
exports.requestsync = functions.https.onRequest(async (request, response) => {
response.set('Access-Control-Allow-Origin', '*');
functions.logger.info(`Request SYNC for user ${USER_ID}`);
try {
const res = await homegraph.devices.requestSync({
requestBody: {
agentUserId: USER_ID,
},
});
functions.logger.info('Request sync response:', res.status, res.data);
response.json(res.data);
} catch (err) {
functions.logger.error(err);
response.status(500).send(`Error requesting sync: ${err}`);
}
});
פריסה ב-Firebase
פורסים את הקוד המעודכן באמצעות ה-CLI של Firebase:
firebase deploy --only functions
בדיקת ההטמעה
לוחצים על הלחצן רענון בממשק המשתמש באינטרנט ומוודאים שמופיעה בקשת סנכרון ביומן של מסוף Firebase.
לאחר מכן, משנים את המאפיינים של מכונת הכביסה בממשק המשתמש באינטרנט של ממשק הקצה ולוחצים על עדכון. מומלץ לוודא שניתן לראות את שינוי המצב שדווח ל-Google ביומנים של מסוף Firebase.
8. מזל טוב
כל הכבוד! שילבת בהצלחה את Assistant עם שירות ענן במכשיר באמצעות פעולות לבית חכם.
מידע נוסף
הנה כמה רעיונות שתוכלו ליישם כדי להעמיק את הידע:
- מוסיפים למכשיר modes ומחליפים.
- אפשר להוסיף למכשיר עוד traits נתמכות.
- מידע על ביצוע מקומי לבית חכם.
- רוצים לקבל מידע נוסף? אתם מוזמנים לצפות בדוגמה שלנו ל-GitHub.
אפשר גם לקרוא מידע נוסף על בדיקה ושליחה של פעולה לבדיקה, כולל תהליך האישור לפרסום הפעולה למשתמשים.