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

1. Wprowadzenie

f154e30306882c74.png

Czym są interfejsy API Home?

Interfejsy API Google Home udostępniają zestaw bibliotek, które umożliwiają deweloperom korzystanie z ekosystemu Google Home. Dzięki interfejsom Home API deweloperzy mogą tworzyć aplikacje, które umożliwiają bezproblemowe sterowanie urządzeniami inteligentnego domu.

3e11583c779a2cec.png

Komponenty interfejsów Home API

Interfejsy API Home składają się z tych elementów:

  • Interfejsy DeviceStructure API: umożliwiają interakcję z ekranem głównym użytkownika. Aplikacje mogą używać tych interfejsów API do odczytywania informacji o urządzeniach, pomieszczeniach i strukturach (np. do wyświetlania bieżącej temperatury na termostacie) oraz do sterowania urządzeniami (np. do zmiany punktu nastawy termostatu).
  • Commissioning API: z minimalnym nakładem pracy możesz uruchamiać (konfigurować) nowe urządzenia Matter w ramach sieci.
  • Automation API: tworzenie, usuwanie i wyszukiwanie automatyzacji działających w domu użytkownika.

Wymagania wstępne

Czego się nauczysz

  • Jak tworzyć aplikacje na iOS przy użyciu interfejsów API Home zgodnie ze sprawdzonymi metodami.
  • Jak używać interfejsów Device i Structure API do reprezentowania i sterowania inteligentnym domem.
  • Jak za pomocą interfejsu Commissioning API dodać urządzenia do ekosystemu Google Home.
  • Jak utworzyć podstawową automatyzację za pomocą interfejsu Automation API.

2. Konfigurowanie domu

Przygotuj urządzenia

Google Home Playground zawiera wiele wstępnie emulowanych inteligentnych urządzeń domowych. Jest ona zalecana do pełnego wykorzystania potencjału interfejsów API Home, zwłaszcza jeśli masz ograniczoną liczbę urządzeń w domu.

Postępuj zgodnie z instrukcjami, aby zalogować się w Google Home Playgrounddokończyć łączenie kontaplikacji 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 skopiowania kodu źródłowego z GitHuba:

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

Katalog przykładowy zawiera 2 gałęzie: startfinished.

  • start: kod początkowy tego projektu, w którym wprowadzisz zmiany, aby ukończyć ćwiczenie.
  • finished: kod w wersji końcowej, który służy do sprawdzenia Twojej pracy.

Poznaj kod „start”

Zacznij to ćwiczenie, przechodząc do gałęzi start sklonowanego repozytorium:

git checkout start

Ta gałąź zawiera kod startowy projektu. W ramach tego ćwiczenia będziesz modyfikować ten kod, aby wdrożyć pełną funkcjonalność. Przykładowa aplikacja w tym ćwiczeniu z programowania zawiera podstawową strukturę utworzoną w języku Swift, która umożliwia interakcję z pakietem SDK interfejsów API Home na iOS. Przyjrzyjmy się najważniejszym elementom projektu start:

  • Main Entry (GoogleHomeAPISampleIOSApp): znajduje się w sekcji GoogleHomeAPISampleIOS/Main/GoogleHomeAPISampleIOS.swift i jest głównym punktem wejścia do aplikacji. Konfiguruje i inicjalizuje pakiet SDK oraz konfiguruje główny interfejs użytkownika.
  • Core Views (View/):
    • MainView.swift: widok okna głównego po uruchomieniu, zawierający główne NavigationView. Obsługuje ona wybór aktywnej struktury Google Home i wyświetla odpowiednią StructureView.
    • StructureView.swift: wyświetla zawartość dla aktualnie wybranej struktury. Za pomocą kart możesz przełączać się między siatką Urządzenia a listą Automatyzacje. Znajdziesz tam też menu dodawania pokoi lub urządzeń.
    • DeviceView.swift: reprezentuje interaktywną płytkę dla pojedynczego urządzenia w siatce StructureView.
    • AutomationsView.swift: wyświetla listę istniejących automatyzacji dla danej struktury i zawiera nawigację umożliwiającą tworzenie lub wyświetlanie szczegółów automatyzacji.
  • 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: umożliwia wyświetlanie pokoi i obiektów DeviceControl 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 podstawowa reprezentująca urządzenia możliwe do kontrolowania w interfejsie.
    • Specyficzne podklasy (LightControl.swift, FanControl.swift, OnOffPlugInUnitControl.swift itd.): implementuj 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 danej klasy HomeDevice.
  • Commissioning (Commissioning/):
    • CommissioningManager.swift: zawiera logikę zarządzania procedurą uruchamiania 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 kodu znajdziesz komentarze takie jak TODO, a także zakomentowane bloki kodu i alerty w projekcie start. Oznaczają one sekcje, w których dodasz lub odkomentujesz kod, aby wdrożyć wymaganą funkcję.

