API cấu trúc trên Android

Bạn có thể truy cập vào Structure API thông qua Home API cho Android. Nhập các gói này vào ứng dụng của bạn:

import com.google.home.Home
import com.google.home.Id
import com.google.home.Structure

Xử lý lỗi

Bất kỳ phương thức nào trong Home API đều có thể gửi một HomeException, vì vậy, bạn nên sử dụng khối try-catch để bắt HomeException trên tất cả lệnh gọi.

Khi xử lý HomeException, hãy kiểm tra các trường error.code và error.message để tìm hiểu xem đã xảy ra lỗi gì. Cũng có thể có mã lỗi phụ, vì vậy, hãy gọi phương thức getSubErrorCodes() và kiểm tra kết quả.

Mọi ngoại lệ không được xử lý sẽ khiến ứng dụng của bạn gặp sự cố.

Để biết thêm thông tin, hãy xem phần Xử lý lỗi.

Cuộc gọi mẫu

Lấy danh sách nhà

Sau khi khởi chạy, lệnh gọi structures() sẽ trả về một Flow gồm các cấu trúc mà bạn có thể truy cập:

// Get a flow of all structures accessible to the user
val allStructuresFlow: HomeObjectsFlow<Structure> = home.structures()

// Calling list() on a HomeObjectsFlow returns the first Set of elements.
val allStructures: Set<Structure> = allStructuresFlow.list()

structures() API là một luồng có thể không trả về ngay danh sách cấu trúc hợp lệ. Nếu ứng dụng của bạn có tính phản ứng và đăng ký luồng đó để điều khiển giao diện người dùng, thì cuối cùng, một danh sách hợp lệ gồm các cấu trúc sẽ được trả về. Có những trường hợp khác có thể trả về danh sách cấu trúc trống, ví dụ: nếu điện thoại của người dùng mất kết nối hoặc nếu người dùng đã thu hồi quyền đối với ứng dụng của bạn. Bạn nên đảm bảo xử lý các trường hợp này trong ứng dụng của mình.

Ngoài ra, nếu bắt buộc phải lập trình mệnh lệnh thay vì lập trình phản ứng, bạn có thể sử dụng toán tử luồng đầu cuối:

val everyStructure = withTimeout(5000) { home.structures().first { it.isNotEmpty() } }

Lệnh gọi này chờ một danh sách hợp lệ các cấu trúc đi qua luồng và hết thời gian chờ nếu không nhận được danh sách trong thời gian chờ do ứng dụng chỉ định.

Lấy thuộc tính cấu trúc

Khi có danh sách các cấu trúc, bạn có thể truy cập vào các thuộc tính của chúng:

// Get a flow on a structure. Flow emits new values on structure metadata changes: name.
val structureFlow: Flow<Structure> = home.structures().itemFlow(myStructureId)

// Get a snapshot of the structure.
val structure: Structure = structureFlow.first()

// Get structure properties
println("id ${structure.id}")
println("name ${structure.name}")

Tìm cấu trúc theo tên

Nếu biết tên của một cấu trúc, bạn cũng có thể truy cập vào cấu trúc đó bằng thuộc tính name:

val myHome = home.structures().list().first { it.name == "My home" }

Từ đó, bạn có thể truy cập vào các nhà, phòng và thiết bị cho từng nhà.

Làm việc với nhiều cấu trúc

Để sử dụng nhiều cấu trúc, hãy lấy một tệp đối chiếu riêng cho từng cấu trúc:

var structure1: Structure? = null
var structure2: Structure? = null

try {
  structure1 = home.structures().list().firstOrNull { it.name == "Main House" }
} catch (e: HomeException) {
  // Code for handling the exception
}
try {
  structure2 = home.structures().list().firstOrNull { it.name == "Guest Cottage" }
} catch (e: HomeException) {
  // Code for handling the exception
}

Lấy danh sách phòng

Khi có cấu trúc, bạn có thể lấy danh sách phòng và truy cập vào các thuộc tính của phòng:

val allRoomsFlow: HomeObjectsFlow<Room> = structure.rooms()
val allRooms: Set<Room> = allRoomsFlow.list()
val room: Room = allRooms.first()

println("id ${room.id}")
println("name ${room.name}")

Tạo một phòng

Cách tạo phòng mới:

val testName = "Test Room Name"
val newRoom: Room = structure.createRoom(testName)

Xoá phòng

Hoặc bạn có thể xoá phòng theo cách sau:

