Tworzenie aplikacji mobilnej przy użyciu interfejsów API Home na iOS

1. Wprowadzenie

f154e30306882c74.png

Czym są interfejsy Home API?

Interfejsy Google Home API to zestaw bibliotek, które umożliwiają deweloperom korzystanie z ekosystemu Google Home. Interfejsy Home API umożliwiają deweloperom tworzenie aplikacji, które bezproblemowo konfigurują inteligentne urządzenia domowe i nimi sterują.

3e11583c779a2cec.png

Ten film zawiera krótkie wprowadzenie do aplikacji mobilnej, którą będziesz tworzyć. Oglądaj go podczas wykonywania ćwiczenia.

Komponenty interfejsów Home API

Interfejsy Home API składają się z:

  • Interfejsy DeviceStructure API: interakcja z domem użytkownika. Aplikacje mogą używać tych interfejsów API do odczytywania informacji o urządzeniach, pomieszczeniach i budynkach (np. sprawdzania aktualnej temperatury termostatu) oraz sterowania urządzeniami (np. zmiany nastawy termostatu).
  • Interfejs Commissioning API: umożliwia łatwe konfigurowanie nowych urządzeń Matter w strukturze.
  • Automation API: tworzenie, usuwanie i wykonywanie zapytań dotyczących automatyzacji działających w domu użytkownika.

Wymagania wstępne

Czego się nauczysz

  • Jak utworzyć aplikację na iOS przy użyciu interfejsów Home API zgodnie ze sprawdzonymi metodami.
  • Jak używać interfejsów Device i Structure API do reprezentowania inteligentnego domu i sterowania nim.
  • Jak używać interfejsu Commissioning API do dodawania urządzeń do ekosystemu Google Home.
  • Jak używać interfejsu Automation API do tworzenia podstawowej automatyzacji.

2. Konfigurowanie domu

Przygotowywanie urządzeń

Google Home Playground oferuje wiele gotowych emulowanych inteligentnych urządzeń domowych i jest zalecane do poznawania pełnego potencjału interfejsów Home API, zwłaszcza jeśli masz w domu ograniczoną liczbę urządzeń.

Postępuj zgodnie z instrukcjami, aby zalogować się na Google Home Playgroundpołączyć kontaaplikacji Google Home. Po wykonaniu tych czynności urządzenia powinny być widoczne na karcie „Urządzenia” w aplikacji Google Home.

c892afce113abe8f.png

3. Przygotowania

Pobieranie kodu przykładowej aplikacji

Zacznij od sklonowania kodu źródłowego z GitHuba:

git clone https://github.com/google-home/google-home-api-sample-app-ios.git

Katalog z przykładami zawiera 2 gałęzie: startfinished.

  • start: kod początkowy tego projektu, w którym wprowadzisz zmiany, aby ukończyć ćwiczenia z programowania.
  • finished: gotowy kod do tego ćwiczenia z programowania, który służy do sprawdzania wykonanej pracy.

Poznaj kod „start”

Zacznij od przełączenia się na gałąź start sklonowanego repozytorium:

git checkout start

