تحسين الإجراءات المنزلية الذكية وتأمينها

1. قبل البدء

تستخدم الإجراءات المنزلية الذكية أنواع الأجهزة لتمكين "مساعد Google" من معرفة القواعد النحوية التي يجب استخدامها على الجهاز. تحدِّد سمات الجهاز إمكانات نوع جهاز معيّن. يرث الجهاز حالات كل سمة جهاز تتم إضافتها إلى أحد الإجراءات.

dc8dce0dea87cd5c.png

يمكنك ربط أي سمات متوافقة بنوع الجهاز الذي اخترته لتخصيص وظائف المستخدمين الأجهزة. إذا أردت تطبيق سمات مخصّصة في "الإجراءات" غير المتوفّرة حاليًا في مخطط الجهاز، ستتيح السمتان الأوضاع والتبديل إعدادات معيّنة" التحكم باستخدام اسم مخصص تحدده.

بالإضافة إلى إمكانات التحكّم الأساسية التي توفّرها الأنواع والسمات، تتضمّن واجهة برمجة تطبيقات Smart Home ميزات إضافية لتحسين تجربة المستخدم. توفّر الردود على الأخطاء ملاحظات تفصيلية من المستخدمين عندما لا تنجح الأهداف. توسّع عملية إثبات هوية المستخدم الثانوي هذه الاستجابات وتضيف أمانًا إضافيًا إلى سمة الجهاز التي تختارها. من خلال إرسال ردود محدّدة عن أخطاء إلى مجموعات الاختبارات الصادرة من "مساعد Google"، يمكن أن يتطلّب "الإجراء المنزلي الذكي" إذنًا إضافيًا لإكمال طلب.

المتطلبات الأساسية

ما الذي ستقوم ببنائه

في هذا الدرس التطبيقي حول الترميز، ستنشر تكاملاً مسبقًا للمنزل المزوّد بأجهزة ذكية مع Firebase، ثم تتعرَّف على كيفية إضافة سمات غير عادية إلى غسالة المنزل المزوّد بأجهزة ذكية لقياس حجم الحمل ووضع "توربو". وعليك أيضًا تنفيذ تقارير الأخطاء والاستثناءات، وتعلُّم كيفية فرض إقرار شفهي لتشغيل الغسّالة باستخدام الطريقة الثانوية للتحقّق من المستخدم.

ما ستتعرَّف عليه

  • كيفية إضافة سمات "الأوضاع" و"التبديلات" إلى الإجراء
  • كيفية الإبلاغ عن الأخطاء والاستثناءات
  • كيفية تطبيق عملية التحقّق الثانوية من المستخدمين

المتطلبات

2. الخطوات الأولى

تفعيل عناصر التحكم في النشاط

لاستخدام "مساعد Google"، يجب مشاركة بيانات أنشطة معيّنة مع Google. يحتاج مساعد Google إلى هذه البيانات ليعمل بشكل سليم؛ مع ذلك، لا تقتصر متطلبات مشاركة البيانات على حزمة تطوير البرامج (SDK). لمشاركة هذه البيانات، أنشئ حساب Google إذا لم يكن لديك حساب. يمكنك استخدام أي حساب على Google، وليس بالضرورة أن يكون هذا الحساب هو حساب المطوِّر الخاص بك.

افتح صفحة عناصر التحكم في النشاط لحساب Google الذي تريد استخدامه مع "مساعد Google".

تأكَّد من أنّ مفاتيح التبديل التالية مفعَّلة:

  • الويب النشاط على التطبيقات - عليك أيضًا وضع علامة في مربّع الاختيار تضمين سجلّ Chrome والأنشطة التي تتم على المواقع والتطبيقات والأجهزة التي تستخدم خدمات Google.
  • معلومات الجهاز
  • الصوت النشاط الصوتي

إنشاء مشروع على "المهام مع مساعد Google"

  1. انتقِل إلى "المهام مع مساعد Google".
  2. انقر على مشروع جديد، وأدخِل اسمًا للمشروع، ثم انقر على إنشاء مشروع.

3d6b68ca79afd54c.png

اختيار تطبيق Smart Home

في شاشة "نظرة عامة" ضمن "وحدة التحكّم في الإجراءات"، اختَر الصفحة الرئيسية الذكية.

2fa4988f44f8914b.png

