برنامه تحقق محلی را پیاده سازی کنید

برای پشتیبانی از تکمیل سفارشات محلی، باید اپلیکیشنی بسازید که این اهداف خانه هوشمند را مدیریت کند:

  • IDENTIFY : از کشف دستگاه‌های هوشمند قابل کنترل محلی پشتیبانی می‌کند. کنترل‌کننده‌ی اینتنت، داده‌هایی را که دستگاه هوشمند شما هنگام کشف برمی‌گرداند، استخراج کرده و در پاسخ به گوگل ارسال می‌کند.
  • EXECUTE : از اجرای دستورات پشتیبانی می‌کند.
  • QUERY : از پرس‌وجو در مورد وضعیت دستگاه پشتیبانی می‌کند.
  • REACHABLE_DEVICES : (اختیاری) از کشف دستگاه‌های انتهایی قابل کنترل محلی در پشت یک دستگاه هاب (یا پل) پشتیبانی می‌کند.

این برنامه روی دستگاه‌های Google Home یا Google Nest کاربر اجرا می‌شود و دستگاه هوشمند شما را به دستیار متصل می‌کند. می‌توانید این برنامه را با استفاده از TypeScript (ترجیحاً) یا JavaScript ایجاد کنید.

تایپ‌اسکریپت توصیه می‌شود زیرا می‌توانید از اتصال‌ها برای اطمینان از اینکه داده‌هایی که برنامه شما برمی‌گرداند با انواعی که پلتفرم انتظار دارد مطابقت دارند، به صورت ایستا استفاده کنید.

برای جزئیات بیشتر در مورد API، به مرجع API 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");
  });

پروژه خود را ایجاد کنید

برای استقرار برنامه‌ی محلی خود، باید یک بسته‌ی جاوا اسکریپت برای کد خود و تمام وابستگی‌های آن بسازید.

از مقداردهنده اولیه پروژه برنامه تکمیل سفارش محلی برای راه‌اندازی ساختار پروژه مناسب با پیکربندی بسته‌بندی دلخواه خود استفاده کنید.

قالب‌های پروژه

برای انتخاب پیکربندی bundler خود، دستور npm init را همانطور که در مثال‌های زیر نشان داده شده است، اجرا کنید:

هیچکدام

تایپ‌اسکریپت بدون پیکربندی باندلر:

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 با یک دایرکتوری جدید که شامل پروژه اپلیکیشن تکمیل سفارشات محلی خواهد بود، جایگزین کنید.

وب‌پک

پیکربندی بسته‌بندی تایپ‌اسکریپت با وب‌پک :

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 :

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

زمانی که دستگاه Google Home یا Google Nest ریبوت شود و دستگاه‌های محلی تأیید نشده (از جمله دستگاه‌های انتهایی متصل به هاب) را ببیند، کنترل‌کننده IDENTIFY فعال می‌شود. پلتفرم Local Home با استفاده از اطلاعات پیکربندی اسکن که قبلاً مشخص کرده‌اید، دستگاه‌های محلی را اسکن می‌کند و کنترل‌کننده IDENTIFY شما را با نتایج اسکن فراخوانی می‌کند.

درخواست IdentifyRequest از پلتفرم Local Home شامل داده‌های اسکن یک نمونه LocalIdentifiedDevice است. بر اساس پیکربندی اسکنی که دستگاه را کشف کرده است، فقط یک نمونه device جمع‌آوری می‌شود.

اگر نتایج اسکن با دستگاه شما مطابقت داشته باشد، کنترل‌کننده IDENTIFY شما باید یک شیء IdentifyResponsePayload را برگرداند که شامل یک شیء device با فراداده‌های خانه هوشمند (مانند انواع، ویژگی‌ها و وضعیت گزارش) است.

اگر 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;
  };

شناسایی دستگاه‌های پشت هاب

اگر گوگل یک دستگاه هاب را شناسایی کند، هاب را به عنوان مجرایی برای دستگاه‌های انتهایی متصل به هاب در نظر می‌گیرد و سعی می‌کند آن دستگاه‌های انتهایی را تأیید کند.

برای اینکه گوگل بتواند وجود یک دستگاه هاب را تأیید کند، این دستورالعمل‌ها را برای کنترل‌کننده‌ی IDENTIFY خود دنبال کنید:

  • اگر پاسخ SYNC شما شناسه دستگاه‌های انتهایی محلی متصل به هاب را گزارش می‌دهد، isProxy در IdentifyResponsePayload روی true تنظیم کنید.
  • اگر پاسخ SYNC شما دستگاه هاب شما را گزارش نمی‌کند، isLocalOnly را در IdentifyResponsePayload روی true تنظیم کنید.
  • فیلد device.id شامل شناسه دستگاه محلی برای خود دستگاه هاب است.

پیاده‌سازی هندلر REACHABLE_DEVICES (فقط ادغام‌های هاب)

هدف REACHABLE_DEVICES توسط گوگل ارسال می‌شود تا تأیید کند کدام دستگاه‌های انتهایی می‌توانند به صورت محلی کنترل شوند. این هدف هر بار که گوگل اسکن اکتشافی را اجرا می‌کند (تقریباً هر دقیقه یک بار)، تا زمانی که هاب آنلاین تشخیص داده شود، فعال می‌شود.

شما هندلر 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 محلی Home برای دسترسی به دستگاه‌های هوشمند شما از طریق یک پروتکل موجود استفاده می‌کند.

پلتفرم Local Home همان بار ورودی را به تابع کنترل‌کننده EXECUTE ارسال می‌کند که برای EXECUTE intent به تکمیل‌کننده ابری شما ارسال می‌شود. به همین ترتیب، کنترل‌کننده EXECUTE شما داده‌های خروجی را با همان فرمتی که از پردازش EXECUTE intent دریافت می‌کند، برمی‌گرداند. برای ساده‌سازی ایجاد پاسخ، می‌توانید از کلاس 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 محلی Home برای گزارش وضعیت دستگاه‌های هوشمند شما استفاده می‌کند.

پلتفرم Local Home همان بار درخواستی را که برای QUERY intent به Cloud شما ارسال می‌کند، به تابع کنترل‌کننده‌ی 'QUERY' ارسال می‌کند. به طور مشابه، کنترل‌کننده‌ی QUERY شما داده‌ها را با همان فرمتی که از پردازش QUERY intent دریافت می‌کند، برمی‌گرداند.

ارسال دستورات به دستگاه‌های پشت هاب

برای کنترل دستگاه‌های انتهایی پشت یک هاب، ممکن است لازم باشد اطلاعات اضافی را در بار داده‌ی دستور مخصوص پروتکل ارسال شده به هاب ارائه دهید تا هاب بتواند تشخیص دهد که دستور برای کدام دستگاه است. در برخی موارد، این را می‌توان مستقیماً از مقدار device.id استنباط کرد، اما وقتی اینطور نیست، باید این داده‌های اضافی را به عنوان بخشی از فیلد customData وارد کنید.

اگر برنامه خود را با استفاده از TypeScript ایجاد کرده‌اید، به یاد داشته باشید که برنامه خود را به جاوا اسکریپت کامپایل کنید. می‌توانید از سیستم ماژول مورد نظر خود برای نوشتن کد خود استفاده کنید. مطمئن شوید که مرورگر کروم از برنامه شما پشتیبانی می‌کند.