Variables

Since workflows are typically written in an event-based manner, you may have a need to store information, such as state data, between received events but within a single workflow instance, so you can do things like continue progressing through your own states when receiving subsequent events. For this purpose the setVar, getVar, and unsetVar methods are available.

๐Ÿ“˜

URN type for Variables

Since variables are stored in the Relay server and not on a specific device, there is no target parameter to these methods, thus no URN parameter is needed.

Setting, Getting, and Unsetting Variables

Variables are simple key/value pairs. Both the key and the value must be a string type. Other types such as int, boolean, etc, are not supported for the value. If you really want to use a non-string type as a value, you'll need to serialize/deserialize to/from a string or use the getNumberVar helper method to retrieve your stored variables as an integer, it will attempt deserialization.

There is an upper limit to the amount of data you can store via setVar: all the key/value pairs in a single workflow instance (including associated overhead) must be less than 1MB.

Here is an example that illustrates the use of setVar and getVar.

...
const interactionName = 'date time'

workflow.on(Event.START, async (event) => {
    const { trigger: { args: { source_uri } } } = event
    
    let date_obj = new Date();
    
    // Create a variable that has the value of the date and time that the workflow
    // was triggered.
    await workflow.setVar("my_start_time", date_obj)
    
    workflow.startInteraction(source_uri, interactionName)
})

workflow.on(Event.INTERACTION_STARTED, async ({ source_uri: interaction_uri }) => {
    // We can use the value of this variable we created in another event.
    // getVar returns the value that is stored in that variable.
    await workflow.say(interaction_uri, `Triggered at` + await workflow.getVar("my_start_time"))
    await workflow.endInteraction(interaction_uri)
})
  
workflow.on(Event.INTERACTION_ENDED, async() => {
    // However, once the workflow instance terminates, the stored value is gone
    await workflow.terminate()
  })
})
...
@my_workflow.on_start
async def start_handler(workflow, trigger):
  target = workflow.make_target_uris(trigger)

  now = datetime.now()
  time = now.strftime("%H:%M:%S")

  # Create a variable that has the value of the date and time that the workflow
  # was triggered.
  await workflow.set_var("my_start_time", time)
  await workflow.start_interaction(target, 'date time')


@my_workflow.on_interaction_lifecycle
async def lifecycle_handler(workflow, itype, interaction_uri, reason):
  if itype == relay.workflow.TYPE_STARTED:
    # We can use the value of this variable we created in another event.
    # getVar returns the value that is stored in that variable.
    await workflow.say(interaction_uri, f'Triggered at {await workflow.get_var("my_start_time", "0:00:00")}')
    await workflow.end_interaction(interaction_uri)
  if itype == relay.workflow.TYPE_ENDED:
    # However, once the workflow instance terminates, the stored value is gone
    await relay.terminate()
public override async void OnStart(IDictionary<string, object> dictionary)
{
  var trigger = (Dictionary<string, object>) dictionary["trigger"];
  var triggerArgs = (Dictionary<string, object>) trigger["args"];
  var deviceUri = (string) triggerArgs["source_uri"];

  string time = System.DateTime.Now.ToString("h:mm:ss tt");
  
	// Create a variable that has the value of the date and time that the workflow
  // was triggered.
  await Relay.SetVar(this, "myStartTime", time);
  
  Relay.StartInteraction(this, deviceUri, "date time", new Dictionary<string, object>());
}

public override async void OnInteractionLifecycle(IDictionary<string, object> dictionary)
{
  var type = (string) dictionary["type"];

  if (type == InteractionLifecycleType.Started)
  {
    var sourceUri = (string) dictionary["source_uri"];
    
    // We can use the value of this variable we created in another event.
    // getVar returns the value that is stored in that variable.
    await Relay.Say(this, sourceUri, $"Triggered at {await Relay.GetVar(this, "myStartTime", "0:00:00")}");

    Relay.EndInteraction(this, sourceUri);
  }
  else if (type == InteractionLifecycleType.Ended)
  {
    // However, once the workflow instance terminates, the stored value is gone
    Relay.Terminate(this);
  }
}
@Override
public void onStart(Relay relay, StartEvent startEvent) {
  super.onStart(relay, startEvent);
  String sourceUri = Relay.getSourceUri(startEvent);

  String time = java.time.LocalTime.now().toString();
  
  // Create a variable that has the value of the date and time that the workflow
  // was triggered.
  relay.setVar("myStartTime", time);

  relay.startInteraction(sourceUri, "date time", null);
}

@Override
public void onInteractionLifecycle(Relay relay, InteractionLifecycleEvent lifecycleEvent) {
  super.onInteractionLifecycle(relay, lifecycleEvent);
  String interactionUri = lifecycleEvent.sourceUri;

  if (lifecycleEvent.isTypeStarted()) {
    // We can use the value of this variable we created in another event.
    // getVar returns the value that is stored in that variable.
    relay.say(interactionUri, "Triggered at" + relay.getVar("myStartTime", "00:00:00"));
    relay.endInteraction(interactionUri);
  }
  if (lifecycleEvent.isTypeEnded()) {
    // However, once the workflow instance terminates, the stored value is gone
    relay.terminate();
  }
}
api.OnStart(func(startEvent sdk.StartEvent) {
  sourceUri := api.GetSourceUri(startEvent)
  
  // Create a variable that has the value of the date and time that the workflow
  // was triggered.
  api.SetVar("myStartTime", time.Now().String())
  
  api.StartInteraction(sourceUri, "date time")
})

api.OnInteractionLifecycle(func(interactionLifecycleEvent sdk.InteractionLifecycleEvent) {

  if interactionLifecycleEvent.LifecycleType == "started" {
    interactionUri := interactionLifecycleEvent.SourceUri
    
    // We can use the value of this variable we created in another event.
    // getVar returns the value that is stored in that variable.
    api.Say(interactionUri, "Triggered at " + api.GetVar("myStartTime", "00:00:00"), sdk.ENGLISH)
    api.EndInteraction(interactionUri)
  }

  if interactionLifecycleEvent.LifecycleType == "ended" {
    // However, once the workflow instance terminates, the stored value is gone
    api.Terminate()
  }
})

Scope

The data via these methods are scoped to the current workflow instance. When the workflow instance terminates, the variable data will automatically be deleted. When a new workflow instance is created, which is what a trigger does, you'll get a new scope that has been initialized with no variable data. Therefore the data you use in these methods is not visible between workflow instances.

Each workflow instance has its own websocket connection, so you can think of the scope as being similar to multiple ssh connections to the same server but that are isolated from each other.

If you need to share data across multiple workflow instances, the recommended way to do that is to use native global variables as provided by the programming language you use, i.e. Node.js, Python, etc.

If you want to delete a variable before the workflow instance terminates, you can use the unsetVar method.