اختَر بطاقة تجربة المنزل المزوّد بأجهزة ذكية، وانقر على بدء البناء، وسيتم توجيهك بعد ذلك إلى وحدة التحكّم في المشروع.

تثبيت واجهة سطر الأوامر في Firebase

ستتيح لك واجهة سطر أوامر Firebase عرض تطبيقات الويب محليًا ونشر تطبيق الويب في استضافة Firebase.

لتثبيت واجهة سطر الأوامر، شغِّل أمر npm التالي من الوحدة الطرفية:

npm install -g firebase-tools

للتحقّق من تثبيت واجهة سطر الأوامر بشكل صحيح، شغِّل:

firebase --version

يمكنك تفويض واجهة سطر الأوامر في Firebase باستخدام حسابك على Google من خلال تنفيذ ما يلي:

firebase login

تفعيل HomeGraph API

تتيح HomeGraph API إمكانية تخزين الأجهزة وحالاتها وإجراء طلبات بحث عنها ضمن "الرسم البياني المنزلي" الخاص بالمستخدم. لاستخدام واجهة برمجة التطبيقات هذه، يجب أولاً فتح وحدة التحكّم في Google Cloud وتفعيل HomeGraph API.

في وحدة تحكُّم Google Cloud، احرص على اختيار المشروع الذي يتطابق مع "الإجراءات" <project-id>.، ثم انقر على تفعيل في شاشة مكتبة واجهة برمجة التطبيقات الخاصة بواجهة برمجة التطبيقات HomeGraph.

ee198858a6eac112.png

3- تشغيل تطبيق إجراء التفعيل

الآن بعد إعداد بيئة التطوير، يمكنك نشر مشروع البداية للتأكّد من ضبط كل شيء بشكل صحيح.

الحصول على رمز المصدر

انقر على الرابط التالي لتنزيل نموذج لهذا الدرس التطبيقي حول الترميز على جهاز التطوير:

...أو يمكنك استنساخ مستودع GitHub من سطر الأوامر:

git clone https://github.com/google-home/smarthome-traits.git

فك ضغط ملف ZIP الذي تم تنزيله.

لمحة عن المشروع

يحتوي مشروع المبتدئين على الأدلة الفرعية التالية:

  • public: واجهة مستخدم أمامية للتحكم في حالة الغسّالة الذكية ومراقبتها بسهولة
  • functions: خدمة سحابة إلكترونية منفَّذة بالكامل لإدارة الغسّالة الذكية من خلال دوال Cloud في Firebase و"قاعدة بيانات Firebase في الوقت الفعلي"

يتضمن التنفيذ المقدَّم من السحابة الإلكترونية الوظائف التالية في index.js:

  • fakeauth: نقطة نهاية التفويض لربط الحسابات
  • faketoken: نقطة نهاية الرمز المميّز لربط الحساب
  • smarthome: نقطة نهاية تلبية الطلب للمنزل المزوّد بأجهزة ذكية
  • reportstate: يستدعي Home Graph API عند تغيير حالة الجهاز.
  • requestsync: لتفعيل تحديثات جهاز المستخدم بدون الحاجة إلى إعادة ربط الحساب

الربط بمنصة Firebase

انتقِل إلى دليل washer-start، ثم أعِدّ واجهة سطر الأوامر في Firebase باستخدام مشروع الإجراءات:

cd washer-start
firebase use <project-id>

إعداد مشروع Firebase

إعداد مشروع Firebase.

firebase init

اختَر ميزات واجهة سطر الأوامر وقاعدة البيانات في الوقت الفعلي والوظائف وميزة الاستضافة التي تتضمن "استضافة 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

سيؤدي ذلك إلى تهيئة واجهات برمجة التطبيقات والميزات اللازمة لمشروعك.

قم بتهيئة قاعدة بيانات الوقت الفعلي عندما يُطلب منك ذلك. يمكنك استخدام الموقع التلقائي لمثيل قاعدة البيانات.

? 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

إذا كنت تعيد إعداد مشروعك، اختَر استبدال عندما يتم سؤالك عمّا إذا كنت تريد إعداد قاعدة رموز أو استبدالها.

? 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 عن طريق الخطأ، هناك طريقتان متاحتان لتعطيله:

  1. باستخدام واجهة المستخدم الرسومية، انتقل إلى مجلد ../functions ضمن المشروع، ثم اختَر الملف المخفي .eslintrc.js واحذفه. ولا تخطئ في تصنيفه بالاسم المشابه .eslintrc.json.
  2. باستخدام سطر الأوامر:
    cd functions
    rm .eslintrc.js
    

