适用于复杂自动化操作的 Android DSL 指南

自动化 DSL 可用于创建比 DSL 指南 - Android 上的基本自动化操作中讨论的自动化操作更复杂的自动化操作。

包含多项操作的序列

包含多项操作的序列

一项自动化操作可以执行多项操作。例如,您可以将单个 action 节点替换为多个 action 节点,这些节点按顺序运行:

automation {
  sequential {
    starter<_>(...)
    condition {...}
    action {...}
    action {...}
    action {...}
    }
}

包含多个并行操作的顺序工作流

包含多个并行操作的顺序工作流

如果您在 parallel 节点中放置多个 action 节点,则这些操作会并发执行。

automation {
  sequential {
    starter<_>(...)
    condition {...}
    parallel {
      action {...}
      action {...}
      action {...}
    }
  }
}

如果 sequential 节点中有 action 节点位于 parallel 节点之后,则这些节点会等待执行,直到 parallel 节点中的所有节点都执行完毕。

条件执行

默认情况下,自动化操作会按顺序或并行执行节点。如果您需要条件分支逻辑(根据运行时条件执行不同的操作或路径),请使用 if-then-else 语句,这些语句是使用条件控制流 DSL 块构建的:ifThen、elseIf 和 orElse。

标准 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.
automation {
  sequential {
    val contactState = starter<_>(
      contactSensor,
      ContactSensorDevice,
      BooleanState,
    )
    val lightState = stateReader<_>(
      light,
      DimmableLightDevice,
      OnOff,
    )

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

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

    // Continue executing subsequent actions in the sequential flow
    action(structure) {
      command(AssistantBroadcast.broadcast("The door was opened."))
    }
  }
}

根据条件执行不同的操作

如需在条件为 true 时执行一组操作,并在条件为 false 时执行另一组操作,请在 ifThen(...) { ... } 之后链接可选的 .orElse { ... } 代码块:

// When the door is unlocked, turn on the entryway light if it is off;
// otherwise, broadcast a welcome message.
automation {
  sequential {
    val doorLockEvent = starter<_>(
      doorLock,
      DoorLockDevice,
      LockOperationEvent,
    )
    val lightState = stateReader<_>(
      light,
      DimmableLightDevice,
      OnOff,
    )

    condition {
      expression =
        doorLockEvent.lockOperationType equals LockOperationTypeEnum.Unlock
    }

    ifThen(lightState.onOff equals false) {
      action(light, DimmableLightDevice) { command(OnOff.on()) }
    }.orElse {
      action(structure) {
        command(AssistantBroadcast.broadcast("Welcome home!"))
      }
    }
  }
}

按顺序链接多个条件

您可以将一个或多个可选 .elseIf(...) { ... } 块链接在一起,以便按顺序评估多个条件。系统会执行条件求值为 true 的第一个分支,并跳过所有剩余分支。如果所有条件求值结果都不为 true,则执行可选的 .orElse { ... } 代码块(如果已提供):

// Adjust climate controls based on room temperature changes.
automation {
  sequential {
    val tempStarter = starter<_>(
      tempSensor,
      TemperatureSensorDevice,
      TemperatureMeasurement,
    )

    // If temperature is high (>= 28°C / 2800 mC), switch thermostat to Cool mode
    ifThen(tempStarter.measuredValue greaterThanOrEquals 2800) {
      action(thermostat, ThermostatDevice) {
        command(
          SimplifiedThermostat.setSystemMode(
            SimplifiedThermostatSystemModeEnum.Cool
          )
        )
      }
    }.elseIf(tempStarter.measuredValue lessThan 1800) {
      // If temperature is low (< 18°C / 1800 mC), switch to Heat mode
      action(thermostat, ThermostatDevice) {
        command(
          SimplifiedThermostat.setSystemMode(
            SimplifiedThermostatSystemModeEnum.Heat
          )
        )
      }
    }.orElse {
      // Otherwise, turn on the fan
      action(fan, FanDevice) {
        command(OnOff.on())
      }
    }
  }
}

