1. Введение
Программа доступа к устройствам предоставляет Smart Device Management API, REST API, позволяющий разработчикам управлять устройствами Google Nest из своих приложений. Пользователям необходимо дать согласие на доступ третьих лиц к их устройствам Nest.
Для успешной интеграции доступа к устройствам необходимо выполнить три ключевых шага:
- Создание проекта . Создайте проект в Google Cloud Platform и зарегистрируйтесь в качестве разработчика в консоли доступа к устройству.
- Привязка учетных записей . Проведите пользователей через процесс связывания учетных записей и получите код доступа. Обменяйте код на токен доступа.
- Управление устройствами — отправляйте запросы к API управления интеллектуальными устройствами для управления устройствами, отправляя команды с токеном доступа.
В этой лаборатории кода мы подробно рассмотрим, как работает доступ к устройствам, создав веб-приложение, обрабатывающее аутентификацию и выполняющее вызовы API управления интеллектуальными устройствами. Мы также рассмотрим развертывание простого прокси-сервера с использованием Node.js и Express для маршрутизации запросов доступа к устройствам.
Прежде чем начать, было бы неплохо освежить в памяти распространенные веб-технологии, которые мы будем использовать в этой лаборатории кода, такие как аутентификация с помощью OAuth 2.0 или создание веб-приложения с помощью Node.js , хотя они не являются обязательными условиями.
Что вам понадобится
- Node.js 8 или выше
- Аккаунт Google со связанным термостатом Nest
Что вы узнаете
- Настройка проекта Firebase, содержащего статические веб-страницы и облачные функции.
- Выдача запросов на доступ к устройству через веб-приложение на основе браузера
- Создание прокси-сервера с Node.js и Express для маршрутизации ваших запросов.
2. Создание проекта
Разработчикам необходимо создать проект Google Cloud Platform (GCP) для настройки интеграции доступа к устройствам. Идентификатор клиента и секрет клиента , созданные в рамках проекта GCP, будут использоваться как часть потока OAuth между приложением разработчика и Google Cloud. Разработчикам также необходимо посетить консоль доступа к устройствам, чтобы создать проект для доступа к API управления интеллектуальными устройствами.
Облачная платформа Google
Перейдите на облачную платформу Google . Нажмите «Создать новый проект» и укажите имя проекта. Также будет отображен идентификатор проекта [GCP-Project-Id] для Google Cloud. Запишите его, поскольку мы будем использовать его во время настройки Firebase. (В этой Codelab мы будем ссылаться на этот идентификатор как [GCP-Project-Id] .)
Первый шаг — включить необходимую библиотеку API в нашем проекте. Перейдите в раздел «API и службы» > «Библиотека» и найдите API управления интеллектуальными устройствами. Вам необходимо включить этот API, чтобы разрешить вашему проекту выполнять запросы к вызовам API доступа к устройствам.
Прежде чем мы перейдем к созданию учетных данных OAuth, нам необходимо настроить экран согласия OAuth для нашего проекта. Откройте API и службы > Экран согласия OAuth . В качестве типа пользователя выберите внешний . Укажите имя и адрес электронной почты службы поддержки вашего приложения, а также контактную информацию разработчика, чтобы заполнить первый экран. При запросе тестовых пользователей на этом этапе обязательно укажите адрес электронной почты со связанными устройствами.
После настройки экрана согласия OAuth перейдите в раздел API и службы > Учетные данные . Нажмите +Создать учетные данные и выберите идентификатор клиента OAuth . В качестве типа приложения выберите Веб-приложение .
Укажите имя вашего клиента и нажмите СОЗДАТЬ . Позже мы добавим авторизованный источник JavaScript и URI авторизованного перенаправления. Завершение этого процесса приведет к появлению [Client-Id] и [Client-Secret], связанных с этим клиентом OAuth 2.0.
Консоль доступа к устройству
Перейдите в консоль доступа к устройствам . Если вы раньше не использовали консоль доступа к устройствам, вам будет предоставлено соглашение об условиях обслуживания и регистрационный взнос в размере 5 долларов США.
Создайте новый проект и дайте ему имя проекта. В следующем окне укажите [Идентификатор клиента] , который вы получили от GCP на предыдущем шаге.
Включив события и завершив этапы создания проекта, вы перейдете на домашнюю страницу вашего проекта. Ваш [Project-Id] будет указан под именем, которое вы дали своему проекту.
Запишите свой [идентификатор проекта], поскольку мы будем использовать его при отправке запросов к API управления смарт-устройствами.
3. Настройка Firebase
Firebase предоставляет разработчикам быстрый и простой способ развертывания веб-приложений. Мы будем разрабатывать клиентское веб-приложение для интеграции доступа к устройствам с использованием Firebase.
Создать проект Firebase
Перейдите в консоль Firebase . Нажмите «Добавить проект» , затем выберите проект, созданный на этапе создания проекта . Это создаст проект Firebase, который будет связан с вашим проектом GCP [GCP-Project-Id] .
После успешного создания проекта Firebase вы должны увидеть следующий экран:
Установите инструменты Firebase
Firebase предоставляет набор инструментов CLI для создания и развертывания вашего приложения. Чтобы установить эти инструменты, откройте новое окно терминала и выполните следующую команду. Это приведет к установке инструментов Firebase по всему миру.
$ npm i -g firebase-tools
Чтобы убедиться, что инструменты Firebase установлены правильно, проверьте информацию о версии.
$ firebase --version
Вы можете войти в инструменты Firebase CLI под своей учетной записью Google с помощью команды входа в систему.
$ firebase login
Инициализировать хостинг-проект
Как только вы сможете войти в систему, следующим шагом будет инициализация хостинг-проекта для вашего веб-приложения. В терминале перейдите в папку, в которой вы хотите создать проект, и выполните следующую команду:
$ firebase init hosting
Firebase задаст вам ряд вопросов, чтобы вы могли начать работу над хостинг-проектом:
- Пожалуйста, выберите вариант — Использовать существующий проект
- Выберите проект Firebase по умолчанию для этого каталога — выберите***[GCP-Project-Id]***.
- Что вы хотите использовать в качестве общедоступного каталога? — Общественный
- Настроить как одностраничное приложение? - Да
- Настроить автоматические сборки и развертывания с помощью GitHub? - Нет
После инициализации вашего проекта вы можете развернуть его в Firebase с помощью следующей команды:
$ firebase deploy
Firebase просканирует ваш проект и развернет необходимые файлы на облачный хостинг.
Когда вы откроете URL-адрес хостинга в браузере, вы должны увидеть только что развернутую страницу:
Теперь, когда вы знаете основы развертывания веб-страницы с помощью Firebase, давайте приступим к развертыванию нашего примера Codelab!
4. Пример Codelab
Вы можете клонировать репозиторий Codelab, размещенный на GitHub, с помощью следующей команды:
$ git clone https://github.com/google/device-access-codelab-web-app.git
В этом репозитории мы предоставляем образцы в двух отдельных папках. В папке codelab-start
содержатся необходимые файлы, которые помогут вам начать с текущей точки этой лаборатории кода. Папка codelab-done
содержит полную версию этого Codelab с полнофункциональным клиентом и сервером node.js.
В этой лаборатории мы будем использовать файлы из папки codelab-start
, однако, если вы в какой-то момент почувствуете, что застряли, не стесняйтесь также ссылаться на версию, созданную кодовой лабораторией.
Примеры файлов Codelab
Файловая структура папки codelab-start следующая:
public ├───index.html ├───scripts.js ├───style.css firebase.json
Папка Public содержит статические страницы нашего приложения. firebase.json
отвечает за маршрутизацию веб-запросов в наше приложение. В версии codelab-done
, вы также увидите каталог functions
, содержащий логику для нашего прокси-сервера (экспресс), который будет развернут в функциях Google Cloud.
Развертывание примера Codelab
Скопируйте файлы из codelab-start
в каталог вашего проекта.
$ firebase deploy
Как только Firebase завершит развертывание, вы сможете увидеть приложение Codelab:
Для инициирования потока аутентификации требуются учетные данные партнера, которые мы рассмотрим в следующем разделе.
5. Обработка OAuth
OAuth — это веб-стандарт делегирования доступа, который обычно используется пользователями для предоставления сторонним приложениям доступа к данным своей учетной записи без обмена паролями. Мы используем OAuth 2.0, чтобы предоставить разработчикам доступ к пользовательским устройствам через доступ к устройствам.
Укажите URI перенаправления
Первый шаг потока OAuth включает передачу набора параметров в конечную точку Google OAuth 2.0. После получения согласия пользователя серверы Google OAuth отправят запрос с кодом авторизации на ваш URI перенаправления.
Обновите константу SERVER_URI
(строка 19), указав свой собственный URL-адрес хостинга в scripts.js
:
const SERVER_URI = "https://[GCP-Project-Id].web.app";
Повторное развертывание приложения с этим изменением обновит URI перенаправления, используемый для вашего проекта.
$ firebase deploy
Включить URI перенаправления
После обновления URI перенаправления в файле сценариев вам также необходимо добавить его в список разрешенных URI перенаправления для идентификатора клиента, который вы создали для своего проекта. Перейдите на страницу учетных данных в Google Cloud Platform, где будут перечислены все учетные данные, созданные для вашего проекта:
В списке идентификаторов клиентов OAuth 2.0 выберите идентификатор клиента, который вы создали на этапе создания проекта . Добавьте URI перенаправления вашего приложения в список разрешенных URI перенаправления для вашего проекта.
Попробуйте войти!
Перейдите по URL-адресу хостинга, который вы настроили с помощью Firebase, введите учетные данные партнера и нажмите кнопку ВОЙТИ . Идентификатор клиента и секрет клиента — это учетные данные, которые вы получили от Google Cloud Platform, а идентификатор проекта — от консоли доступа к устройству.
Кнопка ВОЙТИ проведет ваших пользователей через поток OAuth для вашего предприятия, начиная с экрана входа в их учетную запись Google. После входа в систему пользователям будет предложено предоставить вашему проекту разрешения на доступ к их устройствам Nest.
Поскольку это макет приложения, Google выдаст предупреждение перед выполнением перенаправления!
Нажмите «Дополнительно», затем выберите «Перейти в web.app (небезопасно)», чтобы завершить перенаправление в ваше приложение.
Это предоставит код OAuth как часть входящего запроса GET, который приложение затем обменяет на токен доступа и токен обновления.
6. Контроль устройства
Пример приложения Device Access использует вызовы REST API Smart Device Management для управления устройствами Google Nest. Эти вызовы включают передачу токена доступа в заголовке запроса GET или POST вместе с полезной нагрузкой, необходимой для определенных команд.
Мы написали общую функцию запроса доступа для обработки этих вызовов. Однако вам нужно будет предоставить этой функции правильную конечную точку, а также объект полезной нагрузки, когда это необходимо!
function deviceAccessRequest(method, call, localpath, payload = null) {...}
- метод — Тип HTTP-запроса (
GET
илиPOST)
- call — строка, представляющая наш вызов API, используемая для маршрутизации ответов (
listDevices
,thermostatMode
,temperatureSetpoint
) - localpath — конечная точка, к которой отправляется запрос, содержащая идентификатор проекта и идентификатор устройства (добавляется после
https://smartdevicemanagement.googleapis.com/v1
). - полезная нагрузка (*) — дополнительные данные, необходимые для вызова API (например, числовое значение, представляющее температуру для заданного значения).
Мы создадим образцы элементов управления пользовательского интерфейса («Список устройств», «Установить режим», «Установить температуру») для управления термостатом Nest:
Эти элементы управления пользовательского интерфейса будут вызывать соответствующие функции ( listDevices()
, postThermostatMode()
, postTemperatureSetpoint()
) из scripts.js
. Они оставлены пустыми, чтобы вы могли их реализовать! Цель состоит в том, чтобы выбрать правильный метод/путь и передать полезную нагрузку функции deviceAccessRequest(...)
.
Получение списка устройств
Самый простой вызов доступа к устройствам — listDevices
. Он использует запрос GET
и не требует никакой полезной нагрузки. Конечная точка должна быть структурирована с использованием projectId
. Завершите функцию listDevices()
следующим образом:
function listDevices() { var endpoint = "/enterprises/" + projectId + "/devices"; deviceAccessRequest('GET', 'listDevices', endpoint); }
Сохраните изменения и снова разверните проект Firebase с помощью следующей команды:
$ firebase deploy
После развертывания новой версии приложения попробуйте перезагрузить страницу и нажмите СПИСОК УСТРОЙСТВ . Это должно заполнить список в разделе «Контроль устройств», в котором вы должны увидеть идентификатор вашего термостата:
Выбор устройств из списка обновит поле deviceId
в файле scripts.js
. Для следующих двух элементов управления нам нужно будет указать deviceId
для конкретного устройства, которым мы хотим управлять.
Управление термостатом
В API управления интеллектуальными устройствами есть две особенности базового управления термостатом Nest. Режим термостата и уставка температуры . ThermostatMode устанавливает для вашего термостата Nest один из четырех возможных режимов: {Выкл., Нагрев, Охлаждение, НагревОхлаждение}. Затем нам нужно предоставить выбранный режим как часть полезных данных.
Замените функцию postThermostatMode()
в scripts.js
следующей:
function postThermostatMode() { var endpoint = "/enterprises/" + projectId + "/devices/" + deviceId + ":executeCommand"; var tempMode = id("tempMode").value; var payload = { "command": "sdm.devices.commands.ThermostatMode.SetMode", "params": { "mode": tempMode } }; deviceAccessRequest('POST', 'thermostatMode', endpoint, payload); }
Следующая функция, postTemperatureSetpoint()
, управляет установкой температуры (в градусах Цельсия) для вашего термостата Nest. В полезных данных можно установить два параметра: heatCelsius
и coolCelsius
, в зависимости от выбранного режима термостата.
function postTemperatureSetpoint() { var endpoint = "/enterprises/" + projectId + "/devices/" + deviceId + ":executeCommand"; var heatCelsius = parseFloat(id("heatCelsius").value); var coolCelsius = parseFloat(id("coolCelsius").value); var payload = { "command": "", "params": {} }; if ("HEAT" === id("tempMode").value) { payload.command = "sdm.devices.commands.ThermostatTemperatureSetpoint.SetHeat"; payload.params["heatCelsius"] = heatCelsius; } else if ("COOL" === id("tempMode").value) { payload.command = "sdm.devices.commands.ThermostatTemperatureSetpoint.SetCool"; payload.params["coolCelsius"] = coolCelsius; } else if ("HEATCOOL" === id("tempMode").value) { payload.command = "sdm.devices.commands.ThermostatTemperatureSetpoint.SetRange"; payload.params["heatCelsius"] = heatCelsius; payload.params["coolCelsius"] = coolCelsius; } else { console.log("Off and Eco mode don't allow this function"); return; } deviceAccessRequest('POST', 'temperatureSetpoint', endpoint, payload); }
7. Сервер Node.js (необязательно)
Поздравляем! Вы создали клиентское веб-приложение, которое может отправлять запросы API управления интеллектуальными устройствами из браузера. Для тех из вас, кто хочет использовать серверную часть, мы хотим начать работу с прокси-сервера, который может перенаправлять ваши запросы из браузера.
Для этого прокси-сервера мы будем использовать облачные функции Firebase, Node.js и Express.
Инициализация облачных функций
Откройте новое окно терминала, перейдите в каталог вашего проекта и выполните следующее:
$ firebase init functions
Firebase задаст вам ряд вопросов для инициализации облачных функций:
- Какой язык вы бы хотели использовать для написания облачных функций? — JavaScript
- Хотите ли вы использовать ESLint для выявления возможных ошибок и обеспечения соблюдения стиля? - Нет
- Хотите установить зависимости с помощью npm сейчас? - Да
Это позволит инициализировать папку functions
в вашем проекте, а также установить необходимые зависимости. Вы увидите, что папка вашего проекта содержит каталог функций с файлом index.js для определения наших облачных функций, package.json для определения настроек и каталогом node_modules для хранения зависимостей.
Для создания серверной функциональности мы будем использовать две библиотеки npm
: express и xmlhttprequest. Вам нужно будет добавить следующие записи в список зависимостей в файле package.json:
"xmlhttprequest": "^1.8.0", "express": "^4.17.0"
Затем запуск npm install из каталога функций должен установить зависимости для вашего проекта:
$ npm install
Если у npm возникают проблемы с загрузкой пакетов, вы можете попытаться сохранить xmlhttprequest и явно выразить его с помощью следующей команды:
$ npm install express xmlhttprequest --save
Обновление до плана Blaze
Для использования команды firebase deploy
вам потребуется перейти на план Blaze, который требует добавления способа оплаты в вашу учетную запись. Перейдите в раздел «Обзор проекта» > «Использование и выставление счетов» и обязательно выберите план Blaze для своего проекта.
Создать экспресс-сервер
Сервер Express использует простую структуру для ответа на входящие запросы GET
и POST
. Мы создали сервлет, который прослушивает запросы POST
, передает их на целевой URL-адрес, указанный в полезных данных, и отвечает ответом, полученным в результате передачи.
Измените файл index.js
в каталоге функций, чтобы он выглядел следующим образом:
const XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest; const functions = require('firebase-functions'); const express = require('express'); const http = require('http'); const app = express(); app.use(express.json()); //***** Device Access - Proxy Server *****// // Serving Get Requests (Not used) app.get('*', (request, response) => { response.status(200).send("Hello World!"); }); // Serving Post Requests app.post('*', (request, response) => { setTimeout(() => { // Read the destination address from payload: var destination = request.body.address; // Create a new proxy post request: var xhr = new XMLHttpRequest(); xhr.open('POST', destination); // Add original headers to proxy request: for (var key in request.headers) { var value = request.headers[key]; xhr.setRequestHeader(key, value); } // Add command/parameters to proxy request: var newBody = {}; newBody.command = request.body.command; newBody.params = request.body.params; // Respond to original request with the response coming // back from proxy request (to Device Access Endpoint) xhr.onload = function () { response.status(200).send(xhr.responseText); }; // Send the proxy request! xhr.send(JSON.stringify(newBody)); }, 1000); }); // Export our app to firebase functions: exports.app = functions.https.onRequest(app);
Чтобы направлять запросы на наш сервер, нам нужно настроить перезапись из firebase.json
следующим образом:
{ "hosting": { "public": "public", "ignore": [ "firebase.json", "**/.*", "**/node_modules/**" ], "rewrites": [{ "source": "/proxy**", "function": "app" },{ "source": "**", "destination": "/index.html" } ] } }
Это приведет к маршрутизации URL-адресов, начинающихся с /proxy
, на наш сервер Express, а остальные продолжат идти на наш index.html
.
Вызовы API через прокси
Теперь, когда наш сервер готов, давайте определим URI прокси-сервера в scripts.js
, чтобы наш браузер мог отправлять запросы на этот адрес:
const PROXY_URI = SERVER_URI + "/proxy";
Затем добавьте функцию proxyRequest
— scripts.js
, которая имеет ту же сигнатуру, что и функция deviceAccessRequest(...)
для непрямых вызовов доступа к устройству.
function proxyRequest(method, call, localpath, payload = null) { var xhr = new XMLHttpRequest(); // We are doing our post request to our proxy server: xhr.open(method, PROXY_URI); xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken); xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8'); xhr.onload = function () { // Response is passed to deviceAccessResponse function: deviceAccessResponse(call, xhr.response); }; // We are passing the device access endpoint in address field of the payload: payload.address = "https://smartdevicemanagement.googleapis.com/v1" + localpath; if ('POST' === method && payload) xhr.send(JSON.stringify(payload)); else xhr.send(); }
Последний шаг — заменить вызовы deviceAccessRequest(...)
функцией proxyRequest(...)
в функциях postThermostatMode()
и postTemperatureSetpoint()
в файле scripts.js
.
Запустите firebase deploy
, чтобы обновить приложение.
$ firebase deploy
Теперь у вас есть работающий прокси-сервер Node.js, использующий функции Express on Cloud.
Предоставление разрешений облачным функциям
Последний шаг — проверить права доступа к вашим облачным функциям и убедиться, что ваше клиентское приложение сможет их вызывать.
В Google Cloud Platform перейдите на вкладку «Облачные функции» в меню, затем выберите свою облачную функцию:
Нажмите «Разрешения» , затем «Добавить участника» . Напишите allUsers в новое поле участника и выберите Cloud Functions > Cloud Functions Invoker в качестве роли. При нажатии «Сохранить» появится предупреждающее сообщение:
Если выбрать «Разрешить публичный доступ», ваше клиентское приложение сможет использовать ваши облачные функции.
Поздравляем – вы выполнили все шаги. Теперь вы можете перейти в свое веб-приложение и использовать элементы управления устройством, маршрутизируемые через прокси-сервер!
Следующие шаги
Ищете способы расширить свой опыт в области доступа к устройствам? Ознакомьтесь с документацией по характеристикам, чтобы узнать больше об управлении другими устройствами Nest, а также о процессе сертификации , чтобы узнать, как запустить свой продукт в мир!
Развивайте свои навыки с помощью примера веб-приложения Device Access , в котором вы на основе опыта Codelab развернете работающее веб-приложение для управления камерами Nest, дверными звонками и термостатами.