النشر إلى Firebase

الآن بعد أن قمت بتثبيت التبعيات وتهيئة مشروعك، فأنت جاهز لتشغيل التطبيق لأول مرة.

firebase deploy

هذا هو ناتج وحدة التحكّم الذي يُفترض أن يظهر لك:

...

✔ Deploy complete!

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

يؤدي هذا الأمر إلى نشر تطبيق ويب إلى جانب العديد من وظائف السحابة الإلكترونية لبرنامج Firebase.

افتح عنوان URL للاستضافة في متصفِّحك (https://<project-id>.web.app) لعرض تطبيق الويب. ستظهر لك الواجهة التالية:

5845443e94705557.png

واجهة مستخدم الويب هذه تمثّل نظامًا أساسيًا تابعًا لجهة خارجية من أجل عرض حالات الأجهزة أو تعديلها. لبدء تعبئة قاعدة البيانات بمعلومات الجهاز، انقر على تعديل. لن تظهر لك أي تغييرات في الصفحة، ولكن سيتم تخزين الحالة الحالية للغسّالة في قاعدة البيانات.

حان الآن وقت ربط الخدمة السحابية التي نشرتها بخدمة "مساعد Google" باستخدام وحدة تحكّم المهام.

إعداد مشروع وحدة تحكّم "المهام"

ضمن نظرة عامة > أنشئ الإجراء الخاص بك، ثم اختَر إضافة إجراءات. أدخِل عنوان URL لوظيفة السحابة الإلكترونية التي توفّر تنفيذ عناصر المنزل المزوّد بأجهزة ذكية ثم انقر على حفظ.

https://us-central1-<project-id>.cloudfunctions.net/smarthome

9d7b223427f587ca.png

في التطوير > علامة تبويب "الاستدعاء"، أضِف الاسم المعروض للإجراء، وانقر على حفظ. سيظهر هذا الاسم في تطبيق Google Home.

774d0c40c351c7da.png

a8c4673eb11d76ee.png

لتفعيل ميزة ربط الحساب، اختَر تطوير > ربط الحساب في شريط التنقل الأيمن. استخدِم إعدادات ربط الحسابات التالية:

معرِّف العميل

ABC123

سر العميل

DEF456

عنوان URL للترخيص

https://us-central1-<project-id>.cloudfunctions.net/fakeauth

عنوان URL للرمز المميز

https://us-central1-<project-id>.cloudfunctions.net/faketoken

9730d20b90bcc038.png

انقر على حفظ لحفظ إعدادات ربط الحساب، ثم انقر على اختبار لتفعيل الاختبار على مشروعك.

ee0547f05b5efd98.png

وستتم إعادة توجيهك إلى المحاكي. إذا لم تظهر لك عبارة "الاختبار الآن مفعَّل"، انقر على إعادة ضبط الاختبار للتأكّد من تفعيل الاختبار.

d0495810dbadf059.png

لاختبار الإجراء الخاص بمنزلك المزوّد بأجهزة ذكية، يجب ربط مشروعك بحساب Google. يتيح ذلك إجراء الاختبارات من خلال مساحات عرض "مساعد Google" وتطبيق Google Home الذي تم تسجيل الدخول إلى الحساب نفسه عليه.

  1. افتح إعدادات مساعد Google على هاتفك. تجدر الإشارة إلى أنّه يجب تسجيل الدخول باستخدام الحساب نفسه المستخدَم في وحدة التحكّم.
  2. انتقِل إلى مساعد Google > الإعدادات > الإدارة الآلية للمنزل (ضِمن "مساعد Google")
  3. انقر على رمز البحث في أعلى يسار الصفحة.
  4. ابحث عن تطبيقك التجريبي باستخدام البادئة [test] للعثور على التطبيق التجريبي المحدّد.
  5. اختَر هذا العنصر. سيصادق "مساعد Google" بعد ذلك مع خدمتك ويرسل طلب SYNC، وسيطلب من الخدمة تقديم قائمة بالأجهزة للمستخدم.

يُرجى فتح تطبيق Google Home والتأكّد من إمكانية رؤية جهاز الغسّالة.

ae252220753726f6.png

مُمْكِنِ التَّحَكُّمْ فِي الْغَسَّالَة بِاسْتِخْدَامْ طَلَبَاتْ صَوْتِيَّة فِي تَطْبِيقْ Google Home. من المفترَض أن تظهر لك أيضًا حالة تغيير في حالة الجهاز في واجهة مستخدم الويب الأمامية الخاصة بالتطبيق على السحابة الإلكترونية.

الآن بعد تفعيل غسّالة أساسية، يمكنك تخصيص الأوضاع المتاحة على جهازك.

4. إضافة وسائل النقل

تتيح السمة action.devices.traits.Modes للجهاز الحصول على عدد عشوائي من الإعدادات لوضع معيّن، علمًا بأنّه يمكن ضبط إعداد واحد فقط في كل مرّة. ستضيف وضعًا إلى الغسّالة لتحديد حجم غسيل الملابس: صغير أو متوسط أو كبير.

تعديل استجابة المزامنة

يجب إضافة معلومات عن السمة الجديدة إلى ردّك على SYNC في functions/index.js. وتظهر هذه البيانات في المصفوفة traits والكائن attributes كما هو موضّح في مقتطف الرمز التالي.

index.js

app.onSync(body => {
  return {
    requestId: 'ff36a3cc-ec34-11e6-b1a0-64510650abcf',
    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',
          // Add Modes trait
          'action.devices.traits.Modes',
        ],
        name: { ... },
        deviceInfo: { ... },
        attributes: {
          pausable: true,
          //Add availableModes
          availableModes: [{
            name: 'load',
            name_values: [{
              name_synonym: ['load'],
              lang: 'en',
            }],
            settings: [{
              setting_name: 'small',
              setting_values: [{
                setting_synonym: ['small'],
                lang: 'en',
              }]
            }, {
              setting_name: 'medium',
              setting_values: [{
                setting_synonym: ['medium'],
                lang: 'en',
              }]
            }, {
              setting_name: 'large',
              setting_values: [{
                setting_synonym: ['large'],
                lang: 'en',
              }]
            }],
            ordered: true,
          }],
        },
      }],
    },
  };
});

