Tạo ứng dụng di động bằng Home API trên iOS

1. Giới thiệu

f154e30306882c74.png

Home API là gì?

Google Home API cung cấp một bộ thư viện để nhà phát triển khai thác hệ sinh thái Google Home. Với Home API, nhà phát triển có thể tạo các ứng dụng giúp dễ dàng thiết lập và điều khiển các thiết bị nhà thông minh.

3e11583c779a2cec.png

Video này cung cấp hướng dẫn ngắn gọn về ứng dụng di động mà bạn sẽ tạo. Vì vậy, hãy theo dõi video khi bạn thực hiện lớp học lập trình.

Các thành phần của Home API

Các Home API bao gồm:

  • Device & Structure API: Tương tác với nhà của người dùng. Các ứng dụng có thể dùng những API này để đọc thông tin về thiết bị, phòng và cấu trúc (ví dụ: xem nhiệt độ hiện tại của máy điều nhiệt) và điều khiển thiết bị (ví dụ: thay đổi điểm đặt của máy điều nhiệt).
  • API thiết lập: Thiết lập các thiết bị Matter mới vào cấu trúc liên kết mà không tốn nhiều công sức.
  • Automation API: Tạo, xoá và truy vấn các quy trình tự động hoá đang chạy trong nhà của người dùng.

Điều kiện tiên quyết

Kiến thức bạn sẽ học được

  • Cách tạo ứng dụng iOS bằng Home API theo các phương pháp hay nhất.
  • Cách sử dụng Device và Structure API để thể hiện và điều khiển một ngôi nhà thông minh.
  • Cách sử dụng Commissioning API để thêm thiết bị vào hệ sinh thái Google Home.
  • Cách sử dụng Automation API để tạo một quy trình tự động hoá cơ bản.

2. Thiết lập nhà

Chuẩn bị thiết bị

Google Home Playground cung cấp nhiều thiết bị nhà thông minh được mô phỏng sẵn và bạn nên dùng công cụ này để khám phá toàn bộ tiềm năng của các API Home, đặc biệt là nếu bạn có số lượng thiết bị hạn chế trong nhà.

Làm theo hướng dẫn để đăng nhập vào Google Home Playgroundhoàn tất việc liên kết tài khoản trong ứng dụng Google Home. Sau khi hoàn tất, bạn sẽ có thể thấy các thiết bị trong thẻ "Thiết bị" trong ứng dụng Google Home.

c892afce113abe8f.png

3. Thiết lập

Nhận mã của ứng dụng mẫu

Bắt đầu bằng cách sao chép mã nguồn trên GitHub:

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

Thư mục mẫu chứa 2 nhánh là startfinished cho lớp học lập trình này.

  • start: Đoạn mã khởi đầu dành cho dự án này. Bạn sẽ thực hiện các thay đổi trong các đoạn mã này để hoàn thành lớp học lập trình.
  • finished: Đoạn mã hoàn chỉnh dành cho lớp học lập trình này, dùng để kiểm tra bài làm của bạn.

Khám phá mã bắt đầu

Bắt đầu lớp học lập trình này bằng cách chuyển sang nhánh start của kho lưu trữ đã sao chép:

git checkout start

