Automancer docs

Understanding protocols


Protocols in Automancer are written as text files in a custom language called PCRL. This language is very close to YAML, without the rarely-used features that make YAML a very complex language, contrary to what one make think at first glance.

Despite providing a built-in editor, Automancer directly acts on your filesystem. You are free to edit these files in the editor of your choice, delete them, re-create them, etc. Automancer will detect these changes and act accordingly. The built-in editor provides powerful language features but your are not required to use it.

Let's start with a simple example. Observe the following:

    - wait: 10 min
      Thermostat.temperature: 10 degC
    - wait: 10 min
      Thermostat.temperature: 50 degC
  repeat: 10

In this example, a thermostat is being controlled, and its setpoint is being alternated 10 times between 10 and 50°C. You might notice a few key features on Automancer:

  • Automancer's syntax is declarative, as opposed to imperative. You describe what you want to happen, not instructions on how to make it happen. This protocol could be described as “Wait 10 minutes with the thermostat at 10°C, then wait 10 minutes at 50°C, and repeat this 10 times” rather than “Set the thermostat to 10°C, wait 10 minutes, then set it to 50°C, then wait 10 minutes, ..., [repeated 10 times]”. The difference is subtle in this example, but becomes very important when writing more complex protocols.

  • All quantities must have an explicit unit, unless they are unitless. Here, we set the temperature the 10°C but we could have written 50°F (as 50 degF) with an identical result. For more on this, see Defining quantities.

  • Automancer is modular by design: the repeat, wait, actions and Thermostat.temperature attributes are all defined by different plugins. While the first three are fixed attributes, Thermostat.temperature has been generated by a plugin using a configuration provided by the user. Furthermore, it is possible to create your own plugin to extend the language. For details, see Plugin development.

  • Protocols are defined as blocks which can be infinitely nested. In this example, the protocol structure generated from this file is:

    Repeat (repeat: 10)
    └── Sequence (actions:)
        ├── State (Thermostat.temperature: 10 degC)
        │   └── Timer (wait: 10 min)
        └── State (Thermostat.temperature: 50 degC)
            └── Timer (wait: 10 min)

    A simplified but equivalent structure is visible in the user interface. Understanding the structure of is not necessary for designing simple protocols. For details, see Understanding protocol structure.

Performing actions during protocol steps with processes

Each step in an Automancer protocol must have a single role, known as a process, attached to it. A process describes what the system should do during this step. In particular, a process decides when a step terminates. In the example above, there were two processes, both from the timer plugin, declared with the wait attribute.

Many other processes exist, and new ones can be added by installing plugins. The core processes are listed below.

  • Timer – Waits for a fixed duration. For details, see Timer plugin.
  • Query – Queries the user for data or otherwise waits for the user's confirmation. For details, see Query plugin.
  • Asynchronous executor of Python tasks
  • Synchronization primitives

Processes can also be used to control devices:

  • File upload to an AWS S3 bucket is a process as it can take a significant amount of time to finish. A parallel block could be used to perform other actions in the meantime. For details, see S3 plugin.
  • Camera capture is a process which produces data.

Associating state to protocol fragments

Every step in a protocol has an associated state which describes how each device should behave during that step. The declaration of a state is not limited to a step, however: a state can be defined for a subset of steps, or even for the entire protocol. You might want to keep a device's value constant for the entire protocol, and also change another device's value at each step. At runtime, Automancer computes the exact state of the step (or steps) which are currently running, while making sure to avoid unnecessary device value changes in the process.

Using the Thermostat.temperature attribute in the first example caused a state to be attached to each process. Let's reuse this attribute on other fragments of a protocol.

    - wait: 10 min
      Thermostate.temperature: 12 degC
    - wait: 20 min
  Thermostat.temperature: 10 degC

We have now set a value of 10°C on the root block, meaning it will stay the default for the entire protocol. When starting the protocol, the step defined by the wait: 10 min process will be executed first. As a state is attached to that process, it will override the default of 10°C with a value of 12°C instead. Note that the thermostat won't be instructed to set its value to 10°C, but will directly receive an instruction for 12°C. In the second step, the default applies as no state has been attached to the wait: 12 min process. The thermostat will thus be instructed to set its value to 10°C.

The system will wait for the device to confirm that it has set the temperature to its setpoint before starting each timer. This doesn't mean that the thermostat will have reached its target temperature, however.

State can be infinitely nested as a protocol becomes more complex. The following example makes uses of yet another nesting level.

    - wait: 10 min
    - wait: 10 min
      Thermostat.temperature: 12 degC
    - actions:
        - wait: 10 min
        - wait: 10 min
          Thermostat.temperature: 16 degC
      Thermostat.temperature: 14 degC
  Thermostat.temperature: 10 degC

There are four processes in this protocol, with temperatures increasing from 10°C to 12, 14 and finally 16°C.

It is possible to customize state execution using additional attributes. For details, see Managing protocol state.