Połącz inteligentne urządzenia domowe z Asystentem Google

1. Zanim zaczniesz

Jako deweloper internetu rzeczy (IoT) możesz tworzyć działania inteligentnego domu, które dają użytkownikom możliwość sterowania urządzeniami za pomocą sterowania dotykowego w aplikacji Google Home oraz poleceń głosowych w Asystencie.

79266e5f45e6ae20.gif

Działania związane z inteligentnym domem wykorzystują Home Graph, które dostarcza danych kontekstowych o domu i znajdujących się w nim urządzeniach, tworząc logiczną mapę domu. Dzięki temu Asystent może lepiej zrozumieć prośby użytkownika, biorąc pod uwagę jego lokalizację w domu. Na przykład Home Graph może zapisać koncepcję salonu obejmującego wiele typów urządzeń różnych producentów, takich jak termostat, lampa, wentylator i odkurzacz.

d009cef0f903d284.jpeg

Wymagania wstępne

Co utworzysz

W ramach tego ćwiczenia w programie opublikujesz usługę w chmurze, która zarządza wirtualną inteligentną pralką, a potem utworzysz działanie dotyczące inteligentnego domu i połączysz je z Asystentem.

Czego się nauczysz

  • Jak wdrożyć usługę w chmurze dla inteligentnego domu
  • Jak połączyć usługę z Asystentem
  • Jak publikować zmiany stanu urządzenia w Google

Czego potrzebujesz

2. Pierwsze kroki

Włączanie Zarządzania aktywnością

Aby korzystać z Asystenta Google, musisz udostępniać Google określone dane dotyczące aktywności. Asystent Google potrzebuje tych danych do prawidłowego działania. jednak wymóg udostępniania danych nie dotyczy tylko pakietu SDK. Aby udostępnić te dane, utwórz konto Google, jeśli jeszcze go nie masz. Możesz użyć dowolnego konta Google – nie musi to być Twoje konto dewelopera.

Otwórz stronę Zarządzanie aktywnością dla konta Google, którego chcesz używać z Asystentem.

Upewnij się, że te przełączniki są włączone:

  • Internet i Aktywność w aplikacjach – zaznacz też pole wyboru Uwzględnij historię Chrome i aktywność na stronach, urządzeniach i w aplikacjach, które używają usług Google.
  • Informacje z urządzenia
  • Głos Aktywność związana z dźwiękiem

Tworzenie projektu w Actions

  1. Otwórz konsolę programisty Actions on Google.
  2. Kliknij New Project (Nowy projekt), wpisz nazwę projektu i kliknij UTWÓRZ PROJEKT.

3d6b68ca79afd54c.png

Wybierz aplikację Inteligentny dom.

Na ekranie Przegląd w Konsoli Actions wybierz Inteligentny dom.

2fa4988f44f8914b.png

Wybierz kartę Inteligentny dom i kliknij Rozpocznij tworzenie. Przekierujemy Cię do konsoli projektu.

Instalowanie interfejsu wiersza poleceń Firebase

Interfejs wiersza poleceń Firebase (CLI) pozwala udostępniać aplikacje internetowe lokalnie i wdrażać je w Hostingu Firebase.

Aby zainstalować interfejs wiersza poleceń, uruchom w terminalu to polecenie npm:

npm install -g firebase-tools

Aby sprawdzić, czy interfejs wiersza poleceń został prawidłowo zainstalowany, uruchom polecenie:

firebase --version

Autoryzuj interfejs wiersza poleceń Firebase na swoim koncie Google, uruchamiając polecenie:

firebase login

3. Uruchom aplikację startową

Po skonfigurowaniu środowiska programistycznego możesz wdrożyć projekt startowy, aby sprawdzić, czy wszystko jest prawidłowo skonfigurowane.

Pobieranie kodu źródłowego

Kliknij ten link, aby pobrać na swoim komputerze przykładową wersję ćwiczenia z programowania:

...lub sklonuj repozytorium GitHub, używając wiersza poleceń:

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

Informacje o projekcie

Projekt startowy zawiera te podkatalogi:

  • public: Interfejs frontendu do łatwego kontrolowania i monitorowania stanu inteligentnej pralki.
  • functions: W pełni wdrożona usługa w chmurze, która zarządza inteligentną pralką za pomocą Cloud Functions dla Firebase i Bazy danych czasu rzeczywistego Firebase.

Połącz z Firebase

Przejdź do katalogu washer-start, a potem skonfiguruj interfejs wiersza poleceń Firebase za pomocą projektu Actions:

cd washer-start
firebase use <project-id>

Skonfiguruj projekt Firebase

Zainicjuj projekt Firebase.

firebase init

