홈에 새 Matter 기기 추가하기

iOS용 Home API는 Matter 허브를 사용하여 패브릭에 기기를 커미셔닝합니다. 커미셔닝 중에 앱은 SDK에 명령어를 전송한 다음 허브에 전송합니다.

Xcode 프로젝트 구성

커미셔닝 API를 구현하기 전에 필수 기능, 권한, Info.plist 속성을 Xcode 타겟에 추가해야 합니다.

사용 권한

기본 앱 타겟과 확장 프로그램의 .entitlements 파일에 다음 항목을 포함합니다.

  • 스레드 네트워크 사용자 인증 정보 관리:

    <key>com.apple.developer.networking.manage-thread-network-credentials</key>
    <true/>
    

    분산 빌드에서 이 권한을 사용하려면 Apple에 권한 요청 양식을 제출해야 합니다. Apple에서는 스레드 그룹의 회원임을 증명하고 스레드 보더 라우터가 스레드 그룹의 인증을 받았음을 확인하도록 요구합니다.

  • Wi-Fi 정보:

    <key>com.apple.developer.networking.wifi-info</key>
    <true/>
    
  • 앱 그룹: 기본 앱과 Matter 기기 추가 확장 프로그램이 통신할 수 있도록 공유 앱 그룹을 추가합니다(예: group.com.yourdomain.appgroupname). 이 식별자는 개발자가 Apple Developer Console에서 만들고 등록하며 Xcode의 타겟 기능에 구성된 앱 그룹과 일치해야 합니다. Google Home SDK는 공유 UserDefaults 컨테이너를 사용하여 커미셔닝 상태, 패브릭 사용자 인증 정보, 방 목록을 자동으로 동기화하기 위해 두 타겟 모두에서 이 식별자를 사용합니다.

키체인 공유 (선택사항)

앱 확장 프로그램이 UserInfo.authorizationToken()를 사용하여 기본 앱에 저장된 사용자 인증 정보 또는 액세스 토큰을 검색해야 하는 경우 권한 plist에서 기본 앱과 앱 확장 프로그램 타겟 모두에 대해 공유 키체인 액세스 그룹을 구성해야 합니다.

<key>keychain-access-groups</key>
<array>
    <string>$(AppIdentifierPrefix)your.shared.keychain.group</string>
</array>

Info.plist 속성

기본 앱 타겟의 Info.plist에 다음 설명 키를 포함합니다.

  • 위치 사용 설명:

    <key>NSLocationWhenInUseUsageDescription</key>
    <string>Your custom message explaining why location access is needed to read the current Wi-Fi SSID</string>
    
  • Bonjour 서비스: NSBonjourServices에서 로컬 Matter 및 스레드 검색에 필요한 서비스를 포함합니다.

    <key>NSBonjourServices</key>
    <array>
        <string>_matter._tcp</string>
        <string>_matterc._udp</string>
        <string>_matterd._udp</string>
        <string>_meshcop._udp</string>
    </array>
    
  • 로컬 네트워크 사용 설명: 로컬 네트워크 검색 권한을 설명하는 메시지와 함께 NSLocalNetworkUsageDescription 키를 포함합니다.

기기 설정

