Guide du DSL Android pour les automatisations complexes

Le langage DSL d'automatisation peut être utilisé pour créer des automatisations plus complexes que celles abordées dans le guide DSL – Automatisations de base sur Android.

Séquentiel avec plusieurs actions

Séquentiel avec plusieurs actions

Une automatisation peut effectuer plusieurs actions. Par exemple, au lieu d'un seul nœud action, vous pouvez avoir plusieurs nœuds action qui s'exécutent de manière séquentielle :

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

Séquentiel avec plusieurs actions parallèles

Séquentiel avec plusieurs actions parallèles

Si vous placez plusieurs nœuds action dans un nœud parallel, les actions s'exécutent simultanément.

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

Si des nœuds action du nœud sequential suivent le nœud parallel, ils attendent que tous les nœuds du nœud parallel aient terminé leur exécution.

Exécution conditionnelle

Par défaut, une automatisation exécute les nœuds de manière séquentielle ou en parallèle. Si vous avez besoin d'une logique de branchement conditionnel (exécution de différentes actions ou chemins en fonction des conditions d'exécution), utilisez des instructions if-then-else, qui sont créées à l'aide des blocs DSL de flux de contrôle conditionnel : ifThen, elseIf, et orElse.

Alors qu'un nœud condition standard contrôle l'ensemble de l'automatisation (si la condition renvoie la valeur false, l'exécution de l'automatisation s'arrête immédiatement), les blocs ifThen permettent de contrôler le flux de branchement :

  • Nœud condition : arrête l'exécution de l'ensemble de l'automatisation (ou du chemin d'exécution actuel) si l'expression est false.
  • ifThen / elseIf / orElse : évalue les conditions dans l'ordre. Si une condition est false, l'exécution passe à la branche elseIf suivante, à la branche de secours orElse ou se poursuit vers les nœuds suivants de l'automatisation si aucune condition n'est remplie.

Exécuter une action en fonction d'une condition

Le bloc ifThen évalue une expression conditionnelle. Si l'expression renvoie la valeur true, les actions ou les nœuds DSL imbriqués dans le bloc sont exécutés. Si elle renvoie la valeur false, les actions sont ignorées et l'automatisation passe aux nœuds suivants du flux séquentiel.

Vous pouvez utiliser un bloc ifThen autonome lorsque vous ne souhaitez effectuer une action que de manière conditionnelle, sans bloquer ni arrêter les nœuds suivants de l'automatisation :

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

Exécuter différentes actions en fonction d'une condition

Pour exécuter un ensemble d'actions lorsqu'une condition est vraie et un autre ensemble d'actions lorsqu'elle est fausse, enchaînez le bloc facultatif .orElse { ... } après ifThen(...) { ... } :

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

Enchaîner plusieurs conditions dans une séquence

Vous pouvez enchaîner un ou plusieurs blocs facultatifs .elseIf(...) { ... } pour évaluer plusieurs conditions dans une séquence. La première branche dont la condition renvoie la valeur true est exécutée, et toutes les branches restantes sont ignorées. Si aucune des conditions ne renvoie la valeur true, un bloc facultatif .orElse { ... } s'exécute (s'il est fourni) :

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

Flux conditionnels imbriqués

Les blocs conditionnels peuvent être imbriqués dans d'autres blocs ifThen, elseIf ou orElse, et combinés avec des nœuds parallel, delayFor et stateReader.

Les blocs ifThen, elseIf et orElse exécutent leur contenu sous forme de flux séquentiels. Vous pouvez placer n'importe quel nœud séquentiel dans chaque branche, y compris les blocs action, stateReader, parallel, delayFor et ifThen imbriqués.

Retards

Vous pouvez introduire des pauses dans vos automatisations à l'aide du delayFor mot clé, qui accepte un java.time.Duration argument représentant la durée de la pause avant de poursuivre l'exécution. La durée de la pause peut être de cinq secondes à 24 heures.

Par exemple, pour allumer et éteindre une lumière quatre fois avec une pause de cinq secondes entre chaque action :

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

Suppression du déclencheur

La suppression du déclencheur est une fonctionnalité qui permet à votre automatisation d'ignorer un starter pendant une période spécifiée après l'événement de déclenchement initial. Par exemple, si l'automatisation comporte un starter déclenché par la détection de mouvement et que vous spécifiez une durée de suppression du déclencheur de cinq minutes, lorsque le starter se déclenche, il ne se déclenchera plus pendant les cinq minutes suivantes. Cela empêche l'automatisation de se déclencher rapidement à plusieurs reprises.

Pour appliquer la suppression du déclencheur à votre automatisation, utilisez le suppressFor mot clé avec un java.time.Duration argument représentant le délai d'attente avant de répondre aux déclencheurs suivants. La durée de la suppression peut être de cinq secondes à 24 heures.

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

Notez que la suppression du déclencheur affecte tous les starters d'une automatisation qui précèdent le suppressFor.

Limiter le nombre d'exécutions

Vous pouvez limiter le nombre d'exécutions autorisées pour une automatisation.

Par exemple, vous pouvez configurer une automatisation unique qui exécute l'aspirateur lorsque vous êtes absent de chez vous pendant la journée.

Pour ce faire, définissez le champ de métadonnées maxExecutionCount de l'automatisation. L'exemple suivant est une automatisation qui ne peut s'exécuter qu'une seule fois :

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

L'automatisation est immédiatement supprimée une fois qu'elle a terminé son exécution pour la dernière fois et que maxExecutionCount est atteint. L'entrée d'historique de l'automatisation reste dans l'onglet Google Home app (GHA) Activité, y compris le automation_id.

Définir les attributs de caractéristique dans une action

Pour définir la valeur d'un attribut de caractéristique :

  1. Créez un nœud update dans un nœud action, y compris la caractéristique pertinente comme argument du nœud update :
    action(deviceReference, deviceType) {
      update(trait) {
    
      }
    }
  2. Dans le nœud update, pour chaque attribut à modifier, utilisez une fonction de mutation et transmettez-lui la nouvelle valeur. Pour former le nom de la fonction de mutation :
    1. Mettez en majuscule le nom de l'attribut.
    2. Ajoutez le préfixe set.
    Par exemple, pour mettre à jour un attribut appelé defaultMoveRate, vous devez utiliser une fonction de mutation appelée setDefaultMoveRate.

Notez qu'un nœud update peut comporter plusieurs fonctions de mutation. Voici un exemple dans lequel deux attributs sont mis à jour :

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