إضافة أوامر EXECUTE intent جديدة

في هدف EXECUTE، أضِف الأمر action.devices.commands.SetModes كما هو موضّح في مقتطف الرمز التالي.

index.js

const updateDevice = async (execution,deviceId) => {
  const {params,command} = execution;
  let state, 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;
    // Add SetModes command
    case 'action.devices.commands.SetModes':
      state = {load: params.updateModeSettings.load};
      ref = firebaseRef.child(deviceId).child('Modes');
      break;
}

تعديل ردّ QUERY

بعد ذلك، يُرجى تعديل استجابة QUERY للإبلاغ عن الحالة الحالية للغسّالة.

أضِف التغييرات المعدّلة إلى الدالتين 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,
    // Add Modes snapshot
    load: snapshotVal.Modes.load,
  };
}

const queryDevice = async (deviceId) => {
  const data = await queryFirebase(deviceId);
  return {
    on: data.on,
    isPaused: data.isPaused,
    isRunning: data.isRunning,
    currentRunCycle: [{ ... }],
    currentTotalRemainingTime: 1212,
    currentCycleRemainingTime: 301,
    // Add currentModeSettings
    currentModeSettings: {
      load: data.load,
    },
  };
};

تعديل حالة التقرير

أخيرًا، يمكنك تعديل دالة reportstate للإبلاغ عن إعدادات التحميل الحالية للغسالة إلى الرسم البياني المنزلي.

index.js

const requestBody = {
  requestId: 'ff36a3cc', /* Any unique ID */
  agentUserId: USER_ID,
  payload: {
    devices: {
      states: {
        /* Report the current state of your washer */
        [context.params.deviceId]: {
          on: snapshot.OnOff.on,
          isPaused: snapshot.StartStop.isPaused,
          isRunning: snapshot.StartStop.isRunning,
          // Add currentModeSettings
          currentModeSettings: {
            load: snapshot.Modes.load,
          },
        },
      },
    },
  },
};

النشر إلى Firebase

شغِّل الأمر التالي لنشر الإجراء المُعدَّل:

firebase deploy --only functions

بعد اكتمال النشر، انتقِل إلى واجهة مستخدم الويب وانقر على زر إعادة التحميل ae8d3b25777a5e30.png في شريط الأدوات. يؤدي ذلك إلى تفعيل عملية "مزامنة الطلب" حتى يتلقّى "مساعد Google" بيانات الاستجابة المعدّلة بخصوص "SYNC".

