Chào mừng bạn đến với Trung tâm nhà phát triển Google Home, một điểm đến mới để tìm hiểu cách phát triển các hành động nhà thông minh. Lưu ý: Bạn sẽ tiếp tục tạo các hành động trong Bảng điều khiển Actions.

Triển khai ứng dụng thực hiện đơn hàng địa phương

Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.

Để hỗ trợ việc thực hiện đơn hàng tại địa phương, bạn cần tạo một ứng dụng để xử lý các ý định nhà thông minh sau:

  • IDENTIFY: Hỗ trợ việc khám phá các thiết bị thông minh có thể kiểm soát trên thiết bị. Trình xử lý ý định trích xuất dữ liệu mà thiết bị thông minh của bạn trả về trong quá trình khám phá và gửi dữ liệu này để phản hồi Google.
  • EXECUTE: Hỗ trợ việc thực thi các lệnh.
  • QUERY: Hỗ trợ truy vấn trạng thái thiết bị.
  • REACHABLE_DEVICES: (Không bắt buộc) Hỗ trợ việc khám phá các thiết bị cuối có thể kiểm soát cục bộ đằng sau một thiết bị trung tâm (hoặc cầu nối).

Ứng dụng này chạy trên thiết bị Google Home hoặc Google Nest của người dùng và kết nối thiết bị thông minh của bạn với Trợ lý. Bạn có thể tạo ứng dụng bằng cách sử dụng TypeScript (ưu tiên) hoặc JavaScript.

Bạn nên sử dụng TypeScript vì bạn có thể tận dụng các liên kết để tĩnh đảm bảo rằng dữ liệu mà ứng dụng của bạn trả về khớp với dữ liệu mà nền tảng mong đợi.

Để biết thêm thông tin chi tiết về API, hãy xem Tài liệu tham khảo API cục bộ cho SDK cục bộ.

Các đoạn mã sau đây cho biết cách bạn có thể khởi chạy ứng dụng thực hiện cục bộ và đính kèm trình xử lý.

Riêng biệt
import App = smarthome.App;
const localHomeApp: App = new App("1.0.0");
localHomeApp
  .onIdentify(identifyHandler)
  .onExecute(executeHandler)
  .listen()
  .then(() => {
    console.log("Ready");
  });
Trung tâm
import App = smarthome.App;
const localHomeApp: App = new App("1.0.0");
localHomeApp
  .onIdentify(identifyHandler)
  .onReachableDevices(reachableDevicesHandler)
  .onExecute(executeHandler)
  .listen()
  .then(() => {
    console.log("Ready");
  });

Tạo dự án

Để triển khai ứng dụng thực hiện đơn hàng tại địa phương, bạn cần tạo một gói JavaScript cho mã và tất cả phần phụ thuộc của mã.

Sử dụng trình khởi chạy dự án ứng dụng thực hiện cục bộ để khởi động cấu trúc dự án thích hợp bằng cấu hình gói gói ưu tiên.

Mẫu dự án

Để chọn cấu hình gói, hãy chạy lệnh npm init như trong các ví dụ sau:

Không có

TypeScript không có cấu hình gói:

npm init @google/local-home-app project-directory/ --bundler none

Cấu trúc dự án:

project-directory/
├── node_modules/
├── package.json
├── .gitignore
├── index.ts
├── test.ts
├── tsconfig.json
├── tslint.json
└── serve.js

Thay thế project-directory bằng một thư mục mới chứa dự án ứng dụng thực hiện cục bộ.

Webpack

TypeScript với cấu hình trình đóng gói webpack:

npm init @google/local-home-app project-directory/ --bundler webpack

Cấu trúc dự án:

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

Thay thế project-directory bằng một thư mục mới chứa dự án ứng dụng thực hiện cục bộ.

Cuộn lên

TypeScript với cấu hình gói Rollup:

npm init @google/local-home-app project-directory/ --bundler rollup

Cấu trúc dự án:

project-directory/
├── node_modules/
├── package.json
├── .gitignore
├── index.ts
├── test.ts
├── tsconfig.json
├── tslint.json
├── rollup.config.js
└── serve.js

Thay thế project-directory bằng một thư mục mới chứa dự án ứng dụng thực hiện cục bộ.

