Подключите устройства умного дома к Google Ассистенту

1. Прежде чем начать

Как разработчик Интернета вещей (IoT), вы можете создавать интеграцию между облаками , которая дает вашим пользователям возможность управлять своими устройствами с помощью сенсорного управления в приложении Google Home и голосовых команд с помощью Ассистента.

79266e5f45e6ae20.gif

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

d009cef0f903d284.jpeg

Предварительные условия

Что ты построишь

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

Что вы узнаете

  • Как развернуть облачный сервис умного дома
  • Как подключить свой сервис к Ассистенту
  • Как опубликовать изменения состояния устройства в Google

Что вам понадобится

2. Начало работы

Включить контроль активности

Чтобы использовать Google Assistant, вы должны предоставить Google определенные данные о деятельности. Google Assistant нужны эти данные для правильной работы; однако требование обмена данными не является специфичным для SDK. Чтобы поделиться этими данными, создайте учетную запись Google , если у вас ее еще нет. Вы можете использовать любую учетную запись Google — это не обязательно должна быть ваша учетная запись разработчика.

Откройте страницу «Контроль активности» для учетной записи Google, которую вы хотите использовать с Ассистентом.

Убедитесь, что следующие тумблеры включены:

  • Активность в Интернете и приложениях . Кроме того, обязательно установите флажок Включать историю и активность Chrome на сайтах, приложениях и устройствах, использующих службы Google .
  • Информация об устройстве
  • Голосовая и аудио активность

Создайте проект интеграции облака в облако.

  1. Перейдите в консоль разработчика .
  2. Нажмите «Создать проект» , введите имя проекта и нажмите «Создать проект» .

Название проекта

Выберите интеграцию «облако-облако».

На главной странице проекта в консоли разработчика выберите Добавить интеграцию «облако-облако» в разделе «Облако-облако» .

Добавьте интеграцию между облаками

Установите интерфейс командной строки Firebase

Интерфейс командной строки Firebase (CLI) позволит вам обслуживать ваши веб-приложения локально и развертывать их на хостинге Firebase.

Чтобы установить CLI, выполните следующую команду npm из терминала:

npm install -g firebase-tools

Чтобы убедиться, что CLI установлен правильно, запустите:

firebase --version

Авторизуйте Firebase CLI с помощью своей учетной записи Google, выполнив:

firebase login

3. Запустите стартовое приложение.

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

Получить исходный код

Щелкните следующую ссылку, чтобы загрузить образец этой лаборатории кода на свою машину разработки:

Вы также можете клонировать репозиторий GitHub из командной строки:

git clone https://github.com/google-home/smarthome-washer.git

О проекте

Стартовый проект содержит следующие подкаталоги:

  • public: интерфейсный интерфейс, позволяющий легко управлять и отслеживать состояние умной стиральной машины.
  • functions: полностью реализованный облачный сервис, который управляет интеллектуальной стиральной машиной с помощью облачных функций для Firebase и базы данных Firebase Realtime.

Создать проект Firebase

  1. Перейдите в Firebase .
  2. Нажмите «Создать проект» и введите имя проекта.
  3. Установите флажок «Соглашение» и нажмите «Продолжить» . Если флажок «Соглашение» отсутствует, вы можете пропустить этот шаг.
    Создать проект Firebase
  4. После создания проекта Firebase найдите идентификатор проекта. Перейдите в раздел «Обзор проекта» и щелкните значок настроек > «Настройки проекта» .
    Открыть настройки проекта
  5. Ваш проект указан на вкладке «Общие» .
    Общие настройки проекта

Подключиться к Firebase

Перейдите в каталог washer-start , затем настройте Firebase CLI для вашего проекта интеграции:

cd washer-start
firebase use <firebase-project-id>

Настроить проект Firebase

Инициализируйте проект Firebase.

firebase init

Выберите функции CLI, базу данных реального времени , функции и функцию хостинга , включающую хостинг Firebase.

? Which Firebase CLI features do you want to set up for this directory? Press Space to select features, then
 Enter to confirm your choices.