嵌套条件流

条件块可以嵌套在其他 ifThen、elseIf 或 orElse 块中,也可以与 parallel、delayFor 和 stateReader 节点组合使用。

ifThen、elseIf 和 orElse 块会按顺序执行其内容。您可以在每个分支中放置任何顺序节点,包括 action、stateReader、parallel、delayFor 和嵌套的 ifThen 代码块。

延误

您可以使用 delayFor 关键字在自动化操作中引入暂停,该关键字接受一个 java.time.Duration 实参,表示在继续执行之前要暂停多长时间。暂停时长可能短至 5 秒,也可能长至 24 小时。

例如,如需切换灯光四次,每次切换之间暂停五秒,请执行以下操作:

sequential {
  action(light, OnOffLightDevice) { command(OnOff.toggle()) }
  delayFor(Duration.ofSeconds(5))
  action(light, OnOffLightDevice) { command(OnOff.toggle()) }
  delayFor(Duration.ofSeconds(5))
  action(light, OnOffLightDevice) { command(OnOff.toggle()) }
  delayFor(Duration.ofSeconds(5))
  action(light, OnOffLightDevice) { command(OnOff.toggle()) }
}

触发器抑制

触发器抑制功能可让自动化操作在初始触发事件发生后的指定时间段内忽略 starter。例如,如果自动化操作具有由移动侦测触发的 starter,并且您指定了 5 分钟的触发器抑制时长,那么当 starter 触发后,在接下来的 5 分钟内不会再次触发。这可以防止自动化操作快速重复触发。

如需对自动化操作应用触发器抑制,请使用 suppressFor 关键字,并添加 java.time.Duration 参数,该参数表示在响应后续触发器之前要等待多长时间。抑制时长可能短至 5 秒,也可能长至 24 小时。

automation {
  sequential {
    val starterNode = starter<_>(device, OccupancySensor, MotionDetection)
    suppressFor(Duration.ofMinutes(30))
    action(light, OnOffLightDevice) { command(OnOff.toggle()) }
}

请注意,触发器抑制会影响自动化操作中位于 suppressFor 之前的所有 starters。

限制执行次数

您可以限制自动化操作的运行次数。

例如,您可能希望设置一个一次性自动化操作,在您外出期间运行吸尘器。

为此,请设置自动化的 maxExecutionCount 元数据字段。以下示例是一个只能执行一次的自动化操作:

automation {
  // The automation can only be executed once.
  maxExecutionCount = 1
  // When the door lock state changes
  sequential {
    val doorLockEvent = starter<_>(doorLock, DoorLockDevice, LockOperationEvent)
    // if the door is unlocked
    condition() {
      expression = (doorLockEvent.lockOperationType equals LockOperationTypeEnum.Unlock)
    }
    // turn the light on
    action(light, DimmableLightDevice) { command(OnOff.on()) }
  }
}

自动化操作在最后一次执行完毕并达到 maxExecutionCount 后,会立即被删除。自动化操作的历史记录条目会保留在 Google Home app (GHA) 活动标签页中,包括 automation_id。

在操作中设置特征属性

如需设置特征属性的值,请执行以下操作:

  1. 在 action 节点内创建一个 update 节点,并将相关 trait 作为参数添加到 update 节点:
    action(deviceReference, deviceType) {
      update(trait) {
    
      }
    }
  2. 在 update 节点内,针对要修改的每个属性,使用一个 mutator 函数,并向其传递新值。 如需形成 mutator 函数的名称,请执行以下操作:
    1. 将属性名称首字母大写
    2. 为该地址加上 set 前缀。
    例如,如需更新名为 defaultMoveRate 的属性,您可以使用名为 setDefaultMoveRate 的 mutator 函数。

请注意,一个 update 节点可以有多个 mutator 函数。以下是更新两个属性的示例:

action(device, Fan) {
  update(FanControl) {
    setPercentSetting(50u)
    setRockSetting(FanControlCluster.RockBitmap.rockUpDown)
  }
}