Lâu đài

TypeScript với cấu hình gói Parcel:

npm init @google/local-home-app project-directory/ --bundler parcel

Cấu trúc dự án:

project-directory/
├── node_modules/
├── package.json
├── .gitignore
├── index.ts
├── test.ts
├── tsconfig.json
├── tslint.json
└── serve.js

Thay thế project-directory bằng một thư mục mới chứa dự án ứng dụng thực hiện cục bộ.

Thực hiện các thao tác phổ biến ở cấp dự án

Dự án đã tạo hỗ trợ các tập lệnh npm sau:

Gói
cd project-directory/
npm run build

Tập lệnh này biên dịch nguồn TypeScript và đóng gói ứng dụng bằng các phần phụ thuộc cho môi trường thời gian chạy Chrome trong thư mục con dist/web và môi trường thời gian chạy Node.js trong thư mục con dist/node.

Xác minh
cd project-directory/
npm run lint
npm run compile
npm test

Tập lệnh này xác minh cú pháp của mã TypeScript, biên dịch mã đó mà không tạo ra kết quả nào trong thư mục con dist/ và chạy các thử nghiệm tự động từ test.ts.

Phân phát
cd project-directory/
npm run start

Trong quá trình phát triển, tập lệnh này phân phát gói ứng dụng của bạn cho các môi trường thời gian chạy Chrome và Node.js cục bộ.

Triển khai trình xử lý IDENTIFY

Trình xử lý IDENTIFY sẽ được kích hoạt khi thiết bị Google Home hoặc Google Nest khởi động lại và thấy các thiết bị cục bộ chưa được xác minh (bao gồm cả các thiết bị cuối đã kết nối với một trung tâm). Nền tảng Home Home sẽ quét các thiết bị cục bộ bằng cách sử dụng thông tin cấu hình quét mà bạn đã chỉ định trước đó và gọi trình xử lý IDENTIFY bằng kết quả quét.

IdentifyRequest từ nền tảng Home có chứa dữ liệu quét của một thực thể LocalIdentifiedDevice. Chỉ có một thực thể device được điền sẵn, dựa trên cấu hình quét tìm thấy thiết bị.

Nếu kết quả quét khớp với thiết bị, trình xử lý IDENTIFY của bạn sẽ trả về một đối tượng IdentifyResponsePayload, bao gồm đối tượng device có siêu dữ liệu nhà thông minh (chẳng hạn như loại, đặc điểm và trạng thái báo cáo).

Google sẽ thiết lập mối liên kết thiết bị nếu verificationId từ phản hồi IDENTIFY khớp với một trong các giá trị otherDeviceIds do phản hồi SYNC trả về.

Ví dụ:

Các đoạn mã sau đây cho biết cách bạn có thể tạo IDENTIFY trình xử lý để tích hợp thiết bị và trung tâm độc lập tương ứng.

Riêng biệt
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;
  };
Trung tâm
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;
  };

Xác định các thiết bị phía sau một trung tâm

Nếu xác định một thiết bị trung tâm, Google sẽ coi trung tâm này là kênh dẫn đến các thiết bị cuối đã kết nối của trung tâm và cố gắng xác minh các thiết bị cuối đó.

Để cho phép Google xác nhận rằng có thiết bị trung tâm, hãy làm theo các hướng dẫn sau cho trình xử lý IDENTIFY của bạn:

  • Nếu phản hồi SYNC của bạn báo cáo mã nhận dạng của các thiết bị cuối trên thiết bị được kết nối với trung tâm, hãy đặt isProxy thành true trong IdentifyResponsePayload.
  • Nếu phản hồi SYNC của bạn không báo cáo thiết bị trung tâm, hãy đặt isLocalOnly thành true trong IdentifyResponsePayload.
  • Trường device.id chứa mã thiết bị cục bộ của chính thiết bị trung tâm.

Triển khai trình xử lý REACHABLE_DEVICES (chỉ tích hợp trung tâm)

Ý định REACHABLE_DEVICES được Google gửi để xác nhận thiết bị cuối có thể được kiểm soát trên thiết bị. Ý định này được kích hoạt mỗi khi Google chạy quá trình quét phát hiện (khoảng một lần mỗi phút), miễn là trung tâm này được phát hiện là trực tuyến.