❯◉ Realtime Database: Configure a security rules file for Realtime Database and (optionally) provision default instance
 ◯ Firestore: Configure security rules and indexes files for Firestore
 ◉ Functions: Configure a Cloud Functions directory and its files
 ◉ Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys
 ◯ Hosting: Set up GitHub Action deploys
 ◯ Storage: Configure a security rules file for Cloud Storage
 ◯ Emulators: Set up local emulators for Firebase products
 ◯ Remote Config: Configure a template file for Remote Config
 ◯ Extensions: Set up an empty Extensions manifest

Это инициализирует необходимые API и функции для вашего проекта.

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

? It seems like you haven't initialized Realtime Database in your project yet. Do you want to set it up?
Yes

? Please choose the location for your default Realtime Database instance:
us-central1

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

? File database.rules.json already exists. Do you want to overwrite it with the Realtime Database Security Rules for <project-ID>-default-rtdb from the Firebase Console?
No

Если вы повторно инициализируете свой проект, выберите «Перезаписать», когда вас спросят, хотите ли вы инициализировать или перезаписать базу кода.

? Would you like to initialize a new codebase, or overwrite an existing one?
Overwrite

При настройке функций вам следует использовать файлы по умолчанию и убедиться, что вы не перезаписываете существующие файлы index.js и package.json в образце проекта.

? What language would you like to use to write Cloud Functions?
JavaScript

? Do you want to use ESLint to catch probable bugs and enforce style?
No

? File functions/package.json already exists. Overwrite?
No

? File functions/index.js already exists. Overwrite?
No

Если вы повторно инициализируете свой проект, выберите «Нет» , когда вас спросят, хотите ли вы инициализировать или перезаписать function/.gitignore.

? File functions/.gitignore already exists. Overwrite?
No
? Do you want to install dependencies with npm now?
Yes

Наконец, настройте хостинг на использование public каталога в коде проекта и используйте существующий файл index.html . Выберите Нет, когда вас попросят использовать ESLint.

? What do you want to use as your public directory?
public

? Configure as a single-page app (rewrite all urls to /index.html)?
Yes

? Set up automatic builds and deploys with GitHub?
No

? File public/index.html already exists. Overwrite?
 No

Если ESLint был случайно включен, его можно отключить двумя способами:

  1. Используя графический интерфейс, перейдите в папку ../functions проекта, выберите скрытый файл .eslintrc.js и удалите его. Не путайте его с аналогичным именем .eslintrc.json .
  2. Используя командную строку:
    cd functions
    rm .eslintrc.js
    

В файле washer-done/firebase.json дополните код:

{
  "database": {
    "rules": "database.rules.json"
  },
  "hosting": {
    "public": "public",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  },
    "headers": [{
      "source" : "**/*.@(js|html)",
      "headers" : [ {
        "key" : "Cache-Control",
        "value" : "max-age=0"
      } ]
    }],
  "functions": [
    {
      "source": "functions",
      "codebase": "default",
      "ignore": [
        "node_modules",
        ".git",
        "firebase-debug.log",
        "firebase-debug.*.log",
        "*.local"
      ]
    }
  ]
}

Развертывание в Firebase

Теперь, когда вы установили зависимости и настроили свой проект, вы готовы запустить приложение в первый раз.

firebase deploy

Это вывод консоли, который вы должны увидеть:

...

✔ Deploy complete!

Project Console: https://console.firebase.google.com/project/<firebase-project-id>/overview
Hosting URL: https://<firebase-project-id>.web.app

Эта команда развертывает веб-приложение вместе с несколькими облачными функциями для Firebase .

