Mengontrol perangkat di Android

Memeriksa apakah suatu karakteristik mendukung perintah

Dukungan juga dapat diperiksa untuk perintah karakteristik. Gunakan juga fungsi supports tingkat karakteristik untuk memeriksa apakah perintah didukung untuk perangkat tertentu.

Misalnya, untuk memeriksa dukungan perangkat terhadap perintah toggle karakteristik On/Off:

// Check if the OnOff trait supports the toggle command.
if (onOffTrait.supports(OnOff.Command.Toggle)) {
  println("onOffTrait supports toggle command")
} else {
  println("onOffTrait does not support stateful toggle command")
}

Mengirim perintah ke perangkat

Mengirim perintah mirip dengan membaca atribut status dari suatu karakteristik. Untuk mengaktifkan atau menonaktifkan perangkat, gunakan perintah Toggle karakteristik OnOff, yang ditentukan dalam model data ekosistem Google Home sebagai toggle(). Metode ini mengubah onOff menjadi false jika nilainya true, atau menjadi true jika nilainya false:

// Calling a command on a trait.
try {
  onOffTrait.toggle()
} catch (e: HomeException) {
  // Code for handling the exception
}

Semua perintah karakteristik adalah fungsi suspend dan hanya selesai saat respons ditampilkan oleh API (seperti mengonfirmasi bahwa status perangkat telah berubah). Perintah mungkin menampilkan pengecualian jika masalah terdeteksi pada alur eksekusi. Sebagai developer, Anda harus menggunakan blok try-catch untuk menangani pengecualian ini dengan benar, dan menampilkan informasi mendetail kepada pengguna tentang kasus saat error dapat ditindaklanjuti. Pengecualian yang tidak ditangani akan menghentikan runtime aplikasi dan dapat menyebabkan error pada aplikasi Anda.

Atau, gunakan perintah off() atau on() untuk menetapkan status secara eksplisit:

onOffTrait.off()
onOffTrait.on()

Setelah mengirim perintah untuk mengubah status, setelah selesai, Anda dapat membaca status seperti yang dijelaskan dalam Membaca status perangkat untuk menanganinya di aplikasi Anda. Atau, gunakan alur seperti yang dijelaskan dalam Mengamati status, yang merupakan metode yang direkomendasikan.

Mengirim perintah dengan parameter

Beberapa perintah dapat menggunakan parameter, seperti yang ada pada karakteristik OnOff atau LevelControl:

offWithEffect

// Turn off the light using the DyingLight effect.
onOffTrait.offWithEffect(
  effectIdentifier = OnOffTrait.EffectIdentifierEnum.DyingLight,
  effectVariant = 0u,
)

moveToLevel

// Change the brightness of the light to 50%
levelControlTrait.moveToLevel(
  level = 127u.toUByte(),
  transitionTime = null,
  optionsMask = LevelControlTrait.OptionsBitmap(),
  optionsOverride = LevelControlTrait.OptionsBitmap(),
)

Beberapa perintah memiliki argumen opsional, yang muncul setelah argumen wajib.

Misalnya, perintah step untuk karakteristik FanControl memiliki dua argumen opsional:

val fanControlTraitFlow: Flow<FanControl?> =
  device.type(FanDevice).map { it.standardTraits.fanControl }.distinctUntilChanged()

val fanControl = fanControlTraitFlow.firstOrNull()

// Calling a command with optional parameters not set.
fanControl?.step(direction = FanControlTrait.StepDirectionEnum.Increase)

// Calling a command with optional parameters.
fanControl?.step(direction = FanControlTrait.StepDirectionEnum.Increase) { wrap = true }

Memeriksa apakah suatu ciri mendukung atribut

Beberapa perangkat mungkin mendukung karakteristik Matter, tetapi tidak mendukung atribut tertentu. Misalnya, perangkat Cloud-to-cloud yang dipetakan ke Matter mungkin tidak mendukung setiap atribut Matter. Untuk menangani kasus seperti ini, gunakan fungsi supports tingkat karakteristik dan enum Attribute karakteristik untuk memeriksa apakah atribut didukung untuk perangkat tertentu.

Misalnya, untuk memeriksa dukungan perangkat terhadap atribut onOff pada karakteristik On/Off:

// Check if the OnOff trait supports the onOff attribute.
if (onOffTrait.supports(OnOff.Attribute.onOff)) {
  println("onOffTrait supports onOff state")
} else {
  println("onOffTrait is for a command only device!")
}

