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

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

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

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

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

ดูรายละเอียดเพิ่มเติมเกี่ยวกับ API ได้ที่เอกสารอ้างอิง API ของ 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 Bundle สําหรับโค้ดและทรัพยากร Dependency ทั้งหมด

ใช้โปรเจ็กต์ เริ่มต้นของแอปการปฏิบัติตามคำสั่งในร้าน เพื่อเริ่มต้นโครงสร้างโปรเจ็กต์ที่เหมาะสมด้วยการกำหนดค่า Bundler ที่ต้องการ

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

หากต้องการเลือกการกำหนดค่า Bundler ให้เรียกใช้คำสั่ง npm init ตามที่แสดงในตัวอย่างต่อไปนี้

ไม่มี

TypeScript ที่ไม่มีการกำหนดค่า Bundler

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 และรวมแอปกับทรัพยากร Dependency สำหรับสภาพแวดล้อมรันไทม์ของ Chrome ในไดเรกทอรีย่อย dist/web และสภาพแวดล้อมรันไทม์ของ Node.js ในไดเรกทอรีย่อย dist/node

ยืนยัน
cd project-directory/
npm run lint
npm run compile
npm test

สคริปต์นี้จะตรวจสอบไวยากรณ์ของโค้ด TypeScript คอมไพล์โค้ดโดยไม่สร้างเอาต์พุตใดๆ ในไดเรกทอรีย่อย dist/ และเรียกใช้การทดสอบอัตโนมัติจาก test.ts

Serve
cd project-directory/
npm run start

ในระหว่างการพัฒนา สคริปต์นี้จะให้บริการ App Bundle สำหรับสภาพแวดล้อมรันไทม์ของ Chrome และ Node.js ในเครื่อง

ใช้ตัวแฮนเดิล IDENTIFY

ระบบจะทริกเกอร์แฮนเดิล IDENTIFY เมื่ออุปกรณ์ Google Home หรือ Google Nest รีบูตและ เห็นอุปกรณ์ในพื้นที่ที่ยังไม่ได้รับการยืนยัน (รวมถึงอุปกรณ์ปลายทางที่เชื่อมต่อกับฮับ) แพลตฟอร์มบ้านอัจฉริยะจะสแกนหาอุปกรณ์ในบ้านโดยใช้ข้อมูลการกำหนดค่าการสแกน ที่คุณระบุไว้ก่อนหน้านี้ และเรียกใช้แฮนเดิล 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 response ไม่รายงานอุปกรณ์ฮับ ให้ตั้งค่า isLocalOnlyเป็น true ใน IdentifyResponsePayload
  • ฟิลด์ device.id มีรหัสอุปกรณ์ภายในสำหรับอุปกรณ์ฮับเอง

ใช้ตัวแฮนเดิล REACHABLE_DEVICES (การผสานรวมฮับเท่านั้น)

Google จะส่งREACHABLE_DEVICESเจตนาเพื่อยืนยันว่าอุปกรณ์ปลายทางใด ที่ควบคุมได้ในเครื่อง ระบบจะทริกเกอร์ความตั้งใจนี้ทุกครั้งที่ Google เรียกใช้ การสแกนการค้นพบ (ประมาณทุกๆ 1 นาที) ตราบใดที่ตรวจพบว่าฮับ ออนไลน์อยู่

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

ReachableDevicesRequest จากแพลตฟอร์มบ้านอัจฉริยะในพื้นที่ประกอบด้วยอินสแตนซ์ของ 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 เพื่อรายงานสถานะของอุปกรณ์อัจฉริยะ

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

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

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

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