bf4f6a866160a982.png

يمكنك الآن توجيه طلب لضبط وضع الغسّالة، مثلاً:

"Ok Google، أريد ضبط حِمل الغسّالة على حجم كبير"

بالإضافة إلى ذلك، يمكنك طرح أسئلة حول غسّالة الملابس، مثلاً:

"Ok Google، ما هي عبء الغسّالة؟"

5- إضافة مفاتيح التبديل

تمثّل السمة action.devices.traits.Toggles جوانب مذكورة في الجهاز مضبوطة على حالة "صحيح" أو "خطأ"، مثلاً ما إذا كانت الغسالة في وضع "توربو".

تعديل استجابة المزامنة

في ردّ SYNC، عليك إضافة معلومات حول السمة الجديدة للجهاز. وسيظهر في المصفوفة traits والكائن attributes كما هو موضّح في مقتطف الرمز التالي.

index.js

app.onSync(body => {
  return {
    requestId: 'ff36a3cc-ec34-11e6-b1a0-64510650abcf',
    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',
          'action.devices.traits.Modes',
          // Add Toggles trait
          'action.devices.traits.Toggles',
        ],
        name: { ... },
        deviceInfo: { ... },
        attributes: {
          pausable: true,
          availableModes: [{
            name: 'load',
            name_values: [{
              name_synonym: ['load'],
              lang: 'en'
            }],
            settings: [{ ... }],
            ordered: true,
          }],
          //Add availableToggles
          availableToggles: [{
            name: 'Turbo',
            name_values: [{
              name_synonym: ['turbo'],
              lang: 'en',
            }],
          }],
        },
      }],
    },
  };
});

إضافة أوامر تنفيذ intent جديدة

في هدف EXECUTE، أضِف الأمر action.devices.commands.SetToggles كما هو موضّح في مقتطف الرمز التالي.

index.js

const updateDevice = async (execution,deviceId) => {
  const {params,command} = execution;
  let state, 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;
    case 'action.devices.commands.SetModes':
      state = {load: params.updateModeSettings.load};
      ref = firebaseRef.child(deviceId).child('Modes');
      break;
    // Add SetToggles command
    case 'action.devices.commands.SetToggles':
      state = {Turbo: params.updateToggleSettings.Turbo};
      ref = firebaseRef.child(deviceId).child('Toggles');
      break;
  }

تعديل ردّ QUERY

أَخِيرًا، لَازِمْ تَعْدِيلِ اسْتِجَابِةْ QUERY عَلَشَانْ تِعَرَّفْ عَلَى وَضْعِ التَّشْغِيلِ الْلِّي فِي الْغَسَّالَة. أضِف التغييرات المعدَّلة إلى الدالتين 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,
    load: snapshotVal.Modes.load,
    // Add Toggles snapshot
    Turbo: snapshotVal.Toggles.Turbo,
  };
}

const queryDevice = async (deviceId) => {
  const data = queryFirebase(deviceId);
  return {
    on: data.on,
    isPaused: data.isPaused,
    isRunning: data.isRunning,
    currentRunCycle: [{ ... }],
    currentTotalRemainingTime: 1212,
    currentCycleRemainingTime: 301,
    currentModeSettings: {
      load: data.load,
    },
    // Add currentToggleSettings
    currentToggleSettings: {
      Turbo: data.Turbo,
    },
  };
};

تعديل حالة التقرير

أَخِيرًا، عَدِّلْ دالّة reportstate لِإِبْلَاغْ Home Graph عمّا إذا كانت الغسالة مضبوطة على الوضع توربو.

index.js

const requestBody = {
  requestId: 'ff36a3cc', /* Any unique ID */
  agentUserId: USER_ID,
  payload: {
    devices: {
      states: {
        /* Report the current state of your washer */
        [context.params.deviceId]: {
          on: snapshot.OnOff.on,
          isPaused: snapshot.StartStop.isPaused,
          isRunning: snapshot.StartStop.isRunning,
          currentModeSettings: {
            load: snapshot.Modes.load,
          },
          // Add currentToggleSettings
          currentToggleSettings: {
            Turbo: snapshot.Toggles.Turbo,
          },
        },
      },
    },
  },
};

النشر إلى Firebase

شغِّل الأمر التالي لنشر الدوال المُحدَّثة:

