ใช้แอป Fulfillment ในพื้นที่

หากต้องการรองรับการตอบสนองในร้าน คุณต้องสร้างแอปเพื่อจัดการ Intent ของสมาร์ทโฮมต่อไปนี้

  • IDENTIFY: รองรับการค้นหาอุปกรณ์อัจฉริยะที่ควบคุมจากภายในได้ ตัวแฮนเดิล Intent จะดึงข้อมูลที่อุปกรณ์อัจฉริยะแสดงระหว่างการค้นพบ และส่งข้อมูลนี้เป็นการตอบกลับไปยัง Google
  • EXECUTE: รองรับการดำเนินการตามคําสั่ง
  • QUERY: รองรับการค้นหาสถานะอุปกรณ์
  • REACHABLE_DEVICES: (ไม่บังคับ) รองรับการค้นหาอุปกรณ์ปลายทางที่ควบคุมจากอุปกรณ์ฮับ (หรือบริดจ์) ได้

แอปนี้ทำงานบนอุปกรณ์ Google Home หรือ Google Nest ของผู้ใช้ และเชื่อมต่ออุปกรณ์อัจฉริยะกับ Assistant คุณสร้างแอปได้โดยใช้ TypeScript (แนะนำ) หรือ JavaScript

เราขอแนะนำให้ใช้ TypeScript เนื่องจากคุณสามารถใช้ประโยชน์จากการเชื่อมโยงแบบคงที่เพื่อให้มั่นใจว่าข้อมูลที่แอปแสดงผลตรงกับประเภทที่แพลตฟอร์มคาดหวัง

ดูรายละเอียดเพิ่มเติมเกี่ยวกับ API ได้ที่ข้อมูลอ้างอิง Local Home SDK API

ข้อมูลโค้ดต่อไปนี้แสดงวิธีเริ่มต้นแอปการดำเนินการตามคำสั่งซื้อในพื้นที่และแนบตัวแฮนเดิล

สแตนด์อโลน
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");
  });

สร้างโปรเจ็กต์

หากต้องการทำให้แอปการดำเนินการตามคำสั่งซื้อในพื้นที่ใช้งานได้ คุณต้องสร้าง App Bundle ของ JavaScript สำหรับโค้ดและทรัพยากร Dependency ทั้งหมด

ใช้แอปการดำเนินการตามคำสั่งซื้อในเครื่อง project initializer เพื่อทำการจัดโครงสร้างโปรเจ็กต์ที่เหมาะสมด้วยการกำหนดค่าเครื่องมือจัดกลุ่มที่ต้องการ

เทมเพลตโปรเจ็กต์

หากต้องการเลือกการกำหนดค่าเครื่องมือจัดกลุ่ม ให้เรียกใช้คำสั่ง 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 ต่อไปนี้

แพ็กเกจ
cd project-directory/
npm run build

สคริปต์นี้จะคอมไพล์ซอร์ส TypeScript และรวมแอปของคุณกับ Dependency สำหรับรันไทม์ 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 รีบูตและเห็นอุปกรณ์ภายในที่ไม่ได้รับการยืนยัน (รวมถึงอุปกรณ์ปลายทางที่เชื่อมต่อกับฮับ) แพลตฟอร์มบ้านในพื้นที่จะสแกนหาอุปกรณ์ในพื้นที่โดยใช้ข้อมูลการกําหนดค่าการสแกนที่คุณระบุไว้ก่อนหน้านี้ และเรียกใช้ตัวแฮนเดิล IDENTIFY พร้อมผลลัพธ์การสแกน

ไฟล์ IdentifyRequest จากแพลตฟอร์ม 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;
  };
ฮับ
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 จะส่ง Intent REACHABLE_DEVICES เพื่อยืนยันว่าอุปกรณ์ปลายทางใดบ้างที่ควบคุมจากเครื่องได้ เจตนานี้จะทริกเกอร์ทุกครั้งที่ Google เรียกใช้การสแกนเพื่อค้นหา (ประมาณ 1 ครั้งทุกนาที) ตราบใดที่ระบบตรวจพบว่าฮับออนไลน์อยู่

คุณใช้ตัวแฮนเดิล REACHABLE_DEVICES ในลักษณะเดียวกับตัวแฮนเดิล IDENTIFY ยกเว้นว่าตัวแฮนเดิลของคุณจะต้องรวบรวมรหัสอุปกรณ์เพิ่มเติมที่อุปกรณ์พร็อกซีในเครื่อง (นั่นคือฮับ) เข้าถึงได้ ฟิลด์ device.verificationId มีรหัสอุปกรณ์ภายในสำหรับอุปกรณ์ปลายทางที่เชื่อมต่อกับฮับ

ไฟล์ ReachableDevicesRequest จากแพลตฟอร์ม 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 เพื่อเข้าถึงอุปกรณ์อัจฉริยะผ่านโปรโตคอลที่มีอยู่

แพลตฟอร์ม Home ในพื้นที่จะส่งข้อมูลพรอมต์อินพุตเดียวกันไปยังฟังก์ชัน EXECUTE handler เช่นเดียวกับEXECUTE intent ไปยังการดําเนินการบนระบบคลาวด์ ในทํานองเดียวกัน แฮนเดิล EXECUTE จะแสดงผลลัพธ์ในรูปแบบเดียวกับที่ได้จากการดำเนินการกับ Intent 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 เพื่อรายงานสถานะของอุปกรณ์อัจฉริยะ

แพลตฟอร์ม Home ในพื้นที่จะส่งข้อมูลเพย์โหลดคำขอเดียวกันไปยังฟังก์ชันตัวแฮนเดิล "QUERY" เช่นเดียวกับความตั้งใจสำหรับ QUERY ไปยังการจำหน่ายในระบบคลาวด์ ในทํานองเดียวกัน แฮนเดิล QUERY จะแสดงผลข้อมูลในรูปแบบเดียวกับที่ได้จากการดำเนินการตาม Intent QUERY

การส่งคำสั่งไปยังอุปกรณ์ที่อยู่หลังฮับ

หากต้องการควบคุมอุปกรณ์ปลายทางที่อยู่หลังฮับ คุณอาจต้องระบุข้อมูลเพิ่มเติมในเพย์โหลดคําสั่งเฉพาะโปรโตคอลที่ส่งไปยังฮับเพื่อให้ฮับระบุได้ว่าคําสั่งนั้นมุ่งเป้าไปยังอุปกรณ์ใด ในบางกรณี ระบบอาจอนุมานข้อมูลนี้จากค่า device.id ได้โดยตรง แต่หากไม่ใช่เช่นนั้น คุณควรใส่ข้อมูลเพิ่มเติมนี้เป็นส่วนหนึ่งของฟิลด์ customData

หากคุณสร้างแอปโดยใช้ TypeScript อย่าลืมคอมไพล์แอปเป็น JavaScript คุณใช้ระบบโมดูลที่ต้องการเพื่อเขียนโค้ดได้ ตรวจสอบว่าเบราว์เซอร์ Chrome รองรับเป้าหมายของคุณ