Expressions - AWS IoT Things Graph

Expressions

This topic describes the kinds of expressions that you can write in AWS IoT Things Graph entities. It also explains which AWS IoT Things Graph entities can use expressions and the types and scopes of variables that you can use in each entity.

How to write AWS IoT Things Graph expressions

The AWS IoT Things Graph Data Model (TDM) provides several ways to specify values whenever you define or use TDM constructs. You can often specify literal values, but on many occasions you might need to evaluate information before specifying a particular value.

Workflows need to manipulate and evaluate data before determining what actions to take. For example, certain steps can't be taken unless a given condition is met. In that case, a Boolean expression might be appropriate.

Mappings use expressions to transform information from one device into representations that other devices can understand. A service or device definition might require end user input, and this input can be represented as variables in TDM expressions.

The following list contains the types of expressions that TDM supports. Variables can be used in expressions. Each expression type is followed by a set of examples.

Literal

Expressions that are static instances of the built-in primitive data types.

'hello' # String literal 123 # Int literal [1,2,3] # Array literal 10L # Long literal 10UL # Unsigned long literal
Predicate

Expressions that evaluate to true or false.

True False 10 < 13 $aValue > $bvalue (10 + 11) == 21
Path

Expression that enables navigation through a complex object. TDM supports path expressions that use both the forward slash (/ ) and dot (.) notations.

image.value image/value users[name == 'rob'].height
Arithmetic

Expression that results in a numeric value. The values used in an arithmetic expression can be other kinds of expressions.

1 + 2 100UL - 3.4L 'abc' + 1 + 2 (3 / 4) / (5 / 4) users[name == 'rob'].height * 0.9
Ternary

Conditional expressions that use the ? operator: condition ? expr1 : expr2.

$aValue < $bvalue ? 'This is returned if $aValue is less than $bValue' : 'This is returned if $aValue is not less than $bValue'
IsNull

Expression that uses two ? symbols to determine whether the value to the left of the operator is null. If the value to the left of the operator is null, the value to the right is returned. If the value to the left of the operator is not null, the value to the left is returned.

$aValue ?? 'This string is returned if $aValue is null. $aValue is returned if it isn't null.'
Function calls

TDM supports a set of utility functions. These functions wrap the utility functions in java.lang.math and java.lang.string. All of these begin with tdm.lib. Array type parameters aren't supported.

tdm.lib.Math.log(2.71728) # Evaluates to 0.999999989
InstanceOf

TDM supports the use of instance methods on primitive data types. These are similar to instance methods in Java, such as String.substring() and Integer.doubleValue().

((string) 'a' + 'b').substring(1)
Macro

TDM supports interpolation of values into a string by wrapping expressions inside ${}.

macro(This is a string with ${3 + 4} words) # Evaluates to 'This is a string with 7 words' macro({"name":"${'jeff' + ' ' + 'b'}", "age": ${100 % 52}}) # Evaluates to '{"name":"jeff b", "age": 48}'

You can use expressions in Devices, Services, Triggers, Workflows (Flows), and Mappings. constructs. (See Using conditional logic in choice nodes, Workflow, and Mapping for examples.) The examples in those sections demonstrate how to use expressions to evaluate and manipulate data supplied by devices and services.

Working with variables in expressions

Expressions and variables are available in triggers, devices, services, workflows, and mappings. The variables and parameters that are available to expressions depend on the entities in which you include them. This section explains which AWS IoT Things Graph entities can include expressions and the variable types and scopes that are available to each entity.

Triggers

Expressions in triggers have access to the deviceId bindings inside the deployment parameters and to values in the trigger device output. The following example from Creating a flow with devices by using the AWS CLI shows how you can use expressions in a trigger.

query Room215 @deployment(id: \"urn:tdm:REGION/ACCOUNT_ID/default:Deployment:Room215\", systemId: \"urn:tdm:REGION/ACCOUNT_ID/default:System:SecurityFlow\") { motionSensor(deviceId: "MotionSensorName") screen(deviceId: "ScreenName") camera(deviceId: "CameraName") triggers {MotionEventTrigger(description: "a trigger") { condition(expr: "devices[name == \"motionSensor\"].events[name == \"StateChanged\"].lastEvent") action(expr: "ThingsGraph.startFlow(\"SecurityFlow\", bindings[name == \"camera\"].deviceId, bindings[name == \"screen\"].deviceId)") } } }