Wybierz funkcje interfejsu wiersza poleceń, Bazę danych czasu rzeczywistego, Funkcje i funkcję Hosting, która obejmuje Hosting 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

Spowoduje to zainicjowanie interfejsów API i funkcji niezbędnych dla Twojego projektu.

Gdy pojawi się prośba, zainicjuj Bazę danych czasu rzeczywistego. Możesz użyć domyślnej lokalizacji instancji bazy danych.

? 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

Ponieważ używasz kodu startowego projektu, wybierz domyślny plik dla reguł zabezpieczeń i upewnij się, że nie zastąpisz istniejącego pliku reguł bazy danych.

? 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

Jeśli ponownie inicjujesz projekt, wybierz Zastąp, gdy pojawi się pytanie, czy chcesz zainicjować czy zastąpić bazę kodu.

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

Podczas konfigurowania funkcji należy użyć plików domyślnych i nie zastąpić istniejących plików index.js i package.json w przykładowym projekcie.

? 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

Jeśli ponownie inicjujesz projekt, na pytanie, czy chcesz zainicjować czy zastąpić funkcje/.gitignore, wybierz Nie.

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

Na koniec skonfiguruj konfigurację Hostingu tak, aby użyć katalogu public w kodzie projektu i użyć istniejącego pliku index.html. Gdy pojawi się pytanie o użycie ESLint, wybierz Nie.

? 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

Jeśli ESLint został przypadkowo włączony, można go wyłączyć na 2 sposoby:

  1. Korzystając z GUI, przejdź do folderu ../functions w projekcie, wybierz ukryty plik .eslintrc.js i usuń go. Nie pomyl go z podobną nazwą (.eslintrc.json).
  2. Przy użyciu wiersza poleceń:
    cd functions
    rm .eslintrc.js
    

Aby mieć pewność, że masz prawidłową i pełną konfigurację Firebase, skopiuj plik firebase.json z katalogu washer-done do katalogu washer-start, zastępując go z pliku washer-start.

W katalogu washer-start:

cp -vp ../washer-done/firebase.json .

Wdrażanie w Firebase

Po zainstalowaniu zależności i skonfigurowaniu projektu możesz uruchomić aplikację po raz pierwszy.

firebase deploy

Oto dane wyjściowe konsoli, które powinny Ci się wyświetlić:

...

✔ Deploy complete!

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

To polecenie wdraża aplikację internetową i kilka funkcji Cloud Functions dla Firebase.

Otwórz URL hostingu w przeglądarce (https://<project-id>.web.app), aby wyświetlić aplikację internetową. Zobaczysz następujący interfejs:

5845443e94705557.png

Ten interfejs internetowy reprezentuje platformę innej firmy do wyświetlania lub modyfikowania stanów urządzeń. Aby rozpocząć wypełnianie bazy danych informacjami z urządzenia, kliknij AKTUALIZUJ. Na stronie nie pojawią się żadne zmiany, ale w bazie danych zostanie zapisany bieżący stan pralki.

Pora połączyć wdrożoną przez Ciebie usługę w chmurze z Asystentem Google za pomocą konsoli Actions.

Konfigurowanie projektu w konsoli Actions

W sekcji Przegląd > Utwórz akcję, kliknij Dodaj działania. Wpisz adres URL funkcji w Cloud Functions, która umożliwia realizację intencji inteligentnego domu, i kliknij Zapisz.

https://us-central1-<project-id>.cloudfunctions.net/smarthome

9d7b223427f587ca.png

W menu Programowanie > Na karcie Wywołanie dodaj wyświetlaną nazwę akcji i kliknij Zapisz. Ta nazwa będzie widoczna w aplikacji Google Home.

774d0c40c351c7da.png

a8c4673eb11d76ee.png

Aby włączyć łączenie kont, kliknij menu Programowanie > Łączenie kont w panelu nawigacyjnym po lewej stronie. Użyj tych ustawień łączenia kont:

Identyfikator klienta

ABC123

Tajny klucz klienta

DEF456

Adres URL autoryzacji

https://us-central1-<project-id>.cloudfunctions.net/fakeauth

Adres URL tokena

https://us-central1-<project-id>.cloudfunctions.net/faketoken

9730d20b90bcc038.png

Kliknij Zapisz, aby zapisać konfigurację łączenia kont, a potem kliknij Przetestuj, aby włączyć testowanie w projekcie.

ee0547f05b5efd98.png

Przekierujemy Cię do aplikacji Simulator. Jeśli nie widzisz opcji „Przetestuj teraz”, kliknij Resetuj test, aby sprawdzić, czy testowanie jest włączone.

d0495810dbadf059.png

Teraz możesz zacząć implementować webhooki niezbędne do łączenia stanu urządzenia z Asystentem.

4. Zbuduj pralkę

Po skonfigurowaniu akcji możesz dodawać urządzenia i wysyłać dane. Twoja usługa w chmurze musi obsługiwać te intencje:

  • Intencja SYNC ma miejsce, gdy Asystent chce się dowiedzieć, z jakimi urządzeniami użytkownik połączył dane. Jest ona wysyłana do usługi, gdy użytkownik połączy konto. W odpowiedzi musisz przesłać ładunek JSON zawierający wszystkie urządzenia użytkownika i ich możliwości.
  • Intencja QUERY występuje, gdy Asystent chce poznać bieżący stan lub stan urządzenia. W odpowiedzi należy przesłać ładunek JSON zawierający stan każdego żądanego urządzenia.
  • Intencja EXECUTE występuje, gdy Asystent chce sterować urządzeniem w imieniu użytkownika. W odpowiedzi musisz przesłać ładunek JSON zawierający stan wykonania każdego żądanego urządzenia.
  • Intencja DISCONNECT ma miejsce, gdy użytkownik odłączy swoje konto od Asystenta. Wyłącz wysyłanie do Asystenta zdarzeń dotyczących urządzeń tego użytkownika.

W kolejnych sekcjach zaktualizujesz funkcje, które zostały wcześniej wdrożone do obsługi tych intencji.

Aktualizacja odpowiedzi SYNC

Otwórz aplikację functions/index.js, która zawiera kod służący do odpowiadania na prośby Asystenta.

Musisz obsługiwać intencję SYNC, zwracając metadane i możliwości urządzenia. Zaktualizuj plik JSON w tablicy onSync, aby zawierał informacje o urządzeniu i zalecane cechy pralki.

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,
        },
      }],
    },
  };
});