Nhánh này chứa mã khởi đầu cho dự án. Bạn sẽ sửa đổi mã này trong suốt lớp học lập trình để triển khai đầy đủ chức năng. Ứng dụng mẫu trong lớp học lập trình này cung cấp một cấu trúc cơ bản được xây dựng bằng Swift để tương tác với Home APIs iOS SDK. Hãy xem nhanh các thành phần chính trong dự án start:

  • Main Entry (GoogleHomeAPISampleIOSApp): Nằm trong GoogleHomeAPISampleIOS/Main/GoogleHomeAPISampleIOS.swift, đây là điểm truy cập chính của ứng dụng. Thao tác này định cấu hình và khởi chạy SDK, đồng thời thiết lập giao diện người dùng chính.
  • Core Views (View/):
    • MainView.swift: Khung hiển thị gốc sau khi khởi chạy, chứa NavigationView chính. Thao tác này xử lý việc chọn cấu trúc Google Home đang hoạt động và hiển thị StructureView tương ứng.
    • StructureView.swift: Hiển thị nội dung cho cấu trúc hiện được chọn, sử dụng các thẻ để chuyển đổi giữa lưới Thiết bị và danh sách Tự động hoá. Thao tác này cũng cung cấp các trình đơn để thêm phòng hoặc thiết bị.
    • DeviceView.swift: Biểu thị ô tương tác cho một thiết bị trong lưới StructureView.
    • AutomationsView.swift: Cho biết danh sách các quy trình tự động hoá hiện có cho cấu trúc và cung cấp thông tin điều hướng để tạo hoặc xem thông tin chi tiết về quy trình tự động hoá.
  • ViewModels (ViewModel/): Các lớp này quản lý trạng thái và logic cho các khung hiển thị.
    • AccountViewModel.swift: Xử lý kết nối với đối tượng Home và quản lý trạng thái xác thực.
    • MainViewModel.swift: Quản lý danh sách các đối tượng Structure hiện có và theo dõi cấu trúc đã chọn.
    • StructureViewModel.swift: Quản lý việc hiển thị các phòng và đối tượng DeviceControl trong cấu trúc đã chọn.
    • AutomationList.swift, AutomationViewModel.swift, v.v.: Xử lý việc tìm nạp, hiển thị, tạo và quản lý các quy trình tự động hoá.
  • Device Controls (ViewModel/Device/):
    • DeviceControl.swift: Một lớp cơ sở để biểu thị các thiết bị có thể điều khiển trong giao diện người dùng.
    • Các lớp con cụ thể (LightControl.swift, FanControl.swift, OnOffPlugInUnitControl.swift, v.v.): Triển khai logic giao diện người dùng, chế độ điều khiển thiết bị và ánh xạ trạng thái cho các loại thiết bị khác nhau dựa trên đặc điểm của chúng.
    • DeviceControlFactory.swift: Chịu trách nhiệm tạo lớp con DeviceControl phù hợp cho một HomeDevice nhất định.
  • Commissioning (Commissioning/):
    • CommissioningManager.swift: Chứa logic để quản lý quy trình thiết lập thiết bị Matter.
  • Utilities & UX (Utils/, UX/, Storage/): Chứa mã trợ giúp cho các phần tử giao diện người dùng (màu sắc, kích thước), xử lý lỗi, lưu trữ dữ liệu (SelectedStructureStorage.swift) và các tiện ích khác.

Trong suốt lớp học lập trình này, bạn sẽ thấy các chú thích như TODO hoặc các khối mã và cảnh báo được chú thích trong dự án start. Đây là những dấu đánh dấu các phần mà bạn sẽ thêm hoặc bỏ chú thích mã để triển khai chức năng bắt buộc, theo các bước được cung cấp.

Tạo tệp cấu hình triển khai của Apple

Để định cấu hình App Attest, hãy làm theo hướng dẫn về cách tạo tệp cấu hình triển khai của Apple. Xin lưu ý rằng sau khi thiết lập, ứng dụng chỉ có thể được triển khai trên thiết bị thực chứ không phải trong trình mô phỏng.

Thiết lập chế độ xác thực

Để lấy Mã ứng dụng OAuth và bật Home API, trước tiên, hãy đăng nhập vào Google Cloud rồi tạo một dự án mới hoặc chọn một dự án hiện có. Sau đó, hãy làm theo các bước được cung cấp để tạo Mã ứng dụng OAuth và bật Home API, đồng thời thêm tài khoản của bạn vào danh sách cho phép.

Thiết lập SDK

Lấy Home APIs iOS SDK và định cấu hình bằng cách tham khảo hướng dẫn thiết lập có trong phần Thiết lập SDK. Hãy nhớ thay thế HOME_API_TODO_ADD_APP_GROUP bằng nhóm ứng dụng của riêng bạn.