Matter 기기를 커미셔닝하려면 다음 단계를 따르세요.

  1. structure.prepareForMatterCommissioning()Matter 커미셔닝 요청을 준비하도록 Home APIs iOS SDK에 알립니다. 이 명령어는 다음 작업을 실행합니다.

    • 권한이 부여되었는지 확인합니다.
    • 허브가 온라인 상태이고 연결 가능한지 확인합니다.
    • 진행 중인 다른 활성 커미셔닝 세션이 없는지 확인합니다.
    do {
      try await structure.prepareForMatterCommissioning()
    } catch {
      // Failed to prepare for Matter Commissioning
      return
    }
    
  2. MatterAddDeviceRequest()로 요청을 만들어 Apple의 Matter 지원 흐름을 시작합니다.

    let topology = MatterAddDeviceRequest.Topology(
      ecosystemName: "Google Home",
      homes: [MatterAddDeviceRequest.Home(displayName: structure.name)]
    )
    
    let request = MatterAddDeviceRequest(topology: topology)
    
  3. perform()를 사용하여 요청을 실행합니다. 오류가 발생하면 structure.cancelMatterCommissioning()로 커미셔닝 요청을 취소합니다.

    do {
      // Starting MatterAddDeviceRequest.
      try await request.perform()
      // Successfully completed MatterAddDeviceRequest.
      let commissionedDeviceIDs = try structure.completeMatterCommissioning()
      // Commissioned device IDs.
    } catch let error {
      structure.cancelMatterCommissioning()
      // Failed to complete MatterAddDeviceRequest.
    }
    
  4. 기기를 커미셔닝할 때 앱이 MatterAddDevice 확장 프로그램과 통신할 수 있도록 Apple Developer Console에서 App Group ID을 만듭니다.

    이 그룹 ID를 사용하려면 애플리케이션 번들 식별자와 프로비저닝 프로필도 업데이트해야 합니다.

  5. 초기화할 때 그룹 식별자를 사용하도록 Home 인스턴스를 구성합니다.

    func application(_ application: UIApplication, didFinishLaunchingWithOptions
    launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
      Home.configure {
        $0.sharedAppGroup = "group.com.sample.app.commissioning"
      }
    
      return true
    }
    
  6. Apple의 iOS Matter 앱 확장 프로그램을 구현합니다.

    샘플 코드에서는 Apple의 MatterAddDeviceExtensionRequestHandler API 하위 클래스를 구현하는 예를 보여줍니다.

    최소한 확장 프로그램 타겟에 GoogleHomeMatterCommissionerSDK 프레임워크를 추가하고 Google Home platformHomeMatterCommissioner API를 호출하도록 세 가지 메서드를 재정의합니다.

    • commissionDevice
    • rooms
    • configureDevice
    import MatterSupport
    import GoogleHomeSDK
    import OSLog
    
    final class RequestHandler: MatterAddDeviceExtensionRequestHandler {
      // The App Group ID defined by the application to share information between the extension and main app.
      private static var appGroup = "group.com.sample.app.commissioning"
    
      ...
    
      // MARK: - Home API commissioning handlers
    
      /// Commissions a device to the Google Home ecosystem.
      /// - Parameters:
      ///   - home: The home that the device will be added to
      ///   - onboardingPayload: The payload to be sent to the Matter Commissioning SDK to commission the device.
      ///   - commissioningID: An identifier not used by the Home API SDK.
      override func commissionDevice(in home: MatterAddDeviceRequest.Home?, onboardingPayload: String, commissioningID: UUID) async throws {
        // Commission Matter device with payload.
    
        var onboardingPayloadForHub = onboardingPayload
        let homeMatterCommissioner = try HomeMatterCommissioner(appGroup: RequestHandler.appGroup)
        try await homeMatterCommissioner.commissionMatterDevice(
        onboardingPayload: onboardingPayloadForHub)
      }
    
      /// Obtains rooms from the Home Ecosystem to present to the user during the commissioning flow.
      /// - Parameter home: The home that the device will be added to.
      /// - Returns: A list of rooms if obtained from the Google Home ecosystem or an empty list if there was an error in getting them.
      override func rooms(in home: MatterAddDeviceRequest.Home?) async -> [MatterAddDeviceRequest.Room] {
        do {
          let homeMatterCommissioner = try HomeMatterCommissioner(appGroup: RequestHandler.appGroup)
          let fetchedRooms = try homeMatterCommissioner.fetchRooms()
          // Returning fetched rooms.
          return fetchedRooms
        } catch {
          // Failed to fetch rooms with error
          return []
        }
      }
    
      /// Pushes the device's configurations to the Google Home Ecosystem.
      /// - Parameters:
      ///   - name: The friendly name the user chose to set on the device.
      ///   - room: The room identifier that the user chose to put the device in.
      override func configureDevice(named name: String, in room: MatterAddDeviceRequest.Room?) async {
        // Configure Device name: room
        do {
          let homeMatterCommissioner = try HomeMatterCommissioner(appGroup: RequestHandler.appGroup)
          await homeMatterCommissioner.configureMatterDevice(
            deviceName: name, roomName: room?.displayName)
        } catch {
          // Configure Device failed with error
        }
      }
    }