複雑な自動化のための iOS DSL ガイド

自動化 DSL を使用すると、DSL ガイド - iOS での基本的な自動化で説明されているものよりも複雑な自動化を作成できます。

複数のアクションを含む順次

複数のアクションを含む順次

1 つの自動化で複数の処理を行うことができます。たとえば、1 つの action ノードの代わりに、複数の action ノードを順次実行できます。

import GoogleHomeSDK
import GoogleHomeTypes

automation (
...
) {

  starter(...)
  condition {...}
  action {...}
  action {...}
  action {...}

}

複数の並列アクションを含むシーケンシャル

複数の並列アクションを含むシーケンシャル

parallel ノードに複数の action ノードを配置すると、アクションが同時に実行されます。

import GoogleHomeSDK
import GoogleHomeTypes

automation (
...
) {

  starter(...)
  condition {...}
  parallel {
    action {...}
    action {...}
    action {...}
  }

}

parallel ノードの後に sequential ノードに action ノードがある場合、parallel ノード内のすべてのノードの実行が完了するまで、実行を待機します。

条件付き実行

デフォルトでは、自動化はノードを順次または並列で実行します。条件付き分岐ロジック(実行時の条件に基づいて異なるアクションまたはパスを実行する)が必要な場合は、条件付き制御フロー DSL ブロック(ifThen、elseIf、orElse)を使用して構築された if-then-else ステートメントを使用します。

標準の condition ノードは自動化全体をゲートしますが(条件が false と評価された場合、自動化は実行を直ちに終了します)、ifThen ブロックは分岐制御フローを有効にします。

  • condition ノード: 式が false の場合、自動化全体(または現在の実行パス)の実行を停止します。
  • ifThen / elseIf / orElse: 条件を順番に評価します。条件が false の場合、実行は次の elseIf ブランチ、orElse フォールバック ブランチにフォールスルーするか、条件が満たされない場合は自動化の後続のノードに進みます。

条件に基づいてアクションを実行する

ifThen ブロックは条件式を評価します。式が true と評価された場合、ブロック内にネストされた DSL アクションまたはノードが実行されます。false と評価された場合、アクションはスキップされ、自動化はシーケンシャル フローの後続のノードに進みます。

自動化の後続のノードをブロックまたは終了せずに、条件付きでアクションを実行するだけの場合は、スタンドアロンの ifThen ブロックを使用できます。

// When a door opens, turn on the light only if it is off,
// and always broadcast an announcement.
typealias ContactSensorDevice = Matter.ContactSensorDeviceType
typealias BooleanStateTrait = Matter.BooleanStateTrait
typealias DimmableLightDevice = Matter.DimmableLightDeviceType
typealias OnOffTrait = Matter.OnOffTrait

automation {
  let contactState = starter(
    contactSensor,
    ContactSensorDevice.self,
    BooleanStateTrait.self
  )
  let lightState = stateReader(
    light,
    DimmableLightDevice.self,
    OnOffTrait.self
  )
  contactState
  lightState

  condition {
    // Door opened (contact sensor open)
    contactState.stateValue.equals(false)
  }

  // Conditionally turn on the light if it's off
  ifThen(lightState.onOff.equals(false)) {
    action(light, DimmableLightDevice.self) {
      OnOffTrait.on()
    }
  }

  // Continue executing subsequent actions in the sequential flow
  action(structure) {
    Google.AssistantBroadcastTrait.broadcast(msg: "The door was opened.")
  }
}

条件に基づいて異なるアクションを実行する

条件が true の場合に 1 つのアクション セットを実行し、false の場合に別のアクション セットを実行するには、ifThen(...) { ... } の後にオプションの .orElse { ... } ブロックを連結します。

// When the door is unlocked, turn on the entryway light if it is off;
// otherwise, broadcast a welcome message.
typealias DoorLockDevice = Matter.DoorLockDeviceType
typealias DoorLockTrait = Matter.DoorLockTrait
typealias DimmableLightDevice = Matter.DimmableLightDeviceType
typealias OnOffTrait = Matter.OnOffTrait

automation {
  let doorLockEvent = starter(
    doorLock,
    DoorLockDevice.self,
    DoorLockTrait.LockOperationEvent.self
  )
  let lightState = stateReader(
    light,
    DimmableLightDevice.self,
    OnOffTrait.self
  )
  doorLockEvent
  lightState

  condition {
    doorLockEvent.lockOperationType.equals(.unlock)
  }

  ifThen(lightState.onOff.equals(false)) {
    action(light, DimmableLightDevice.self) {
      OnOffTrait.on()
    }
  }.orElse {
    action(structure) {
      Google.AssistantBroadcastTrait.broadcast(msg: "Welcome home!")
    }
  }
}

複数の条件を順番に連結する

1 つ以上のオプションの .elseIf(...) { ... } ブロックを連結して、複数の条件を順番に評価できます。条件が true と評価される最初の分岐が実行され、残りの分岐はすべてスキップされます。true と評価される条件がない場合、オプションの .orElse { ... } ブロックが実行されます(指定されている場合)。

// Adjust climate controls based on room temperature changes.
typealias TemperatureSensorDeviceType = Matter.TemperatureSensorDeviceType
typealias TemperatureMeasurementTrait = Matter.TemperatureMeasurementTrait
typealias ThermostatDeviceType = Matter.ThermostatDeviceType
typealias SimplifiedThermostatTrait = Google.SimplifiedThermostatTrait
typealias FanDeviceType = Matter.FanDeviceType
typealias OnOffTrait = Matter.OnOffTrait