Откройте URL-адрес хостинга в браузере ( https:// .web.app https:// .web.app ) для просмотра веб-приложения. Вы увидите следующий интерфейс:

5845443e94705557.png

Этот веб-интерфейс представляет собой стороннюю платформу для просмотра или изменения состояний устройства. Чтобы начать заполнять базу данных информацией об устройстве, нажмите «ОБНОВИТЬ» . На странице вы не увидите никаких изменений, но текущее состояние вашей стиральной машины будет сохранено в базе данных.

Теперь пришло время подключить развернутый вами облачный сервис к Google Assistant с помощью консоли разработчика Google Home .

Настройте проект консоли разработчика

На вкладке «Разработка» добавьте отображаемое имя для вашего взаимодействия. Это имя появится в приложении Google Home.

Добавить отображаемое имя

В разделе «Фирменный стиль приложения» загрузите png файл значка приложения размером 144 x 144 пикселей с именем .png .

Добавить значок приложения

Чтобы включить привязку учетной записи, используйте следующие настройки привязки учетной записи:

Идентификатор клиента

ABC123

Секрет клиента

DEF456

URL-адрес авторизации

https://us-central1-
.cloudfunctions.net/fakeauth

URL-адрес токена

https://us-central1-
.cloudfunctions.net/faketoken

Обновить URL-адреса, связывающие аккаунты

В разделе «URL-адрес выполнения облака» введите URL-адрес вашей облачной функции, которая обеспечивает выполнение намерений «умного дома».

https://us-central1- -cloudfunctions.net/smarthome https://us-central1- -cloudfunctions.net/smarthome

Добавить URL-адрес облачной функции

Нажмите «Сохранить» , чтобы сохранить конфигурацию проекта, затем нажмите «Далее: Тест», чтобы включить тестирование вашего проекта.

Проверьте интеграцию между облаками

Теперь вы можете приступить к реализации веб-перехватчиков, необходимых для связи состояния устройства с Ассистентом.

4. Создайте шайбу

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

  • Намерение SYNC возникает, когда Ассистент хочет знать, какие устройства подключил пользователь. Он отправляется в вашу службу, когда пользователь связывает учетную запись. Вы должны ответить, отправив в формате JSON данные обо всех устройствах пользователя и их возможностях.
  • Намерение QUERY возникает, когда Ассистент хочет узнать текущее состояние или статус устройства. Вы должны ответить с помощью полезных данных JSON с состоянием каждого запрошенного устройства.
  • Намерение EXECUTE возникает, когда Помощник хочет управлять устройством от имени пользователя. Вы должны ответить с помощью полезных данных JSON со статусом выполнения каждого запрошенного устройства.
  • Намерение DISCONNECT возникает, когда пользователь отключает связь своей учетной записи с Ассистентом. Вам следует прекратить отправлять события для устройств этого пользователя в Ассистент.

В следующих разделах вы обновите функции, которые вы ранее развернули для обработки этих намерений.

Обновить ответ SYNC

Откройте functions/index.js , который содержит код для ответа на запросы Ассистента.

Вам нужно будет обработать намерение SYNC , вернув метаданные и возможности устройства. Обновите JSON в массиве onSync , включив в него информацию об устройстве и рекомендуемые характеристики стиральной машины.

index.js

app.onSync((body) => {
  return {
    requestId: body.requestId,
    payload: {
      agentUserId: USER_ID,
      devices: [{
        id: 'washer',
        type: 'action.devices.types.WASHER',
        traits: [
          'action.devices.traits.OnOff',
          'action.devices.traits.StartStop',
          'action.devices.traits.RunCycle',
        ],
        name: {
          defaultNames: ['My Washer'],
          name: 'Washer',
          nicknames: ['Washer'],
        },
        deviceInfo: {
          manufacturer: 'Acme Co',
          model: 'acme-washer',
          hwVersion: '1.0',
          swVersion: '1.0.1',
        },
        willReportState: true,
        attributes: {
          pausable: true,
        },
      }],
    },
  };
});

Развертывание в Firebase

Разверните обновленное облачное выполнение с помощью интерфейса командной строки Firebase:

firebase deploy --only functions

Чтобы протестировать интеграцию между облаками, вам необходимо связать свой проект с учетной записью Google. Это позволяет проводить тестирование через поверхности Google Assistant и приложение Google Home, которые вошли в одну и ту же учетную запись.

  1. На телефоне откройте настройки Google Assistant. Обратите внимание, что вы должны войти в систему под той же учетной записью, что и в консоли.
  2. Перейдите в «Google Ассистент» > «Настройки» > «Управление домом» (в разделе «Ассистент»).
  3. Нажмите значок поиска в правом верхнем углу.
  4. Найдите свое тестовое приложение, используя префикс [test] , чтобы найти конкретное тестовое приложение.
  5. Выберите этот элемент. Затем Google Assistant пройдет аутентификацию в вашей службе и отправит запрос SYNC , попросив вашу службу предоставить пользователю список устройств.

Откройте приложение Google Home и убедитесь, что вы видите стиральную машину.

ae252220753726f6.png

5. Обработка команд и запросов

Теперь, когда ваш облачный сервис правильно сообщает Google о стиральном устройстве, вам необходимо добавить возможность запрашивать состояние устройства и отправлять команды.

Обработка намерения QUERY

Намерение QUERY включает в себя набор устройств. Для каждого устройства вы должны ответить его текущим состоянием.

В functions/index.js отредактируйте обработчик QUERY , чтобы обработать список целевых устройств, содержащийся в запросе намерения.

index.js

app.onQuery(async (body) => {
  const {requestId} = body;
  const payload = {
    devices: {},
  };
  const queryPromises = [];
  const intent = body.inputs[0];
  for (const device of intent.payload.devices) {
    const deviceId = device.id;
    queryPromises.push(queryDevice(deviceId)
        .then((data) => {
        // Add response to device payload
          payload.devices[deviceId] = data;
        }
        ));
  }
  // Wait for all promises to resolve
  await Promise.all(queryPromises);
  return {
    requestId: requestId,
    payload: payload,
  };
});

Для каждого устройства, содержащегося в запросе, верните текущее состояние, хранящееся в базе данных реального времени. Обновите функции queryFirebase и queryDevice , чтобы они возвращали данные о состоянии стиральной машины.

index.js

const queryFirebase = async (deviceId) => {
  const snapshot = await firebaseRef.child(deviceId).once('value');
  const snapshotVal = snapshot.val();
  return {
    on: snapshotVal.OnOff.on,
    isPaused: snapshotVal.StartStop.isPaused,
    isRunning: snapshotVal.StartStop.isRunning,
  };
};

const queryDevice = async (deviceId) => {
  const data = await queryFirebase(deviceId);
  return {
    on: data.on,
    isPaused: data.isPaused,
    isRunning: data.isRunning,
    currentRunCycle: [{
      currentCycle: 'rinse',
      nextCycle: 'spin',
      lang: 'en',
    }],
    currentTotalRemainingTime: 1212,
    currentCycleRemainingTime: 301,
  };
};

Обработка намерения EXECUTE

Намерение EXECUTE обрабатывает команды для обновления состояния устройства. Ответ возвращает статус каждой команды — например, SUCCESS , ERROR или PENDING — и новое состояние устройства.

В functions/index.js отредактируйте обработчик EXECUTE для обработки списка свойств, требующих обновления, и набора целевых устройств для каждой команды:

index.js

app.onExecute(async (body) => {
  const {requestId} = body;
  // Execution results are grouped by status
  const result = {
    ids: [],
    status: 'SUCCESS',
    states: {
      online: true,
    },
  };

  const executePromises = [];
  const intent = body.inputs[0];
  for (const command of intent.payload.commands) {
    for (const device of command.devices) {
      for (const execution of command.execution) {
        executePromises.push(
            updateDevice(execution, device.id)
                .then((data) => {
                  result.ids.push(device.id);
                  Object.assign(result.states, data);
                })
                .catch(() => functions.logger.error('EXECUTE', device.id)));
      }
    }
  }

  await Promise.all(executePromises);
  return {
    requestId: requestId,
    payload: {
      commands: [result],
    },
  };
});

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

index.js

const updateDevice = async (execution, deviceId) => {
  const {params, command} = execution;
  let state; let ref;
  switch (command) {
    case 'action.devices.commands.OnOff':
      state = {on: params.on};
      ref = firebaseRef.child(deviceId).child('OnOff');
      break;
    case 'action.devices.commands.StartStop':
      state = params.start
      ? {isRunning: true, isPaused: false}
      : {isRunning: false, isPaused: false};
      ref = firebaseRef.child(deviceId).child('StartStop');
      break;
    case 'action.devices.commands.PauseUnpause':
      const data = await queryDevice(deviceId);
      state = (data.isPaused === false && data.isRunning === false)
        ? {isRunning: false, isPaused: false}
        : {isRunning: !params.pause, isPaused: params.pause};
      ref = firebaseRef.child(deviceId).child('StartStop');
      break;
  }

  return ref.update(state)
      .then(() => state);
};

6. Проверьте свою интеграцию

После реализации всех трех намерений вы можете проверить, контролирует ли ваша интеграция стиральную машину.

Развертывание в Firebase

Разверните обновленное облачное выполнение с помощью интерфейса командной строки Firebase:

firebase deploy --only functions

Проверьте стиральную машину

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

«Эй, Google, включи мою стиральную машину».

«Окей, Google, приостанови мою стиральную машину».

«Эй, Google, останови мою стиральную машину».

Вы также можете увидеть текущее состояние вашей стиральной машины, задавая вопросы.

«Окей, Google, моя стиральная машина включена?»

«Окей, Google, моя стиральная машина работает?»

«Окей, Google, на каком цикле работает моя стиральная машина?»

Вы можете просмотреть эти запросы и команды в журналах, которые отображаются под вашей функцией в разделе «Функции» консоли Firebase . Узнайте больше о журналах Firebase в разделе «Запись и просмотр журналов» .

Вы также можете найти эти запросы и команды в Google Cloud Console, перейдя в раздел «Ведение журналов» > «Обозреватель журналов» . Узнайте больше о ведении журналов Google Cloud в статье Доступ к журналам событий с помощью Cloud Logging .

7. Сообщайте об обновлениях в Google.

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

С помощью Request Sync вы можете инициировать новый запрос на синхронизацию, когда пользователи добавляют или удаляют устройства или когда изменяются возможности их устройств. С помощью Report State ваша облачная служба может заранее отправлять состояние устройства в Home Graph, когда пользователи физически изменяют состояние устройства (например, включают выключатель света) или меняют состояние с помощью другой службы.

В этом разделе вы добавите код для вызова этих методов из внешнего веб-приложения.

Включите API HomeGraph

API HomeGraph позволяет хранить и запрашивать устройства и их состояния в Home Graph пользователя. Чтобы использовать этот API, необходимо сначала открыть консоль Google Cloud и включить HomeGraph API .

В консоли Google Cloud обязательно выберите проект, соответствующий вашей интеграции <project-id>. Затем на экране библиотеки API для HomeGraph API нажмите « Включить» .

ee198858a6eac112.png

Включить состояние отчета

Запись в базу данных реального времени запускает функцию reportstate в стартовом проекте. Обновите функцию reportstate в functions/index.js чтобы захватывать данные, записанные в базу данных, и публиковать их в Home Graph через Report State.

index.js

exports.reportstate = functions.database.ref('{deviceId}').onWrite(
    async (change, context) => {
      functions.logger.info('Firebase write event triggered Report State');
      const snapshot = change.after.val();

      const requestBody = {
        requestId: 'ff36a3cc', /* Any unique ID */
        agentUserId: USER_ID,
        payload: {
          devices: {
            states: {
              /* Report the current state of our washer */
              [context.params.deviceId]: {
                on: snapshot.OnOff.on,
                isPaused: snapshot.StartStop.isPaused,
                isRunning: snapshot.StartStop.isRunning,
              },
            },
          },
        },
      };

      const res = await homegraph.devices.reportStateAndNotification({
        requestBody,
      });
      functions.logger.info('Report state response:', res.status, res.data);
    });

Включить синхронизацию запросов

Обновление значка в веб-интерфейсе внешнего интерфейса запускает функцию requestsync в стартовом проекте. Реализуйте функцию requestsync в functions/index.js для вызова API HomeGraph.

index.js

exports.requestsync = functions.https.onRequest(async (request, response) => {
  response.set('Access-Control-Allow-Origin', '*');
  functions.logger.info(`Request SYNC for user ${USER_ID}`);
  try {
    const res = await homegraph.devices.requestSync({
      requestBody: {
        agentUserId: USER_ID,
      },
    });
    functions.logger.info('Request sync response:', res.status, res.data);
    response.json(res.data);
  } catch (err) {
    functions.logger.error(err);
    response.status(500).send(`Error requesting sync: ${err}`);
  }
});

Развертывание в Firebase

Разверните обновленный код с помощью Firebase CLI:

firebase deploy --only functions

Проверьте свою реализацию

Нажмите Обновить ae8d3b25777a5e30.png в веб-интерфейсе и убедитесь, что вы видите запрос синхронизации в журнале консоли Firebase.

Затем настройте атрибуты стирального устройства в интерфейсном веб-интерфейсе и нажмите «Обновить» . Убедитесь, что вы видите изменение состояния, о котором сообщается в Google, в журналах консоли Firebase.

8. Поздравления

674c4f4392e98c1.png

Поздравляем! Вы успешно интегрировали Ассистент с облачной службой устройства с помощью интеграции «облако-облако».

Узнать больше

Вот несколько идей, которые вы можете реализовать, чтобы пойти глубже:

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