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

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

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

Это приложение работает на устройствах Google Home или Google Nest пользователя и подключает ваше интеллектуальное устройство к Ассистенту. Вы можете создать приложение, используя 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 для вашего кода и всех его зависимостей.

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

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

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

Веб-пакет

TypeScript с конфигурацией упаковщика веб-пакетов :

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 с конфигурацией упаковщика посылок :

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 локально.

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

Намерение 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 , предоставляемый 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 для сообщения о состоянии ваших интеллектуальных устройств.

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

Отправка команд на устройства за хабом

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

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