Ta gałąź zawiera kod początkowy projektu. W trakcie ćwiczenia będziesz modyfikować ten kod, aby wdrożyć pełną funkcjonalność. Przykładowa aplikacja w ramach ćwiczeń z programowania udostępnia podstawową strukturę napisaną w języku Swift, która umożliwia interakcję z pakietem SDK Home API na iOS. Przyjrzyjmy się najważniejszym komponentom w projekcie start:

  • Main Entry (GoogleHomeAPISampleIOSApp): znajduje się w GoogleHomeAPISampleIOS/Main/GoogleHomeAPISampleIOS.swift i jest głównym punktem wejścia do aplikacji. Konfiguruje i inicjuje pakiet SDK oraz ustawia główny interfejs użytkownika.
  • Core Views (View/):
    • MainView.swift: widok główny po uruchomieniu, zawierający główny element NavigationView. Odpowiada za wybór aktywnej struktury Google Home i wyświetla odpowiedni symbol StructureView.
    • StructureView.swift: wyświetla zawartość aktualnie wybranej struktury. Możesz przełączać się między siatką Urządzenia a listą Automatyzacje za pomocą kart. Zawiera też menu do dodawania pomieszczeń i urządzeń.
    • DeviceView.swift: reprezentuje interaktywny kafelek pojedynczego urządzenia w StructureView siatce.
    • AutomationsView.swift: wyświetla listę istniejących automatyzacji struktury i umożliwia tworzenie nowych automatyzacji oraz wyświetlanie ich szczegółów.
  • ViewModels (ViewModel/): Te klasy zarządzają stanem i logiką widoków.
    • AccountViewModel.swift: obsługuje połączenie z obiektem Home i zarządza stanem uwierzytelniania.
    • MainViewModel.swift: zarządza listą dostępnych obiektów Structure i śledzi wybraną strukturę.
    • StructureViewModel.swift: zarządza wyświetlaniem pomieszczeń i DeviceControl obiektów w wybranej strukturze.
    • AutomationList.swift, AutomationViewModel.swift itd.: obsługuje pobieranie, wyświetlanie, tworzenie i zarządzanie automatyzacjami.
  • Device Controls (ViewModel/Device/):
    • DeviceControl.swift: klasa bazowa do reprezentowania urządzeń, którymi można sterować, w interfejsie.
    • Konkretne podklasy (LightControl.swift, FanControl.swift, OnOffPlugInUnitControl.swift itd.): wdrażaj logikę interfejsu, sterowanie urządzeniem i mapowanie stanu dla różnych typów urządzeń na podstawie ich cech.
    • DeviceControlFactory.swift: Odpowiada za utworzenie odpowiedniej podklasy DeviceControl dla danego HomeDevice.
  • Commissioning (Commissioning/):
    • CommissioningManager.swift: Zawiera logikę zarządzania procesem wdrażania urządzenia Matter.
  • Utilities & UX (Utils/, UX/, Storage/): zawiera kod pomocniczy dla elementów interfejsu (kolory, wymiary), obsługi błędów, przechowywania danych (SelectedStructureStorage.swift) i innych narzędzi.

W tym laboratorium znajdziesz komentarze, takie jak TODO, lub zakomentowane bloki kodu i alerty w projekcie start. Oznaczają one sekcje, w których musisz dodać lub odkomentować kod, aby wdrożyć wymaganą funkcję, zgodnie z podanymi instrukcjami.

Tworzenie plików konfiguracji wdrożenia Apple

Aby skonfigurować App Attest, postępuj zgodnie z instrukcjami tworzenia plików konfiguracji wdrożenia Apple. Pamiętaj, że po skonfigurowaniu aplikację można wdrożyć tylko na prawdziwym urządzeniu, a nie w symulatorze.

Konfigurowanie uwierzytelniania

Aby uzyskać identyfikator klienta OAuth i włączyć interfejsy Home API, najpierw zaloguj się w Google Cloud i utwórz nowy projekt lub wybierz istniejący. Następnie wykonaj podane czynności, aby wygenerować identyfikator klienta OAuth i włączyć interfejsy Home API oraz dodać swoje konto do listy dozwolonych.

Konfigurowanie pakietu SDK

Pobierz pakiet Home APIs SDK na iOS i skonfiguruj go, korzystając z instrukcji konfiguracji podanych w sekcji Konfigurowanie pakietu SDK. Pamiętaj, aby zastąpić HOME_API_TODO_ADD_APP_GROUP nazwą swojej grupy aplikacji.

Kompilowanie i uruchamianie projektu

Po utworzeniu i uruchomieniu projektu z gałęzią start powinno pojawić się okno TODO i ekran z komunikatem „Wymagane logowanie”. Interakcja z interfejsami Home API zostanie zaimplementowana w kolejnych sekcjach.

