Bixby's Expression Language (EL) is used in conditionals and strings in the *.bxb
format.
Strings can include EL functions via the #{%expression%}
(or ${%expression%}
) notation.
Concept values can be bound to an EL expression by wrapping the expression in the $expr()
construct, optionally prefixing it with a type to coerce the value to.
// untyped expression
value: $expr(brand)
// typed expression
value: Distance$expr(calculateDistance($user.currentLocation, point, 'Miles'))
The following list describes EL function parameters:
Business
with its name, address, categories, etc. A node can be either a structure or a primitive unless otherwise specified.integer
, text
, enum
, Business
, HotelRoom
.datetime
for a viv.time.DateTime
concept (or another concept convertible to it); unit
for a string indicating a measurement unit; point
for a viv.geo.GeoPoint
(or another concept convertible to it). Read the function's description for context.[brackets]
.You can use the various functions listed in this reference in conjunction with the EL Operators, such as ternary conditionals and logic, as well as accessors.
These functions provide support for location-based GIS functions. Most of them rely on concepts in the viv.geo
library.
calculateDistance(point1, point2, units)
:
Calculate the distance between two geopoints.
In this example, calculateDistance()
is used in a selection strategy to find points of interest within 25 miles of the current user location:
selection-strategy {
id (user-near-named-point)
match: geo.NamedPoint (this)
named-advice ("near-current") {
advice ("${calculateDistance($user.currentLocation, this.point, 'Miles')}")
advise-for { lowerBoundClosed(0) upperBound(25.0) }
}
}
within(point, shape)
:
Return true
if a geopoint is within a certain geospatial region, false
if not.
selection-strategy {
id (user-in-search-region)
match: SearchRegion (this)
named-advice ("user-in-search-region") {
advice ("${within($user.currentLocation, this.shape) ? 1.0 : 0.0}")
advise-for { lowerBound(1.0) upperBoundClosed(1.0) }
advise-against { lowerBound(0.0) upperBoundClosed(0.0) }
}
}
These functions operate on DateTime
values. They utilize concepts that must be imported from the viv.time
Core Capsule.
isFuture(datetime)
:
Return true
if the datetime occurs in the future, false
if not.
activity-support {
match: Receipt (this)
states {
if (exists(this.relevantDateTime) && isFuture(this.relevantDateTime)) {
...
} else-if (exists(this.relevantDateTime) && isPast(this.relevantDateTime)) {
...
}
...
}
}
isPast(datetime)
:
Return true
if a datetime occurs in the past, false
if not.
isLastDayOfMonth(datetime)
:
Return true
if the day of the datetime is the last day of the month. If it is not, or if any of the month, day, or year fields are not set, return false
.
durationBetween(datetime1, datetime2)
:
Return the duration between two datetimes, as a time.DurationPeriod
. Duration is returned in absolute time, regardless of which date is before the other.
In this example, durationBetween()
and addDuration
are used to set the initial value of a duration-picker
in an input view:
input-view {
match: MyDuration (duration)
message ("How long of a duration?")
render {
duration-picker {
initial-value("#{durationBetween(addDuration(now(), 'PT1H'), now())}")
type (MyDuration)
}
}
}
durationInUnit(datetime1, datetime2, unit)
:
Return the number of specified date/time units present between two datetime nodes. The unit can be any valid chronounit enum.
In this example, durationInUnit()
is used to provide special handling for businesses that are open 24 hours a day:
dialog (Value) {
match: OpenHours (this)
if ("#{durationInUnit(this.start, this.end, 'MINUTES') < 2 || durationInUnit(this.start, this.end, 'MINUTES') > 1438}") {
template("24H") {
speech ("24 hours")
}
} else {
template("#{dateTime(this.start, 'h:mm a')} - #{dateTime(this.end, 'h:mm a')}") {
speech ("from #{dateTime(this.start, 'h:mm a')} to #{dateTime(this.end, 'h:mm a')}")
}
}
}
Note that chronounit enums must be all caps, such as 'DAYS', 'HOURS', 'SECONDS'.
addDuration(datetime, period)
and subtractDuration(datetime, period)
:
Add or subtract time duration. Takes a date/date-time and a duration, either as an ISO 8601 duration format, a viv.time.DurationPeriod
, or a viv.core.BasePeriod
.
See durationBetween()
above for an example of addDuration()
in use.
now([timezone])
:
Return the current DateTime as a viv.core.BaseDateTime
. (The viv.time.DateTime
model is an extension of this core structure.) Uses the default user timezone, unless one is provided. Use now().date
to return the date only.
See durationBetween()
above for an example of now()
in use.
These functions are intended to be used in template strings to aid in formatting output. This can be especially useful in dialog.
concept(node[, feature])
:
Format the supplied node as a concept dialog fragment. You can optionally pass an argument for the concept feature: Definite
("the concept"), Indefinite
("a concept"), or Proximal
("these concepts").
dialog (Input) {
match: SearchCriteria (this)
if (size(this) == 0) {
template("")
} else-if (size(this) == 1) {
template ("with #{concept(this, 'Definite')}")
} else {
template ("with #{concept(this, 'Indefinite')}")
}
}
action(node)
:
Format the supplied node as an action dialog fragment.
dialog (NoResult) {
match {
_(this) {
from-property: Recipe(recipe)
}
}
template("I couldn't #{action(this.pre())}.")
}
input(node)
:
Format the supplied node as an input dialog fragment.
value(node)
:
Format the supplied node as a value dialog fragment. If a value fragment is not defined for the node's concept, the node's value will be rendered as a string (like the raw()
EL function).
event(node, dialogMode)
:
Format the supplied node as a specific dialog event mode. In addition to providing the node to render, you also must provide the event mode to render the node as.
dialog {
template ("#{event(weather, 'Result')}")
}
macro(macro-id[, param1, param2, param3, ...])
:
Invoke a macro with the specified parameters. For example, suppose this macro has been defined with macro-def
:
macro-def (Bookmarks) {
params {
param (number) {
type (example.book.Bookmarks)
max (One) min (Required)
}
}
content: template ("#{value(number)} bookmarks")
}
You can invoke that macro in EL with the macro
command. This lets you include templates within other templates:
template ("You have #{macro('Bookmarks', 23)}.")
The macro ID is specified as a string in the first parameter. Parameters must be specified in the order they are specified in the macro definition; parameters can be omitted at the end if they are Optional
or have a default value.
list(node, format)
:
Format the individual values of the specified node in the specified format, then join those values as a conjunctive list ("value1, value2, and value3"). The format
argument must be one of the fragment template functions: concept
, action
, value
, or event
.
if (exists(alarms.repeatDays) && size(alarms.repeatDays) < 7) {
dialog-template ("#{list(alarms.repeatDays.day, 'value')}")
} else {
dialog-template ("")
}
If alarms were set on Monday, Wednesday, and Friday, the resulting string from list
would be "Monday, Wednesday, and Friday".
listWithLimit(node, format, limit)
:
Format up to limit
individual values of the specified node in the specified format, then join those values as a conjunctive list ("value1, value2, and value3"). The format
argument must be one of the fragment template functions. If limit
is exceeded, include the number of omitted values in the formatted list, for example, "John, Cindy, and 3 others".
joinAs(format, node1[, node2, node3, ...])
:
Render the specified nodes in the specified format, then join those formatted strings as a conjunctive list ("value1, value2, and value3"). The format
argument must be one of the fragment template functions. (The list
and listWithLimit
functions operate on values of a single node; joinAs
operates on multiple nodes that contain single values.)
dialog (Result) {
match {
BusinessCategory (this) {
from-property: Business (business)
}
}
template("#{value(business.name)} has #{joinAs('value', this)}.")
}
flattenAs(format, node1[, node2, node3, ...])
:
Flatten the specified nodes, regardless of cardinality, into a single list, format them in the specified format, then join those formatted strings as a conjunctive list ("value1, value2, and value3"). The format
argument must be one of the fragment template functions.
The difference between joinAs()
and flattenAs()
is how they handle nodes with max (Many)
cardinality and multiple values. The joinAs()
function will render each multi-cardinal node as separate conjunctive lists and then join those in a final list, which can lead to awkward constructions such as "lemons, apples, and pears and butter, milk, and cheese". Using flattenAs
in this situation will flatten the lists in each node together before joining them, resulting in the correct construction: "lemons, apples, pears, butter, milk, and cheese".
raw(node)
:
Render the single, primitive data value in the specified node as a string, if one exists. Unlike value()
, the raw()
function does not use a value dialog fragment.
result-view {
...
paragraph {
value ("#{raw(this)}")
}
...
}
integer(value)
:
Format the specified value as an integer in the locale of the current rendering context. A value of 1000.1 would be rendered as "1,000" in the en-US locale.
percent(value)
:
Format the specified value as an percentage in the locale of the current rendering context. A value of 1.1 would be rendered as "110%" in the en-US locale.
scientific(value)
:
Format the specified value as an scientific number in the locale of the current rendering context. A value of 1000.1 would be rendered as "1.0001E3" in the en-US locale.
ordinal(value)
:
Format the specified value as an ordinal number in the locale of the current rendering context. A value of 2 would be rendered as "2nd" in the en-US locale.
spell(value)
:
Format the specified value as words in the locale of the current rendering context. A value of 32 would be rendered as "thirty-two" in the en-US locale.
number(value[, formatString])
:
Format the specified value as a number either in the locale of the current rendering context or using the optionally supplied DecimalFormat string. Example: number(airfare.total.price, '#,##0')
dateTime(node[, formatString])
:
Format the specified node as a date/time number either using the default format of the locale of the current rendering context or using the optionally supplied Joda DateTimeFormatter string. Example: dateTime(log.timeStampInfo, "h:mm a")
These functions are intended for use within page-marker
blocks for Hands-Free List Navigation:
...
page-content (page) {
page-marker {
if (isFirstNavPage(page)) {
template ("The first #{size(page)} items are")
} else-if (isLastNavPage(page)){
if (size() == 1) {
template ("The last item is")
} else {
template ("The last #{size(page)} is")
}
} else {
template ("The next #{size(page)} items are")
}
}
}
...
isFirstNavPage(node)
:
returns true
if the user is on the first page of items, false
if not.
isLastNavPage(node)
:
returns true
if the user is on the last page of items, false
if not.
These functions work on a collection of items where EL functions are supported.
contains(node, value)
:
Return true
if the specified node contains the specified value, false
if it does not.render {
form {
elements {
if (contains(requiredFields, 'structuredName')) {
...
}
}
}
}
indexOf(node1, node2)
:
If node2
is a member of node1
, returns the integer index value of the position of node2
. If node2
is not a member of node1
, then -1
will be returned.
subtract(node1, node2)
:
Remove elements in node1
that are found in node2
, then return the resulting subset of node1
.
intersect(node1, node2)
:
Remove elements in node1
that are not found in node2
, then return the resulting subset of node1
(the intersection of the two nodes).
dedupe(node)
:
Remove elements in node
that are equivalent, returning a subset with only unique values. Read Merging and Equivalence for details and a usage example.
macroEach(macro-id, node[, index, param1, param2, ...])
:
Invoke a macro with the specified parameters for each element in a multiple-value node, such as a property of a structure concept with a max (Many)
cardinality. The macroEach
function will return a new multiple-value node that replaces each value of its input node with the results of the invoked macro. This can be used, for instance, to apply the plural()
EL function to individual values within a node.
macro-id
: the name of the template macro to invoke (as a string)node
: the multiple-value node to iterate throughindex
: the 0-based index of the macro parameter where each element should be injected (optional if the macro has only one parameter)param1
, param2
, etc.: other parameters to use when invoking the macroExample 1
dialog (Result) {
match: MyStruct (this)
// if the macro has only one parameter, index can be omitted
template("Result #{list(macroEach('pluralize-thing', this.thingName), 'value')}")
}
macro-def (pluralize-thing) {
params {
param (thingName) {
type (ThingName)
min (Required)
max (One)
}
}
content {
template ("#{value(thingName.plural('Many'))}")
}
}
Example 2 (specifying parameters)
dialog (Result) {
match: MyStruct (this)
// this macro has two parameters, so the index specifies the
// second parameter (0-based) as the one being replaced and passes
// the first one (thingType) to the macro as a parameter
template("Result #{list(macroEach('pluralize-thing', this.thingName, 1, 'X'), 'value')}")
}
macro-def (pluralize-thing) {
params {
param (thingType) {
type (ThingType)
min (Required)
max (One)
}
param (thingName) {
type (ThingName)
min (Required)
max (One)
}
}
content {
template ("#{value(thingName.plural('Many'))} of type #{thingType}")
}
}
The elements returned by macroEach
are always of type viv.core.MultiModalString
, a simple structure containing two text properties: text
and speech
.
getPreviousPage(node)
:
Returns the collection from the previous selection context. This is intended for use in intent
blocks.
In the following example, filterTerm
is used to filter a set of businesses, retrieved in the computed-input
block using getPreviousPage('Business')
.
action (SelectBusiness) {
type (Search)
collect {
input (filterTerm) {
type (BusinessFilterTerm)
min (Optional) max (One)
}
//select from items in this list
computed-input (business) {
type (Business)
min (Required) max (Many)
compute {
intent {
goal: Business
value-set: Business {$expr("getPreviousPage('Business')")}
}
}
}
}
output (Business) {
on-empty {
flag-as-off-topic
}
}
}
When enhanced list navigation is enabled and either the show-ordinals-on-list
view override or show-ordinals-on-list
capsule runtime override are also enabled, then getPreviousPage()
will return the full list of results. To return only the filtered items even under those conditions, use the getPreviousFocus()
function.
getPreviousFocus(node)
:
Returns the collection from the previous selection context. This is intended for use in intent
blocks.
The getPreviousFocus()
command functions identically to getPreviousPage()
, but always returns the filtered items, regardless of whether either show-ordinals-on-list
override is enabled. See the note at getPreviousPage()
.
These functions return information about the user or their device.
getLocale(locale)
:
Get locale-related information for a given locale identifier. Returns a node with the following properties:
country
: ISO 3166-1 alpha-2 country code (for example: "US", "KR")iso3Country
: ISO 3166-1 alpha-3 country code (for example: "USA", "KOR")language
: ISO 639-1 language code (for example: "en", "ko")iso3Language
: ISO 639-2 language code (for example: "eng", "kor")currencyCode
: ISO 4217 currency code (for example: "USD", "KRW")currencySymbol
: currency symbol (for example: "$", "₩")measurementSystem
: one of USC
, Imperial
, or Metric
In this example, getLocale
is used to determine the user's measurement system. The calculateDistance
geospatial function is also used.
action (GetCurrentDistance) {
type (Fetch)
collect {
input (point) {
type (GeoPoint)
min (Required)
plan-behavior (Always)
}
}
output (Distance) {
evaluate {
if (getLocale($user.locale).measurementSystem == 'USC') {
Distance$expr(calculateDistance($user.currentLocation, point, 'Miles'))
} else {
Distance$expr(calculateDistance($user.currentLocation, point, 'Kilometers'))
}
}
}
}
permissionGranted(permission)
:
Return true
if the passed permission
has been granted for the capsule. The available permission
values are listed in the capsule.permissions
reference key.
output (Something) {
evaluate {
if("#{permissionGranted('device-location-access')}") {
...
} else {
...
}
}
}
For more information, see the Grant Capsule Permissions section of Preparing Your Capsule in the developer's guide.
The permissionGranted()
function cannot check for permissions granted via imported library capsules, such as bixby.contact
.
min(node[, property])
:
Return the minimum value of the given node (using an optional property for comparison, if provided).
max(node[, property])
:
Return the maximum value of the given node (using an optional property for comparison, if provided).
action (maxWithPropertyInAction) {
type(Search)
description (returns the employee with the max int)
collect {
input (structureOfNameIntBool) {
type (StructureOfNameIntBool)
min (Optional) max (Many)
default-init{
intent{
goal: GetEmployees
}
}
}
}
output (StructureOfNameIntBool){
evaluate{
$expr(max(structureOfNameIntBool, structureOfNameIntBool.name))
}
}
}
compareAll(node1, node2)
:
Compare two specified nodes, using default natural ordering. Returns a negative integer when node1
is less than node2
, a positive integer when node1
is greater than node2
, or zero if node1
and node2
are equal.
For purposes of comparison, non-null
is less than null
and non-empty is less than empty. When comparing two nodes that have a different number of values, the one with fewer values is less than the one with more. Comparison of multi-value nodes of an equal number of values is made on a per-value basis. For example, with the following nodes:
node1 = null/empty node
node2 = ["yyy"]
node3 = ["aaa", "bbb"]
node4 = ["ccc", "ddd"]
The following comparisons result:
if (compareAll(node1, node1) == 0) // both are null/empty
if (compareAll(node1, node2) == 1) // null/empty > non-null/non-empty
if (compareAll(node2, node3) == -1) // node2 has fewer values
if (compareAll(node4, node3) == 1) // same length, node4's values > node3's
if (compareAll(node3, node3) == 0) // same length, equal values
The compareAll()
function replaces the now-deprecated older compare
function, which cannot handle empty or multi-value nodes. You should update any existing capsule code that uses compare()
to use compareAll()
.
compareSemVer(ver1, ver2)
:
Compares SemVer versions using the default natural ordering of version strings. The ver1
and ver
parameters are different semantic version strings. Returns a negative integer when ver1
is less than ver2
, a positive integer when ver1
is greater than ver2
, or zero if ver1
and ver2
are equal.
if (compareSemVer(this.version, '2.0.0')) >= 0 {
template (version2)
} else {
template (version1)
}
regexAllMatch(node, regex)
:
Return true
if (and only if) all values in the node match the specified regular expression, false
if any values do not match.
regexAnyMatch(node, regex)
:
Return true
if any values in the node match the specified regular expression, false
if no values match.
if (regexAnyMatch(this.name, 'Les? .+')) {
template ()
} else {
template ("à #{value(this.name)}")
}
lower(string)
:
Format all characters to lowercase in the supplied string.
dialog (Result) {
match {
Receipt (receipt)
}
template ("#{value (receipt.order)} is #{lower(value (receipt.orderState))}!")
}
upper(string)
:
Format all characters to uppercase in the supplied string.
title(string)
:
Convert the first character of the supplied string to title case.
truncate(string, length)
:
Truncate the supplied string to the desired maximum length.
length(string)
:
Return the length of a string. On a MultiModalString structure as returned by the macroEach()
function, it will return the length of the text
property. length()
should only be given single-value arguments; if it is given a multi-cardinal argument with more than one result, it will return 0
.
similarity(string1, string2)
:
Return the 3-gram edit distance between two strings.
This strategy uses similarity
to help find localities the user is searching for:
selection-strategy {
id (locality-name-similarity)
match {
NamedPoint {
from-output: ConstructNamedPointFromRegion {
from-input: Locality (locality) {
from-output: FindLocality {
from-input: LocalityName (find)
}
}
}
}
}
named-advice ("rank") {
advice ("${ similarity(locality.name, find) }")
advise-against { lowerBoundClosed (0.0) upperBoundClosed (0.5) }
advise-for { lowerBoundOpen (0.5) upperBoundClosed (1.0) }
}
}
editDistance(string1, string2)
:
Return the Levenshtein distance between two strings.
test:instanceOf(node, type)
:
Return true
if the value(s) of the specified node are instances of the specified type, false
if they are not.
if (test:instanceOf(region, 'viv.geo.LevelOneDivision')) {
// region is an instance of viv.geo.LevelOneDivision
}
test:assignableFrom(type1, type2)
:
Return true
if type1
is a valid type identifier and can be assigned from type2
, false
otherwise.
typedValue
:
Assign a concept type to a value and return it as a new node.
template ("#{typedValue('contact.EmailType', 'home')}")
size(node)
:
Return the number of values contained in a node. A single-item node has a size
of 1
; a multi-item node, such as a result list, has a size of the list's length. This is often used in result views to choose between displaying a summary of items or the details of an item.
result-view {
match {
RideShare (rideShare)
}
message ("Here's your ride[ from #{value(rideShare.sourcePoint)}][ to #{value(rideShare.destinationPoint)}].")
render {
if (size(rideShare) == 1) {
layout-match (rideShare) {
mode (Details)
}
}
}
}
exists(node)
:
Return true
if the node has a value, false
if it does not.
empty(node)
:
Return true
if the node has no value, false
if it does. This function is the inverse of exists()
: when one is true
, the other is false
.
isElement(node)
:
Return true
if the node is an element of an array. This occurs when the node is being accessed with an array-style iterator such as for-each
and where-each
, or when the user navigates from a summary layout to a detail layout. Note that isElement()
always returns true
if the node being tested is an array element, even if it is the only element in the array.
relaxed(node)
:
Return true
if one or more of the node's search constraints have been relaxed, false
if they have not. See Relaxation for more about constraints and relaxation.
groupRelaxed(node)
:
Return true
if the node or any of its upstream input nodes have had any of their search constraints relaxed, false
if none have been. See Relaxation for more about constraints and relaxation.
The distinction between relaxed()
and groupRelaxed()
is that relaxed()
only tests the specified node, while groupRelaxed()
tests the upstream nodes that were part of the specified node's input.
plural(node)
:
Returns the Unicode Common Locale Data Repository plural category of cardinal type for this node's value based on pluralization rules for the current locale. You can use this to choose whether and how to pluralize a concept name based on its bound value.
For languages that have only two forms such as English, only two return values are possible, One
or Other
. Other languages, though, might return other values: the complete set of possible return values are Zero
, One
, Two
, Few
, Many
, or Other
. You must check the standard for your language to determine its possible values.
In this example, "restaurant" or "restaurants" is chosen depending on the value of the Restaurant
node, which will be One
or Other
. Note the return values are enums, not strings.
dialog (Concept) {
match: Restaurant (this)
switch (plural(this)) {
case (One) {
template (restaurant)
} default {
template (restaurants)
}
}
}
The plural
function cannot pluralize individual values within a multiple-value node. However, you can use the macroEach
function to apply plural
to each value within such a node.
concept.plural(node)
and concept.plural('string')
:
A form of plural()
that overrides the pluralization logic built into Bixby's dialog system, explicitly forcing it to choose a specified pluralization, such as "restaurants" vs. "restaurant" as defined in the example above.
You can pass either a string that corresponds to one of the enumerated values listed for plural()
, or a value to use as the basis for pluralization.
// the current locale's "Many concepts" pluralization, or the
// closest analogue if "Many"'" does not apply to that locale
template ("#{value(concept.plural('Other'))}")
// the current locale pluralization for "2 concepts"
template ("#{value(concept.plural(2))}")
You can use this form to force one concept to take on the pluralization of another concept. For instance, a restaurant search capsule could have both a Restaurant
model and a RestaurantStyle
model with styles like pub
, taqueria
, cafe
, and so on. To choose a pluralization for RestaurantStyle
based on the value of Restaurant
, you could write:
template ("Searching for #{concept(restaurantStyle.plural(plural(restaurant)))}...")
contextual(node)
:
Returns true
if the node is copied over from a previous request, preserving the earlier request's context.
If a user asks a capsule "find sushi restaurants in Miami" and then follows up with "what about Chicago", the type of restaurant, "sushi," is contextual: it was given in the previous request. In this example, contextual()
is used to handle the case where the continuation search yields no results: the restaurantType
is dropped and the search is performed just with the requested location.
action (MyRestaurantSearch) {
type (Search)
collect {
input (location) {
type (TheLocation)
min (Required) max (One)
}
input (restaurantType) {
type (RestaurantType)
min (Optional) max (One)
}
}
output (Restaurant) {
on-empty {
// if we find no results, and the `restaurantType` input is
// `contextual`, drop it and just search by location
if (contextual (restaurantType)) {
drop (restaurantType)
}
}
}
state(node)
:
Returns the execution state of this node, one of Pre
(the node has not yet been evaluated), Mid
(the node is currently being evaluated), or Post
(the node has already been evaluated). This can be used to test or indicate Bixby's processing state. For instance, in an action that computes the distance between two points, state()
could be used to customize dialog to indicate current progress:
dialog (Action) {
match: Distance (this) {
from-output: GetDistance (action)
}
if (state(this) == 'Pre') {
template ("compute the distance[ between #{value (action.point)} and #{value (action.relativePoint)}][ in #{value (action.unit.plural(2))}]")
} else-if (state(this) == 'Mid') {
template ("calculating the distance[ between #{value (action.point)} and #{value (action.relativePoint)}][ in #{value (action.unit.plural(2))}]")
}
}
pre(node)
, mid(node)
, post(node)
:
Return a copy of the node with the evaluation state set to Pre
(the node has not yet been evaluated), Mid
(the node is currently being evaluated), or Post
(the node has already been evaluated). This can be used to affect Bixby's processing state. For instance, a No Result dialog event might use pre(this)
on an action node to reset its evaluation state. Some default templates for dialog events use pre()
:
// default for dialog (NoResult)
template ("I couldn't #{action(pre(this))}.")
typeOf(node)
:
Return the type of the node. The return value is the fully qualified node name: in a capsule with a capsule ID of example.shoe
, the Accessory
model has a fully qualified name of example.shoe.Accessory
.
cast(node, type)
, node.cast(type)
:
Return a copy of the node cast to the specified type.
computed-input (message) {
type (message.MessageInfo)
min (Required)
max (One)
compute {
intent {
goal: message.ComposeMessage
value: $expr (phoneNumber.cast('message.PhoneNumber')) //I am casting this because the end user capsule might not want to extend message.PhoneNumber
value: $expr (recipientName.cast('message.RecipientName')) //I am casting this because the end user capsule might not want to extend message.RecipientName
value: $expr (messageText)
}
}
}
There are several variables available in EL that can be used to access contextual information about the user and device.
These variables are read-only. You cannot modify these variables to change user information, environment information, or sorting.
$handsFree
:
set to true
if the device is operating in hands-free mode.
$sortOrder
and $sortType
:
provides information about the current action's sorting. The sort order and type could be specified within the action or by the ordering of concepts being passed to the action. These variable is intended to be used if your action is handling sorting output internally. See sort
and sort-orderings
for more details.
$sortOrder
: set to one of ascending
, descending
, or unspecified
$sortType
: set to one of natural
, compound
, binding
, explicit
, or undefined
.$user
:
contains several properties with more information about the user and/or device:
$user.currentLocation
:
the device's current location, as a GeoPoint. Access to this variable requires the device-location-access
permission; if your capsule does not request it or the user does not grant access, this will be null
.$user.timeZoneId
:
the device's current timezone. This may be null
if the timezone is not set.$user.locale
:
contains the structure returned by getLocale()
for the device's current locale (for example, $user.locale.country
). See User and Device Information Functions for the complete list. Note that this is distinct from $can.locale
, below, which is derived from the capsule target identifier string.$user.is24HourFormat
:
boolean indicating whether the device is set to display time in 24-hour format (true
) or AM/PM format (false
).$can
:
contains several properties with more information about the environment, all derived from the capsule target identifier string:
$can.id
:
the identifier string for the device Bixby is running on. This is the same as the target string set in the simulator, and begins with a bixby
prefix, followed by a device identifier such as mobile
, followed by a locale. Examples: bixby-mobile-en-US
or bixby-watch-ko-KR
.$can.device
:
the device identifier string without the locale. Examples: bixby-mobile
or bixby-watch
.$can.locale
:
the current locale's identifier string with no device information. Examples: en-US
or ko-KR
.$can.language
:
just the two-character language identifier. Examples: en
or ko
.These EL variables can be used, for example, as computed inputs to actions. Suppose you wanted to set a concept's value to the user's current location. You could set a myLocation
concept to that value by creating an action file that takes this computed-input
block:
computed-input (myLocation) {
min (Required) max(One)
type (geo.CurrentLocation)
compute {
if (exists($user.currentLocation)) {
intent {
goal: geo.CurrentLocation
value-set: geo.CurrentLocation { $expr($user.currentLocation) }
}
}
}
}