Wdrażanie w Firebase

Wdróż zaktualizowaną realizację w chmurze za pomocą interfejsu wiersza poleceń Firebase:

firebase deploy --only functions

Aby przetestować działanie inteligentnego domu, musisz połączyć swój projekt z kontem Google. Dzięki temu możesz przeprowadzać testy za pomocą Asystenta Google i aplikacji Google Home, na których zalogujesz się na to samo konto.

  1. Na telefonie otwórz ustawienia Asystenta Google. Pamiętaj, że musisz zalogować się na to samo konto co w konsoli.
  2. Kliknij Asystent Google > Ustawienia > Sterowanie domem (w sekcji Asystent).
  3. W prawym górnym rogu kliknij ikonę wyszukiwania.
  4. Aby znaleźć swoją aplikację testową, wyszukaj ją, używając prefiksu [test].
  5. Wybierz ten element. Asystent Google uwierzytelni się w usłudze i wyśle żądanie SYNC, prosząc usługę o udostępnienie listy urządzeń użytkownika.

Otwórz aplikację Google Home i sprawdź, czy widzisz pralkę.

ae252220753726f6.png

5. Obsługa poleceń i zapytań

Teraz gdy Twoja usługa w chmurze prawidłowo zgłasza pralkę do Google, musisz dodać możliwość żądania stanu urządzenia i wysyłania poleceń.

Obsługa intencji QUERY

Intencja QUERY obejmuje zestaw urządzeń. W przypadku każdego urządzenia należy podać jego bieżący stan.

W narzędziu functions/index.js zmodyfikuj moduł obsługi QUERY, aby przetwarzał listę urządzeń docelowych zawartych w żądaniu intencji.

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,
  };
});

Dla każdego urządzenia zawartego w żądaniu zwróć bieżący stan zapisany w bazie danych czasu rzeczywistego. Zaktualizuj funkcje queryFirebase i queryDevice, aby zwracać dane o stanie pralki.

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,
  };
};

Obsługa intencji EXECUTE

Intencja EXECUTE obsługuje polecenia służące do aktualizowania stanu urządzenia. Odpowiedź zwróci stan każdego polecenia, na przykład SUCCESS, ERROR lub PENDING, oraz nowy stan urządzenia.

W functions/index.js zmodyfikuj moduł obsługi EXECUTE, aby przetworzyć listę cech, które wymagają aktualizacji, oraz zbiór urządzeń docelowych w przypadku każdego polecenia:

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],
    },
  };
});

Dla każdego polecenia i urządzenia docelowego zaktualizuj wartości w bazie danych czasu rzeczywistego, które odpowiadają żądanej cechy. Zmodyfikuj funkcję updateDevice, aby zaktualizować odpowiednie odwołanie do Firebase i zwrócić zaktualizowany stan urządzenia.

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 = {isRunning: params.start};
      ref = firebaseRef.child(deviceId).child('StartStop');
      break;
    case 'action.devices.commands.PauseUnpause':
      state = {isPaused: params.pause};
      ref = firebaseRef.child(deviceId).child('StartStop');
      break;
  }

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