In triggers, you wrap expressions in quotation marks and use them as values of the expr: field. You access the trigger device output by specifying the device name (name = 'deviceName'). You then use the .events path to specify the triggering event that generates the output (name == 'eventName').

The preceding code sample does the following:

  • The first expression, in the condition section, uses output from the motion sensor device that triggers the flow (motionSensor.events['StateChanged'].lastEvent).

  • The second expression, in the action section, uses the bindings defined in the parameters section of the deployment directive (motionSensor, screen, and camera).

  • The action section also uses the startFlow method available from the ThingsGraph global. You can use this function only in the action section of a trigger.

You must identify the name of the associated deployment parameter for each deviceId binding.

Workflows (Flows)

Flows can include expressions inside steps and choice node rules inside those steps. Expressions in flows have access to device and service outputs and to variables defined in the flow. Expressions also have access to global values that are stored in the systemRuntime namespace.

The following list contains the values that are available in this namespace:

  • systemRuntime.awsAccountId

  • systemRuntime.awsRegion

The following example choice node shows how you can use expressions in a flow.

ChoiceActivity { rule(expr: "${getHourResult > 14}", name: "Night") { setEvent(name: "isNight") } rule(expr: "${getHourResult <= 13}", name: "Day") { setEvent(name: "isDay") } default { setEvent(name: "isNight") } }

In flows, you wrap expressions in ${} blocks. In choice node rules, you also use them as values of the expr: field.

The getHourResult value can be an output from a service or a device, such as a clock, that performs an earlier step inside the flow.

Flows use expressions to retrieve values from service and device outputs and make them inputs for devices and services later in the flow. The following example shows how you can use expressions to retrieve an output value from an AWS Lambda service and use it as an input to another service. (This example is adapted from Creating a flow with Lambda functions by using the AWS CLI.)

step(name:"getS3ObjectAsStringStep", outEvent:["TextReady"]) { WebserviceActivity(webservice:"urn:tdm:NAMESPACE:Service:getS3Lambda", out:"textDocument") { getS3ObjectAsString(key: "HelloWorld.txt") } } step(name:"wordCounts", inEvent:["TextReady"], outEvent:["DoneProcessing"]) { WebserviceActivity(webservice:"urn:tdm:NAMESPACE:Service:wordCountLambda", out:"wordCounts") { wordCount(message: "${textDocument.message}") } } step(name:"save", inEvent:["DoneProcessing"], outEvent:["lambdaDone"]) { WebserviceActivity(webservice:"urn:tdm:NAMESPACE:Service:saveResponseLambda") { save(response: "${wordCounts}") } }

The preceding example does the following:

  • The getS3Lambda service outputs textDocument, and the wordCountLambda service uses the ${textDocument.message} expression to retrieve the message value from the textDocument object.

  • The saveResponseLambda service takes the wordCounts output from the wordCountLambda service and uses the ${wordCounts} expression to retrieve the word count value.

Devices and services

Devices and services have access to the following values that are stored in the systemRuntime namespace:

  • systemRuntime.deviceId

  • systemRuntime.awsAccountId

  • systemRuntime.awsRegion

In devices and services, expressions have access to different kinds of parameters depending on where you use them:

  • Expressions in devices have access to device and action parameters.

  • Expressions in services have access to service and action parameters.

  • Expressions in the Request and Response topics used by MQTT devices have access only to device parameters.

You can use expressions only as parameter values (after the value: field).

The following example shows how to use expressions in a device.