automation {
  let tempStarter = starter(
    tempSensor,
    TemperatureSensorDeviceType.self,
    TemperatureMeasurementTrait.self
  )
  tempStarter

  // If temperature is high (>= 28°C / 2800 mC), switch thermostat to Cool mode
  ifThen(tempStarter.measuredValue.greaterThanOrEquals(2800)) {
    action(thermostat, ThermostatDeviceType.self) {
      SimplifiedThermostatTrait.setSystemMode(systemMode: .cool)
    }
  }.elseIf(tempStarter.measuredValue.lessThan(1800)) {
    // If temperature is low (< 18°C / 1800 mC), switch to Heat mode
    action(thermostat, ThermostatDeviceType.self) {
      SimplifiedThermostatTrait.setSystemMode(systemMode: .heat)
    }
  }.orElse {
    // Otherwise, turn on the fan
    action(fan, FanDeviceType.self) {
      OnOffTrait.on()
    }
  }
}

ネストされた条件付きフロー

条件付きブロックは、他の ifThen、elseIf、orElse ブロック内にネストしたり、parallel、delay(for:)、stateReader ノードと組み合わせたりできます。

ifThen、elseIf、orElse ブロックは、その内容をシーケンシャル フローとして実行します。各ブランチ内には、action、stateReader、parallel、delay(for:)、ネストされた ifThen ブロックなど、任意のシーケンシャル ノードを配置できます。

処理の遅れ

自動化に一時停止を導入するには、delay(for:) メソッドを使用します。このメソッドは、実行を続行する前に一時停止する時間を表す Duration 引数を取ります。一時停止の期間は、最短で 5 秒、最長で 24 時間です。

たとえば、ライトを 4 回切り替え、各切り替えの間に 5 秒間の一時停止を挿入するには、次のようにします。

typealias OnOffLightDevice = Matter.OnOffLightDeviceType
typealias OnOffTrait = Matter.OnOffTrait

sequential {
  action(light, OnOffLightDevice.self) { OnOffTrait.toggle() }
  delay(for:.seconds(5))
  action(light, OnOffLightDevice.self) { OnOffTrait.toggle() }
  delay(for:.seconds(5))
  action(light, OnOffLightDevice.self) { OnOffTrait.toggle() }
  delay(for:.seconds(5))
  action(light, OnOffLightDevice.self) { OnOffTrait.toggle() }
}

トリガーの抑制

トリガーの抑制は、最初のトリガー イベントの後、指定された期間、自動化が starter を無視できるようにする機能です。たとえば、自動化にモーション検知によってトリガーされる starter があり、トリガー抑制期間を 5 分に指定した場合、starter がトリガーされると、次の 5 分間はトリガーされません。これにより、自動化が何度も繰り返しトリガーされるのを防ぐことができます。

自動化にトリガーの抑制を適用するには、suppress(for:) キーワードと、後続のトリガーに応答するまでの待機時間を表す Duration 引数を使用します。抑制期間は、最短で 5 秒、最長で 24 時間です。

typealias OccupancySensorDevice = Matter.OccupancySensorDeviceType
typealias OnOffLightDevice = Matter.OnOffLightDeviceType
typealias MotionDetectionTrait = Google.MotionDetectionTrait
typealias OnOffTrait = Matter.OnOffTrait

automation {
  let starterNode = starter(device, OccupancySensorDevice.self, MotionDetectionTrait.self)
  starterNode
  suppress(for: .seconds(30 * 60)) // 30 minutes
  action(light, OnOffLightDevice.self) { OnOffTrait.toggle() }
}

トリガーの抑制は、Suppression の前にある自動化のすべての starters に影響します。

実行数を制限する

自動化の実行回数を制限できます。

たとえば、1 日外出している間に掃除機をかける 1 回限りの自動化を設定できます。

これを行うには、自動化の maxExecutionCount メタデータ フィールドを設定します。次の例は、1 回だけ実行できる自動化です。

import GoogleHomeSDK
import GoogleHomeTypes

typealias RoboticVacuumCleanerDevice = Matter.RoboticVacuumCleanerDeviceType
typealias RvcRunModeTrait = Matter.RvcRunModeTrait
typealias AreaPresenceStateTrait = Google.AreaPresenceStateTrait

let draftAutomation = automation(
  name: "Vacuum home away",
  description: "Run the vacuum once when everyone is away.",
  maxExecutionCount: 1
) {
  let homeAwayState = starter(structure, AreaPresenceStateTrait.self)
  homeAwayState

  condition {
    homeAwayState.presenceState.equals(.presenceStateVacant)
  }

  action(vacuum, RoboticVacuumCleanerDevice.self) {
    RvcRunModeTrait.changeToMode(newMode: 1)
  }
}

自動化は、最後の実行が完了して maxExecutionCount に達するとすぐに削除されます。自動化の履歴エントリは、automation_id を含め、Google Home app (GHA) の [アクティビティ] タブに残ります。

アクションでトレイト属性を設定する

トレイト属性の値を設定するには:

  1. action ノード内に update ノードを作成し、関連するトレイトを update ノードの引数として含めます。
    action(deviceReference, deviceType) {
      update(trait) {
    
      }
    }
    
  2. update ノード内で、変更する属性ごとにミューテーター関数を使用し、新しい値を渡します。ミューテーター関数の名前を形成するには:
    1. 属性名の先頭を大文字にする
    2. set という単語を接頭辞として付けます。
    たとえば、defaultMoveRate という属性を更新するには、setDefaultMoveRate というミューテーター関数を使用します。

update ノードには複数のミューテーター関数を設定できます。次の例では、2 つの属性が更新されています。

typealias FanDeviceType = Matter.FanDeviceType
typealias FanControlTrait = Matter.FanControlTrait

action(fan, FanDeviceType.self) {
  update(FanControlTrait.self) {
    $0.setFanMode(.on)
  }
}