Variables and Control Flow Guide
DittoMation supports variables, expressions, and control flow constructs for building dynamic automation scripts.
Table of Contents
Variables
Variables allow you to store and reuse values throughout your automation scripts.
Defining Variables
Variables can be defined in multiple ways:
1. In the script file:
{
"variables": {
"username": "testuser",
"password": "secret123",
"max_retries": 3,
"items": ["Item A", "Item B", "Item C"]
},
"steps": [...]
}
2. Via command line:
3. From a variables file:
4. Using set_variable action:
{"action": "set_variable", "variable": "counter", "value": "0"}
{"action": "set_variable", "variable": "greeting", "expr": "'Hello, ' + username"}
Variable Priority
When the same variable is defined in multiple places, the priority is:
1. CLI --var options (highest)
2. --vars-file file
3. Script variables section (lowest)
Variable Resolution
Use {{variable}} syntax to reference variables in step fields.
Supported Fields
Variables can be used in these step fields:
- text, id, desc - Element locators
- value - For type action
- app - App name for open action
- message - For log action
- direction - For swipe/scroll
Basic Usage
{
"variables": {"username": "john"},
"steps": [
{"action": "type", "value": "{{username}}"},
{"action": "log", "message": "Logged in as {{username}}"}
]
}
Nested Access
Access nested object properties with dot notation:
{
"variables": {
"user": {"name": "Alice", "email": "alice@example.com"}
},
"steps": [
{"action": "type", "value": "{{user.name}}"},
{"action": "type", "value": "{{user.email}}"}
]
}
Array Access
Access array elements with bracket notation:
{
"variables": {
"items": ["first", "second", "third"]
},
"steps": [
{"action": "tap", "text": "{{items[0]}}"},
{"action": "tap", "text": "{{items[1]}}"}
]
}
Default Values
Provide default values with the pipe syntax:
If username is not defined, "guest" will be used.
Expressions
The expression engine allows evaluating conditions and computing values safely.
Supported Operations
Comparisons:
- ==, !=, <, >, <=, >=
- in, not in
Boolean:
- and, or, not
Arithmetic:
- +, -, *, /, //, %, **
String Methods:
- .upper(), .lower(), .strip()
- .startswith(), .endswith()
- .replace(), .split()
Built-in Functions:
- len(), str(), int(), float(), bool()
- min(), max(), sum(), abs(), round()
- any(), all(), sorted(), range()
Element Functions
These functions interact with the device UI:
| Function | Description |
|---|---|
element_exists(text=, id=, desc=) |
Check if element exists |
element_text(text=, id=, desc=) |
Get element's text content |
element_count(text=, id=, desc=) |
Count matching elements |
element_visible(text=, id=, desc=) |
Check if element is visible |
Examples:
{"action": "if", "expr": "element_exists(text='Login')"}
{"action": "if", "expr": "element_count(text='Item') > 5"}
{"action": "while", "expr": "not element_exists(text='Complete')"}
Safety
The expression engine is sandboxed: - No imports allowed - No file system access - No arbitrary code execution - Only whitelisted functions
Control Flow
if / elif / else
Conditional execution based on expressions:
{
"action": "if",
"expr": "element_exists(text='Welcome')",
"then_steps": [
{"action": "log", "message": "Already logged in"}
],
"else_steps": [
{"action": "tap", "text": "Login"},
{"action": "type", "value": "{{username}}"},
{"action": "tap", "text": "Submit"}
]
}
With elif:
{
"action": "if",
"expr": "count > 100",
"then_steps": [
{"action": "log", "message": "Large count"}
],
"elif_blocks": [
{
"condition": "count > 50",
"steps": [{"action": "log", "message": "Medium count"}]
},
{
"condition": "count > 10",
"steps": [{"action": "log", "message": "Small count"}]
}
],
"else_steps": [
{"action": "log", "message": "Very small count"}
]
}
for
Iterate over a list of items:
{
"action": "for",
"items": "['Item A', 'Item B', 'Item C']",
"item_var": "item",
"index_var": "idx",
"loop_steps": [
{"action": "tap", "text": "{{item}}"},
{"action": "wait", "timeout": 0.5}
],
"max_iterations": 100
}
Using a variable as items:
{
"variables": {"products": ["Phone", "Tablet", "Laptop"]},
"steps": [
{
"action": "for",
"items": "products",
"item_var": "product",
"loop_steps": [
{"action": "tap", "text": "{{product}}"}
]
}
]
}
while
Loop while condition is true:
{
"action": "while",
"expr": "counter < 5",
"counter_var": "iteration",
"loop_steps": [
{"action": "log", "message": "Iteration {{iteration}}"},
{"action": "set_variable", "variable": "counter", "expr": "counter + 1"}
],
"max_iterations": 100
}
until
Loop until condition becomes true:
{
"action": "until",
"expr": "element_exists(text='Success')",
"counter_var": "attempt",
"loop_steps": [
{"action": "log", "message": "Attempt {{attempt}}"},
{"action": "tap", "text": "Retry"},
{"action": "wait", "timeout": 2}
],
"max_iterations": 10
}
break / continue
Control loop execution:
{
"action": "for",
"items": "range(10)",
"item_var": "i",
"loop_steps": [
{
"action": "if",
"expr": "i == 5",
"then_steps": [{"action": "break"}]
},
{"action": "log", "message": "Processing {{i}}"}
]
}
Actions Reference
set_variable
Set a variable value.
| Field | Required | Description |
|---|---|---|
variable |
Yes | Variable name |
value |
No | Direct value to set |
expr |
No | Expression to evaluate |
Examples:
{"action": "set_variable", "variable": "count", "value": "0"}
{"action": "set_variable", "variable": "doubled", "expr": "count * 2"}
{"action": "set_variable", "variable": "name", "expr": "'Hello, ' + username"}
extract
Extract data from a UI element into a variable.
| Field | Required | Description |
|---|---|---|
variable |
Yes | Variable name to store result |
text/id/desc |
Yes | Element locator |
extract_source |
No | What to extract: text, attribute, bounds, resource_id, content_desc, class (default: text) |
extract_attr |
No | Attribute name when extract_source="attribute" |
regex |
No | Regex pattern to extract substring |
Examples:
{"action": "extract", "variable": "balance", "text": "Balance:", "extract_source": "text"}
{"action": "extract", "variable": "amount", "id": "price", "regex": "\\$([\\d,]+\\.\\d{2})"}
log
Log a message.
| Field | Required | Description |
|---|---|---|
message |
Yes | Message to log |
level |
No | Log level: debug, info, warning, error (default: info) |
Examples:
{"action": "log", "message": "Starting login process"}
{"action": "log", "message": "Error: {{error_msg}}", "level": "error"}
assert
Assert a condition is true.
| Field | Required | Description |
|---|---|---|
expr |
Yes | Condition to assert |
message |
No | Error message if assertion fails |
Examples:
{"action": "assert", "expr": "count > 0", "message": "Count must be positive"}
{"action": "assert", "expr": "element_exists(text='Success')"}
Examples
Login with Retry
{
"name": "login_with_retry",
"variables": {
"username": "testuser",
"password": "secret123",
"max_attempts": 3
},
"steps": [
{"action": "open", "app": "MyApp"},
{"action": "wait", "timeout": 2},
{
"action": "set_variable",
"variable": "attempt",
"value": "0"
},
{
"action": "until",
"expr": "element_exists(text='Welcome') or attempt >= max_attempts",
"loop_steps": [
{"action": "set_variable", "variable": "attempt", "expr": "attempt + 1"},
{"action": "log", "message": "Login attempt {{attempt}}"},
{"action": "tap", "text": "Login"},
{"action": "type", "value": "{{username}}"},
{"action": "tap", "text": "Next"},
{"action": "type", "value": "{{password}}"},
{"action": "tap", "text": "Submit"},
{"action": "wait", "timeout": 3}
],
"max_iterations": 5
},
{
"action": "assert",
"expr": "element_exists(text='Welcome')",
"message": "Login failed after max attempts"
}
]
}
Process List of Items
{
"name": "process_items",
"variables": {
"items": ["Product A", "Product B", "Product C"]
},
"steps": [
{"action": "open", "app": "Shopping"},
{"action": "wait", "timeout": 2},
{
"action": "for",
"items": "items",
"item_var": "product",
"index_var": "idx",
"loop_steps": [
{"action": "log", "message": "Processing item {{idx}}: {{product}}"},
{"action": "tap", "text": "Search"},
{"action": "type", "value": "{{product}}"},
{"action": "tap", "text": "Go"},
{"action": "wait", "timeout": 1},
{
"action": "if",
"expr": "element_exists(text='Add to Cart')",
"then_steps": [
{"action": "tap", "text": "Add to Cart"},
{"action": "log", "message": "Added {{product}} to cart"}
],
"else_steps": [
{"action": "log", "message": "{{product}} not available", "level": "warning"}
]
},
{"action": "press", "value": "back"}
]
},
{"action": "log", "message": "Processed all items"}
]
}
Wait for Loading
{
"name": "wait_for_loading",
"steps": [
{"action": "tap", "text": "Load Data"},
{
"action": "until",
"expr": "not element_exists(text='Loading...')",
"loop_steps": [
{"action": "log", "message": "Still loading...", "level": "debug"},
{"action": "wait", "timeout": 1}
],
"max_iterations": 30
},
{"action": "log", "message": "Loading complete"},
{"action": "screenshot", "value": "loaded_screen.png"}
]
}
Python API
You can also use variables and control flow from Python:
from core.automation import Automation, Step
# Create automation with initial variables
auto = Automation(initial_vars={"username": "test"})
# Set variables programmatically
auto.set_variable("count", 5)
# Run with additional variables
result = auto.run(steps, initial_vars={"password": "secret"})
# Get variable value
value = auto.get_variable("count")
Direct Expression Evaluation
from core.variables import VariableContext
from core.expressions import SafeExpressionEngine
ctx = VariableContext({"x": 10, "name": "test"})
engine = SafeExpressionEngine(ctx)
# Evaluate expressions
result = engine.evaluate("x > 5")
print(result.value) # True
result = engine.evaluate("name.upper()")
print(result.value) # "TEST"
# Boolean evaluation
if engine.evaluate_bool("x > 5 and len(name) > 3"):
print("Condition met")