برای پشتیبانی از اجرای محلی، باید برنامهای بسازید که این اهداف خانه هوشمند را مدیریت کند:
IDENTIFY: از شناسایی دستگاههای هوشمند قابلکنترل بهصورت محلی پشتیبانی میکند. گرداننده هدف دادههایی را که دستگاه هوشمندتان درطول کاوش برمیگرداند استخراج میکند و آن را در پاسخ به Google ارسال میکند.-
EXECUTE: از اجرای فرمانها پشتیبانی میکند. -
QUERY: از پرسمان وضعیت دستگاه پشتیبانی میکند. REACHABLE_DEVICES: (اختیاری) از کاوش دستگاههای نهایی با قابلیت کنترل محلی که پشت دستگاه مرکز کنترل (یا پل) قرار دارند پشتیبانی میکند.
این برنامه در دستگاههای Google Home یا Google Nest کاربر اجرا میشود و دستگاه هوشمند شما را به «دستیار» متصل میکند. میتوانید برنامه را بااستفاده از TypeScript (ترجیحاً) یا جاوا اسکریپت ایجاد کنید.
TypeScript توصیه میشود زیرا میتوانید از اتصالها استفاده کنید تا بهطور ایستا مطمئن شوید دادههایی که برنامهتان برمیگرداند با انواع موردانتظار پلاتفرم مطابقت دارد.
برای جزئیات بیشتر درباره میانای برنامهسازی کاربردی، مرجع میانای برنامهسازی کاربردی کیت توسعه نرمافزار Local Home را ببینید.
قطعههای زیر نشان میدهند که چگونه میتوانید برنامه اجرای محلی را مقداردهی اولیه کنید و گردانندههای خود را پیوست کنید.
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"); });
پروژهتان را ایجاد کنید
برای استقرار برنامه اجرای محلی، باید دسته جاوا اسکریپت برای کدتان و همه وابستگیهای آن بسازید.
از راهانداز پروژه برنامه اجرای محلی برای راهاندازی ساختار پروژه مناسب با پیکربندی بستهبندی ترجیحی خود استفاده کنید.
الگوهای پروژه
برای انتخاب پیکربندی بستهبندیکننده، فرمان 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:
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 زیر پشتیبانی میکند:
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 مطابقت داشته باشد، Google ارتباط دستگاه را برقرار میکند.
مثال
قطعههای زیر نشان میدهند که چگونه میتوانید بهترتیب گردانندههای 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 (فقط یکپارچهسازیهای مرکز کنترل)
هدف 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 شما در برنامه، فرمانهای کاربر را پردازش میکند و از
Local Home SDK برای دسترسی به دستگاههای هوشمندتان ازطریق پروتکل موجود استفاده میکند.
پلاتفرم Local Home همان بار ورودی را به تابع گرداننده EXECUTE ارسال میکند که برای هدف EXECUTE به اجرای ابری شما ارسال میشود. بههمین ترتیب، گرداننده EXECUTE شما دادههای خروجی را در همان قالبی برمیگرداند که از پردازش قصد EXECUTE حاصل میشود.
برای ساده کردن ایجاد پاسخ، میتوانید از کلاس
Execute.Response.Builder
که Local Home 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 پشتیبانی میشود.