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

1. Zanim zaczniesz

Jako deweloper Internetu Rzeczy (IoT) możesz tworzyć integracje chmur, które umożliwiają użytkownikom sterowanie urządzeniami za pomocą elementów sterujących w aplikacji Google Home i poleceń głosowych Asystenta.

79266e5f45e6ae20.gif

Integracje między chmurami korzystają z Home Graph, aby dostarczać dane kontekstowe o domu i jego urządzeniach, tworząc logiczną mapę domu. Dzięki temu kontekstowi Asystent może lepiej zrozumieć prośby użytkownika w zależności od jego lokalizacji w domu. Na przykład Home Graph może przechowywać koncepcję salonu, w którym znajdują się różne typy urządzeń różnych producentów, takich jak termostat, lampa, wentylator i odkurzacz.

d009cef0f903d284.jpeg

Wymagania wstępne

Co utworzysz

W tym ćwiczeniu z programowania opublikujesz usługę w chmurze, która zarządza wirtualną inteligentną pralką, a potem utworzysz integrację chmur i połączysz ją z Asystentem.

Czego się nauczysz

  • Jak wdrożyć usługę chmurową 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ępnić Google określone dane o aktywności. Asystent Google potrzebuje tych danych do prawidłowego działania, ale udostępnianie danych nie jest wymagane w przypadku pakietu SDK. Aby udostępniać 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ą na koncie Google, którego chcesz używać z Asystentem.

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

  • Aktywność w internecie i aplikacjach – pamiętaj też, aby zaznaczyć pole wyboru Uwzględnij historię Chrome i aktywność na stronach, urządzeniach i w aplikacjach, które używają usług Google.
  • Informacje o urządzeniu
  • Aktywność związana z głosem i dźwiękiem

Tworzenie projektu integracji między chmurami

  1. Otwórz konsolę dewelopera.
  2. Kliknij Utwórz projekt, wpisz nazwę projektu i kliknij Utwórz projekt.

Nazwa projektu

Wybierz integrację chmur

W sekcji Cloud-to-cloud (Między chmurami) na stronie Project Home (Strona projektu) w Konsoli programisty kliknij Add cloud-to-cloud integration (Dodaj integrację między chmurami).

Dodawanie integracji między chmurami

Zainstaluj wiersz poleceń Firebase

Interfejs wiersza poleceń Firebase (CLI) umożliwia obsługę aplikacji internetowych lokalnie i wdrażanie ich do 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:

firebase --version

Autoryzuj wiersz poleceń Firebase za pomocą swojego konta Google, wykonując:

firebase login

3. Uruchamianie aplikacji startowej

Gdy już skonfigurujesz środowisko programistyczne, możesz wdrożyć projekt startowy, aby sprawdzić, czy wszystko jest prawidłowo skonfigurowane.

Pobieranie kodu źródłowego

Aby pobrać na maszynę programistyczną przykładowy projekt tego ćwiczenia, kliknij ten link:

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

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

Informacje o projekcie

Projekt startowy zawiera te katalogi podrzędne:

  • public: Interfejs użytkownika umożliwiający łatwe sterowanie pralką i monitorowanie jej stanu.
  • functions: W pełni zaimplementowana usługa w chmurze, która zarządza inteligentną pralką za pomocą Cloud Functions dla Firebase i Bazy danych czasu rzeczywistego Firebase.

Tworzenie projektu Firebase

  1. Otwórz Firebase.
  2. Kliknij Utwórz projekt i wpisz nazwę projektu.
  3. Zaznacz pole wyboru akceptacji i kliknij Dalej. Jeśli nie ma pola wyboru dotyczącego umowy, możesz pominąć ten krok.
    Utwórz projekt Firebase
  4. Po utworzeniu projektu Firebase odszukaj jego identyfikator. Kliknij Przegląd projektu, a potem ikonę ustawień > Ustawienia projektu.
    Otwieranie ustawień projektu
  5. Twój projekt jest widoczny na karcie Ogólne.
    Ogólne ustawienia projektu

Połącz się z Firebase

Przejdź do katalogu washer-start, a potem skonfiguruj wiersz poleceń Firebase w ramach projektu integracji:

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

Skonfiguruj projekt Firebase

Inicjalizacja projektu Firebase.

firebase init

Wybierz funkcje wiersza poleceń, Baza 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 niezbędnych interfejsów API i funkcji w projekcie.

Po wyświetleniu odpowiedniej prośby zainicjuj bazę danych Firebase Realtime Database. Możesz użyć domyślnej lokalizacji dla 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 podstawowego, wybierz domyślny plik 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, gdy pojawi się pytanie, czy chcesz zainicjować lub 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 upewnij się, że nie zastąpisz istniejących plików index.jspackage.json w próbnym 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, gdy pojawi się pytanie, czy chcesz zainicjować lub zastąpić pliki functions/.gitignore, wybierz Nie.

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

