Android'de cihazları kontrol etme

Bir özelliğin bir komutu destekleyip desteklemediğini kontrol etme

Özellik komutunun desteklenip desteklenmediği de kontrol edilebilir. Ayrıca, bir komutun belirli bir cihaz için desteklenip desteklenmediğini kontrol etmek için özellik düzeyindeki supports işlevini kullanın.

Örneğin, bir cihazın Açma/Kapatma özelliğinin toggle komutunu destekleyip desteklemediğini kontrol etmek için:

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

Cihaza komut gönderme

Komut gönderme, bir özellikten durum özelliği okumaya benzer. Cihazı açmak veya kapatmak için OnOff özelliğinin, Google Home ekosistemi veri modelinde toggle() olarak tanımlanan açma/kapatma komutunu kullanın. Bu yöntem, onOff değerini true ise false, false ise true olarak değiştirir:

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

Tüm özellik komutları suspend işlevleridir ve yalnızca API tarafından bir yanıt döndürüldüğünde (ör. cihaz durumunun değiştiğini onaylama) tamamlanır. Yürütme akışında bir sorun algılanırsa komutlar bir istisna döndürebilir. Geliştirici olarak, bu istisnaları düzgün bir şekilde ele almak için try-catch bloğu kullanmanız ve hataların düzeltilebileceği durumlarda kullanıcılara ayrıntılı bilgi vermeniz gerekir. İşlenmeyen istisnalar, uygulamanın çalışma zamanını durdurur ve uygulamanızda kilitlenmelere neden olabilir.

Alternatif olarak, durumu açıkça ayarlamak için off() veya on() komutlarını kullanın:

onOffTrait.off()
onOffTrait.on()

Durumu değiştirmek için bir komut gönderdikten sonra, komut tamamlandığında durumu Cihaz durumunu okuma bölümünde açıklandığı gibi okuyarak uygulamanızda işleyebilirsiniz. Alternatif olarak, tercih edilen yöntem olan Durumu gözlemleme bölümünde açıklandığı gibi akışları kullanabilirsiniz.

Parametrelerle komut gönderme

Bazı komutlar, OnOff veya LevelControl özelliklerindekiler gibi parametreler kullanabilir:

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(),
)

Bazı komutlarda, zorunlu bağımsız değişkenlerden sonra gelen isteğe bağlı bağımsız değişkenler bulunur.

Örneğin, FanControl özelliğinin step komutunda iki isteğe bağlı bağımsız değişken vardır:

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 }

Bir özelliğin bir niteliği destekleyip desteklemediğini kontrol etme

Bazı cihazlar bir Matter özelliğini destekleyebilir ancak belirli bir özelliği desteklemeyebilir. Örneğin, Matter ile eşlenen bir Cloud-to-cloud cihazı, her Matter özelliğini desteklemeyebilir. Bu gibi durumları yönetmek için özellik düzeyindeki supports işlevini ve özelliğin Attribute enum değerini kullanarak özelliğin belirli bir cihaz için desteklenip desteklenmediğini kontrol edin.

Örneğin, bir cihazın Açma/Kapatma özelliğinin onOff özelliğini destekleyip desteklemediğini kontrol etmek için:

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

Bazı özellikler Matter spesifikasyonunda veya Cloud-to-cloud smart home şemasında null değer alabilir. Bu özellikler için, özelliğin döndürdüğü null değerinin, cihazın bu değeri bildirmemesinden mi yoksa özelliğin değerinin gerçekten null olmasından mı kaynaklandığını belirlemek için supports özelliğine ek olarak isNullable özelliğini kullanabilirsiniz:

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

Karakter özelliklerini güncelleme

Belirli bir özelliğin değerini değiştirmek istiyorsanız ve özelliğin komutlarından hiçbiri bunu yapmıyorsa özellik, değerinin açıkça ayarlanmasını destekleyebilir.

Bir özelliğin değerinin değiştirilip değiştirilemeyeceği iki faktöre bağlıdır:

  • Özellik yazılabilir mi?
  • Özellik komutu göndermenin yan etkisi olarak özelliğin değeri değişebilir mi?

Özellikler ve nitelikleriyle ilgili referans belgelerinde bu bilgiler yer alır.