val roomToDelete = structure.rooms().list().filter { it.name == "room_id1" }.firstOrNull()
    structure.deleteRoom(roomToDelete!!)

Bạn cũng có thể xoá phòng chỉ bằng mã nhận dạng:

val roomToDelete1 = allRooms.filter { it.id == testRoomId }.firstOrNull()
structure.deleteRoom(roomToDelete1!!)

Nếu một phòng có thiết bị bị xoá, thì các thiết bị đó vẫn sẽ nằm trong cấu trúc nhưng không còn được chỉ định cho phòng nào nữa.

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

Sau khi có cấu trúc, bạn có thể di chuyển thiết bị sang một phòng khác trong cấu trúc đó:

val room2 = structure.rooms().get(Id("room_id_other_structure"))
    val device1 = structure.devices().get(Id("device_id1"))
    structure.moveDevicesToRoom(room2!!, listOf(device1!!))

Nếu chỉ có mã thiết bị và mã phòng, bạn cũng có thể di chuyển thiết bị:

structure.moveDevicesToRoom(Id("room_id_other_structure"), listOf(Id("device_id1")))

Thay đổi tên phòng

Gọi phương thức setName() để đổi tên phòng:

livingRoom.setName("Living Room")

Tên sẽ bị cắt bớt nếu vượt quá giới hạn 60 điểm mã Unicode (ký tự) và không có lỗi nào được gửi. Nhà phát triển chịu trách nhiệm xử lý tên dài và có thể quyết định xem họ có muốn thông báo cho người dùng rằng tên sẽ bị cắt bớt hay không.

Trong hệ sinh thái Google Home, đối với hầu hết các loại thiết bị, người dùng có thể cấp quyền cho tất cả thiết bị thuộc loại đó cùng một lúc. Đối với các loại thiết bị nhạy cảm hoặc bị hạn chế, chẳng hạn như khoá, camera hoặc chuông cửa, người dùng phải cấp quyền riêng cho từng thiết bị.

Để xác định xem người dùng đã cấp quyền truy cập vào một loại thiết bị nhạy cảm hoặc bị hạn chế hay chưa, hãy sử dụng hàm consentedDeviceTypes() ở cấp Cấu trúc:

import com.google.home.Structure
import com.google.home.DeviceType
import com.google.home.DeviceTypeFactory
import com.google.home.consentedDeviceTypes // Extension function from the SDK
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch

/**
 * Example of how an app may monitor which device types have been granted access by a user.
 */
fun monitorDeviceConsent(structure: Structure, myScope: CoroutineScope) {
    // Obtain the flow of consented device type factories
    val consentedTypesFlow: Flow<Set<DeviceTypeFactory<out DeviceType>>> =
        structure.consentedDeviceTypes()

    myScope.launch {
        consentedTypesFlow.collect { consentedSet ->
            // Check if the user has consented to share a specific restricted
            // type, such as a Doorbell or Camera.
            val hasCameraAccess = consentedSet.any {
                it.toString() == "matter.google.type.GoogleDoorbellDevice"
            }

            if (hasCameraAccess) {
                // Enable features that require camera access
            } else {
                // Inform the user or disable camera-specific features
            }
        }
    }
}

Nhóm thiết bị

Nhóm thiết bị đại diện cho một tập hợp thiết bị do người dùng xác định trong một cấu trúc duy nhất. Tư cách thành viên nhóm được giữ lại trong các dịch vụ của Google và hiển thị cho các thành viên khác trong cấu trúc và các ứng dụng khác có quyền truy cập vào cấu trúc.

Giống như phòng thực tế, các nhóm thiết bị thuộc về một cấu trúc duy nhất. Nhưng không giống như phòng, nhóm thiết bị là nhóm logic. Một thiết bị có thể thuộc về nhiều nhóm thiết bị cùng một lúc. Nhóm thiết bị có thể chứa tối đa 100 thiết bị.

Nhóm thiết bị được biểu thị bằng thực thể DeviceGroup, khác với HomeDevice. Để truy vấn và tương tác với các nhóm thiết bị, hãy thêm DeviceGroup vào danh sách entityTypes khi khởi chạy FactoryRegistry, cùng với các đặc điểm Group, GroupManagement và GroupMembership.

