ใช้แอป 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");
  });
ฮับ
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

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

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

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

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