bd56b7080037e38a.png 9c0f08a3f4197a77.png

Uwaga: znajdź kod, który wymaga modyfikacji, wyszukując w projekcie tekst wyświetlany w oknie. Wyszukaj na przykład „TODO: initialize Home”.

4. Zdarzenie inicjujące

Inicjowanie Home

Zanim zaczniesz korzystać z interfejsów Home API na iOS, musisz zainicjować w aplikacji klasę Home. Jest to element najwyższego poziomu w pakiecie SDK, który zapewnia dostęp do wszystkich elementów w strukturze użytkownika.Home Gdy zażądasz wszystkich encji określonego typu, interfejs API zwróci obiekt Query, który umożliwia wybór sposobu otrzymywania wyników. W GoogleHomeAPISampleIOS/Accounts/AccountViewModel.swift usuń komentarz i alert w connect(), aby wdrożyć inicjowanie domu.

  /// TODO: initialize Home
  /// Remove comments to initialize Home and handling permission.
  private func connect() {
    Task {
      do {
        self.home = try await Home.connect()
      } catch {
        Logger().error("Auth error: \(error).")
      }
    }
  }

Uprawnienia do korzystania z interfejsów Home API

Ekran zgody pojawi się po uruchomieniu aplikacji. Wybierz strukturę Google Home i konto, które znajduje się na liście dozwolonych w projekcie Google Cloud.

47310f458c0094d9.png 4a571dbd9979a88c.png e29c75891a3a67af.png

5. Urządzenia i konstrukcje

Pobieranie informacji o pomieszczeniach i urządzeniach

GoogleHomeAPISampleIOS/ViewModel/StructureViewModel.swift usuń komentarz i alert w getRoomsAndDevices(), aby uzyskać sale i urządzenia w wybranej strukturze z home.rooms()home.devices().

  /// TODO: get rooms and devices
  /// Remove comments to get the rooms and devices from home entry
  private func getRoomsAndDevices(){
    self.home.rooms().batched()
      .combineLatest(self.home.devices().batched())
      .receive(on: DispatchQueue.main)
      .catch { error in
        Logger().error("Failed to load rooms and devices: \(error)")
        return Just((Set<Room>(), Set<HomeDevice>()))
      }
      .map { [weak self] rooms, devices in
        guard let self = self else { return [] }
        self.hasLoaded = true
        return self.process(rooms: rooms, devices: devices)
      }
      /// receive from .map and .assign() to publisher entries
      .assign(to: &self.$entries)
  }

Funkcja process() najpierw sprawdza, czy urządzenia znajdują się w tym samym pomieszczeniu, a potem sprawia, że działają one jako HomeDevices, używając DeviceControlDeviceControlFactory.

4c677c4c294e67ca.png

Uwaga: jeśli Twojego urządzenia nie ma na liście DeviceControlFactory, będzie ono wyświetlane jako „Nieobsługiwane”. Więcej informacji o obsługiwanych urządzeniach znajdziesz na stronie Obsługiwane typy urządzeń w iOS.

Interakcja z urządzeniem

Wtyczka outlet1 jest początkowo nieaktywna podczas klikania lub przesuwania na urządzeniach. Aby umożliwić interakcję z tym elementem, znajdź symbol GoogleHomeAPISampleIOS/ViewModel/Device/OnOffPlugInUnitControl.swift i usuń komentarz oraz alert w funkcji primaryAction().

  /// TODO: primary action of OnOffPlug
  /// Toggles the plug; usually provided as the `action` callback on a Button.
  public override func primaryAction() {
    self.updateTileInfo(isBusy: true)
    Task { @MainActor [weak self] in
      guard
        let self = self,
        let onOffPluginUnitDeviceType = self.onOffPluginUnitDeviceType,
        let onOffTrait = onOffPluginUnitDeviceType.matterTraits.onOffTrait
      else { return }

      do {
        try await onOffTrait.toggle()
      } catch {
        Logger().error("Failed to to toggle OnOffPluginUnit on/off trait: \(error)")
        self.updateTileInfo(isBusy: false)
      }
    }
  }

