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

1. Zanim zaczniesz

Jako deweloper internetu rzeczy możesz tworzyć działania w inteligentnym domu, które umożliwiają użytkownikom sterowanie urządzeniami za pomocą sterowania dotykowego w aplikacji Google Home oraz poleceń głosowych przy użyciu Asystenta.

79266e5f45e6ae20.gif

Działania związane z inteligentnym domem korzystają z wykresu domowego, aby dostarczać dane kontekstowe o domu i jego urządzeniach, tworząc logiczną mapę domu. Dzięki temu Asystent będzie mógł lepiej zrozumieć, czego domaga się użytkownik, względem jego lokalizacji w domu. Na przykład Home Graph może przechowywać informacje o salonie, w których znajdują się różne rodzaje urządzeń różnych producentów, takie jak termostat, lampa, wentylator czy odkurzacz.

d009cef0f903d284.jpeg

Wymagania wstępne

Co utworzysz

W ramach tego ćwiczenia w programie opublikujesz usługę w chmurze, która będzie zarządzać wirtualną pralką, a następnie utworzysz akcję inteligentnego domu i połączysz ją z Asystentem.

Czego się nauczysz

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

Czego potrzebujesz

2. Wprowadzenie

Włączanie Zarządzania aktywnością

Aby korzystać z Asystenta Google, musisz udostępnić Google określone dane o aktywności. Asystent Google potrzebuje tych danych do prawidłowego działania, ale wymóg udostępniania danych nie dotyczy tylko pakietu SDK. Aby udostępnić te dane, musisz utworzyć konto Google. Możesz użyć dowolnego konta Google – nie musi to być konto dewelopera.

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

Sprawdź, czy te przełączniki są włączone:

  • Aktywność w internecie i 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
  • Aktywność związana z głosem i dźwiękiem

Tworzenie projektu w Actions

  1. Otwórz Konsolę programisty Actions on Google.
  2. Kliknij 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

Aby przejść do konsoli projektu, wybierz kartę Inteligentny dom i kliknij Rozpocznij tworzenie.

Instalowanie interfejsu wiersza poleceń Firebase

Interfejs wiersza poleceń Firebase (CLI) umożliwia udostępnianie aplikacji internetowych lokalnie i wdrażanie ich 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. Uruchamianie aplikacji startowej

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

Pobieranie kodu źródłowego

Kliknij poniższy link, aby pobrać na komputerze przykładowy program do przykładowego ćwiczenia:

Możesz też sklonować repozytorium GitHub z wiersza poleceń:

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

Informacje o projekcie

Projekt startowy zawiera te podkatalogi:

  • public: Interfejs frontendu umożliwiający łatwe kontrolowanie i monitorowanie stanu inteligentnej pralki.
  • functions: W pełni wdrożona usługa w chmurze, która umożliwia zarządzanie inteligentną pralką za pomocą Cloud Functions dla Firebase i Bazy danych czasu rzeczywistego Firebase.

Łączenie z Firebase

Przejdź do katalogu washer-start, a następnie skonfiguruj interfejs wiersza poleceń Firebase w projekcie Actions:

cd washer-start
firebase use <project-id>

Konfigurowanie projektu Firebase

Zainicjuj projekt Firebase.

firebase init

Wybierz funkcje interfejsu wiersza poleceń, Bazę danych czasu rzeczywistego, Funkcje i funkcję Hosting obejmującą 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 wymaganych interfejsów API i funkcji w projekcie.

Po wyświetleniu monitu 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 projektu początkowego, wybierz domyślny plik reguł zabezpieczeń i upewnij się, że nie zastąpisz istniejącego pliku z regułami 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, w odpowiedzi na pytanie, czy chcesz zainicjować, czy zastąpić bazę kodu, wybierz Zastąp.

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

Podczas konfigurowania funkcji użyj plików domyślnych i nie zastępuj istniejących plików index.js ani 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, w odpowiedzi 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żywała katalogu public w kodzie projektu i używała istniejącego pliku index.html. W odpowiedzi na pytanie o korzystanie z 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ł włączony przypadkowo, można go wyłączyć na 2 sposoby:

  1. W interfejsie 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 upewnić się, że konfiguracja Firebase jest prawidłowa, skopiuj plik firebase.json z katalogu app-done do katalogu app-start, zastępując plik w pliku app-start.