Bu nedenle, bir özelliğin değerinin nasıl değiştirilebileceğini belirleyen özellik kombinasyonları şunlardır:

  • Salt okunurdur ve diğer komutlardan etkilenmez. Bu, özelliğin değerinin değişmediği anlamına gelir. Örneğin, Switch özelliğinin currentPosition özelliği.

  • Salt okunurdur ve diğer komutlardan etkilenir. Bu, özelliğin değerinin yalnızca bir komut gönderilmesi sonucunda değişebileceği anlamına gelir. Örneğin, LevelControl Matter özelliğinin currentLevel özelliği salt okunurdur ancak değeri moveToLevel gibi komutlarla değiştirilebilir.

  • Yazılabilir ve diğer komutlardan etkilenmez. Yani özelliğin update işlevini kullanarak özelliğin değerini doğrudan değiştirebilirsiniz ancak özelliğin değerini etkileyecek komutlar yoktur. Örneğin, DoorLock özelliğinin WrongCodeEntryLimit özelliği.

  • Yazılabilir ve diğer komutlardan etkilenir. Yani özelliğin update işlevini kullanarak özelliğin değerini doğrudan değiştirebilirsiniz ve özellik değeri, bir komut gönderilmesi sonucunda değişebilir. Örneğin, FanControlTrait öğesinin speedSetting özelliğine doğrudan yazılabilir ancak bu özellik step komutu kullanılarak da değiştirilebilir.

Bir özelliğin değerini değiştirmek için güncelleme işlevini kullanma örneği

Bu örnekte, DoorLockTrait.WrongCodeEntryLimit özelliğinin değerinin açıkça nasıl ayarlanacağı gösterilmektedir.

Bir özellik değeri ayarlamak için özelliğin update işlevini çağırın ve yeni değeri ayarlayan bir mutator işlevi iletin. Öncelikle özelliğin bir özelliği desteklediğini doğrulamanız önerilir.

Örneğin:

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

Aynı anda birden fazla komut gönderme

Batching API, istemcinin tek bir yükte birden fazla Home API cihaz komutu göndermesine olanak tanır. Komutlar tek bir yükte gruplandırılır ve paralel olarak yürütülür. Bu, paralel düğüm kullanarak Home API otomasyonu oluşturmaya benzer. Örneğin, Güneş doğmadan önce storları aç örneği. Ancak Batching API, Automation API'ye kıyasla daha karmaşık ve gelişmiş davranışlara olanak tanır. Örneğin, çalışma zamanında cihazları herhangi bir ölçüte göre dinamik olarak seçebilirsiniz.

Bir gruptaki komutlar birden fazla yapıdaki birden fazla odada bulunan birden fazla cihazda birden fazla özelliği hedefleyebilir.

Komutların toplu olarak gönderilmesi, cihazların işlemleri aynı anda gerçekleştirmesine olanak tanır. Komutlar ayrı isteklerde sırayla gönderildiğinde bu pek mümkün olmaz. Toplu komutlar kullanılarak elde edilen davranış, geliştiricinin bir grup cihazın durumunu önceden belirlenmiş bir toplu durumla eşleşecek şekilde ayarlamasına olanak tanır.

Batching API'yi kullanma

Batching API aracılığıyla komutları çağırmanın üç temel adımı vardır:

  1. Home.sendBatchedCommands() yöntemini çağırın.
  2. sendBatchedCommands() bloğunun gövdesinde, gruba dahil edilecek komutları belirtin.
  3. Gönderilen komutların sonuçlarını kontrol ederek başarılı olup olmadıklarını görebilirsiniz.

Toplu komut gönderme

Home.sendBatchedCommands() yöntemini çağırın. Bu yöntem, arka planda özel bir toplu işlem bağlamında lambda ifadesi oluşturur.