Funkcja primaryAction(), która znajduje się w klasie OnOffPlugInUnitControl, przełącza stan włączenia/wyłączenia inteligentnego gniazdka lub dowolnego urządzenia reprezentowanego przez OnOffPluginUnitDeviceType.

Dodatkowe przykłady sterowania urządzeniami znajdziesz w GoogleHomeAPISampleIOS/ViewModel/Device.

Tworzenie nowego pomieszczenia

Interfejs Structure API umożliwia tworzenie i usuwanie pomieszczeń oraz przenoszenie urządzeń między nimi.

W sekcji GoogleHomeAPISampleIOS/ViewModel/StructureViewModel.swift usuń komentarz i alert w sekcji addRoom().

  /// TODO: add room
  /// Add a new room in a given structure.
  func addRoom(name: String, structure: Structure) {
    Task {
      do {
        // The view will be updated with the values from the devices publisher.
        _ = try await structure.createRoom(name: name)
      } catch {
        Logger().error("Failed to create room: \(error)")
      }
    }
  }

Aby utworzyć nowy pokój z structure.createRoom(), w lewym górnym rogu kliknij ikonę „+” > Dodaj pokój. Wpisz nazwę nowego pokoju i kliknij „Utwórz pokój”. Nowy pokój pojawi się po kilku sekundach.

b122ae6642b7da1c.png a45f785e1d51938e.png 7753b56cbdcff8d6.png

Przenoszenie urządzenia do innego pomieszczenia

W sekcji GoogleHomeAPISampleIOS/ViewModel/StructureViewModel.swift usuń komentarz i alert w sekcji moveDevice().

  /// TODO: move device
  /// Move a device into a different room.
  func moveDevice(device deviceID: String, to roomID: String, structure: Structure) {
    Task {
      do {
        _ = try await structure.move(device: deviceID, to: roomID)
      } catch {
        Logger().error("Failed to move to room: \(error)")
      }
    }
  }

Aby przenieść urządzenie z structure.move(), naciśnij je i przytrzymaj, wybierz „Przenieś do innego pomieszczenia” i wskaż nowe pomieszczenie.

f9627592af44163d.png fd126fabb454f2bf.png 813e1e23e50cd9f6.png

Usuwanie pustego pokoju

W sekcji GoogleHomeAPISampleIOS/ViewModel/StructureViewModel.swift usuń komentarz i alert w sekcji removeRoom().

  /// TODO: delete room
  /// Delete an empty room in a given structure.
  func removeRoom(id: String, structure: Structure) {
    Task {
      do {
        // The view will be updated with the values from the devices publisher.
        _ = try await structure.deleteRoom(id: id)
      } catch {
        Logger().error("Failed to remove room: \(error)")
      }
    }
  }

Aby usunąć pusty pokój z structure.deleteRoom(), kliknij ikonę kosza po prawej stronie nazwy pokoju i potwierdź działanie. Pamiętaj, że możesz usuwać tylko puste pokoje.

4f129262ad67f564.png

Uwaga: aby utworzyć puste pomieszczenie, przenieś urządzenie z powrotem.

6. Uruchamianie

Uwaga: ta sekcja wymaga hubu Google i urządzenia Matter. Sprawdź, czy hub Google w Twojej strukturze jest połączony z internetem i osiągalny. Jeśli nie masz urządzenia Matter, spróbuj użyć aplikacji Matter Virtual Device.

Dodawanie urządzenia Matter

Interfejs Commissioning API umożliwia aplikacji dodawanie nowych urządzeń Matter do domu użytkownika i jego konta Google. Zapewnia to płynny proces konfiguracji bezpośrednio w aplikacji.

