تنفيذ تطبيق توصيل الطلبات المحلي

لتفعيل ميزة "الاستلام داخل المتجر"، عليك إنشاء تطبيق للتعامل مع طلبات المنزل الذكي التالية:

  • IDENTIFY: يتيح هذا الخيار العثور على الأجهزة الذكية التي يمكن التحكّم فيها محليًا. يستخرج معالج الإحالات المقصودة البيانات التي يعرضها جهازك الذكي أثناء عملية الاكتشاف ويرسلها في ردّ إلى Google.
  • EXECUTE: يتيح تنفيذ الطلبات.
  • QUERY: يتيح طلب معلومات عن حالة الجهاز.
  • REACHABLE_DEVICES: (اختياري) يتيح اكتشاف devices العميلة التي يمكن التحكّم فيها محليًا والتي تعمل من خلال جهاز مركزي (أو جسر).

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

ننصح باستخدام TypeScript لأنّه يمكنك الاستفادة من عمليات الربط للتأكّد بشكل ثابت من أنّ البيانات التي يعرضها تطبيقك تتطابق مع الأنواع التي تتوقّعها المنصة.

لمزيد من التفاصيل عن واجهة برمجة التطبيقات، يُرجى الاطّلاع على مرجع واجهة برمجة التطبيقات لـ Local Home SDK.

توضِّح المقتطفات التالية كيفية بدء تطبيق التسليم داخل المتجر وإرفاق معالِجاتك.

مستقلة
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 لرمزك البرمجي وجميع العناصر التي يعتمد عليها.

استخدِم تطبيق التنفيذ المحلي project initializer لبدء بنية المشروع المناسبة باستخدام إعدادات أداة تجميع التطبيقات المفضّلة لديك.

نماذج المشاريع

لاختيار إعدادات أداة تجميع الحِزم، شغِّل الأمر npm init كما هو موضّح في المثالين التاليين:

لا ينطبق

TypeScript بدون إعداد أداة تجميع الحِزم:

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 بدليل جديد سيحتوي على مشروع تطبيق التسليم المحلي.

Webpack

‫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 scripts التالية:

الحزمة
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 على الجهاز.

تنفيذ معالِج IDENTIFY

سيتم تنشيط معالِج IDENTIFY عند إعادة تشغيل جهاز Google Home أو Google Nest و رصد الأجهزة المحلية غير التي تم التحقّق منها (بما في ذلك الأجهزة الطرفية المتصلة بمركز). ستبدأ منصّة Local Home في البحث عن الأجهزة المحلية باستخدام معلومات إعدادات الفحص التي حدّدتها سابقًا وستستدعي معالِج IDENTIFY مع نتائج الفحص.

يحتوي ملف IdentifyRequest من منصة Local Home على بيانات المسح الضوئي لمثيل LocalIdentifiedDevice. يتمّ تعبئة مثيل واحد فقط من device استنادًا إلى إعدادات الفحص التي اكتشفت الجهاز.

إذا كانت نتائج الفحص تتطابق مع جهازك، من المفترض أن يعرض معالِج IDENTIFY كائنًا IdentifyResponsePayload يتضمّن كائنًا device يحتوي على بيانات وصفية للمنزل الذكي (مثل الأنواع والسمات وحالة التقرير).

تُنشئ Google عملية ربط جهاز إذا كانت قيمةverificationId من استجابةIDENTIFY تتطابق مع إحدى قيمotherDeviceIds التي تعرضها استجابةSYNC.

مثال

توضِّح المقتطفات التالية كيفية إنشاء معالِجات 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 التالية:

  • إذا كان ردّ SYNC يُبلغ عن أرقام تعريف الأجهزة الطرفية المحلية المتصلة بالبوابة، اضبط isProxy على true في IdentifyResponsePayload.
  • إذا لم يُبلغ ردّ SYNC عن جهاز المحور، اضبط isLocalOnly على true في IdentifyResponsePayload.
  • يحتوي الحقل device.id على رقم تعريف الجهاز المحلي لجهاز المحور نفسه.

