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

1- قبل البدء

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

dc8dce0dea87cd5c.png

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

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

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

ما ستقوم بإنشائه

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

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

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

المتطلبات

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

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

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

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

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

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

إنشاء مشروع "المهام"

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

3d6b68ca79afd54c.png

يُرجى النقر على تطبيق المنزل المزوّد بأجهزة ذكية.

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

2fa4988f44f8914b.png

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

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

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

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

npm install -g firebase-tools

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

firebase --version

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

firebase login

تفعيل HomeGraph API

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

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

ee198858a6eac112.png

3- تشغيل تطبيق المبتدئين

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

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

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

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

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

اختر ميزات 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

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

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

? 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

يؤدي هذا الأمر إلى نشر تطبيق ويب، مع العديد من دوال Cloud لمنصة 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، أضِف الأمر 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" لضبط إعدادات الحمولة الحالية في الغسّالة على قاعدة البيانات 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,
          // 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',
            }],
          }],
        },
      }],
    },
  };
});

إضافة أوامر "تنفيذ الغرض" الجديدة

في غرض 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، هل الغسّالة في وضع Turbo؟"

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، أريد تشغيل الغسّالة".

المساعد: "هل المطلوب فعلاً تشغيل الغسّالة؟"

أنت: "نعم"

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

289dbe48f4bb8106.png

8. تهانينا

674c4f4392e98c1.png

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

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

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

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