Tworzenie plików konfiguracji wdrożenia Apple

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

Konfigurowanie uwierzytelniania

Aby uzyskać identyfikator klienta OAuth i włączyć interfejsy API Home, zaloguj się najpierw 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 API Home, a potem dodać swoje konto do listy dozwolonych.

Konfigurowanie pakietu SDK

Pobierz pakiet SDK interfejsów API Home na iOS i skonfiguruj go, korzystając z instrukcji podanych w artykule 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 się wyświetlić okno TODO i ekran z komunikatem „Wymagane logowanie”. Interakcje z interfejsami API Home zostaną zaimplementowane w następnych sekcjach.

bd56b7080037e38a.png 9c0f08a3f4197a77.png

Uwaga: odszukaj kod, który należy zmodyfikować, wyszukując w projekcie tekst wyświetlany w oknie dialogowym. Możesz na przykład wyszukać „TODO: initialize Home” (do zrobienia: zainicjuj Home).

4. Zdarzenie inicjujące

Inicjowanie Home

Zanim użyjesz dowolnego interfejsu API Home na iOS, musisz zainicjować w aplikacji obiekt Home. Home to element najwyższego poziomu w pakiecie SDK, który zapewnia dostęp do wszystkich elementów w strukturze użytkownika. Gdy żądasz wszystkich elementów danego typu, interfejs API zwraca obiekt Query, który pozwala wybrać sposób otrzymywania wyników. Aby zaimplementować inicjalizację domu, w GoogleHomeAPISampleIOS/Accounts/AccountViewModel.swift usuń komentarz i alert w connect().

  /// 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

Gdy uruchomisz aplikację, pojawi się ekran z prośbą o zgodę. Wybierz strukturę Google Home i konto, które znajduje się na liście dozwolonych kont w Twoim projekcie Google Cloud.

47310f458c0094d9.png 4a571dbd9979a88c.png e29c75891a3a67af.png

5. Urządzenia i struktury

Pobieranie informacji o pokojach i urządzeniach

GoogleHomeAPISampleIOS/ViewModel/StructureViewModel.swift usuń komentarz i alert w getRoomsAndDevices(), aby uzyskać pokoje i urządzenia w wybranej strukturze odpowiednio w 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 następnie pozwala im na interakcję jako HomeDevices za pomocą funkcji DeviceControlDeviceControlFactory.

4c677c4c294e67ca.png

Uwaga: jeśli Twoje urządzenie nie jest wymienione w sekcji DeviceControlFactory, będzie widoczne 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, gdy dotyka się lub przesuwa się po urządzeniu. Aby umożliwić interakcję z tą funkcją, znajdź GoogleHomeAPISampleIOS/ViewModel/Device/OnOffPlugInUnitControl.swift i usuń komentarz oraz alert w ramach 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 inteligentnej wtyczki lub dowolnego urządzenia reprezentowanego przez OnOffPluginUnitDeviceType.

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

Tworzenie nowego pomieszczenia

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

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

  /// 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 w usłudze 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 pokoju

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

  /// 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 zmienić lokalizację urządzenia z structure.move(), naciśnij je i przytrzymaj, wybierz „Przenieś do innego pomieszczenia” i wybierz nowe pomieszczenie.

f9627592af44163d.png fd126fabb454f2bf.png 813e1e23e50cd9f6.png

Usuwanie pustego pokoju

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

  /// 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 usunąć tylko puste pokoje.

4f129262ad67f564.png

Uwaga: aby utworzyć pusty pokój, przesuń urządzenie z powrotem.

6. Konfiguracja

