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

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

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

مجموعة حسابات

‫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 (عمليات دمج الموزّع فقط)

يتم إرسال الغرض REACHABLE_DEVICES من Google لتأكيد الأجهزة الطرفية التي يمكن التحكّم بها محليًا. يتم تفعيل هذا الغرض في كل مرة يجري فيها محرّك بحث 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 في التطبيق طلبات المستخدمين ويستخدم حزمة تطوير البرامج (SDK) الخاصة بميزة "المنزل الذكي" للوصول إلى أجهزتك الذكية من خلال بروتوكول حالي.

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

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