firebase deploy --only functions

انقر على الزر إعادة تحميل ae8d3b25777a5e30.png في واجهة المستخدم على الويب لتشغيل طلب مزامنة بعد اكتمال النشر.

مُمْكِنْ دِلْوَقْتِي تَوْجِيهْ طَلَبْ ضَبْطِ الْغَسَّالَة عَلَى وَضْعْ تَرْبُوطْ عَنْ طَرِيقْ قَوْلْ:

"Ok Google، أريد تشغيل التوربو للغسّالة"

يمكنك أيضًا التحقّق ممّا إذا كانت الغسالة في وضع التوربو من خلال طرح السؤال التالي:

"Ok Google، هل الغسّالة في وضع التوربو؟"

6- الإبلاغ عن الأخطاء والاستثناءات

عند معالجة الأخطاء في الإجراء الخاص داخل المنزل المزوّد بأجهزة ذكية، يمكنك إبلاغ المستخدمين بحدوث مشاكل تؤدّي إلى عدم استجابة "EXECUTE" و"QUERY". توفّر الإشعارات تجربة مستخدم أكثر إيجابية عند تفاعلهم مع الجهاز الذكي والإجراء.

عند تعذّر تنفيذ طلب EXECUTE أو QUERY، من المفترض أن يعرض الإجراء الخاص بك رمز خطأ. على سبيل المثال، إذا أردت عرض رسالة خطأ عندما يحاول أحد المستخدمين تشغيل الغسّالة مع فتح غطاء الجهاز، ستظهر استجابة EXECUTE كمقتطف الرمز التالي:

{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "commands": [
      {
        "ids": [
          "456"
        ],
        "status": "ERROR",
        "errorCode": "deviceLidOpen"
      }
    ]
  }
}

والآن، عندما يطلب أحد المستخدمين تشغيل الغسّالة، يستجيب "مساعد Google" بقول:

"غطاء الغسّالة مفتوح. يُرجى إغلاقه وإعادة المحاولة".

تتشابه الاستثناءات مع الأخطاء، ولكنها تشير إلى ارتباط تنبيه بأمر، ما قد يؤدي إلى حظر التنفيذ الناجح أو عدم حظره. يمكن أن يقدّم استثناء معلومات ذات صلة باستخدام سمة StatusReport، مثل مستوى البطارية أو تغيير الحالة مؤخرًا. يتم عرض رموز الاستثناء التي لا تحظر الحظر مع حالة SUCCESS، بينما يتم عرض رموز استثناء الحظر بالحالة EXCEPTIONS.

يوجد مثال لرد مع استثناء في مقتطف الرمز التالي:

{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "commands": [{
      "ids": ["123"],
      "status": "SUCCESS",
      "states": {
        "online": true,
        "isPaused": false,
        "isRunning": false,
        "exceptionCode": "runCycleFinished"
      }
    }]
  }
}

يستجيب "مساعد Google" بقول:

"انتهى تشغيل الغسّالة"

لإضافة ميزة إعداد تقارير الأخطاء في الغسّالة، افتح functions/index.js وأضِف تعريف فئة الخطأ كما هو موضّح في مقتطف الرمز التالي:

index.js

app.onQuery(async (body) => {...});

// Add SmartHome error handling
class SmartHomeError extends Error {
  constructor(errorCode, message) {
    super(message);
    this.name = this.constructor.name;
    this.errorCode = errorCode;
  }
}

تعديل استجابة التنفيذ لعرض رمز الخطأ وحالة الخطأ:

index.js

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( ... )
          //Add error response handling
          .catch((error) => {
            functions.logger.error('EXECUTE', device.id, error);
            result.ids.push(device.id);
            if(error instanceof SmartHomeError) {
              result.status = 'ERROR';
              result.errorCode = error.errorCode;
            }
          })
      );
    }
  }
}

يستطيع "مساعد Google" الآن إخبار المستخدمين بأي رمز خطأ يتم الإبلاغ عنه. سترى مثالاً محددًا في القسم التالي.

7. إضافة عملية إثبات هوية مستخدم ثانوي

يجب تنفيذ عملية التحقّق الثانوي من المستخدم في الإجراء الخاص بك إذا كان جهازك يحتوي على أي أوضاع يجب تأمينها أو يجب أن يقتصر على مجموعة معيّنة من المستخدمين المصرَّح لهم، مثل تحديث البرنامج أو إيقاف ميزة إيقاف التفاعل.