Tạo bản dựng và chạy dự án

Sau khi tạo và chạy dự án bằng nhánh start, một hộp thoại TODO và màn hình hiển thị "Yêu cầu đăng nhập" sẽ xuất hiện. Tương tác Home API sẽ được triển khai trong các phần sau.

bd56b7080037e38a.png 9c0f08a3f4197a77.png

Lưu ý: Xác định vị trí mã cần sửa đổi bằng cách tìm kiếm văn bản xuất hiện trong hộp thoại trong dự án. Ví dụ: tìm kiếm "TODO: initialize Home" (Việc cần làm: khởi động Home).

4. Khởi chạy

Khởi chạy Home

Trước khi sử dụng bất kỳ Home API nào cho iOS, bạn phải khởi chạy Home trong ứng dụng của mình. Home là mục nhập cấp cao nhất vào SDK và cung cấp quyền truy cập vào tất cả các thực thể trong cấu trúc của người dùng. Khi yêu cầu tất cả các thực thể thuộc một loại cụ thể, API sẽ trả về một đối tượng Query cho phép bạn chọn cách nhận kết quả. Trong GoogleHomeAPISampleIOS/Accounts/AccountViewModel.swift, hãy xoá nhận xét và cảnh báo trong connect() để triển khai quá trình khởi chạy trang chủ.

  /// 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).")
      }
    }
  }

Quyền sử dụng Home API

Màn hình đồng ý sẽ xuất hiện khi bạn chạy ứng dụng. Chọn cấu trúc Google Home và chọn tài khoản có trong danh sách cho phép của dự án Google Cloud.

47310f458c0094d9.png 4a571dbd9979a88c.png e29c75891a3a67af.png

5. Thiết bị và cấu trúc

Nhận phòng và thiết bị

Trong GoogleHomeAPISampleIOS/ViewModel/StructureViewModel.swift, hãy xoá nhận xét và cảnh báo trong getRoomsAndDevices() để lấy các phòng và thiết bị trong cấu trúc đã chọn bằng home.rooms()home.devices(), tương ứng.

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

Trước tiên, hàm process() đảm bảo các thiết bị ở cùng phòng trước khi khiến các thiết bị tương tác với nhau dưới dạng HomeDevices bằng cách sử dụng DeviceControlDeviceControlFactory.

4c677c4c294e67ca.png

Lưu ý: Nếu thiết bị của bạn không có trong danh sách DeviceControlFactory, thì thiết bị đó sẽ hiển thị là "Không được hỗ trợ". Để tìm hiểu thêm về các thiết bị được hỗ trợ, hãy xem trang Các loại thiết bị được hỗ trợ trên iOS.

Tương tác với thiết bị

Ban đầu, phích cắm outlet1 sẽ không hoạt động khi bạn nhấn hoặc trượt trên các thiết bị. Để cho phép tương tác với nút này, hãy tìm GoogleHomeAPISampleIOS/ViewModel/Device/OnOffPlugInUnitControl.swift rồi xoá nhận xét và cảnh báo trong hàm 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)
      }
    }
  }

Hàm primaryAction() (nằm trong lớp OnOffPlugInUnitControl) sẽ bật/tắt trạng thái của phích cắm thông minh hoặc mọi thiết bị do OnOffPluginUnitDeviceType đại diện.

Bạn có thể xem thêm các ví dụ về chế độ điều khiển thiết bị trong GoogleHomeAPISampleIOS/ViewModel/Device.

Tạo phòng mới

Structure API cho phép tạo và xoá phòng, cũng như chuyển thiết bị giữa các phòng.

Trong GoogleHomeAPISampleIOS/ViewModel/StructureViewModel.swift, hãy xoá bình luận và cảnh báo trong 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)")
      }
    }
  }

Để tạo một phòng mới bằng structure.createRoom(), hãy chuyển đến góc trên cùng bên trái rồi chọn biểu tượng"+" > Thêm phòng. Nhập tên phòng mới rồi nhấp vào "Tạo phòng". Phòng mới sẽ xuất hiện sau vài giây.