Bạn có thể tương tác với các thiết bị trong một nhóm bằng cách lặp lại trực tiếp trên các thiết bị thành viên (xem phần Tương tác với các thiết bị thành viên trong nhóm) hoặc bằng cách nhắm mục tiêu một phần loại thiết bị trên DeviceGroup (xem phần Điều khiển một nhóm thiết bị).

Lấy danh sách nhóm thiết bị

Để quan sát tất cả các nhóm thiết bị trong một nhà, hãy gọi home.entities(DeviceGroup) và lọc theo mã nhận dạng của nhà:

home.entities(DeviceGroup).collect { allGroups ->
  val structureGroups = allGroups.filter { it.structureId == structure.id }
  for (group in structureGroups) {
    val groupName = group.trait(Group)?.Name
    println("Found group: $groupName with ID: ${group.id.id}")
  }
}

Tương tác với thiết bị của thành viên trong nhóm

Việc lặp lại trên các thiết bị thành viên của một nhóm bằng group.devices() giúp bạn kiểm soát chi tiết từng thành viên, cho phép bạn gộp các lệnh vào một yêu cầu và cho phép bạn tổng hợp hoặc ghi thuộc tính trên các thành viên.

Ví dụ: để gửi một yêu cầu theo lô duy nhất có các lệnh cho một đặc điểm trên tất cả thành viên tương thích của một nhóm:

val members = group.devices(enableMultipartDevices = true).first()
val onOffTraits = members.mapNotNull { device ->
  device.partOrNull(OnOffLightDevice).first()?.standardTraits?.onOff
}

val responses = home.sendBatchedCommands {
  onOffTraits.map { onOff ->
    add(onOff.onBatchable())
  }
}

for (response in responses) {
  response.getOrThrow()
}

Bạn cũng có thể cập nhật một thuộc tính trên tất cả thiết bị thành viên phù hợp trong một nhóm (ví dụ: đặt onTransitionTime thành 1u trên tất cả thành viên DimmableLightDevice) và chờ hoàn tất trong thời hạn:

val members = group.devices(enableMultipartDevices = true).first()

withTimeout(5.seconds) {
  members.map { device ->
    async {
      val light = device.partOrNull(DimmableLightDevice).first()
      val levelControl = light?.standardTraits?.levelControl
      if (
        levelControl?.supports(
          LevelControl.Attribute.onTransitionTime
        ) == true
      ) {
        levelControl.update { setOnTransitionTime(1u) }
      }
    }
  }.awaitAll()
}

Để tổng hợp một thuộc tính trên các thành viên của một nhóm, chẳng hạn như tìm colorTempPhysicalMaxMireds tối thiểu trên tất cả đèn nhiệt độ màu trong nhóm:

val members = group.devices(enableMultipartDevices = true).first()
val minMaxMireds: UShort? = members.mapNotNull { device ->
  val light = device.partOrNull(ColorTemperatureLightDevice).first()
  light?.standardTraits?.colorControl?.colorTempPhysicalMaxMireds
}.minOrNull()

Tạo nhóm thiết bị

Sử dụng GroupManagement trên cấu trúc để tạo nhóm thiết bị:

val groupManagement = structure.trait(GroupManagementTrait)
val response = groupManagement?.createUserDefinedGroup(
  name = "Living room lights",
  memberDeviceObjectIds = listOf(light1.id.id, light2.id.id)
)

Quản lý tư cách thành viên trong nhóm

Cách thêm thành viên vào một nhóm hiện có:

structure.trait(GroupManagementTrait)?.addGroupMembers(
  groupObjectId = group.id.id,
  memberDeviceObjectIds = listOf(light3.id.id)
)

Cách xoá thành viên khỏi nhóm:

structure.trait(GroupManagementTrait)?.removeGroupMembers(
  groupObjectId = group.id.id,
  memberDeviceObjectIds = listOf(light1.id.id)
)

Thay đổi tên của một nhóm thiết bị

Để đổi tên một nhóm thiết bị, hãy cập nhật thuộc tính tên trên GroupTrait của nhóm:

group.trait(GroupTrait)?.update {
  name = "Movie corner"
}

Xoá nhóm thiết bị

Cách xoá một nhóm thiết bị khỏi một nhà:

structure.trait(GroupManagementTrait)?.deleteGroup(
  groupObjectId = group.id.id
)

Tự động hoá

Điểm truy cập vào Automation API là thông qua một nhà. Để tìm hiểu thêm về tính năng Tự động hoá trong các API cho Home, hãy xem bài viết Tổng quan về API Tự động hoá trên Android.