home.sendBatchedCommands() {

Toplu komutları belirtme

sendBatchedCommands() bloğunun gövdesinde toplu olarak çalıştırılabilir komutları doldurun. Toplu olarak çalıştırılabilen komutlar, mevcut Device API komutlarının toplu bağlamda kullanılabilen "gölge" sürümleridir ve Batchable son eki eklenerek adlandırılır. Örneğin, LevelControl özelliğinin moveToLevel() komutunun moveToLevelBatchable() adında bir karşılığı vardır.

Örnek:

  val response1 = add(command1)

  val response2 = add(command2)

Tüm komutlar toplu iş bağlamına eklendikten ve yürütme bağlamdan ayrıldıktan sonra toplu iş otomatik olarak gönderilir.

Yanıtlar DeferredResponse<T> nesnelerinde yakalanır.

DeferredResponse<T> örnekleri, Collection gibi herhangi bir türdeki nesnede veya tanımladığınız bir veri sınıfında toplanabilir. Yanıtları bir araya getirmek için seçtiğiniz nesne türü, sendBatchedCommands() tarafından döndürülür. Örneğin, toplu bağlam bir Pair içinde iki DeferredResponse örneği döndürebilir:

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

Alternatif olarak, toplu bağlam, özel bir veri sınıfındaki DeferredResponse örneklerini döndürebilir:

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

Her yanıtı kontrol etme

sendBatchedCommands() bloğunun dışında, ilgili komutun başarılı olup olmadığını belirlemek için yanıtları kontrol edin. Bu işlem, DeferredResponse.getOrThrow() çağrılarak yapılır. Bu çağrı: - Yürütülen komutun sonucunu döndürür. - Toplu işlem kapsamı tamamlanmadıysa veya komut başarısız olduysa hata verir.

Sonuçları yalnızca sendBatchedCommands() lambda kapsamının dışında kontrol etmeniz gerekir.

Örnek

Batching API'yi kullanarak evdeki tüm cihazları gece herkes uyurken gece moduna geçiren bir "iyi geceler" sahnesi ayarlayan bir uygulama geliştirmek istediğinizi varsayalım. Bu uygulama ışıkları kapatıp ön ve arka kapıları kilitlemeli.

Bu göreve şu şekilde yaklaşabilirsiniz:

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

Cihaz grubunu kontrol etme

Üye cihazları arasında gezinerek bir grubun her üyesi üzerinde ayrıntılı kontrol sahibi olmanın yanı sıra DeviceGroup'yi tek bir varlık olarak kontrol edebilirsiniz. DeviceGroup, gruptaki cihazlar tarafından desteklenen DeviceType parçalarını gösteren PartsProvider arayüzünü uygular:

  • group.parts(), gruptaki tüm cihaz türlerinin Flow<Set<DeviceType>> değerini döndürür.
  • group.part() ve group.partOrNull(), OnOffLightDevice veya DimmableLightDevice gibi belirli bir cihaz türü için akış döndürür.

Bir DeviceGroup kapsamındaki cihaz türünde özellik komutu çağırdığınızda SDK, temel bağlantı veya aktarım türünden (ör. Matter veya Cloud-to-cloud) bağımsız olarak komutu gruptaki bu özelliği destekleyen tüm ilişkili üye cihazlara iletir.

Grup parça özelliği nitelikleri ve hata işleme

Bir grup parçasını hedeflemek, bir grup genelinde idempotent komut (ışıkları açma veya kapatma ya da parlaklık seviyesi ayarlama gibi) göndermek istediğiniz yaygın durumları basitleştirir. DeviceGroup parçasında özellikler ve komutlarla çalışırken aşağıdaki davranışları göz önünde bulundurun:

  • Dinamik durum özellikleri (ör. OnOff özelliğinin onOff özelliği veya LevelControl özelliğinin currentLevel özelliği) genellikle bir grup parçasının özelliğinde doldurulmaz. Bunun nedeni, bir gruptaki üye cihazların tek bir değerde birleştirilemeyen farklı durumlarda olabilmesidir. Ancak desteklenen komutlar (supports() ile kontrol edilir, örneğin kısılabilir bir ışıkta LevelControl.Command.MoveToLevelWithOnOff) ve belirli özellik nitelikleri (örneğin, renkli ışıklar için ColorControl üzerinde colorTempPhysicalMinMireds ve colorTempPhysicalMaxMireds) doldurulur. Böylece uygulamanız, kullanıcı arayüzü kontrollerini uygun şekilde yapılandırabilir.
  • Grup üyelerinin özellikleriyle ilgili tüm komutlar grup parçasında kullanılamaz. Özellikle değer döndüren komutlar kullanılamaz.
  • Grup bölümündeki bir komut, her bir temel üye cihaz komutu için ayrı sonuçlar döndürmez. Bağımsız komut sonuçlarını incelemek veya değer döndüren komutları çağırmak için doğrudan grubun üye cihazları arasında gezinin.
  • DeviceGroup üzerindeki bir parçaya gönderilen komutlar, üye cihazlar genelindeki tüm fanout işlemi tamamlandığında ve tüm temel komut başarıları veya başarısızlıkları toplandığında tamamlanır. Bir komut yalnızca ilişkili her üye cihaz için başarısız olursa veya tüm hedef cihazlar kullanılamıyorsa başarısız olur (HomeException hatası verir). Bu durumda, tek komut çağrısı söz dizimiyle eşleşen tek bir komut hatası gösterilir. En az bir üye cihaz komutu yürütürse grup komutu, istisna oluşturmadan tamamlanır.

Grup bölümüne komut gönderme

Bir cihaz grubundaki tüm OnOffLightDevice ışıkları açmak için:

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

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

Birden fazla ilgili cihaz türünde bir özelliği hedeflemek için group.parts() kullanarak bir gruptaki tüm cihaz türlerini de inceleyebilirsiniz (örneğin, grupta OnOff özelliğini destekleyen tüm ışık cihazı türlerini kapatma):

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()

Komut göndermeden önce komut desteğini kontrol edebilir veya bir grup parçasındaki özellik sınırlarını okuyabilirsiniz (örneğin, kısılabilir ışık parçasında MoveToLevelWithOnOff özelliğini kontrol etme veya renk sıcaklığı ışık parçasında colorTempPhysicalMinMireds ve colorTempPhysicalMaxMireds özelliklerini okuma):

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(),
  )
}