b122ae6642b7da1c.png a45f785e1d51938e.png 7753b56cbdcff8d6.png

Di chuyển thiết bị sang phòng khác

Trong GoogleHomeAPISampleIOS/ViewModel/StructureViewModel.swift, hãy xoá bình luận và cảnh báo trong 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)")
      }
    }
  }

Để di chuyển thiết bị có structure.move(), hãy nhấn và giữ thiết bị đó, chọn "Di chuyển sang phòng khác" rồi chọn phòng mới.

f9627592af44163d.png fd126fabb454f2bf.png 813e1e23e50cd9f6.png

Xoá phòng trống

Trong GoogleHomeAPISampleIOS/ViewModel/StructureViewModel.swift, hãy xoá bình luận và cảnh báo trong 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)")
      }
    }
  }

Để xoá một phòng trống bằng structure.deleteRoom(), hãy nhấp vào biểu tượng thùng rác ở bên phải tên phòng rồi xác nhận thao tác. Xin lưu ý rằng bạn chỉ có thể xoá các phòng trống.

4f129262ad67f564.png

Lưu ý: Di chuyển thiết bị trở lại để tạo một phòng trống.

6. Đang uỷ quyền

Lưu ý: Phần này yêu cầu phải có thiết bị trung tâm Google và thiết bị Matter. Đảm bảo rằng trung tâm Google trong nhà của bạn đang kết nối mạng và có thể truy cập được. Nếu không có thiết bị Matter, hãy thử dùng ứng dụng Thiết bị ảo Matter.

Thêm thiết bị Matter

Commissioning API cho phép ứng dụng của bạn thêm các thiết bị Matter mới vào nhà và Tài khoản Google của người dùng. Điều này mang đến trải nghiệm thiết lập liền mạch ngay trong ứng dụng của bạn.

Trong GoogleHomeAPISampleIOS/Commissioning/CommissioningManager.swift, hãy xoá bình luận và cảnh báo trong 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
    }
  }

Để tạo một phòng mới bằng structure.prepareForMatterCommissioning(), hãy chuyển đến góc trên cùng bên trái rồi chọn biểu tượng"+" > Thêm thiết bị vào Google Fabric. Ứng dụng này sử dụng MatterAddDeviceRequest để thêm thiết bị Matter vào phòng của bạn. Sau khi bạn chọn tên phòng và tên thiết bị, thiết bị sẽ xuất hiện trên màn hình "Thiết bị".

adf6cbb531787aaf.png f002bd6320bc480d.png

7. Tự động hóa

Xem tất cả các thao tác tự động trong cấu trúc

Nhấn vào Tự động hoá trên thanh điều hướng ở dưới cùng. Thao tác này sẽ liệt kê tất cả các quy trình tự động hoá trong nhà của bạn bằng structure.listAutomations().

cc6d50f72f812c24.png

Lưu ý: Nếu chưa thiết lập bất kỳ tính năng tự động hoá nào cho nhà ở, bạn sẽ thấy thông báo "Thêm một tính năng tự động hoá để bắt đầu".

Tạo một quy trình tự động hoá

Giờ đây, khi đã quen thuộc với Device API và Structure API cũng như cách thêm thiết bị mới, bạn có thể tạo một quy trình tự động hoá mới bằng Automation API.

Trong GoogleHomeAPISampleIOS/ViewModel/Automation/AutomationsRepository.swift, hãy xoá bình luận, cảnh báo và quy tắc tự động hoá trống trong 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()
      }
    }
  }

Để tạo một quy trình tự động hoá nhằm tắt đèn sau khi bật 5 giây, hãy chuyển đến chế độ xem quy trình tự động hoá rồi nhấp vào nút "+ Thêm". Sau đó, chọn "Tắt đèn sau 5 giây". Thông tin chi tiết về tính năng tự động hoá, bao gồm starter, conditionaction, sẽ xuất hiện. Nhấp vào "Lưu" để tạo quy trình tự động hoá theo structure.createAutomation().