Beberapa atribut dapat bernilai null dalam spesifikasi Matter atau skema Cloud-to-cloud smart home. Untuk atribut ini, Anda dapat menentukan apakah null yang ditampilkan oleh atribut disebabkan oleh perangkat yang tidak melaporkan nilai tersebut, atau apakah nilai atribut sebenarnya adalah null, dengan menggunakan isNullable selain supports:

// Check if a nullable attribute is set or is not supported.
if (onOffTrait.supports(OnOff.Attribute.startUpOnOff)) {
  // The device supports startupOnOff, it is safe to expect this value in the trait.
  if (OnOff.Attribute.startUpOnOff.isNullable && onOffTrait.startUpOnOff == null) {
    // This value is nullable and set to null. Check the specification as to
    // what null in this case means
    println("onOffTrait supports startUpOnOff and it is null")
  } else {
    // This value is nullable and set to a value.
    println("onOffTrait supports startUpOnOff and it is set to ${onOffTrait.startUpOnOff}")
  }
} else {
  println("onOffTrait does not support startUpOnOff!")
}

Memperbarui atribut karakteristik

Jika Anda ingin mengubah nilai atribut tertentu, dan tidak ada perintah karakteristik yang melakukannya, atribut tersebut mungkin mendukung penetapan nilainya secara eksplisit.

Apakah nilai atribut dapat diubah atau tidak bergantung pada dua faktor:

  • Apakah atribut dapat ditulis?
  • Dapatkah nilai atribut berubah sebagai efek samping dari pengiriman perintah ciri?

Dokumentasi referensi untuk karakteristik dan atributnya memberikan informasi ini.

Oleh karena itu, kombinasi properti yang menentukan cara nilai atribut dapat diubah adalah:

Contoh penggunaan fungsi update untuk mengubah nilai atribut

Contoh ini menunjukkan cara menetapkan nilai atribut DoorLockTrait.WrongCodeEntryLimit secara eksplisit.

Untuk menetapkan nilai atribut, panggil fungsi update karakteristik dan teruskan fungsi mutator yang menetapkan nilai baru. Sebaiknya verifikasi terlebih dahulu bahwa karakteristik mendukung atribut.

Contoh:

    val doorLockDevice = home.devices().list().first { device -> device.has(DoorLock) }

    val traitFlow: Flow<DoorLock?> =
      doorLockDevice.type(DoorLockDevice).map { it.standardTraits.doorLock }.distinctUntilChanged()

    val doorLockTrait: DoorLock = traitFlow.first()!!

    if (doorLockTrait.supports(DoorLock.Attribute.wrongCodeEntryLimit)) {
      val unused = doorLockTrait.update { setWrongCodeEntryLimit(3u) }
    }

Mengirim beberapa perintah sekaligus

Batching API memungkinkan klien mengirim beberapa perintah perangkat Home API dalam satu payload. Perintah dikelompokkan ke dalam satu payload dan dieksekusi secara paralel, mirip dengan cara seseorang membuat otomatisasi Home API menggunakan node paralel, seperti contoh Buka tirai sebelum matahari terbit. Namun, Batching API memungkinkan perilaku yang lebih kompleks dan canggih daripada Automation API, seperti kemampuan untuk memilih perangkat secara dinamis saat runtime sesuai dengan kriteria apa pun.

Perintah dalam satu batch dapat menargetkan beberapa karakteristik di beberapa perangkat, di beberapa ruangan, dalam beberapa struktur.

Mengirim perintah dalam batch memungkinkan perangkat melakukan tindakan secara bersamaan, yang tidak mungkin dilakukan jika perintah dikirim secara berurutan dalam permintaan terpisah. Perilaku yang dicapai menggunakan perintah batch memungkinkan developer menetapkan status grup perangkat agar sesuai dengan status gabungan yang telah ditentukan sebelumnya.

Menggunakan Batching API

Ada tiga langkah dasar yang diperlukan dalam memanggil perintah melalui Batching API:

  1. Panggil metode Home.sendBatchedCommands().
  2. Dalam isi blok sendBatchedCommands(), tentukan perintah yang akan disertakan dalam batch.
  3. Periksa hasil perintah yang dikirim untuk melihat apakah perintah tersebut berhasil atau gagal.

Mengirim perintah dalam batch

Panggil metode Home.sendBatchedCommands(). Di balik layar, metode ini menyiapkan ekspresi lambda dalam konteks batch khusus.