تنفيذ معالِج REACHABLE_DEVICES (عمليات دمج الموزّعين فقط)

تُرسِل Google طلب REACHABLE_DEVICES لتأكيد الأجهزة الطرفية التي يمكن التحكّم فيها محليًا. يتم بدء هذا الإجراء في كل مرة تُجري فيها Google عملية فحص للاكتشاف (مرة واحدة تقريبًا كل دقيقة)، ما دام قد تم رصد أنّ المحور متصل بالإنترنت.

يمكنك تنفيذ معالِج REACHABLE_DEVICES بالطريقة نفسها التي تنفِّذ بها معالِج IDENTIFY ، باستثناء أنّ معالِجك يحتاج إلى جمع أرقام تعريف أجهزة إضافية يمكن للوكيل المحلي (أي المحور) الوصول إليها. يحتوي الحقل device.verificationId على رقم تعريف الجهاز المحلي لجهاز نهائي متصل بالمركز.

يحتوي ملف ReachableDevicesRequest من منصة Local Home على مثيل LocalIdentifiedDevice. من خلال هذه الحالة، يمكنك الحصول على معرّف الجهاز الوكيل بالإضافة إلى بيانات من نتائج الفحص.

يجب أن يعرِض معالِج REACHABLE_DEVICES عنصرًا ReachableDevicesPayload يتضمّن عنصرًا devices يحتوي على صفيف من قيم verificationId تمثّل الأجهزة الطرفية التي يتحكّم فيها المحور. يجب أن تتطابق قيم verificationId مع إحدى قيم otherDeviceIds من الردّ SYNC.

يوضّح المقتطف التالي كيفية إنشاء REACHABLE_DEVICES الاسم المعرِّف.

المركز
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;
  };

تنفيذ معالِج EXECUTE

يعالج معالِج EXECUTE في التطبيق أوامر المستخدمين ويستخدم حزمة Local Home SDK للوصول إلى أجهزتك الذكية من خلال بروتوكول حالي.

تُرسِل منصة Local Home حمولة الإدخال نفسها إلى EXECUTE دالة معالِج EXECUTE بهدف تلبية الطلب في السحابة الإلكترونية. وبالمثل، يعرض معالِج EXECUTE بيانات الإخراج بالتنسيق نفسه المستخدَم في معالجة طلب EXECUTE. لتبسيط عملية إنشاء الردّ، يمكنك استخدام فئة Execute.Response.Builder التي يوفّرها حزمة تطوير البرامج (SDK) لتطبيق Local Home.

لا يمكن لتطبيقك الوصول مباشرةً إلى عنوان IP للجهاز. بدلاً من ذلك، استخدِم واجهة CommandRequest لإنشاء أوامر استنادًا إلى أحد البروتوكولات التالية: UDP أو TCP أو HTTP. بعد ذلك، استخدِم الدالة deviceManager.send() لإرسال الأوامر.

عند توجيه الأوامر إلى الأجهزة، استخدِم معرّف الجهاز (والمَعلمات من الحقل customData، إذا كان مضمّنًا) من استجابة SYNC للتواصل مع الجهاز.

مثال

يوضّح المقتطف التالي من الرمز البرمجي كيفية إنشاء معالِج 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());
  };

تنفيذ معالِج QUERY

يعالج معالِج QUERY في التطبيق طلبات المستخدمين ويستخدم حزمة Local Home SDK للإبلاغ عن حالة أجهزتك الذكية.

تُرسِل منصة Local Home حمولة الطلب نفسها إلى دالّة معالِج QUERY، كما هو الحال بالنسبة إلى QUERY قصد تلبية الطلب في السحابة الإلكترونية. وبالمثل، يعرض معالِج QUERY البيانات بالتنسيق نفسه المستخدَم في معالجة طلب QUERY.

إرسال الأوامر إلى الأجهزة التي تتصل بمركز

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

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