6. Przetestuj działanie

Po zaimplementowaniu wszystkich 3 intencji możesz sprawdzić, czy akcja kontroluje pralkę.

Wdrażanie w Firebase

Wdróż zaktualizowaną realizację w chmurze za pomocą interfejsu wiersza poleceń Firebase:

firebase deploy --only functions

Sprawdzanie pralki

Teraz zobaczysz, jak zmieniła się wartość, kiedy wypróbujesz na telefonie dowolne z tych poleceń głosowych:

„OK Google, włącz pralkę”.

„OK Google, wstrzymaj pralkę”.

„OK Google, wyłącz pralkę”.

Możesz też sprawdzać bieżący stan pralki, zadając pytania.

„OK Google, czy pralka jest włączona?”

„OK Google, czy pralka działa?”

„OK Google, jaki cykl ma teraz włączona pralka?”

Możesz je zobaczyć w dziennikach, które pojawiają się w sekcji Funkcje w konsoli Firebase w sekcji Funkcje. Więcej informacji o dziennikach Firebase znajdziesz w artykule Zapisywanie i wyświetlanie logów.

Te zapytania i polecenia znajdziesz też w konsoli Google Cloud. Aby to zrobić, kliknij Logowanie > Eksplorator logów. Więcej informacji o logowaniu w Google Cloud znajdziesz w artykule Uzyskiwanie dostępu do logów zdarzeń za pomocą Cloud Logging.

7. Zgłaszanie aktualizacji do Google

Usługa w chmurze została w pełni zintegrowana z intencjami inteligentnego domu, dzięki czemu użytkownicy mogą kontrolować bieżący stan ich urządzeń i wysyłać dotyczące ich zapytania. Implementacja nadal nie pozwala jednak usłudze aktywnie wysyłać do Asystenta informacji o zdarzeniach, takich jak zmiany obecności lub stanu urządzenia.

Funkcja Poproś o synchronizację pozwala wysłać nowe żądanie synchronizacji, gdy użytkownicy dodadzą lub usuną urządzenia albo zmienią się ich możliwości. Dzięki funkcji Stan raportu usługa w chmurze może aktywnie wysyłać informacje o stanie urządzenia do Home Graph, gdy użytkownicy zmienią stan urządzenia (na przykład przez włączenie przełącznika światła) lub zmienią go przy użyciu innej usługi.

W tej sekcji dodasz kod wywołujący te metody z frontendowej aplikacji internetowej.

Włączanie interfejsu HomeGraph API

Interfejs HomeGraph API umożliwia przechowywanie urządzeń i tworzenie o nich zapytań oraz wysyłanie do nich zapytań w obrębie Home Graph użytkownika. Aby korzystać z tego interfejsu API, musisz najpierw otworzyć konsolę Google Cloud i włączyć interfejs HomeGraph API.

W konsoli Google Cloud wybierz projekt, który pasuje do działań.<project-id>.Następnie na ekranie „API Library” (Biblioteka API) interfejsu HomeGraph API kliknij Włącz.

ee198858a6eac112.png

Włącz stan raportu

Zapisuje w bazie danych czasu rzeczywistego aktywując funkcję reportstate w projekcie startowym. Zaktualizuj funkcję reportstate w narzędziu functions/index.js, aby przechwytywać dane zapisane w bazie danych i publikować je na wykresie głównym przez stan raportu.

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);
    });

Włącz synchronizację żądań

Odświeżenie ikony w interfejsie internetowym frontendu aktywuje funkcję requestsync w projekcie startowym. Zaimplementuj funkcję requestsync w functions/index.js, aby wywoływać interfejs HomeGraph API.

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}`);
  }
});

Wdrażanie w Firebase

Wdróż zaktualizowany kod za pomocą interfejsu wiersza poleceń Firebase:

firebase deploy --only functions

Testowanie implementacji

Kliknij przycisk Odśwież ae8d3b25777a5e30.png w interfejsie internetowym i sprawdź, czy w dzienniku konsoli Firebase widzisz żądanie synchronizacji.

Następnie dostosuj atrybuty pralki w interfejsie internetowym frontendu i kliknij Aktualizuj. Sprawdź w dziennikach konsoli Firebase, czy zmiany stanu zgłoszone do Google są widoczne.

8. Gratulacje

674c4f4392e98c1.png

Gratulacje! Udało Ci się zintegrować Asystenta z usługą w chmurze urządzenia za pomocą działań inteligentnego domu.

Więcej informacji

Oto kilka pomysłów, które możesz zastosować, aby zgłębić temat:

Możesz też dowiedzieć się więcej o testowaniu i przesyłaniu akcji do sprawdzenia, w tym o procesie certyfikacji umożliwiającym publikowanie akcji dla użytkowników.