デバイスを操作する

トレイトがコマンドをサポートしているかどうかを確認する

サポートは、trait コマンドで確認することもできます。また、トレイトレベルの supports 関数を使用して、特定のデバイスでコマンドがサポートされているかどうかを確認します。

たとえば、On/Off トレイトの toggle コマンドをデバイスがサポートしているかどうかを確認するには、次のようにします。

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

デバイスにコマンドを送信する

コマンドの送信は、トレイトから状態属性を読み取ることに似ています。デバイスのオン / オフを切り替えるには、OnOff トレイトの切り替えコマンドを使用します。このコマンドは、Google Home エコシステム データモデルで toggle() として定義されています。このメソッドは、onOfftrue の場合は false に、false の場合は true に変更します。

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

すべての特性コマンドは suspend 関数であり、API からレスポンスが返された場合にのみ完了します(デバイスの状態が変更されたことを確認するなど)。実行フローで問題が検出されると、コマンドが例外を返すことがあります。デベロッパーは、try-catch ブロックを使用してこれらの例外を適切に処理し、エラーに対処できる場合はユーザーに詳細情報を表示する必要があります。例外が処理されないと、アプリのランタイムが停止し、アプリがクラッシュする可能性があります。

または、off() コマンドまたは on() コマンドを使用して、状態を明示的に設定します。

onOffTrait.off()
onOffTrait.on()

状態を変更するコマンドを送信したら、コマンドが完了したら、デバイスの状態を読み取るで説明されているように状態を読み取り、アプリで処理できます。または、状態をモニタリングするで説明されているようにフローを使用します。これは推奨される方法です。

パラメータ付きのコマンドを送信する

一部のコマンドは、OnOff または 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(),
)

一部のコマンドには、必須の引数の後に続くオプションの引数があります。

たとえば、FanControl トレイトの step コマンドには、次の 2 つのオプション引数があります。

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 }

トレイトが属性をサポートしているかどうかを確認する

デバイスによっては、Matter トレイトはサポートしていても、特定の属性はサポートしていない場合があります。たとえば、Matter にマッピングされた Cloud-to-cloud デバイスは、すべての Matter 属性をサポートしていない場合があります。このようなケースを処理するには、トレイトレベルの supports 関数とトレイトの Attribute 列挙型を使用して、特定のデバイスで属性がサポートされているかどうかを確認します。

たとえば、On/Off トレイトの onOff 属性をデバイスがサポートしているかどうかを確認するには、次のようにします。

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

一部の属性は、Matter 仕様または Cloud-to-cloud smart home スキーマで null にできます。これらの属性の場合、supports に加えて isNullable を使用すると、属性から返された null がデバイスがその値を報告していないためか、属性の値が実際に null であるかを判断できます。

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

特徴属性を更新する

特定の属性の値を変更したいが、トレイトのコマンドでは変更できない場合、その属性は値を明示的に設定できる可能性があります。

属性の値を変更できるかどうかは、次の 2 つの要因によって異なります。

  • 属性は書き込み可能か
  • トレイト コマンドの送信の副作用として、属性の値が変更される可能性がありますか?

この情報は、トレイトとその属性のリファレンス ドキュメントで確認できます。

したがって、属性の値を変更する方法を指定するプロパティの組み合わせは次のとおりです。

  • 読み取り専用で、他のコマンドの影響を受けません。つまり、属性の値は変更されません。たとえば、Switch トレイトの currentPosition 属性などです。

  • 読み取り専用で、他のコマンドの影響を受けます。つまり、属性の値を変更できるのは、コマンドを送信した結果のみです。たとえば、LevelControl Matter トレイトの currentLevel 属性は読み取り専用ですが、その値は moveToLevel などのコマンドで変更できます。

  • 書き込み可能で、他のコマンドの影響を受けません。つまり、トレイトの update 関数を使用して属性の値を直接変更できますが、属性の値に影響するコマンドは存在しません。たとえば、DoorLock トレイトの WrongCodeEntryLimit 属性などです。

  • 書き込み可能で、他のコマンドの影響を受けます。つまり、トレイトの update 関数を使用して属性の値を直接変更できます。また、コマンドの送信の結果として属性の値が変更されることもあります。たとえば、Thermostat トレイトの occupiedCoolingSetpoint 属性は書き込み可能であるだけでなく、setpointRaiseLower コマンドで更新することもできます。

update 関数を使用して属性の値を変更する例

この例は、DoorLockTrait.WrongCodeEntryLimit 属性の値を明示的に設定する方法を示しています。

属性値を設定するには、トレイトの update 関数を呼び出し、新しい値を設定するミュータタ関数を渡します。最初に、トレイトが属性をサポートしていることを確認することをおすすめします。

次に例を示します。

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