Bạn triển khai trình xử lý REACHABLE_DEVICES tương tự như trình xử lý IDENTIFY, ngoại trừ trình xử lý cần thu thập thêm mã thiết bị có thể được truy cập qua proxy cục bộ (tức là trung tâm). Trường device.verificationId chứa mã thiết bị cục bộ của một thiết bị cuối được kết nối với trung tâm.

ReachableDevicesRequest từ nền tảng Trang chủ cục bộ chứa một bản sao của LocalIdentifiedDevice. Trong trường hợp này, bạn có thể lấy mã thiết bị proxy cũng như dữ liệu từ kết quả quét.

Trình xử lý REACHABLE_DEVICES của bạn sẽ trả về một đối tượng ReachableDevicesPayload bao gồm đối tượng devices chứa một mảng các giá trị verificationId đại diện cho các thiết bị cuối mà trung tâm điều khiển. Các giá trị verificationId phải khớp với một trong các otherDeviceIds từ phản hồi SYNC.

Đoạn mã sau đây cho biết cách bạn có thể tạo trình xử lý REACHABLE_DEVICES.

Trung tâm
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;
  };

Triển khai trình xử lý executE

Trình xử lý EXECUTE của bạn trong ứng dụng sẽ xử lý lệnh của người dùng và sử dụng SDK Home cục bộ để truy cập các thiết bị thông minh của bạn thông qua một giao thức hiện có.

Nền tảng cục bộ cho nhà riêng chuyển trọng tải đầu vào giống như cho hàm xử lý EXECUTE tương tự như ý định EXECUTE đối với việc thực hiện đám mây của bạn. Tương tự như vậy, trình xử lý EXECUTE của bạn sẽ trả về dữ liệu đầu ra ở cùng định dạng như khi xử lý ý định EXECUTE. Để đơn giản hoá việc tạo phản hồi, bạn có thể sử dụng lớp Execute.Response.Builder mà SDK Home cục bộ cung cấp.

Ứng dụng của bạn không có quyền truy cập trực tiếp vào địa chỉ IP của thiết bị. Thay vào đó, hãy sử dụng giao diện CommandRequest để tạo các lệnh dựa trên một trong những giao thức sau: UDP, TCP hoặc HTTP. Sau đó, hãy gọi hàm deviceManager.send() để gửi các lệnh.

Khi nhắm mục tiêu lệnh đến thiết bị, hãy sử dụng mã thiết bị (và các thông số từ trường customData (nếu có) trong phản hồi SYNC để giao tiếp với thiết bị.

Ví dụ:

Đoạn mã sau đây cho thấy cách bạn có thể tạo trình xử lý EXECUTE.

Độc lập/Trung tâm
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());
  };

Triển khai trình xử lý QUERY

Trình xử lý QUERY trong ứng dụng sẽ xử lý yêu cầu của người dùng và sử dụng SDK Home cục bộ để báo cáo trạng thái của thiết bị thông minh.

Nền tảng Trang chủ cục bộ sẽ chuyển cùng một trọng tải yêu cầu tới hàm xử lý "QUERY" như đối với ý định QUERY thực hiện đám mây. Tương tự, trình xử lý QUERY của bạn sẽ trả về dữ liệu ở định dạng giống như khi xử lý ý định QUERY.

Gửi lệnh tới thiết bị được cài đặt qua trung tâm

Để kiểm soát các thiết bị cuối phía sau một trung tâm, bạn có thể cần cung cấp thêm thông tin trong tải trọng lệnh dành riêng cho giao thức được gửi tới trung tâm để trung tâm xác định thiết bị mà lệnh đó nhắm đến. Trong một số trường hợp, giá trị này có thể được suy ra trực tiếp từ giá trị device.id. Tuy nhiên, trong trường hợp này, bạn không nên thêm dữ liệu bổ sung này vào trường customData.

Nếu bạn đã tạo ứng dụng bằng TypeScript, hãy nhớ biên dịch ứng dụng sang JavaScript. Bạn có thể sử dụng hệ thống mô-đun mà bạn chọn để viết mã của mình. Đảm bảo trình duyệt Chrome hỗ trợ mục tiêu của bạn.