Guia de DSL do iOS para automações complexas

A DSL de automação pode ser usada para criar automações mais complexas do que as discutidas em Guia da DSL: automações básicas no iOS.

Sequencial com várias ações

Sequencial com várias ações

Uma automação pode fazer mais de uma coisa. Por exemplo, em vez do único nó action, você pode ter vários nós action, que são executados em ordem sequencial:

import GoogleHomeSDK
import GoogleHomeTypes

automation (
...
) {

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

}

Sequencial com várias ações paralelas

Sequencial com várias ações paralelas

Se você colocar vários nós action em um nó parallel, as ações serão executadas simultaneamente.

import GoogleHomeSDK
import GoogleHomeTypes

automation (
...
) {

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

}

Se houver nós action no nó sequential que vêm depois do nó parallel, eles vão esperar para serem executados até que todos os nós dentro do nó parallel tenham terminado a execução.

Execução condicional

Por padrão, uma automação executa nós sequencialmente ou em paralelo. Se você precisar de uma lógica de ramificação condicional, executando diferentes ações ou caminhos com base nas condições de execução, use instruções if-then-else, que são criadas usando os blocos DSL de fluxo de controle condicional: ifThen, elseIf, e orElse.

Enquanto um nó condition padrão limita toda a automação (se a condição for avaliada como false, a automação encerra a execução imediatamente), os blocos ifThen permitem o fluxo de controle de ramificação:

  • condition: interrompe a execução de toda a automação (ou caminho de execução atual) se a expressão for false.
  • ifThen / elseIf / orElse: avalia as condições em ordem. Se uma condição for false, a execução vai para a próxima ramificação elseIf, a ramificação de fallback orElse ou continua para os nós subsequentes na automação se nenhuma condição for atendida.

Executar uma ação com base em uma condição

O bloco ifThen avalia uma expressão condicional. Se a expressão for avaliada como true, as ações ou nós DSL aninhados dentro do bloco serão executados. Se for avaliada como false, as ações serão ignoradas, e a automação continuará para os nós subsequentes no fluxo sequencial.

Você pode usar um bloco ifThen independente quando quiser realizar uma ação condicionalmente sem bloquear ou encerrar os nós subsequentes na automação:

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

Executar ações diferentes com base em uma condição

Para executar um conjunto de ações quando uma condição for verdadeira e um conjunto alternativo de ações quando for falsa, encadeie o bloco .orElse { ... } opcional após ifThen(...) { ... }:

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

Encadear várias condições em sequência

Você pode encadear um ou mais blocos .elseIf(...) { ... } opcionais para avaliar várias condições em sequência. A primeira ramificação cuja condição é avaliada como true é executada, e todas as ramificações restantes são ignoradas. Se nenhuma das condições for avaliada como true, um bloco .orElse { ... } opcional será executado (se fornecido):

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

Fluxos condicionais aninhados

Os blocos condicionais podem ser aninhados dentro de outros blocos ifThen, elseIf ou orElse, além de combinados com nós parallel, delay(for:) e stateReader.

Os blocos ifThen, elseIf e orElse executam o conteúdo como fluxos sequenciais. Você pode colocar qualquer nó sequencial dentro de cada ramificação, incluindo action, stateReader, parallel, delay(for:) e blocos ifThen aninhados.

Atrasos

Você pode introduzir pausas nas automações usando o delay(for:) método, que recebe um Duration argumento que representa o tempo de pausa antes de continuar a execução. A duração da pausa pode ser de cinco segundos a 24 horas.

Por exemplo, para alternar uma luz quatro vezes com uma pausa de cinco segundos entre cada alternância:

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

Supressão de acionadores

A supressão de acionadores é um recurso que permite que a automação ignore um starter por um período especificado após o evento de acionamento inicial. Por exemplo, se a automação tiver um starter acionado pela detecção de movimento e você especificar uma duração de supressão de acionador de cinco minutos, quando o starter for acionado, ele não será acionado novamente nos próximos cinco minutos. Isso impede que a automação seja acionada rapidamente várias vezes.

Para aplicar a supressão de acionadores à automação, use a suppress(for:) palavra-chave com um Duration argumento que representa o tempo de espera antes de responder a acionadores subsequentes. A duração da supressão pode ser de cinco segundos a 24 horas.

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

Observação: a supressão de acionadores afeta todos os starters em uma automação que antecedem a Suppression.

Limitar o número de execuções

Você pode limitar o número de vezes que uma automação pode ser executada.

Por exemplo, você pode configurar uma automação única que executa o aspirador de pó enquanto você estiver fora de casa durante o dia.

Para fazer isso, defina o campo de metadados maxExecutionCount da automação. O exemplo a seguir é uma automação que só pode ser executada uma vez:

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

A automação é excluída imediatamente quando conclui a execução pela última vez e maxExecutionCount é atingido. A entrada do histórico da automação permanece na guia Google Home app (GHA) Atividade, incluindo o automation_id.

Definir atributos de característica em uma ação

Para definir o valor de um atributo de característica:

  1. Crie um nó update dentro de um nó action, incluindo a característica relevante como um argumento para o nó update:
    action(deviceReference, deviceType) {
      update(trait) {
    
      }
    }
    
  2. Dentro do nó update, para cada atributo a ser modificado, use uma função mutadora e transmita o novo valor. Para formar o nome da função mutadora:
    1. Coloque o nome do atributo em maiúsculas.
    2. Adicione o prefixo set.
    Por exemplo, para atualizar um atributo chamado defaultMoveRate, você usaria uma função mutadora chamada setDefaultMoveRate.

Um nó update pode ter várias funções mutadoras. Confira um exemplo em que dois atributos são atualizados:

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

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