W sekcji GoogleHomeAPISampleIOS/Commissioning/CommissioningManager.swift usuń komentarz i alert w sekcji addMatterDevice().

  /// TODO: add Matter Device
  /// Starts the Matter device commissioning flow to add the device to the user's home.
  /// - Parameters:
  ///   - structure: The structure to add the device to.
  ///   - add3PFabricFirst: Whether to add the device to a third party fabric first.
  public func addMatterDevice(to structure: Structure, add3PFabricFirst: Bool) {
    self.isCommissioning = true

    /// pass if it's 1p or 3p commissioning
    let userDefaults = UserDefaults(
      suiteName: CommissioningManager.appGroup)
    userDefaults?.set(
    add3PFabricFirst, forKey: CommissioningUserDefaultsKeys.shouldPerform3PFabricCommissioning)

    Task {
      do {
        try await structure.prepareForMatterCommissioning()
      } catch {
        Logger().error("Failed to prepare for Matter Commissioning: \(error).")
        self.isCommissioning = false
        return
      }

      // Prepare the Matter request by providing the ecosystem name and home to be added to.
      let topology = MatterAddDeviceRequest.Topology(
        ecosystemName: "Google Home",
        homes: [MatterAddDeviceRequest.Home(displayName: structure.name)]
      )
      let request = MatterAddDeviceRequest(topology: topology)

      do {
        Logger().info("Starting MatterAddDeviceRequest.")
        try await request.perform()
        Logger().info("Completed MatterAddDeviceRequest.")
        let commissionedDeviceIDs = try structure.completeMatterCommissioning()
        Logger().info("Commissioned device IDs: \(commissionedDeviceIDs).")
      } catch let error {
        structure.cancelMatterCommissioning()
        Logger().error("Failed to complete MatterAddDeviceRequest: \(error).")
      }

      self.isCommissioning = false
    }
  }

Aby utworzyć nowy pokój z structure.prepareForMatterCommissioning(), w lewym górnym rogu kliknij ikonę „+” > Dodaj urządzenie do Google Fabric. Do dodania urządzenia Matter do pomieszczenia używa MatterAddDeviceRequest. Po wybraniu pokoju i nazwy urządzenia pojawi się ono na ekranie „Urządzenia”.

adf6cbb531787aaf.png f002bd6320bc480d.png

7. Automatyzacja

Wyświetlanie wszystkich automatyzacji w obrębie domu

Na pasku nawigacyjnym u dołu kliknij Automatyzacja. Wyświetli wszystkie automatyzacje w Twojej strukturze z ikoną structure.listAutomations().

cc6d50f72f812c24.png

Uwaga: jeśli nie masz skonfigurowanych żadnych automatyzacji domu, zobaczysz komunikat „Aby rozpocząć, dodaj automatyzację”.

Utwórz automatyzację

Teraz, gdy znasz już interfejsy API urządzeń i struktur oraz wiesz, jak dodać nowe urządzenie, możesz utworzyć nową automatyzację za pomocą interfejsu Automation API.

GoogleHomeAPISampleIOS/ViewModel/Automation/AutomationsRepository.swift usuń komentarz, alert i pustą automatyzację w lightAutomation().

  /// TODO: create automation
  /// - Parameter devices: devices in current selected structure
  /// - Returns: the automation object to be created
  /// This automation will turn off the light after 5 seconds.
  public func lightAutomation(devices: Set<HomeDevice>) async throws -> any DraftAutomation {
    let light = devices.first { $0.name == "light2" }
    
    guard let light else {
      Logger().error("Unable to find light device with name light2")
      throw HomeError.notFound("No devices support OnOffLightDeviceType")
    }
    
    return automation(
      name: "Turn off light after 5 seconds",
      description:
        """
        Turns off light2 after it has been on for 5 seconds.
        """
    ) {
      let onOffStarter = starter(light, OnOffLightDeviceType.self, OnOffTrait.self)
      onOffStarter
      condition {
        onOffStarter.onOff.equals(true)
      }
      delay(for: Duration.seconds(5))
      action(light, OnOffLightDeviceType.self) {
        OnOffTrait.off()
      }
    }
  }

