Внедрите приложение для местного выполнения заказов

Для поддержки локального выполнения заказов вам необходимо разработать приложение, способное обрабатывать следующие запросы к системе умного дома:

  • IDENTIFY : Поддерживает обнаружение локально управляемых интеллектуальных устройств. Обработчик намерений извлекает данные, которые ваше интеллектуальное устройство возвращает во время обнаружения, и отправляет их в ответе в Google.
  • EXECUTE : Поддерживает выполнение команд.
  • QUERY : Поддерживает запрос состояния устройства.
  • REACHABLE_DEVICES : (Необязательно) Поддерживает обнаружение локально управляемых конечных устройств, находящихся за концентратором (или мостом).

Это приложение работает на устройствах Google Home или Google Nest и подключает ваше умное устройство к Google Ассистенту. Вы можете создать приложение, используя TypeScript (предпочтительно) или JavaScript.

Рекомендуется использовать TypeScript, поскольку он позволяет использовать привязки для статической проверки соответствия возвращаемых приложением данных типам, ожидаемым платформой.

Для получения более подробной информации об API см. справочник по API 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-пакет для вашего кода и всех его зависимостей.

Используйте локальный инициализатор проекта приложения для выполнения заказов, чтобы создать соответствующую структуру проекта с предпочитаемой конфигурацией сборщика.

Шаблоны проектов

Чтобы выбрать конфигурацию сборщика, выполните команду 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 и объединяет ваше приложение с зависимостями для среды выполнения 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.

Реализуйте обработчик IDENTIFIY.

Обработчик 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 (только для интеграций с хабом).

Интент 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 , предоставляемый SDK Local Home.

Ваше приложение не имеет прямого доступа к 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 обрабатывает запросы пользователей и использует SDK Local Home для сообщения о состоянии ваших умных устройств.

Платформа Local Home передает функции обработчика запроса 'QUERY' те же данные запроса, что и для намерения QUERY в вашу облачную службу обработки запросов. Аналогично, ваш обработчик QUERY возвращает данные в том же формате, что и при обработке намерения QUERY .

Отправка команд устройствам, находящимся за хабом.

Для управления конечными устройствами, находящимися за концентратором, может потребоваться дополнительная информация в полезной нагрузке команды, отправляемой концентратору, чтобы он мог определить, какому устройству адресована команда. В некоторых случаях это можно определить непосредственно из значения device.id , но если это невозможно, следует включить эти дополнительные данные в поле customData .

Если вы создали приложение с использованием TypeScript, не забудьте скомпилировать его в JavaScript. Для написания кода вы можете использовать любую модульную систему по своему выбору. Убедитесь, что ваша целевая платформа поддерживается браузером Chrome.