home.sendBatchedCommands() {

Menentukan perintah batch

Dalam isi blok sendBatchedCommands(), isi perintah yang dapat dikelompokkan. Perintah yang dapat di-batch adalah versi "bayangan" dari perintah Device API yang ada yang dapat digunakan dalam konteks batch, dan diberi nama dengan akhiran tambahan Batchable. Misalnya, perintah moveToLevel() pada karakteristik LevelControl memiliki padanan bernama moveToLevelBatchable().

Contoh:

  val response1 = add(command1)

  val response2 = add(command2)

Batch akan otomatis dikirim setelah semua perintah ditambahkan ke konteks batch dan eksekusi telah keluar dari konteks.

Respons dicatat dalam objek DeferredResponse<T>.

Instance DeferredResponse<T> dapat dikumpulkan ke dalam objek jenis apa pun, seperti Collection, atau class data yang Anda tentukan. Apa pun jenis objek yang Anda pilih untuk menyusun respons, itulah yang akan ditampilkan oleh sendBatchedCommands(). Misalnya, konteks batch dapat menampilkan dua instance DeferredResponse dalam Pair:

  val (response1, response2) = homeClient.sendBatchedComamnds {
    val response1 = add(someCommandBatched(...))
    val response2 = add(someOtherCommandBatched(...))
    Pair(response1, response2)
  }

Atau, konteks batch dapat menampilkan instance DeferredResponse dalam class data kustom:

  // Custom data class
  data class SpecialResponseHolder(
    val response1: DeferredResponse<String>,
    val response2: DeferredResponse<Int>,
    val other: OtherResponses
  )
  data class OtherResponses(...)

Memeriksa setiap respons

Di luar blok sendBatchedCommands(), periksa respons untuk menentukan apakah perintah yang sesuai berhasil atau gagal. Hal ini dilakukan dengan memanggil DeferredResponse.getOrThrow(), yang: - menampilkan hasil perintah yang dijalankan, - atau, jika cakupan batch belum selesai atau perintah tidak berhasil, menampilkan error.

Anda hanya boleh memeriksa hasilnya di luar cakupan lambda sendBatchedCommands().

Contoh

Misalnya, Anda ingin membuat aplikasi yang menggunakan Batching API untuk menyiapkan mode 'malam' yang mengonfigurasi semua perangkat di rumah untuk malam hari, saat semua orang tidur. Aplikasi ini akan mematikan lampu dan mengunci pintu depan dan belakang.

Berikut adalah salah satu cara untuk menyelesaikan tugas:

val lightDevices: List<OnOffLightDevice>
val doorlockDevices: List<DoorLockDevice>

// Send all the commands
val responses: List<DeferredResponse<Unit>> = home.sendBatchedCommands {
  // For each light device, send a Batchable command to turn it on
  val lightResponses: List<DeferredResponse<Unit>> = lightDevices.map { lightDevice ->
    add(lightDevice.standardTraits.onOff.onBatchable())
  }

  // For each doorlock device, send a Batchable command to lock it
  val doorLockResponse: List<DeferredResponse<Unit>> = doorlockDevices.map { doorlockDevice ->
    add(doorlockDevice.standardTraits.doorLock.lockDoorBatchable())
  }

  lightResponses + doorLockResponses
}

// Check that all responses were successful
for (response in responses) {
  response.getOrThrow()
}

Mengontrol grup perangkat

Selain kontrol mendetail atas setiap anggota grup dengan melakukan iterasi melalui perangkat anggotanya, Anda dapat mengontrol DeviceGroup sebagai satu entitas. DeviceGroup mengimplementasikan antarmuka PartsProvider, yang mengekspos bagian DeviceType yang didukung oleh perangkat dalam grup:

  • group.parts() menampilkan Flow<Set<DeviceType>> dari semua jenis perangkat yang ada dalam grup.
  • group.part() dan group.partOrNull() menampilkan alur untuk jenis perangkat tertentu, seperti OnOffLightDevice atau DimmableLightDevice.

Saat Anda memanggil perintah karakteristik pada jenis perangkat yang merupakan bagian dari DeviceGroup, SDK akan menyampaikan perintah tersebut ke semua perangkat anggota terkait dalam grup yang mendukung kemampuan tersebut, terlepas dari konektivitas atau transportasi yang mendasarinya (seperti Matter atau Cloud-to-cloud).

Atribut karakteristik bagian grup dan penanganan error

Menargetkan bagian grup akan menyederhanakan kasus umum saat Anda ingin mengirim perintah idempoten (seperti menyalakan atau mematikan lampu, atau menyetel tingkat kecerahan) di seluruh grup. Saat menggunakan karakteristik dan perintah di bagian DeviceGroup, perhatikan perilaku berikut:

  • Atribut status dinamis (seperti atribut onOff dari karakteristik OnOff atau atribut currentLevel dari karakteristik LevelControl) umumnya tidak diisi pada karakteristik bagian grup karena perangkat anggota dalam grup dapat berada dalam status berbeda yang tidak dapat digabungkan menjadi satu nilai. Namun, perintah yang didukung (diperiksa dengan supports(), seperti LevelControl.Command.MoveToLevelWithOnOff pada lampu yang dapat diredupkan) dan atribut kemampuan tertentu (seperti colorTempPhysicalMinMireds dan colorTempPhysicalMaxMireds pada ColorControl untuk lampu warna) diisi sehingga aplikasi Anda dapat mengonfigurasi kontrol UI dengan tepat.
  • Tidak semua perintah pada karakteristik anggota grup tersedia di bagian grup. Secara khusus, perintah yang menampilkan nilai tidak tersedia.
  • Perintah pada bagian grup tidak menampilkan hasil individual untuk setiap perintah perangkat anggota yang mendasarinya. Untuk memeriksa hasil perintah individual atau memanggil perintah yang menampilkan nilai, lakukan iterasi melalui perangkat anggota grup secara langsung.
  • Perintah yang dikirim ke bagian di DeviceGroup akan selesai setelah seluruh fanout di seluruh perangkat anggota selesai dan semua keberhasilan atau kegagalan perintah yang mendasarinya dikumpulkan. Perintah hanya akan gagal (menampilkan HomeException) jika perintah gagal untuk setiap perangkat anggota terkait atau jika semua perangkat target tidak tersedia, yang muncul sebagai kegagalan perintah tunggal yang cocok dengan sintaksis panggilan perintah tunggal. Jika setidaknya satu perangkat anggota menjalankan perintah, perintah grup akan selesai tanpa menampilkan pengecualian.

Mengirim perintah ke bagian grup

Untuk menyalakan semua lampu OnOffLightDevice di grup perangkat:

val lightPart = group.partOrNull(OnOffLightDevice).firstOrNull()
val onOffTrait = lightPart?.trait(OnOff)

if (onOffTrait?.supports(OnOff.Command.On) == true) {
  onOffTrait.on()
}

Anda juga dapat memeriksa semua jenis perangkat yang ada dalam grup menggunakan group.parts() untuk menargetkan ciri di beberapa jenis perangkat terkait (misalnya, menonaktifkan jenis perangkat lampu apa pun dalam grup yang mendukung OnOff):

val lightTypes = listOf(
  OnOffLightDevice,
  DimmableLightDevice,
  ColorTemperatureLightDevice,
  ExtendedColorLightDevice,
)

val parts = group.parts().first()
val onOffTrait = lightTypes.firstNotNullOfOrNull { type ->
  parts.firstOrNull { it.factory == type }
}?.trait(OnOff)

onOffTrait?.off()

Anda dapat memeriksa dukungan perintah atau membaca batas kemampuan pada bagian grup sebelum mengirim perintah (misalnya, memeriksa MoveToLevelWithOnOff pada bagian lampu yang dapat diredupkan atau membaca colorTempPhysicalMinMireds dan colorTempPhysicalMaxMireds pada bagian lampu color temperature):

val dimmableLight = group.partOrNull(DimmableLightDevice).firstOrNull()
val levelControl = dimmableLight?.trait(LevelControl)

if (
  levelControl?.supports(
    LevelControl.Command.MoveToLevelWithOnOff
  ) == true
) {
  levelControl.moveToLevelWithOnOff(
    level = 127u,
    transitionTime = null,
    optionsMask = LevelControlTrait.OptionsBitmap(),
    optionsOverride = LevelControlTrait.OptionsBitmap(),
  )
}

val colorLight = group.partOrNull(ColorTemperatureLightDevice).firstOrNull()
val colorControl = colorLight?.trait(ColorControl)
val minMireds = colorControl?.colorTempPhysicalMinMireds
val maxMireds = colorControl?.colorTempPhysicalMaxMireds

if (colorControl != null && minMireds != null && maxMireds != null) {
  val midMireds = ((minMireds.toInt() + maxMireds.toInt()) / 2).toUShort()
  colorControl.moveToColorTemperature(
    colorTemperatureMireds = midMireds,
    transitionTime = 0u,
    optionsMask = ColorControlTrait.OptionsBitmap(),
    optionsOverride = ColorControlTrait.OptionsBitmap(),
  )
}