Uwaga: w tej sekcji musisz mieć bramę Google i urządzenie Matter. Upewnij się, że centrala Google w Twojej strukturze jest dostępna online. 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. Dzięki temu użytkownicy będą mogli skonfigurować usługę bezpośrednio w aplikacji.

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

  /// 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 w usłudze structure.prepareForMatterCommissioning(), kliknij w lewym górnym rogu ikonę „+” > Dodaj urządzenie do Google Fabric. Używa ona aplikacji MatterAddDeviceRequest do dodania urządzenia Matter do pomieszczenia. Po wybraniu nazwy pokoju i urządzenia urządzenie wyświetli się na ekranie „Urządzenia”.

adf6cbb531787aaf.png f002bd6320bc480d.png

7. Automatyzacja

Wyświetlanie wszystkich automatyzacji w strukturze

Na pasku nawigacyjnym u dołu kliknij Automatyzacje. Lista ta zawiera wszystkie automatyzacje w Twojej strukturze z wartością structure.listAutomations().

cc6d50f72f812c24.png

Uwaga: jeśli nie masz skonfigurowanej automatyzacji domowej, zobaczysz komunikat „Dodaj automatyzację, aby rozpocząć”.

Utwórz automatyzację

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

W 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, przejdź do widoku automatyzacji i kliknij przycisk „+ Dodaj”. Następnie wybierz Wyłącz światło po 5 sekundach. Pojawią się szczegóły automatyzacji, w tym starter, condition i action. Aby utworzyć automatyzację za pomocą structure.createAutomation(), kliknij „Zapisz”.

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 sterującego oświetleniem na „light2”.

Wróć do karty „Urządzenia” i włącz światło o nazwie „light2”. Po 5 sekundach wyłączy się automatycznie.

Komponenty automatyzacji:

  • Starter: to zdarzenie, które inicjuje automatyzację. W tym przykładzie automatyzacja zostanie uruchomiona, gdy nastąpi zmiana w OnOffTrait.
  • Warunek: sprawdza, czy urządzenie startowe spełnia określone wymagania. W takim przypadku automatyzacja zostanie wykonana, jeśli światło jest włączone.
  • Działanie: to automatyzacja, którą chcesz wykonać, ale tylko wtedy, gdy starter spełnia wymagania. Jeśli warunki są spełnione, dioda jest wyłączana.

Aby zobaczyć więcej przykładów, otwórz stronę Przykłady automatyzacji.

Usuwanie automatyzacji

Metoda structure.deleteAutomation() jest wywoływana, gdy przesuniesz palcem w lewo istniejące automatyzacje oraz klikniesz ikonę kosza, aby usunąć je z struktury.

dc678cd9e16f89a5.png

8. Gratulacje

Gratulacje! Udało Ci się utworzyć podstawową aplikację do sterowania inteligentnym domem przy użyciu interfejsów API Home na iOS.

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

  • Inicjowanie: aplikacja została połączona z ekosystemem Google Home za pomocą Home.connect().
  • Uprawnienia: obsługa uwierzytelniania i autoryzacji użytkownika w przypadku dostępu do danych dotyczących domu.
  • Urządzenia i struktury: pobrane i wyświetlone pomieszczenia oraz urządzenia za pomocą home.rooms() i home.devices().
  • Zarządzanie urządzeniem: implementacja interakcji z urządzeniem, np. przełączanie stanu OnOffPluginUnitDeviceType przez wywołanie poleceń dotyczących jego atrybutów.
  • Zarządzanie strukturą: dodaliśmy możliwość tworzenia nowych pokoi (structure.createRoom()), przenoszenia urządzeń między pokojami (structure.move()) i usuwania pustych pokoi (structure.deleteRoom()).
  • Uruchamianie: zintegrowano proces uruchamiania pakietu SDK w celu dodania nowych urządzeń Matter (MatterAddDeviceRequest).
  • Automatyzacja: poznaliśmy sposoby wyświetlania, tworzenia (structure.createAutomation()) i usuwania (structure.deleteAutomation()) automatyzacji w ramach struktury.

Masz już podstawową wiedzę o tym, jak korzystać z interfejsów API Home do tworzenia bogatych funkcji sterowania inteligentnym domem w iOS.

Dalsze kroki:

  • Poznaj sterowanie innymi typami urządzeń w przykładowej aplikacji (ś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 za pomocą różnych elementów inicjujących, warunków i działań.
  • Więcej informacji o zaawansowanych funkcjach znajdziesz w dokumentacji interfejsów API Home.

Brawo!