Na koniec skonfiguruj ustawienia hostingu, aby używać katalogu public w kodzie projektu, i użyj istniejącego pliku index.html. Gdy pojawi się pytanie o używanie 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ło przypadkowo włączone, możesz je wyłączyć na 2 sposoby:

  1. Za pomocą interfejsu graficznego przejdź do folderu ../functions w ramach projektu, wybierz ukryty plik .eslintrc.js i usuń go. Nie myl go z elementem o podobnej nazwie .eslintrc.json.
  2. W wierszu poleceń:
    cd functions
    rm .eslintrc.js
    

W pliku washer-done/firebase.json uzupełnij kod:

{
  "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"
      ]
    }
  ]
}

Wdrażanie w Firebase

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

firebase deploy

Dane wyjściowe konsoli powinny wyglądać tak:

...

✔ Deploy complete!

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

To polecenie wdraża aplikację internetową wraz z kilkoma funkcjami Cloud Functions dla Firebase.

Aby wyświetlić aplikację internetową, otwórz w przeglądarce (https://.web.app) adres URL hostingu. Zobaczysz ten interfejs:

5845443e94705557.png

Ten interfejs internetowy reprezentuje platformę innej firmy, która umożliwia wyświetlanie i modyfikowanie stanów urządzenia. Aby rozpocząć wypełnianie bazy danych informacjami o urządzeniach, kliknij Zaktualizuj. Nie zobaczysz żadnych zmian na stronie, ale bieżący stan pralki zostanie zapisany w bazie danych.

Teraz możesz połączyć wdrożony przez siebie serwis chmurowy z Asystentem Google za pomocą Konsoli dewelopera Google Home.

Konfigurowanie projektu w Konsoli deweloperów

Na karcie Tworzenie dodaj Wyświetlaną nazwę interakcji. Ta nazwa będzie widoczna w aplikacji Google Home.

Dodawanie wyświetlanej nazwy

W sekcji Marka aplikacji prześlij plik png ikony aplikacji o rozmiarze 144 × 144 pikseli i nazwie .png.

Dodawanie ikony aplikacji

Aby włączyć łączenie kont, użyj tych ustawień:

Identyfikator klienta

ABC123

Tajny klucz klienta

DEF456

Adres URL autoryzacji

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

Adres URL tokena

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

Aktualizacja adresów URL służących do łączenia kont

W sekcji URL realizacji w chmurze wpisz adres URL funkcji w chmurze, która zapewnia realizację intencji związanych z inteligentnym domem.

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

Dodawanie adresu URL funkcji w Cloud Functions

Aby zapisać konfigurację projektu, kliknij Zapisz, a potem kliknij Dalej: testowanie, aby włączyć testowanie projektu.

Testowanie integracji z chmury do chmury

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

4. Tworzenie pralki

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

  • Intencje SYNC występują, gdy Asystent chce wiedzieć, jakie urządzenia są połączone z użytkownikiem. Jest on wysyłany do Twojej usługi, gdy użytkownik połączy konto. W odpowiedzi należy podać dane JSON wszystkich urządzeń użytkownika i ich możliwości.
  • Intencją QUERY jest, aby Asystent poznał bieżący stan urządzenia. Powinieneś odpowiedzieć ładunkiem JSON z stanem każdego żądanego urządzenia.
  • Intencje EXECUTE występują, gdy Asystent chce sterować urządzeniem w imieniu użytkownika. W odpowiedzi należy podać treść w formacie JSON z informacjami o stanie wykonania na każdym z wybranych urządzeń.
  • Intencje DISCONNECT występują, gdy użytkownik odłączy swoje konto od Asystenta. Należy przestać wysyłać do Asystenta zdarzenia z urządzeń tego użytkownika.

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

Aktualizowanie odpowiedzi SYNC

Otwórz functions/index.js, który zawiera kod odpowiadający na żądania Asystenta.

Musisz obsłużyć SYNC, zwracając metadane i możliwości urządzenia. Zaktualizuj dane JSON w tablicy onSync, aby zawierały 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óż zaktualizowane wypełnianie w chmurze za pomocą wiersza poleceń Firebase:

firebase deploy --only functions

Aby przetestować integrację między chmurami, musisz połączyć projekt z kontem Google. Umożliwia to testowanie w interfejsach Asystenta Google i aplikacji Google Home, w których użytkownik jest zalogowany na to samo konto.

  1. Na telefonie otwórz ustawienia Asystenta Google. Pamiętaj, że musisz zalogować się na to samo konto, którego używasz w konsoli.
  2. Kliknij Asystent Google > Ustawienia > Sterowanie domem (w sekcji Asystent).
  3. W prawym górnym rogu kliknij ikonę wyszukiwania.
  4. Aby znaleźć konkretną aplikację testową, wyszukaj ją, używając prefiksu [test].
  5. Wybierz ten element. Asystent Google uwierzytelnia się w Twojej usłudze i wysyła żądanie SYNC, aby Twoja usługa podała listę urządzeń użytkownika.

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

ae252220753726f6.png

5. Obsługa poleceń i zapytań

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

Obsługa zamiaru QUERY

QUERY intencja obejmuje zestaw urządzeń. W przypadku każdego urządzenia podaj jego aktualny stan.

functions/index.js zmodyfikuj QUERY, aby przetworzyć listę urządzeń docelowych zawartych w prośbie o intencję.

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

W przypadku każdego urządzenia zawartego w żądaniu zwraca aktualny stan przechowywany w BazaDanychWczasieRzealnym. Zmodyfikuj funkcje queryFirebasequeryDevice, aby zwracały 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 zamiaru EXECUTE

Intencją EXECUTE zarządza poleceniami aktualizowania stanu urządzenia. Odpowiedź zwraca stan każdego polecenia (na przykład SUCCESS, ERROR lub PENDING) oraz nowy stan urządzenia.

functions/index.js zmodyfikuj element obsługi EXECUTE, aby przetworzyć listę cech, które wymagają aktualizacji, oraz zestaw 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],
    },
  };
});