يمكنك تنفيذ التحقُّق الثانوي من المستخدِم على جميع أنواع الأجهزة وسماتها، مع تخصيص ما إذا كان التحقُّق من الأمان سيحدث في كل مرة أو في حال استيفاء معايير محدَّدة.

هناك ثلاثة أنواع معتمَدة من الاختبارات:

  • No challenge: طلب واستجابة لا يستخدمان اختبار مصادقة (هذا هو السلوك التلقائي)
  • ackNeeded — عملية تحقق ثانوية من المستخدم تتطلب إقرارًا صريحًا (نعم أو لا)
  • pinNeeded — عملية تحقق ثانوية من المستخدم تتطلب رقم تعريف شخصي (PIN)

في هذا الدرس التطبيقي حول الترميز، أضِف تحدّي ackNeeded إلى الأمر الخاص بتشغيل الغسّالة والوظيفة لعرض رسالة خطأ في حال تعذّر إكمال عملية التحقّق الثانوية.

افتح functions/index.js وأضِف تعريفًا لفئة الخطأ يعرض رمز الخطأ ونوع الاختبار كما هو موضّح في مقتطف الرمز التالي:

index.js

class SmartHomeError extends Error { ... }

// Add secondary user verification error handling
class ChallengeNeededError extends SmartHomeError {
  /**
   * Create a new ChallengeNeededError
   * @param {string} suvType secondary user verification challenge type
   */
  constructor(suvType) {
    super('challengeNeeded', suvType);
    this.suvType = suvType;
  }
}

عليك أيضًا تعديل استجابة التنفيذ لعرض الخطأ challengeNeeded على النحو التالي:

index.js

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( ... )
          .catch((error) => {
            functions.logger.error('EXECUTE', device.id, error);
            result.ids.push(device.id);
            if(error instanceof SmartHomeError) {
              result.status = 'ERROR';
              result.errorCode = error.errorCode;
              //Add error response handling
              if(error instanceof ChallengeNeededError) {
                result.challengeNeeded = {
                  type: error.suvType
                };
              }
            }
          })
      );
    }
  }
}

وأخيرًا، عدِّل updateDevice لطلب الإقرار الصريح بتشغيل الغسّالة أو إيقافها.

index.js

const updateDevice = async (execution,deviceId) => {
  const {challenge,params,command} = execution; //Add secondary user challenge
  let state, ref;
  switch (command) {
    case 'action.devices.commands.OnOff':
      //Add secondary user verification challenge
      if (!challenge || !challenge.ack) {
        throw new ChallengeNeededError('ackNeeded');
      }
      state = {on: params.on};
      ref = firebaseRef.child(deviceId).child('OnOff');
      break;
    ...
  }

  return ref.update(state)
      .then(() => state);
};

النشر إلى Firebase

شغِّل الأمر التالي لنشر الدالة المُحدَّثة:

firebase deploy --only functions

بعد نشر الرمز المعدّل، يجب الإقرار شفهيًا بالإجراء عندما تطلب من "مساعد Google" تشغيل الغسّالة أو إيقافها، كما يلي:

أنت: "Ok Google، أريد تشغيل الغسّالة"

مساعد Google: "هل تريد فعلاً تشغيل الغسّالة؟"

أنت: "نعم"

يمكنك أيضًا الاطّلاع على رد تفصيلي لكل خطوة من خطوات العملية الثانوية للتحقّق من المستخدم من خلال فتح سجلّات Firebase.

289dbe48f4bb8106.png

8. تهانينا

674c4f4392e98c1.png

تهانينا! لقد وسّعت نطاق ميزات "إجراءات المنزل المزوّد بأجهزة ذكية" من خلال السمتين Modes وToggles، وأمنت تنفيذ هذه الإجراءات من خلال التحقق الثانوي من المستخدم.

مزيد من المعلومات

في ما يلي بعض الأفكار التي يمكنك تنفيذها للتعمق أكثر:

  • يمكنك إضافة إمكانيات التنفيذ المحلي إلى أجهزتك.
  • استخدِم نوعًا مختلفًا من الاختبارات الثانوية للتحقّق من هوية المستخدم لتعديل حالة جهازك.
  • عدِّل استجابة QUERY لسمة RunCycle ليتم تحديثها ديناميكيًا.
  • يمكنك الاطّلاع على نموذج GitHub هذا.