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()
andInteger.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 = '
'). You then use
the deviceName
.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 thedeployment
directive (motionSensor
,screen
, andcamera
). -
The
action
section also uses thestartFlow
method available from theThingsGraph
global. You can use this function only in theaction
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 outputstextDocument
, and thewordCountLambda
service uses the${textDocument.message}
expression to retrieve themessage
value from thetextDocument
object. -
The
saveResponseLambda
service takes thewordCounts
output from thewordCountLambda
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
andResponse
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
.