W katalogu app-start:

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

Wdrażanie w Firebase

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

firebase deploy

Powinny pojawić się te dane wyjściowe konsoli:

...

✔ 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 w przeglądarce URL hostowania (https://<project-id>.web.app), aby wyświetlić aplikację internetową. Zobaczysz ten interfejs:

5845443e94705557.png

Ten interfejs internetowy reprezentuje platformę firmy zewnętrznej do wyświetlania i modyfikowania stanów urządzeń. Aby rozpocząć wypełnianie bazy danych informacjami o urządzeniu, kliknij AKTUALIZUJ. Na stronie nie pojawią się żadne zmiany, ale bieżący stan pralki będzie przechowywany w bazie danych.

Teraz możesz połączyć wdrożoną z Asystenta Google usługę w chmurze za pomocą Konsoli Actions.

Konfigurowanie projektu w Konsoli Actions

W sekcji Przegląd > Utwórz działanie wybierz Dodaj działania. Wpisz adres URL funkcji w Cloud Functions, która realizuje intencje inteligentnego domu, i kliknij Zapisz.

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

9d7b223427f587ca.png

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

774d0c40c351c7da.png

a8c4673eb11d76ee.png

Żeby włączyć łączenie kont, w menu nawigacyjnym po lewej stronie wybierz opcję Programowanie > Łączenie kont. Użyj tych ustawień łączenia kont:

Identyfikator klienta

ABC123

Tajny klucz klienta

DEF456

Adres URL autoryzacji

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

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 Symulatora. Jeśli nie widzisz opcji „Testuj teraz włączony”, kliknij Resetuj test, aby sprawdzić, czy testowanie jest włączone.

d0495810dbadf059.png

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

4. Utwórz pralkę

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

  • Intencja SYNC występuje, gdy Asystent chce sprawdzić, jakie urządzenia połączył użytkownik. Jest ona wysyłana do Twojej usługi, gdy użytkownik połączy konto. W odpowiedzi musisz przesłać ładunek JSON dotyczący wszystkich urządzeń użytkownika i ich możliwości.
  • Intencja QUERY występuje, gdy Asystent chce poznać bieżący stan urządzenia. W odpowiedzi należy przesłać ładunek JSON zawierający informacje o stanie każdego żądanego urządzenia.
  • Intencja EXECUTE występuje, gdy Asystent chce sterować urządzeniem w imieniu użytkownika. W odpowiedzi należy przesłać ładunek JSON zawierający informacje o stanie wykonania każdego żądanego urządzenia.
  • Intencja DISCONNECT występuje, gdy użytkownik odłącza swoje konto od Asystenta. Musisz przestać wysyłać do Asystenta zdarzenia z urządzeń tego użytkownika.

W kolejnych sekcjach zaktualizujesz wdrożone wcześniej funkcje na potrzeby obsługi tych intencji.

Zaktualizuj odpowiedź SYNC

Otwórz aplikację functions/index.js, która zawiera kod odpowiadający na prośby od Asystenta.

Musisz obsłużyć intencję SYNC, zwracając metadane i funkcje urządzenia. Zaktualizuj plik JSON w tablicy onSync, aby uwzględnić informacje o urządzeniu i zalecane cechy pralki do ubrań.

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. Umożliwi Ci to testowanie na platformach Asystenta Google i w aplikacji Google Home po zalogowaniu się na to samo konto.

  1. Na telefonie otwórz ustawienia Asystenta Google. Pamiętaj, aby zalogować się na to samo konto co w konsoli.
  2. Wybierz Asystent Google > Ustawienia > Sterowanie domem (w sekcji Asystent).
  3. Kliknij ikonę wyszukiwania w prawym górnym rogu.
  4. Wyszukaj swoją aplikację testową, korzystając z prefiksu [test].
  5. Wybierz ten element. Asystent Google uwierzytelni się w Twojej usłudze i wyśle żądanie SYNC, prosząc usługę o podanie listy urządzeń dla 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 zbiór urządzeń. W odpowiedzi na każde urządzenie należy podać bieżący stan.

W functions/index.js zmodyfikuj moduł obsługi QUERY, aby przetworzyć 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 w żądaniu zwraca 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 do aktualizowania stanu urządzenia. Odpowiedź zwraca stan każdego polecenia (na przykład SUCCESS, ERROR lub PENDING) oraz nowy stan urządzenia.

W functions/index.js edytuj moduł obsługi EXECUTE, aby przetworzyć listę cech, które wymagają aktualizacji, oraz zbiór urządzeń docelowych dla 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 odniesienie 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. Testowanie działania

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

Testowanie pralki

Teraz możesz sprawdzić tę zmianę po wypróbowaniu na telefonie dowolnego z tych poleceń głosowych:

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

„OK Google, wstrzymaj pralkę”.

„OK Google, zatrzymaj pralkę”.

Możesz też zadać pytania, aby sprawdzić bieżący stan pralki.

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

„OK Google, czy moja pralka działa?”

„OK Google, jaki jest cykl pralki?”

Te zapytania i polecenia możesz wyświetlać w dziennikach wyświetlanych w obszarze funkcji w sekcji Funkcje konsoli Firebase. Więcej informacji o logach Firebase znajdziesz w artykule Zapisywanie i wyświetlanie logów.

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

7. Zgłaszanie aktualizacji do Google

Usługa w chmurze jest w pełni zintegrowana z intencjami inteligentnego domu, dzięki czemu użytkownicy mogą kontrolować bieżący stan swoich urządzeń i wysyłać dotyczące ich zapytania. Jednak w dalszym ciągu nie ma możliwości, aby usługa aktywnie wysyłała do Asystenta informacje o zdarzeniach, np. o zmianach obecności lub stanu urządzenia.

Za pomocą opcji Poproś o synchronizację możesz wysłać nowe żądanie synchronizacji, gdy użytkownicy dodają lub usuwają urządzenia albo gdy zmienią się możliwości ich urządzeń. Dzięki stanowi raportu usługa w chmurze może proaktywnie wysyłać informacje o stanie urządzenia do Home Graph, gdy użytkownik fizycznie zmieni stan urządzenia (np. włączy przełącznik światła) lub zmieni go przy użyciu innej usługi.

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

Włączanie interfejsu HomeGraph API

HomeGraph API umożliwia przechowywanie i zapytania dotyczące urządzeń oraz ich stanów w Home Graph. Aby używać tego interfejsu API, musisz najpierw otworzyć konsolę Google Cloud i włączyć interfejs HomeGraph API.

W konsoli Google Cloud wybierz projekt zgodny z działaniami <project-id>.. Następnie na ekranie Biblioteka API interfejsu HomeGraph kliknij Włącz.

ee198858a6eac112.png

Włącz stan raportu

Zapisy w Bazie danych czasu rzeczywistego aktywują funkcję reportstate w projekcie startowym. Zaktualizuj funkcję reportstate w narzędziu functions/index.js, aby przechwytywać dane zapisane w bazie danych i opublikować je w Home Graph w ramach Stanu 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łączanie synchronizacji żądań

Odświeżenie ikony w interfejsie internetowym frontendu powoduje uruchomienie funkcji requestsync w projekcie startowym. Zaimplementuj funkcję requestsync w tabeli functions/index.js, aby wywoływać interfejs 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}`);
  }
});

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ź, czy w dziennikach konsoli Firebase widzisz zmianę stanu zgłoszona do Google.

8. Gratulacje

674c4f4392e98c1.png

Gratulacje! Udało Ci się zintegrować Asystenta z usługą w chmurze na urządzeniu za pomocą akcji inteligentnego domu.

Więcej informacji

Oto kilka pomysłów, które możesz wykorzystać, aby zwiększyć swoją skuteczność:

Możesz też dowiedzieć się więcej o testowaniu i przesyłaniu akcji do sprawdzenia, w tym o procesie certyfikacji w celu publikowania go użytkownikom.