W przypadku każdego polecenia i urządzenia docelowego zaktualizuj w Bazadzie danych w czasie rzeczywistym wartości odpowiadające żądanej właściwości. 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 = 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. Testowanie integracji

Po zaimplementowaniu wszystkich 3 zamierowań możesz przetestować, czy integracja steruje pralką.

Wdrażanie w Firebase

Wdróż zaktualizowane wypełnianie w chmurze za pomocą wiersza poleceń Firebase:

firebase deploy --only functions

Testowanie pralki

Teraz możesz zobaczyć, jak zmienia się wartość, gdy na telefonie użyjesz któregoś z tych poleceń głosowych:

„OK Google, włącz pralkę”

„OK Google, wstrzymaj pranie”

„OK Google, zatrzymaj pralkę”

Możesz też sprawdzić aktualny stan pralki, zadawać pytania.

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

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

„OK Google, na którym cyklu jest moja pralka?”

Te zapytania i polecenia możesz wyświetlić w logach, które pojawiają się pod funkcją w sekcji FunkcjeKonsoli 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, gdy klikniesz Logowanie > Eksplorator logów. Więcej informacji o logowaniu w Google Cloud znajdziesz w artykule Dostęp do dzienników zdarzeń za pomocą Cloud Logging.

7. Zgłaszanie zmian do Google

Twoja usługa w chmurze jest w pełni zintegrowana z intencjami inteligentnego domu, co umożliwia użytkownikom kontrolowanie i wysyłanie zapytań dotyczących bieżącego stanu ich urządzeń. W obecnej implementacji brakuje jednak sposobu na to, aby Twoja usługa mogła aktywnie wysyłać do Asystenta informacje o wydarzeniach, np. o zmianach stanu lub obecności urządzenia.

Za pomocą opcji Poproś o synchronizację możesz wysłać prośbę o synchronizację, gdy użytkownicy dodają lub usuwają urządzenia albo gdy zmieniają się możliwości ich urządzeń. Dzięki raportowaniu stanu usługa w chmurze może proaktywnie wysyłać stan urządzenia do Home Graph, gdy użytkownicy fizycznie zmienią stan urządzenia (np. włączą przełącznik światła) lub zmienią stan za pomocą innej usługi.

W tej sekcji dodasz kod, który wywoła te metody z frontendowej aplikacji internetowej.

Włącz interfejs HomeGraph API

Interfejs HomeGraph API umożliwia przechowywanie urządzeń i ich stanów oraz wysyłanie zapytań o te dane w ramach 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 odpowiada integracji. <project-id>.Następnie na ekranie Biblioteka interfejsów API dla interfejsu HomeGraph API kliknij Włącz.

ee198858a6eac112.png

Włączanie raportowania stanu

Zapisy w BDB uruchamiają funkcję reportstate w projekcie startowym. Zaktualizuj funkcję reportstate w funkcji functions/index.js, aby rejestrować dane zapisane w bazie danych i publikować je w usłudze Dom Graph za pomocą 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łącz synchronizację żądań

Odświeżenie ikony w interfejsie internetowym front-endu powoduje uruchomienie funkcji requestsync w projekcie startowym. Aby wywołać interfejs HomeGraph API, zaimplementuj funkcję requestsync w funkcji functions/index.js.

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ą wiersza poleceń Firebase:

firebase deploy --only functions

Testowanie implementacji

W interfejsie internetowym kliknij przycisk Odśwież ae8d3b25777a5e30.png i sprawdź, czy w rejestrze konsoli Firebase pojawia się prośba o synchronizację.

Następnie dostosuj atrybuty urządzenia do prania w interfejsie internetowym i kliknij Zaktualizuj. Sprawdź, czy w logach konsoli Firebase widać stan zgłoszony do Google.

8. Gratulacje

674c4f4392e98c1.png

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

Więcej informacji

Oto kilka pomysłów, które możesz wdrożyć, aby uzyskać więcej informacji:

Możesz też dowiedzieć się więcej o testowaniu i przesyłaniu integracji do sprawdzenia, w tym o procesie certyfikacji w celu opublikowania integracji dla użytkowników.