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

لإتاحة التنفيذ المحلي، عليك إنشاء تطبيق لمعالجة طلبات المنزل المزوّد بأجهزة ذكية التالية:

  • ‫IDENTIFY: تتيح هذه الميزة اكتشاف الأجهزة الذكية التي يمكن التحكّم فيها محليًا. يستخرج معالج الطلب البيانات التي يعرضها جهازك الذكي أثناء عملية الاكتشاف ويرسلها في ردّ إلى Google.
  • ‫EXECUTE: يتيح تنفيذ الطلبات.
  • ‫QUERY: يتيح الاستعلام عن حالة الجهاز.
  • ‫REACHABLE_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");
  });
Hub
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 بدليل جديد يحتوي على مشروع تطبيق التنفيذ المحلي.

Rollup

إعدادات حزمة 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 بدليل جديد يحتوي على مشروع تطبيق التنفيذ المحلي.

Parcel

إعدادات 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 البرمجية التالية:

الحزمة
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;
  };
Hub
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.

Hub
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 في التطبيق أوامر المستخدم ويستخدم حزمة تطوير البرامج (SDK) الخاصة بـ Local Home للوصول إلى أجهزتك الذكية من خلال بروتوكول حالي.

تمرّر منصة 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 في التطبيق طلبات المستخدمين ويستخدم حزمة SDK الخاصة بـ Local Home للإبلاغ عن حالة أجهزتك الذكية.

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

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

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

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