query MockDeviceLambdaB @device(id: 'urn:tdm:NAMESPACE:device:MockDeviceLambdaB' deviceModel: 'urn:tdm:NAMESPACE:deviceModel:MockDeviceLambdaB') { MQTT { Capability(id: 'urn:tdm:NAMESPACE:capability:MockDeviceLambdaB') { Action(name: 'doSomething') { params { param(name: 'input', property: 'urn:tdm:NAMESPACE:property:Barcode') } Publish { Request(topic: 'LambdaB/in') { params { param(name: 'readId', property: 'urn:tdm:NAMESPACE:property:Barcode' value: '${input.value}') } } } } Event(name: 'readingEvent') { Subscribe(topic: 'LambdaB/event') { responsePayload(property: 'urn:tdm:NAMESPACE:property:Barcode') } } } } }

In services and devices, you wrap expressions in ${} blocks. You access the values of variables inside expressions by using the .value path.

In this example, the Publish block inside the doSomething action accesses the value of the input action parameter by using the ${input.value} expression.

The following example shows how to use expressions in a service.

query Sqs @service(id: "urn:tdm:NAMESPACE:service:Sqs") { REST { SqsCap(id: "urn:tdm:NAMESPACE:capability:SqsCap") { Action(name: "createQueue") { params { param(name:'queueName', property:'urn:tdm:aws:property:String') } HttpGet { Request(url: "https://sqs.REGION.amazonaws.com", auth: "SigV4", awsServiceName: "sqs") { headerParams { param(name: "Accept", property: "urn:tdm:aws:property:String", value: "application/json") param(name: "Content-Type", property: "urn:tdm:aws:property:String", value: "application/json") } queryParams { param(name: "Action", property: "urn:tdm:aws:property:String", value: "CreateQueue") param(name: "QueueName", property: "urn:tdm:aws:property:String", value: "${queueName.value}") param(name: "Attribute.1.Name", property: "urn:tdm:aws:property:String", value: "VisibilityTimeout") param(name: "Attribute.1.Value", property: "urn:tdm:NAMESPACE:property:Integer", value: "40") param(name: "Expires", property: "urn:tdm:aws:property:String", value: "2020-10-18T22:52:43PST") param(name: "Version", property: "urn:tdm:aws:property:String", value: "2012-11-05") } } } } Action(name: "sendMessage") { params { param(name:'message', property:'urn:tdm:aws:property:String') } HttpPost { Request(url: "https://sqs.REGION.amazonaws.com/ACCOUNT_ID/test-queue-REGION", auth: "SigV4", awsServiceName: "sqs") { headerParams { param(name: "Accept", property: "urn:tdm:aws:property:String", value: "application/json") param(name: "Content-Type", property: "urn:tdm:aws:property:String", value: "application/json") } queryParams { param(name: "Action", property: "urn:tdm:aws:property:String", value: "SendMessage") param(name: "MessageBody", property: "urn:tdm:aws:property:String", value: "${message.value}") param(name: "Expires", property: "urn:tdm:aws:property:String", value: "2020-10-18T22:52:43PST") param(name: "Version", property: "urn:tdm:aws:property:String", value: "2012-11-05") } } } } } } }

Expressions in the service use expressions in parameter values whenever the value isn't a literal string. As in the device example, expressions are wrapped inside ${} blocks and access the variable values that are specified in action parameters (queueName and message). The expressions use the .value path to retrieve the variable values ${queueName.value} and ${message.value}.

Mappings

Mappings use expressions to access the from and to values specified in each mapping. The following example shows how to use expressions in a mapping.

query brightnessEnumToInt @mapping(id:"urn:tdm:NAMESPACE:mapping:brightnessEnumToInt", name:"brightnessEnumToInt", from:"urn:tdm:NAMESPACE:property:Brightness/enumBrightness s", to:"urn:tdm:NAMESPACE:property:Brightness/intBrightness t") { forward { map(expr:'s==Low => 1'), map(expr:'s==Medium => 3'), map(expr:'s==High => 5') } reverse { map(expr:'t <= 1 => Low'), map(expr:'t > 1 && t <= 3 => Medium'), map(expr:'t > 3 => High') } }

In mappings, you wrap expressions in quotation marks and use them as values of the expr: field.

The brightnessEnumToInt mapping specifies s and t as aliases for the from and to parameters. Each forward and reverse mapping accesses the values of those parameters by using the expressions expr:'t <= 1 => Low.