Examples and Use Cases for Pebble Language in evalink talos
The topics below list several common use cases for the use of Pebble in evalink talos.
Inspecting Available Data in a Workflow Stepβ
When you use Pebble in any context in evalink talos, it is important to know what data is available inside each context, for example, in a workflow step. Pebble templates can only reference variable that are available in each particular working scenario.
There are several ways to find which Pebble entities are available to use:
-
Navigate to Three-dot-menu > Advanced Settings
This makes Step Reference panel of the workflow step available.
This panel shows:
-
All variables available to the step
-
Device and alarm fields
-
Step-specific inputs and previous outputs
-
Data structures with nested properties (e.g., callList, schedule, form, etc.)
- Execute a workflow, open its Event Log and select the workflow step you want to inspect and then click info More Details
This opens a JSON dialog window with the step result. For example:
"result": {
"responder": {
"name": "John Snow",
"phone": "+41123456789",
"outcome": "VERIFIED"
}
}
Both of these methods show you which entities can be accessed in this workflow step through the use of Pebble.
In this particular case it can be:
{{ callList.responder.name }}
{{ callList.responder.phone }}
{{ callList.responder.outcome }}
In this case, callList is the name of the step in a workflow. You can substitute it with your own workflow step name.
Using dump() to Return Entire Object Dataβ
Using Pebble, you can reference available object data directly and, for example, send it in an email, Slack message or a Twilio SMS text.
{{ dump(device) }}
{{ dump(alarm) }}
{{ dump(callList) }}
This prints full JSON representation of the objects.
Conditional Logic for Alarm Handlingβ
Pebble templates in evalink talos support conditional logic, allowing workflow authors to create dynamic call descriptions, SMS messages, email notifications, and webhook payloads based on the properties of the alarm, device, or workflow state.
This enables workflows to adapt their behavior based on:
-
alarm code, type or definition
-
user actions
-
zone and / or partition
-
action repetition count
-
schedule
-
presence or absence of fields
-
tags, attributes, or metadata
Below are real examples taken from active evalink talos workflows.
Conditional text based on alarm code
You can use this example if you need to:
-
Provide different operator instructions depending on the event type
-
Change SMS or other message content based on alarm category
-
Generate legally required descriptions for intrusion vs. fire events
{% if alarm.alarmCode == 'E205' %}
Unauthorized disarming or partition '{{ alarmPartition }}' by user '{{ alarm.user }}'.
{% elseif alarm.alarmCode == 'E220' %}
Partition '{{ alarmPartition }}' was not armed within the allowed limit by the user '{{ alarm.user }}'.
{% endif %}
Send the surveillance! Inform the guards about the situation.
Conditional logic based on call attempts (repetition)
You can use this example for escalation policies, automated fallback logic or handling unreachable contacts.
{% if call.repetition >= '3' %}
Call the police!
{% elseif call.repetition == '2' %}
Last attempt before calling the police...
{% elseif call.repetition == '1' %}
Try calling twice; Call the police in the 3rd attempt
{% endif %}
Conditionals combined with workflow objects
You can reference nested fields such as:
callList.responder...
dispatchCall.success...
twilioCall.acknowledged...
Only include responder info if the call was acknowledged
{% if callList.responder is not empty %}
Responder: {{ callList.responder.name }} ({{ callList.responder.phone }})
{% endif %}
Only if there was responder in the call, then we will see the following text as example:
Responder: John Smith (+41779988555)
Checking if a field is empty
You can use is empty operator when you need to display fallback values, handle missing
metadata or make sure that templates don't show blank fields.
For example, the Pebble code below calls onto the device.customerCompany value and if it's not specified, returns evalinkDeviceId instead.
{% if device.customerCompany is empty %}
{{ device.evalinkDeviceId }}
{% else %}
{{ device.customerCompany }}
{% endif %}
Conditional routing and dynamic values
These conditions can also be used in Form steps, Twilio messages, Emails, SMS etc.
Examples:
{% if alarm.alarmType == 'FIRE' %}
π₯ Fire alarm at {{ device.customerCompany }}
{% elseif alarm.alarmType == 'BURGLARY' %}
π¨ Intrusion detected at {{ device.customerCompany }}
{% endif %}
Referencing Alarm Headersβ
Alarm headers in evalink talos allow panels, integrations, receivers, or upstream systems to attach additional metadata to incoming alarms. evalink talos lists available headers in Alarm Details and when processing alarms in workflows you can reference these headers in Pebble templates.
Headers are commonly used for:
-
routing alarms in the Dispatcher
-
selecting dynamic account numbers
-
extracting virtual receiver information (e.g., Surgard, FlexC, Generic Receiver)
-
attaching file URLs or payloads
-
passing integration-specific data
Pebble exposes alarm headers through the alarm.headers object.
To read a header, use:
{{ alarm.headers['header-name'] }}
Examples:
{{ alarm.headers['x-receiver'] }}
{{ alarm.headers['attachment.url'] }}
If the header exists, Pebble outputs its value. If not, Pebble returns an empty string.
Alarm Supervision Rulesβ
Supervision rules frequently use header values to identify receivers or channels.
Expected Alarm: 602 { headers.x-receiver = generic-surgard:1001 }
Restore Alarm: E709 { headers.x-receiver = generic-surgard:1001 }
This instructs evalink talos to apply the rule only when the alarm header matches a specific receiver.
Use this if you need to:
-
differentiate multiple receivers under the same panel
-
route events from different IP paths
-
treat supervision signals differently depending on source
Extracting URLs from header valuesβ
Some alarm systems embed file URLs inside headers (such as attachments from panels or integrations). This is often used in SMS, email, or webhook templates.
Example:
{{ alarm.headers['attachment.url'] }}
Combined with filters:
{{ alarm.headers['attachment.url'] | urlEncode }}
For Twilio SMS:
Alarm {{ alarm.alarmCode }} β File: {{ alarm.headers['attachment.url'] }}
Combining header values with other Pebble featuresβ
You can use Pebble to decode, parse or split headers into more readable formats.
The example below allows you to decode a Base64 prefix, append a deviceID and URL-encode spaces. You can use this for Twilio, webhooks, etc.
{{ alarm.alarmCode }} { headers.attachment.url = {{
'aHAgAljaAHJJHglkjlkjAhjHgpeeroHGKSJD8786dm8vc3RyZWFtaW5nL3NpdGUv'
| decodeBase64 | toString }}{{ device.evalinkDeviceId | replace({' ': '%20'}) }} }
Loops: Listing Responders or Acknowledgementsβ
Pebble supports looping through arrays and lists, which is useful in evalink talos because many workflow steps return collections inside their result object.
For example:
-
lists of people who need to be called
-
lists of responders who can acknowledge an action
-
lists of successful notifications
-
arrays of form fields
-
arrays inside JSON payloads
Loops let you dynamically create call descriptions, emails, Slack messages, or webhook payloads based on the number of people involved.
Looping over acknowledged responders (Twilio Call example)
{% for person in Twilio_Call_Step.acknowledged %}
{{ person.name }},
{% endfor %}
An output for this can be a string of names taken from the contacts that need to acknowledge a step result.
You can use this inside Pebble templates for email notifications, slack messages, operator call descriptions, etc.
Listing acknowledged people and total count
Pebble allows combining loops with filters and inline values:
{{ Twilio_Call_Step.acknowledged | length }} people acknowledged.
They are:
{% for person in Twilio_Call_Step.acknowledged %}
- {{ person.name }} ({{ person.phone }})
{% endfor %}
Example output:
2 people acknowledged.
They are:
- Anonymous (+4179 111 22 44)
- Anonymous (+4179 444 22 33)
Looping through βcalledβ, βsuccessβ, and other arrays
A Twilio step result (visible in Event Logs β step β info (i)) typically contains:
{
"called": [
{ "name": "A", "phone": "+41..." }
],
"acknowledged": [
{ "name": "A", "phone": "+41..." }
],
"success": [
{ "name": "A", "phone": "+41..." }
]
}
Any of these can be looped:
{% for person in callList.called %}
Called: {{ person.name }} ({{ person.phone }})
{% endfor %}
or:
{% for person in callList.success %}
Successfully notified: {{ person.name }}
{% endfor %}
This allows you to automatically generate accurate audit messages without hardcoding anything.
Looping over form fields (Form Step)
Form Steps return an array of fields inside form.fields.
{% for field in fromJson(form.fields) %}
{{ field.label }}: {{ field.value }}
{% endfor %}
Looping in webhook payloads
Pebble loops can be used even inside JSON payloads.
Example:
{
"responders": [
{% for person in callList.acknowledged %}
{ "name": "{{ person.name }}", "phone": "{{ person.phone }}" }{% if not loop.last %},{% endif %}
{% endfor %}
]
}
This produces JSON array without trailing commas.
Extracting Form Dataβ
When you use a Form Step in a workflow, evalink talos stores all submitted responses inside form.fields as a JSON string.
To work with this data in Pebble templates, you typically need to:
-
Parse the JSON
-
Select a specific field by its label
-
Extract its value
Pebble in evalink talos provides two tools for this:
fromJson() β converts the JSON string into a usable object
get() β selects values using a JSONPath expression
This allows you to insert form responses into call descriptions, emails, SMS, or webhook payloads.
Below is a basic example:
Every form field is stored with a structure like:
{
"label": "Customer Name",
"value": "John Doe"
}
To extract a specific field:
{{ (fromJson(form.fields) | get('$[?(@.label=="Customer Name")].value'))[0] }}
Explanation:
fromJson(form.fields) β translates an input data array to JSON
get(...) β selects only items whose label matches
[0] β returns the first (and usually only) value
You can find more information no how to use JSONpath here: https://jsonpath.com/.
Example to generate a message for a technician:
πππ¦π: {{ (fromJson(form.fields) | get('$[?(@.label=="Customer Name")].value'))[0] }}
πππ₯ππ©π‘π¨π§π: {{ (fromJson(form.fields) | get('$[?(@.label=="Customer Contact details")].value'))[0] }}
ππ¨π¦π¦ππ§ππ¬: {{ (fromJson(form.fields) | get('$[?(@.label=="Comments")].value'))[0] }}
The example output of this:
Name: John Doe
Telephone: +41790000000
Comments: Please call before arriving
Example for use inside a Twilio Call (phone number field)
{{ (fromJson(form.fields) | get('$[?(@.label=="Customer Contact details")].value'))[0] }}
You can use this in the Twilio Call workflow step to dynamically set the phone number the system should call.
Extracting all fields dynamically (for reports)
{% for field in fromJson(form.fields) %}
{{ field.label }}: {{ field.value }}
{% endfor %}
This example can generate a complete readable summary for operator handovers, reports, slack notifications, etc.
Example output:
Customer Name: John Doe
Address: Industriestrasse 6
Phone: +4179...
Comments: Please check fire panel
Extracting non-text values (numbers, checkboxes, selects)
Form fields may contain:
-
numbers
-
booleans (true / false)
-
dropdown selections
Example:
{{ (fromJson(form.fields) | get('$[?(@.label=="Temperature")].value'))[0] | toFloat }}
Handling a missing form
If a form field is optional, you can add a fallback.
{% set name = (fromJson(form.fields) | get('$[?(@.label=="Customer Name")].value'))[0] %}
{% if name is empty %}
Name not provided
{% else %}
{{ name }}
{% endif %}
Formatting Dates & Timesβ
Working with timestamps is one of the most common tasks when building Pebble templates in evalink talos.
Pebble provides several filters and functions that allow you to:
-
format timestamps
-
apply timezones
-
calculate dates in the future or past
-
generate human-readable date strings
-
combine date math with workflow logic
evalink talos extends Pebble with additional helpers such as toTime, numberOfDays, and more.
The most common scenario is converting a Unix timestamp (e.g., alarm.timestamp) into a readable date.
{{ alarm.timestamp | date("dd.MM.yyyy HH:mm:ss") }}
Output:
07.09.2023 13:23:25
Using time zones
By default, evalink talos uses UTC unless a timezone is specified. You can use the following example to set a specific time zone in customer-facing messages, region-specific notifications, etc.
{{ alarm.timestamp | date("dd.MM.yyyy HH:mm:ss", timeZone="Europe/Zurich") }}
Formatting βtodayβ using now
now produces the current timestamp.
{{ now | date("dd.MM.yyyy") }}
You can use this in messages like report generated on ... or notification sent on...
Generating future times (e.g., +1 day, +2 days)
n evalink talos, date arithmetic and time-of-day selection are handled by the toTime filter. There is no standalone numberOfDays filter.
The toTime filter allows you to:
-
set a specific time of day
-
add a day offset
-
specify a timezone
{{ baseTimestamp | toTime("HH:mm", numberOfDays, "TimeZone") }}
Example: tomorrow at 13:00
{{ now | toTime("13:00", 1, "UTC") }}
You can use this for reminders, escalation deadlines, etc.
Formatting alarm timestamps
Alarm triggered at:
{{ alarm.timestamp | date("dd.MM.yyyy HH:mm:ss", timeZone="Europe/Zurich") }}
When an alarm attribute is missing or empty
Use this code to prevent broken messages when some alarm details are missing or empty.
{% if alarm.alarmZoneName is empty %}
Zone information not available
{% else %}
Zone: {{ alarm.alarmZoneName }}
{% endif %}
Using Pebble to Configure Alarm Dispatcherβ
You can user Pebble to configure Monitoring Stations and their Account Templated for Alarm Dispatcher on the company level.
For example:
{{ alarm.headers['dispatch-account'] }}
This routes the alarm to the Alarm Dispatcher account in the alarm header.
ARC-{{ alarm.headers['dispatch-account'] }}
This adds the ARC as a prefix to the previous string.
{% if alarm.headers['dispatch-account'] is not empty %}
{{ alarm.headers['dispatch-account'] }}
{% else %}
9999
{% endif %}
This creates a fallback if the dispatch-account is missing in the alarm headers.
Pebble in Alarm Supervision Logicβ
Alarm Supervision in evalink talos allows you to monitor whether a device or panel sends expected signals within a specified interval. You can use Pebble to configure dynamic alarm supervison rules.
Matching Supervision Signals Using Alarm Headers
SurGard Supervision
Expected Alarm: 602 { headers.x-receiver = generic-surgard:1001 }
Expected Interval: Every 5m
Timeout Alarm: E708 { headers.x-receiver = generic-surgard:1001 }
Restore Alarm: E709 { headers.x-receiver = generic-surgard:1001 }
This shows that a supervision rule can:
-
Look for an Expected Alarm code (602)
-
Only consider alarms whose header matches a specific receiver:
headers.x-receiver = generic-surgard:1001 -
Trigger a Timeout Alarm (e.g., E708), including the receiver type and account in the headers
-
Trigger a Restore Alarm (e.g., E709), including the receiver type and account in the headers
This demonstrates how alarms originating from multiple receivers can be distinguished by evalink talos.
Sanitizing URL parameters with encodeURL()β
Some workflow steps send webhook requests that include parameters coming from devices, sensors, or zones. If a value contains characters that are not valid in a URL (for example spaces, +, &, etc.), the request may fail.
evalink talos provides the urlEncode filter to safely escape such values.
urlEncode is a filter, not a function.
It must be used with the pipe (|) syntax.
https://api.example.com/jobs/search?zone={{ step.zone.name | urlEncode }}
If the zone name is: "Entrance + Parking", the URL becomes https://api.example.com/jobs/search?zone=Entrance%20%2B%20Parking
This ensures the webhook executes correctly even when zone names contain special characters.