1. לפני שמתחילים
מפתחים של "אינטרנט של דברים" (IoT) יכולים ליצור פעולות בית חכם שנותנות למשתמשים את היכולת לשלוט במכשירים שלהם באמצעות מקשי מגע באפליקציית Google Home ופקודות קוליות עם Assistant.
פעולות בבית החכם מסתמכות על תרשים הבית כדי לספק נתונים הקשריים לגבי הבית והמכשירים הקשורים אליו, וליצור מפה הגיונית של הבית. בהקשר הזה, ל-Assistant תהיה הבנה טבעית יותר של הבקשות של המשתמש ביחס למיקום שלו בבית. לדוגמה, ב-Home Graph אפשר לאחסן את הקונספט של סלון שמכיל כמה סוגים של מכשירים מיצרנים שונים, כמו תרמוסטט, מנורה, מאוורר ושואב אבק.
דרישות מוקדמות
- מדריך למפתחים בנושא יצירת פעולה בבית החכם
מה תפַתחו
בשיעור ה-Lab הזה תפרסמו שירות ענן שמנהל מכונת כביסה חכמה וירטואלית, ולאחר מכן יבנו פעולה של בית חכם ותחברו אותה ל-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.
- לוחצים על פרויקט חדש, נותנים שם לפרויקט ולוחצים על יצירת פרויקט.
בחירה של אפליקציית הבית החכם
במסך הסקירה הכללית במסוף 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
מכיוון שבחרת להשתמש בקוד הפרויקט למתחילים, עליך לבחור את קובץ ברירת המחדל של כללי האבטחה ולוודא שלא מחליפים את קובץ כללי מסד הנתונים הקיים.
? 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
אם אתם מאתחלים מחדש את הפרויקט, כשתישאלו אם אתם רוצים לאתחל או להחליף פונקציות/.gitignore, יש לבחור באפשרות לא.
? File functions/.gitignore 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 ? 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
עכשיו, לאחר שהתקנתם את יחסי התלות והגדרת את הפרויקט, אתם מוכנים להפעיל את האפליקציה בפעם הראשונה.
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
בקטע סקירה כללית > בניית הפעולה, בוחרים באפשרות הוספת פעולות. מזינים את כתובת ה-URL של הפונקציה ב-Cloud Functions שמספקת מילוי הזמנות עבור ה-Intents של הבית החכם ולוחצים על שמירה.
https://us-central1-<project-id>.cloudfunctions.net/smarthome
בכרטיסייה פיתוח > הפעלה, מוסיפים שם תצוגה לפעולה ולוחצים על שמירה. השם הזה יופיע באפליקציית Google Home.
כדי להפעיל את קישור החשבונות, בוחרים באפשרות פיתוח > קישור חשבון בתפריט הניווט הימני. השתמש בהגדרות הבאות לקישור חשבונות:
Client ID |
|
סוד לקוח |
|
כתובת אתר להרשאה |
|
כתובת ה-URL של האסימון |
|
לוחצים על Save כדי לשמור את הגדרת הקישור של החשבון, ואז לוחצים על Test כדי להפעיל את הבדיקה בפרויקט.
המערכת תפנה אתכם אל הסימולטור. אם לא מופיעה האפשרות הבדיקה מופעלת, לוחצים על איפוס הבדיקה כדי לוודא שהבדיקה מופעלת.
עכשיו אפשר להתחיל להטמיע את ה-webhooks הנחוצים כדי לחבר את מצב המכשיר ל-Assistant.
4. יצירת מכונת כביסה
אחרי שהגדרת את הפעולה, אפשר להוסיף מכשירים ולשלוח נתונים. שירות הענן שלכם צריך לטפל בכוונות הבאות:
- ה-Intent
SYNC
מופעל כש-Assistant רוצה לדעת אילו מכשירים המשתמש חיבר. המזהה נשלח לשירות שלכם כשהמשתמש מקשר חשבון. עליך להגיב באמצעות מטען ייעודי (payload) של JSON שכולל את כל המכשירים של המשתמש והיכולות שלהם. - Intent של
QUERY
מתרחשת כש-Assistant רוצה לדעת מה המצב הנוכחי או הסטטוס של מכשיר. עליך להגיב באמצעות מטען ייעודי (payload) של JSON שבו מצוין המצב של כל מכשיר מבוקש. - Intent של
EXECUTE
מתרחשת כש-Assistant רוצה לשלוט במכשיר בשמו של משתמש. עליך להגיב באמצעות מטען ייעודי (payload) של JSON עם סטטוס הביצוע של כל מכשיר מבוקש. - הכוונה של
DISCONNECT
מתרחשת כשהמשתמש מבטל את הקישור של החשבון שלו ל-Assistant. צריך להפסיק לשלוח ל-Assistant אירועים מהמכשירים של המשתמש הזה.
בקטעים הבאים נעדכן את הפונקציות שפרסתם בעבר כדי לטפל באובייקטים האלה.
עדכון תגובת הסנכרון
פתיחה של functions/index.js
, שמכיל את הקוד להגיב לבקשות מ-Assistant.
צריך לטפל ב-Intent SYNC
על ידי החזרת המטא-נתונים והיכולות של המכשיר. צריך לעדכן את ה-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 על המכשיר באופן תקין. עכשיו צריך להוסיף אפשרות לבקש את מצב המכשיר ולשלוח פקודות.
טיפול בכוונת QUERY
אובייקט Intent מסוג QUERY
כולל קבוצת מכשירים. לגבי כל מכשיר, עליך להגיב ולציין את מצבו הנוכחי.
ב-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
מטפל בפקודות לעדכון מצב המכשיר. התגובה מחזירה את הסטטוס של כל פקודה — לדוגמה, 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 weather"
"Ok Google, paused my Waher".
"Ok Google, stop my washer"
אפשר גם לשאול שאלות כדי לראות את המצב הנוכחי של מכונת הכביסה.
"Ok Google, is my washer on?"
"Ok Google, is my Wahr running?"
"Ok Google, what cycle is my Waher on?"
את השאילתות והפקודות האלה אתם יכולים לראות ביומנים שמופיעים מתחת לפונקציה שלכם בקטע Functions (פונקציות) במסוף Firebase. מידע נוסף על יומני Firebase זמין במאמר בנושא כתיבה והצגה של יומנים.
אפשר למצוא את השאילתות והפקודות האלה גם במסוף Google Cloud בקטע Logging > Logs Explorer. מידע נוסף על התחברות ב-Google Cloud זמין במאמר גישה ליומני אירועים באמצעות Cloud Logging.
7. דיווח על עדכונים ל-Google
שילבת את שירות הענן שלך עם כוונות הבית החכם, וכך המשתמשים יכולים לשלוט במצב הנוכחי של המכשירים שלהם ולשלוח שאילתות לגביו. עם זאת, בתהליך ההטמעה עדיין אין לשירות אפשרות לשלוח ל-Assistant מידע על אירועים באופן יזום, כמו שינויים בנוכחות או במצב של המכשיר.
בעזרת בקשת סנכרון, ניתן להפעיל בקשת סנכרון חדשה כשמשתמשים מוסיפים או מסירים מכשירים, או כאשר יכולות המכשיר שלהם משתנות. באמצעות מצב הדיווח, שירות הענן שלכם יכול לשלוח באופן יזום את מצב המכשיר לתרשים הבית, כשמשתמשים משנים פיזית את מצב המכשיר – למשל, הפעלה של מתג תאורה – או שינוי המצב באמצעות שירות אחר.
בקטע הזה מוסיפים קוד להפעלת השיטות האלה מאפליקציית האינטרנט של ממשק הקצה.
הפעלת HomeGraph API
HomeGraph 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 ומחליפים למכשיר.
- אפשר להוסיף למכשיר עוד תכונות נתמכות.
- מידע על ביצוע מקומי לבית חכם.
- רוצים לקבל מידע נוסף? אתם מוזמנים לעיין בדוגמה שלנו ל-GitHub.
אפשר גם לעיין במידע נוסף על בדיקה ושליחה של פעולה לבדיקה, כולל תהליך האישור לפרסום הפעולה למשתמשים.