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

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

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

استخدِم مهيئ المشروع لتطبيق التنفيذ على الجهاز لتشغيل بنية المشروع المناسبة باستخدام الإعداد المفضّل لديك في أداة التجميع.

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

لاختيار إعدادات أداة التجميع، شغِّل الأمر 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 من منصة الصفحة الرئيسية المحلية بيانات عملية المسح الضوئي لمثيل 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_جرب (عمليات دمج المركز فقط)

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

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

يتضمّن ReachableDevicesRequest من منصّة الصفحة الرئيسية المحلية مثالاً على 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 للوصول إلى أجهزتك الذكية من خلال بروتوكول حالي.

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

لا يمكن لتطبيقك الوصول مباشرةً إلى عنوان 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 للإبلاغ عن حالة أجهزتك الذكية.

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

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

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

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