Aby utworzyć automatyzację, która wyłączy światło 5 sekund po jego włączeniu, otwórz widok automatyzacji i kliknij przycisk „+ Dodaj”. Następnie wybierz „Wyłącz światło po 5 sekundach”. Wyświetlą się szczegóły automatyzacji, w tym starter, conditionaction. Aby utworzyć automatyzację, kliknij „Zapisz” structure.createAutomation().

21c1f8ea2a29134b.png 4bd36f6ed9c5f6e9.png

Uwaga: dostępne automatyzacje zależą od urządzeń w Twoim domu. Jeśli nie widzisz żadnych dostępnych automatyzacji, spróbuj zmienić nazwę urządzenia oświetleniowego na „światło2”.

Wróć na kartę „Urządzenia” i włącz światło o nazwie „light2”. Wyłączy się automatycznie po 5 sekundach.

Automatyzacja składa się z tych komponentów:

  • Polecenie inicjujące: to zdarzenie, które inicjuje automatyzację. W tym przykładzie automatyzacja zostanie uruchomiona, gdy nastąpi zmiana w OnOffTrait.
  • Warunek: sprawdza, czy urządzenie początkowe spełnia określone wymagania. W takim przypadku automatyzacja zostanie wykonana, jeśli światło będzie włączone.
  • Działanie: automatyzacja, którą chcesz wykonać, ale tylko wtedy, gdy polecenie inicjujące spełnia wymagania. Jeśli warunki zostaną spełnione, światło zostanie wyłączone.

Więcej przykładów znajdziesz na stronie Przykładowe automatyzacje.

Usuwanie automatyzacji

Metoda structure.deleteAutomation() jest wywoływana, gdy przesuniesz palcem w lewo po istniejącej automatyzacji i klikniesz ikonę kosza, aby usunąć ją ze struktury.

dc678cd9e16f89a5.png

8. Gratulacje

Gratulacje! Udało Ci się utworzyć podstawową aplikację do inteligentnego domu za pomocą interfejsów Home API na iOS.

Co udało Ci się osiągnąć:

  • Inicjowanie: połącz aplikację z ekosystemem Google Home za pomocą Home.connect().
  • Uprawnienia: obsługuje uwierzytelnianie i autoryzację użytkowników w celu uzyskania dostępu do danych dotyczących domu.
  • Urządzenia i struktury: pobrane i wyświetlone pomieszczenia i urządzenia za pomocą home.rooms()home.devices().
  • Sterowanie urządzeniem: wdrożono interakcję z urządzeniem, np. przełączanie stanu OnOffPluginUnitDeviceType przez wywoływanie poleceń dotyczących jego cech.
  • Zarządzanie strukturą: dodaliśmy funkcje tworzenia nowych pomieszczeń (structure.createRoom()), przenoszenia urządzeń między pomieszczeniami (structure.move()) i usuwania pustych pomieszczeń (structure.deleteRoom()).
  • Uruchamianie: zintegrowano proces uruchamiania pakietu SDK, aby dodawać nowe urządzenia Matter (MatterAddDeviceRequest).
  • Automatyzacja: omówiliśmy, jak wyświetlać, tworzyć (structure.createAutomation()) i usuwać (structure.deleteAutomation()) automatyzacje w strukturze.

Masz już podstawową wiedzę o tym, jak korzystać z interfejsów Home API, aby tworzyć zaawansowane funkcje sterowania inteligentnym domem na urządzeniach z iOS.

Dalsze kroki:

  • Sprawdź sterowanie innymi typami urządzeń dostępnymi w aplikacji przykładowej (światła, wentylatory, rolety itp.).
  • Poznaj bliżej różne cechy i polecenia dostępne na różnych urządzeniach.
  • Eksperymentuj z tworzeniem bardziej złożonych automatyzacji, używając różnych elementów początkowych, warunków i działań.
  • Więcej informacji o zaawansowanych funkcjach znajdziesz w dokumentacji interfejsów Home API.

Brawo!