1. Wprowadzenie
Program dostępu do urządzeń udostępnia Smart Device Management API, czyli interfejs API typu REST, który umożliwia programistom sterowanie urządzeniami Google Nest z poziomu aplikacji. Użytkownicy muszą wyrazić zgodę na dostęp osób trzecich do ich urządzeń Nest.
Do przeprowadzenia udanej integracji dostępu do urządzenia należy wykonać 3 kluczowe kroki:
- Tworzenie projektu – utwórz projekt w Google Cloud Platform i zarejestruj się jako deweloper w konsoli dostępu do urządzenia.
- Łączenie kont – pozyskaj użytkowników poprzez proces łączenia kont i odzyskaj kod dostępu. Wymień kod na token dostępu.
- Kontrola urządzeń – wysyłaj żądania do interfejsu Smart Device Management API, aby kontrolować urządzenia, wysyłając polecenia z tokenem dostępu.
W ramach tego ćwiczenia z programowania przeanalizujemy szczegółowo, jak działa dostęp do urządzenia, tworząc aplikację internetową do uwierzytelniania i wykonując wywołania interfejsu Smart Device Management API. Zajmiemy się też wdrażaniem prostego serwera proxy za pomocą Node.js i Express, aby kierować żądania dostępu do urządzenia.
Zanim zaczniesz, warto przypomnieć sobie popularne technologie internetowe, których użyjemy w ramach tego ćwiczenia z programowania, takie jak uwierzytelnianie przy użyciu OAuth 2.0 lub tworzenie aplikacji internetowej w Node.js (chociaż nie są to wymagania wstępne).
Potrzebne elementy
- Node.js w wersji 8 lub nowszej
- Konto Google z połączonym urządzeniem Nest Thermostat
Czego się nauczysz
- Konfigurowanie projektu Firebase hostującego statyczne strony internetowe i funkcje w Cloud Functions
- Wysyłanie żądań dostępu do urządzenia za pomocą aplikacji internetowej opartej na przeglądarce
- Tworzenie serwera proxy z Node.js i ekspresem do kierowania żądań
2. Tworzenie projektu
Aby skonfigurować integrację z dostępem do urządzenia, deweloperzy muszą utworzyć projekt Google Cloud Platform (GCP). Identyfikator klienta i Tajny klucz klienta wygenerowane w projekcie GCP będą używane w ramach przepływu OAuth między aplikacją dewelopera a Google Cloud. Deweloperzy muszą też korzystać z Konsoli dostępu do urządzenia, aby utworzyć projekt umożliwiający dostęp do interfejsu Smart Device Management API.
Google Cloud Platform
Otwórz Google Cloud Platform. Kliknij „Utwórz nowy projekt” i podaj jego nazwę. Będzie też wyświetlany identyfikator projektu [GCP-Project-Id] dla Google Cloud. Zapisz go, ponieważ będzie on używany podczas konfiguracji Firebase. W trakcie tego ćwiczenia w Codelabs będziemy używać tego identyfikatora jako [GCP-Project-Id].
Najpierw włącz w projekcie niezbędną bibliotekę interfejsów API. Otwórz Interfejsy API i Usługi > Bibliotekę i wyszukaj interfejs Smart Device Management API. Musisz włączyć ten interfejs API, aby autoryzować swój projekt do wysyłania żądań do wywołań interfejsu Device Access API.
Zanim przejdziemy do tworzenia danych logowania OAuth, musimy skonfigurować w projekcie ekran zgody OAuth. Otwórz Interfejsy API i Usługi > Ekran zgody OAuth. Jako Typ użytkownika wybierz zewnętrzny. Aby wypełnić pierwszy ekran, podaj nazwę i adres e-mail zespołu pomocy aplikacji, a także informacje kontaktowe dewelopera. Gdy pojawi się pytanie o użytkowników testowych, pamiętaj, aby na tym etapie podać adresy e-mail połączonych urządzeń.
Po skonfigurowaniu ekranu zgody OAuth otwórz Interfejsy API Usługi > Dane logowania. Kliknij + Utwórz dane logowania i wybierz Identyfikator klienta OAuth. Jako typ aplikacji wybierz Aplikacja internetowa.
Podaj nazwę klienta i kliknij UTWÓRZ. Później dodamy Autoryzowane źródło JavaScript oraz Autoryzowany identyfikator URI przekierowania. Gdy zakończysz ten proces, wyświetlą się identyfikatory [Client-Id] i [Client-Secret] powiązane z tym klientem OAuth 2.0.
Konsola dostępu do urządzenia
Otwórz konsolę dostępu do urządzenia. Jeśli po raz pierwszy korzystasz z Konsoli dostępu do urządzenia, otrzymasz Warunki korzystania z usługi i opłatę rejestracyjną w wysokości 5 USD.
Utwórz nowy projekt i nadaj mu nazwę. W następnym oknie podaj identyfikator [Client-Id] otrzymany z GCP w poprzednim kroku.
Po włączeniu zdarzeń i dokończeniu tworzenia projektu otworzy się strona główna Twojego projektu. Twój projekt [Project-Id] będzie widoczny pod nazwą nadaną Twojemu projektowi.
Zapisz swój [Project-Id], ponieważ będziemy go używać podczas wysyłania żądań do interfejsu Smart Device Management API.
3. Konfiguracja Firebase
Firebase umożliwia programistom szybkie i łatwe wdrażanie aplikacji internetowych. Będziemy opracowywać aplikację internetową po stronie klienta na potrzeby integracji dostępu do urządzenia za pomocą Firebase.
Tworzenie projektu Firebase
Otwórz konsolę Firebase. Kliknij Dodaj projekt i w kroku Tworzenie projektu wybierz utworzony przez siebie projekt. Spowoduje to utworzenie projektu Firebase, który zostanie połączony z projektem GCP [GCP-Project-Id].
Po utworzeniu projektu Firebase powinien wyświetlić się ten ekran:
Zainstaluj narzędzia Firebase
Firebase udostępnia zestaw narzędzi interfejsu wiersza poleceń umożliwiających kompilowanie i wdrażanie aplikacji. Aby zainstalować te narzędzia, otwórz nowe okno terminala i uruchom następujące polecenie. Spowoduje to zainstalowanie narzędzi Firebase na całym świecie.
$ npm i -g firebase-tools
Aby upewnić się, że narzędzia Firebase zostały prawidłowo zainstalowane, sprawdź informacje o wersji.
$ firebase --version
Możesz zalogować się w narzędziach interfejsu wiersza poleceń Firebase za pomocą konta Google za pomocą polecenia logowania.
$ firebase login
Zainicjuj projekt Hostingu
Gdy uda Ci się zalogować, kolejnym krokiem jest zainicjowanie hostowanego projektu dla Twojej aplikacji internetowej. W terminalu przejdź do folderu, w którym chcesz utworzyć projekt, i uruchom to polecenie:
$ firebase init hosting
Firebase zada Ci kilka pytań, które pomogą rozpocząć hosting projektu:
- Wybierz opcję – Użyj istniejącego projektu
- Wybierz domyślny projekt Firebase dla tego katalogu – wybierz***[GCP-Project-Id]***
- Jakiego katalogu chcesz użyć jako katalogu publicznego? – Publiczny
- Skonfigurować jako aplikację na jednej stronie? — Tak
- Skonfigurować automatyczne kompilacje i wdrożenia na GitHubie? – Nie
Po zainicjowaniu projektu możesz wdrożyć go w Firebase za pomocą tego polecenia:
$ firebase deploy
Firebase przeskanuje Twój projekt i wdroży niezbędne pliki w hostelu w chmurze.
Gdy otworzysz adres URL Hostingu w przeglądarce, powinna się wyświetlić wdrożona właśnie strona:
Znasz już podstawy wdrażania stron internetowych za pomocą Firebase, więc teraz zajmijmy się wdrażaniem naszego przykładowego ćwiczenia z programowania.
4. Przykładowe ćwiczenia z programowania
Możesz skopiować repozytorium Codelab hostowane na GitHubie za pomocą tego polecenia:
$ git clone https://github.com/google/device-access-codelab-web-app.git
W tym repozytorium udostępniamy przykłady w 2 osobnych folderach. Folder codelab-start
zawiera pliki niezbędne do rozpoczęcia pracy od bieżącego punktu w tym ćwiczeniu z programowania. Folder codelab-done
zawiera pełną wersję tego ćwiczenia z Codelabs z w pełni działającym klientem i serwerem node.js.
W trakcie tego ćwiczenia w Codelabs będziemy używać plików z folderu codelab-start
. Jeśli jednak napotkasz problemy, możesz też w każdej chwili spojrzeć na wersję z Codelabs.
Przykładowe pliki z ćwiczeniami w Codelabs
Struktura pliku folderu codelab-start jest następująca:
public ├───index.html ├───scripts.js ├───style.css firebase.json
Folder publiczny zawiera statyczne strony naszej aplikacji. Domena firebase.json
odpowiada za kierowanie żądań sieciowych do naszej aplikacji. W wersji codelab-done
dostępny jest też katalog functions
zawierający logikę szybkiego serwera proxy do wdrożenia w funkcjach Google Cloud.
Wdrażanie przykładowego ćwiczenia z programowania
Skopiuj pliki z katalogu codelab-start
do katalogu projektu.
$ firebase deploy
Gdy Firebase zakończy wdrażanie, aplikacja Codelab powinna być widoczna:
Zainicjowanie procesu uwierzytelniania wymaga danych logowania partnera, które omówimy w następnej sekcji.
5. Obsługa protokołu OAuth
Protokół OAuth to standard internetowy przekazywania dostępu, który jest często używany przez użytkowników do przyznawania aplikacjom innych firm dostępu do informacji o swoich kontach bez konieczności udostępniania haseł. Używamy protokołu OAuth 2.0, aby umożliwić deweloperom uzyskiwanie dostępu do urządzeń użytkowników przez Dostęp do urządzenia.
Określ identyfikator URI przekierowania
Pierwszy krok procesu OAuth obejmuje przekazanie zestawu parametrów do punktu końcowego Google OAuth 2.0. Po uzyskaniu zgody użytkownika serwery OAuth Google wyślą żądanie z kodem autoryzacji do identyfikatora URI przekierowania.
Zaktualizuj stałą SERVER_URI
(wiersz 19) swoim adresem URL w Hostingu w scripts.js
:
const SERVER_URI = "https://[GCP-Project-Id].web.app";
Ponowne wdrożenie aplikacji po wprowadzeniu tej zmiany spowoduje zaktualizowanie identyfikatora URI przekierowania używanego w projekcie.
$ firebase deploy
Włącz identyfikator URI przekierowania
Po zaktualizowaniu identyfikatora URI przekierowania w pliku skryptów musisz też dodać go do listy dozwolonych identyfikatorów URI przekierowania dla identyfikatora klienta utworzonego dla Twojego projektu. W Google Cloud Platform otwórz stronę Dane logowania, która zawiera listę wszystkich danych logowania utworzonych dla Twojego projektu:
Na liście Identyfikatory klienta OAuth 2.0 wybierz identyfikator klienta utworzony w kroku Tworzenie projektu. Dodaj identyfikator URI przekierowania aplikacji do listy Autoryzowanych identyfikatorów URI przekierowania w projekcie.
Spróbuj się zalogować
Otwórz adres URL Hostingu skonfigurowany w Firebase, wpisz swoje dane logowania do partnera i kliknij przycisk SIGN IN (ZALOGUJ SIĘ). Identyfikator klienta i tajny klucz klienta to dane logowania uzyskane z Google Cloud Platform. Identyfikator projektu pochodzi z konsoli dostępu do urządzenia.
Przycisk ZALOGUJ SIĘ przeprowadzi użytkowników przez proces OAuth w Twojej firmie, zaczynając od ekranu logowania na ich konta Google. Po zalogowaniu się użytkownicy zostaną poproszeni o przyznanie projektowi uprawnień dostępu do ich urządzeń Nest.
Ponieważ jest to przykładowa aplikacja, przed wykonaniem przekierowania Google wyświetli ostrzeżenie.
Kliknij „Zaawansowane”, a następnie wybierz „Otwórz stronę web.app (niebezpieczną)” aby wykonać przekierowanie do Twojej aplikacji.
W ramach przychodzącego żądania GET aplikacja otrzyma kod OAuth, który następnie wymieni aplikację na token dostępu i token odświeżania.
6. Sterowanie urządzeniami
Przykładowa aplikacja z dostępem do urządzenia używa wywołań interfejsu API REST Smart Device Management do sterowania urządzeniami Google Nest. Wywołania te obejmują przekazanie tokena dostępu w nagłówku żądania GET lub POST razem z ładunkiem wymaganym przez niektóre polecenia.
Opracowaliśmy ogólną funkcję żądania dostępu do obsługi tych wywołań. Musisz jednak podać w przypadku tej funkcji prawidłowy punkt końcowy oraz w razie potrzeby obiekt ładunku.
function deviceAccessRequest(method, call, localpath, payload = null) {...}
- metoda – typ żądania HTTP (
GET
lubPOST)
) - Call – ciąg reprezentujący wywołanie interfejsu API używany do kierowania odpowiedzi (
listDevices
,thermostatMode
,temperatureSetpoint
). - localpath – punkt końcowy, do którego wysyłane jest żądanie, zawierający identyfikator projektu i identyfikator urządzenia (dołączony po
https://smartdevicemanagement.googleapis.com/v1
). - ładunek (*) – dodatkowe dane wymagane do wywołania interfejsu API (np. wartość liczbowa reprezentująca temperaturę dla nastawy).
Tworzymy przykładowe elementy interfejsu (Lista urządzeń, Tryb ustawiania, Ustaw temperaturę), aby sterować urządzeniem Nest Thermostat:
Te elementy sterujące interfejsu będą wywoływać odpowiednie funkcje (listDevices()
, postThermostatMode()
, postTemperatureSetpoint()
) z usługi scripts.js
. Są one puste – możesz je wdrożyć. Celem jest wybranie właściwej metody/ścieżki i przekazanie ładunku do funkcji deviceAccessRequest(...)
.
Wyświetlanie listy urządzeń
Najprostsze wywołanie dostępu do urządzenia to listDevices
. Używa ona żądania GET
i nie wymaga ładunku. Struktura punktu końcowego musi mieć format projectId
. Dokończ funkcję listDevices()
w ten sposób:
function listDevices() { var endpoint = "/enterprises/" + projectId + "/devices"; deviceAccessRequest('GET', 'listDevices', endpoint); }
Zapisz zmiany i ponownie wdróż projekt Firebase za pomocą tego polecenia:
$ firebase deploy
Po wdrożeniu nowej wersji aplikacji odśwież stronę i kliknij LISTA URZĄDZEŃ. Powinno to zapełnić listę w sekcji Sterowanie urządzeniami. Powinien tam być widoczny identyfikator termostatu:
Wybranie urządzeń z listy spowoduje zaktualizowanie pola deviceId
w pliku scripts.js
. W przypadku 2 kolejnych elementów sterujących trzeba określić deviceId
w przypadku konkretnego urządzenia, którym mamy sterować.
Sterowanie termostatem
W interfejsie Smart Device Management API dostępne są 2 podstawowe opcje sterowania urządzeniem Nest Thermostat. ThermostatMode i TemperatureSetpoint. ThermostatMode ustawia tryb urządzenia Nest Thermostat na jeden z 4 dostępnych trybów: {Wyłączono, Ogrzewanie, Chłodzenie, HeatCool}. Następnie musimy dodać wybrany tryb jako część ładunku.
Zastąp funkcję postThermostatMode()
w scripts.js
taką funkcją:
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); }
Kolejna funkcja – postTemperatureSetpoint()
– służy do ustawiania temperatury (w stopniach Celsjusza) urządzenia Nest Thermostat. W ładunku dostępne są 2 parametry: heatCelsius
i coolCelsius
, w zależności od wybranego trybu termostatu.
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 Server (opcjonalnie)
Gratulacje! Masz stworzoną aplikację internetową po stronie klienta, która może wysyłać żądania do interfejsu Smart Device Management API z przeglądarki. Dla osób, które chcą tworzyć rozwiązania po stronie serwera, chcemy ułatwić im rozpoczęcie pracy od serwera proxy, który może przekierowywać żądania z przeglądarki.
Na potrzeby tego serwera proxy użyjemy funkcji w chmurze Firebase, Node.js oraz Express.
Inicjowanie Cloud Functions
Otwórz nowe okno terminala, przejdź do katalogu projektu i uruchom to polecenie:
$ firebase init functions
Firebase zada Ci zestaw pytań, aby zainicjować funkcje w Cloud Functions:
- W jakim języku chcesz używać do pisania funkcji w Cloud Functions? – JavaScript
- Czy chcesz używać języka ESLint do wychwycenia prawdopodobnych błędów i egzekwowania stylu? – Nie
- Czy chcesz teraz zainstalować zależności przy użyciu npm? — Tak
Spowoduje to zainicjowanie folderu functions
w projekcie oraz zainstalowanie niezbędnych zależności. Zobaczysz, że folder projektu zawiera katalog funkcji, w którym plik index.js definiuje funkcje w Cloud Functions, package.json do definiowania ustawień i katalog node_modules do przechowywania zależności.
Do utworzenia funkcji po stronie serwera użyjemy 2 bibliotek npm
: express i xmlhttprequest. Do listy zależności w pliku package.json musisz dodać te pozycje:
"xmlhttprequest": "^1.8.0", "express": "^4.17.0"
Następnie uruchomienie npm instalacji z katalogu funkcji powinno zainstalować zależności w Twoim projekcie:
$ npm install
Jeśli npm wystąpi problem z pobieraniem pakietów, możesz spróbować zapisać xmlhttprequest i wyrazić zgodę za pomocą tego polecenia:
$ npm install express xmlhttprequest --save
Przejdź na abonament Blaze
Aby użyć polecenia firebase deploy
, musisz przejść na abonament Blaze, co wymaga dodania formy płatności do konta. Otwórz Przegląd projektu > Informacje o wykorzystaniu i rozliczeniach i wybierz abonament Blaze dla swojego projektu.
Utwórz serwer Express
Serwer Express korzysta z prostej platformy do odpowiadania na przychodzące żądania GET
i POST
. Utworzyliśmy serwlet, który nasłuchuje żądań POST
, przesyła je do docelowego adresu URL określonego w ładunku i odpowiada z użyciem odpowiedzi otrzymanej z transferu.
Zmodyfikuj plik index.js
w katalogu funkcji na taki:
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);
Aby kierować żądania na nasz serwer, musimy skorygować przepisy z firebase.json
w ten sposób:
{ "hosting": { "public": "public", "ignore": [ "firebase.json", "**/.*", "**/node_modules/**" ], "rewrites": [{ "source": "/proxy**", "function": "app" },{ "source": "**", "destination": "/index.html" } ] } }
Spowoduje to przekierowanie adresów URL rozpoczynających się od /proxy
na nasz serwer Express, a reszta na adres index.html
.
Wywołania interfejsu API serwera proxy
Skoro mamy już serwer, zdefiniujmy identyfikator URI serwera proxy w scripts.js
, aby przeglądarka wysyłała żądania na ten adres:
const PROXY_URI = SERVER_URI + "/proxy";
Następnie dodaj funkcję proxyRequest
o nazwie scripts.js
, która ma taki sam podpis jak funkcja deviceAccessRequest(...)
, na potrzeby pośrednich wywołań dostępu do urządzenia.
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(); }
Ostatnim krokiem jest zastąpienie wywołań deviceAccessRequest(...)
funkcją proxyRequest(...)
w funkcjach postThermostatMode()
i postTemperatureSetpoint()
w elemencie scripts.js
.
Uruchom firebase deploy
, aby zaktualizować aplikację.
$ firebase deploy
Dzięki temu masz teraz uruchomiony serwer proxy Node.js korzystający z usługi Express w Cloud Functions.
Przyznaj uprawnienia funkcji w Cloud Functions
Ostatnim krokiem jest sprawdzenie uprawnień dostępu do funkcji w Cloud Functions i upewnienie się, że aplikacja po stronie klienta może je wywoływać.
W Google Cloud Platform otwórz w menu kartę Cloud Functions i wybierz funkcję w Cloud Functions:
Kliknij Uprawnienia, a następnie Dodaj użytkownika. W polu nowego użytkownika wpisz allUsers i wybierz Cloud Functions > rolę wywołującego funkcje Cloud Functions. Gdy klikniesz Zapisz, pojawi się ostrzeżenie:
Wybranie opcji Zezwalaj na dostęp publiczny umożliwi aplikacji po stronie klienta dostęp do funkcji w Cloud Functions.
Gratulacje! Wszystkie kroki zostały ukończone. Teraz możesz otworzyć aplikację internetową i włączyć ustawienia urządzenia kierowane przez serwer proxy.
Następne kroki
Szukasz sposobów na poszerzenie wiedzy na temat dostępu do urządzenia? Zapoznaj się z dokumentacją funkcji, aby dowiedzieć się więcej o sterowaniu innymi urządzeniami Nest oraz o procesie certyfikacji, które musisz wykonać, aby wprowadzić produkt na rynek.
Rozwiń swoje umiejętności dzięki przykładowej aplikacji internetowej z dostępem do urządzenia, w której w ramach ćwiczenia z Codelabs wdrożysz działającą aplikację internetową do sterowania kamerami, dzwonkami i termostatami Nest.