21c1f8ea2a29134b.png 4bd36f6ed9c5f6e9.png

Lưu ý: Các hoạt động tự động hoá có sẵn sẽ tuỳ thuộc vào các thiết bị trong nhà bạn. Nếu bạn không thấy bất kỳ quy trình tự động hoá nào, hãy thử đổi tên thiết bị chiếu sáng thành "light2".

Quay lại thẻ "Thiết bị" rồi bật đèn có tên "light2". Đèn sẽ tự động tắt sau 5 giây.

Các thành phần của một quy trình tự động hoá là:

  • Điều kiện khởi động: Đây là một sự kiện khởi chạy quy trình tự động hoá. Trong ví dụ này, quy trình tự động hoá sẽ bắt đầu khi có thay đổi về OnOffTrait.
  • Điều kiện: Bước này kiểm tra xem thiết bị khởi động có đáp ứng các yêu cầu cụ thể hay không. Trong trường hợp này, quy trình tự động hoá sẽ được thực thi nếu đèn đang bật.
  • Hành động: Đây là quy trình tự động hoá mà bạn muốn thực hiện, nhưng chỉ khi điều kiện bắt đầu đáp ứng các yêu cầu. Nếu đáp ứng các điều kiện, đèn sẽ tắt.

Để xem thêm ví dụ, hãy truy cập vào trang Ví dụ về quy trình tự động hoá.

Xoá quy trình tự động hoá

Phương thức structure.deleteAutomation() được gọi khi bạn vuốt sang trái trên một quy trình tự động hoá hiện có và nhấn vào biểu tượng thùng rác để xoá quy trình đó khỏi nhà của bạn.

dc678cd9e16f89a5.png

8. Xin chúc mừng

Xin chúc mừng! Bạn đã tạo thành công một ứng dụng nhà thông minh cơ bản bằng cách sử dụng Home API cho iOS.

Những thành tựu bạn đạt được:

  • Khởi chạy: Kết nối ứng dụng của bạn với hệ sinh thái Google Home bằng Home.connect().
  • Quyền: Xử lý hoạt động xác thực và uỷ quyền người dùng để truy cập vào dữ liệu nhà.
  • Thiết bị và cấu trúc: Lấy và hiển thị các phòng và thiết bị bằng cách sử dụng home.rooms()home.devices().
  • Device Control (Điều khiển thiết bị): Triển khai hoạt động tương tác với thiết bị, chẳng hạn như chuyển đổi trạng thái của một OnOffPluginUnitDeviceType bằng cách gọi các lệnh trên đặc điểm của thiết bị đó.
  • Quản lý cấu trúc: Thêm chức năng tạo phòng mới (structure.createRoom()), di chuyển thiết bị giữa các phòng (structure.move()) và xoá phòng trống (structure.deleteRoom()).
  • Thiết lập: Tích hợp quy trình thiết lập của SDK để thêm các thiết bị Matter mới (MatterAddDeviceRequest).
  • Tự động hoá: Khám phá cách liệt kê, tạo (structure.createAutomation()) và xoá (structure.deleteAutomation()) các quy trình tự động hoá trong một cấu trúc.

Giờ đây, bạn đã nắm được kiến thức cơ bản về cách tận dụng Home API để tạo trải nghiệm điều khiển nhà thông minh phong phú trên iOS.

Các bước tiếp theo:

  • Khám phá cách điều khiển các loại thiết bị khác có trong ứng dụng mẫu (đèn, quạt, rèm, v.v.).
  • Tìm hiểu kỹ hơn về các đặc điểm và lệnh có sẵn cho nhiều thiết bị.
  • Thử nghiệm tạo các quy trình tự động hoá phức tạp hơn bằng cách sử dụng nhiều điều kiện kích hoạt, điều kiện và hành động.
  • Hãy tham khảo tài liệu về Home API để biết thêm các tính năng và thông tin chi tiết nâng cao.

Chính xác!