Welcome to the documentation for Volang, Voldeno’s proprietary programming language. It serves as the core tool for implementing logic across blocks in our smart home ecosystem.
Introduction
Volang is a specialized programming language developed by Voldeno, specifically designed to implement the logic of blocks within our smart home ecosystem. It bridges the gap between hardware capabilities and high-level automation, allowing developers to create advanced control scenarios efficiently and securely.
The primary purpose of Volang is to define the behavior of Blocks. In the Voldeno system, a block represents an autonomous functional unit (e.g., a roller shutter controller, a thermostat, or an energy meter). Volang allows developers to define how these blocks react to input signals, process internal state, and trigger actions based on events.
Architecture: The Compiled Model
Unlike simple scripting languages that are interpreted directly from text, Volang utilizes a Virtual Machine (VM) architecture. The execution process consists of two distinct stages:
- Compilation: The source code written by the developer is processed by the Volang Compiler. This step verifies syntax, data types, and logical integrity, transforming the code into an optimized binary format known as bytecode.
- Execution: The resulting bytecode is loaded into the Volang Virtual Machine (Volang VM)—a dedicated runtime environment operating on Voldeno controllers. The VM is responsible for interpreting the bytecode instructions and executing them safely on the physical hardware.
Why Volang VM?
Adopting a virtual machine architecture offers significant advantages for smart home environments, unifies the development workflow and extends the capabilities of the hardware:
- Safety (Sandboxing): User code runs in an isolated environment, separated from the core operating system. A runtime error (e.g., division by zero) will only stop the specific script within the VM, ensuring the overall stability of the controller remains uncompromised.
- Performance: Executing compact binary bytecode is significantly faster and more resource-efficient than parsing text files at runtime, which is crucial for embedded systems and microcontrollers.
- Portability: Logic written in Volang is hardware-agnostic. It can be executed on any device capable of running the Volang VM, regardless of the underlying processor architecture.
- Identical Simulation: Since the Volang VM runs consistently across platforms, the exact same block logic can be executed within Voldeno Studio. This allows developers to test and debug their code in a desktop environment with the guarantee that the behavior will be identical to the actual production system.
- Distributed Execution: The efficiency of the VM allows it to run not only on the central Voldeno Hub but also on peripheral extension modules. This capability enables true distributed logic, where complex processing can occur locally on the device (at the edge) without being limited to basic event reporting.
Lexical Conventions
Keywords
The following words are reserved in Volang and cannot be used as variable or function names. They form the structural foundation of the language.
and break else
extern false fn
if or return
true while
Literals
In Volang, a literal is a notation for representing a fixed value directly within the source code. These are the raw data values that you assign to variables or pass to functions. Volang supports specific formats for boolean, strings, integer, and floating-point literals.
Boolean Literals
Used to represent logical states. There are exactly two boolean literals:
true: Represents a positive or active state (On).false: Represents a negative or inactive state (Off).
Integer Literals
Integer literals represent whole numbers. Volang supports two notations:
- Decimal (Base-10): Standard numeric representation using digits
0-9. Negative numbers are prefixed with a minus sign-. - Hexadecimal (Base-16): Often used for color codes, bitmasks, or hardware addresses. These literals must start with the prefix
0xfollowed by digits0-9and lettersA-F(case-insensitive).
Floating-Point Literals
Floating-point literals represent numbers with fractional parts.
- Syntax: They are written using decimal notation with a dot
.acting as the decimal separator. - Requirement: A valid float literal typically requires at least one digit before or after the decimal point (e.g.,
0.5,10.0,23.34).
Text Literals (Strings)
Text literals represent sequences of characters and are used for messages, identifiers, or labels.
- Syntax: Text must be enclosed in double quotes
". - Multi-line Support: A string literal in Volang can span multiple lines. The newline characters within the quotes are preserved, making it easy to format text for display panels or logs without using special escape codes like
\n.
Example:
// --- Boolean Literals ---
is_light_on = true
is_door_locked = false
// --- Integer Literals (Decimal) ---
counter = 10
negative_offset = -5
// --- Integer Literals (Hexadecimal) ---
// Useful for bitwise operations or hardware flags
status_mask = 0xFF // Decimal 255
color_red = 0xFF0000 // Color code
// --- Floating-Point Literals ---
temperature = 23.34
calibration_factor = 0.95
voltage = 12.0 // .0 indicates this is a Float, not an Integer
// Standard single-line string
device_name = "Living Room Thermostat"
// Multi-line string (preserves formatting)
notification_msg = "
Warning: High Temperature!
Action: Cooling Activated
Time: 12:00
"
Values and Types
Volang operates on a dynamic type system, offering a flexible approach to data management. In Volang, variables function as generic containers rather than strictly typed storage. Consequently, there is no need to explicitly declare types (such as int or float) within the code. The type information is intrinsic to the value itself, not the variable assigned to hold it.
First-Class Values
All values in Volang are first-class values. This implies that every value, regardless of its type, can be:
- Stored in variables.
- Passed as arguments to functions.
- Returned as results from functions.
Type Consistency (Strict Typing)
While Volang does not require type declarations, it enforces type stability. Once a variable is assigned a value of a specific type (e.g., an Integer), it becomes bound to that type. You cannot subsequently assign a value of a different type (e.g., a String) to that same variable. This mechanism prevents common runtime errors and ensures predictable controller behavior.
Value Categories
Volang distinguishes between three primary kinds of values:
1. Numeric (Integer & Float)
Used for mathematical calculations, sensor readings, and counters.
- Integer: Whole numbers without a fractional component (e.g.,
0,42,-15). - Float: Floating-point numbers representing fractional values (e.g.,
3.14,21.5,-0.001).
2. Textual (String)
Used for status messages, logs, names, and text processing. Strings are sequences of characters enclosed in double quotes ". They are used for names, logs, and status messages.
- Multi-line Support: Volang supports multi-line strings. You can press logical “Enter” within the quotes to create a text block that spans several lines.
3. Logical (Boolean)
Booleans represent truth values and are crucial for decision-making logic (if, while).
- Values:
trueorfalse.
Examples
// 1. Dynamic Typing
status = 25.5
// 2. Multi-line Strings
// Useful for formatting display messages or logs
config_info = "Device: Thermostat
Location: Living Room
Firmware: v1.2"
// 3. Passing values
fn logStatus(message) {
// 'message' can be any type
print(message)
}
logStatus(config_info) // Passing a Multi-line String
Variables
Volang employs a streamlined approach to variable management. There are no declaration keywords (such as var, let, or int). A variable is created the moment you assign a value to a name. The location where you define the variable determines its scope (visibility) throughout the script.
Creating Variables
To define a variable, simply use the assignment operator =. The variable’s data type is inferred from the assigned value and becomes fixed for the lifetime of that variable.
Syntax
variable_name = value
Variable Scope
Scope defines where a variable can be accessed within your code. Volang distinguishes between two types of scope:
1. Global Variables
Variables defined at the top level of the script (outside of any function or control block) are Global.
- Visibility: They are accessible from anywhere in the script, including inside functions.
- Use Case: Ideal for storing the overall state of the device, such as
target_temperature,system_mode, oralarm_status.
2. Local Variables
Variables defined inside a function (fn) or a control block are Local.
- Visibility: They exist only within the block where they were created. They are destroyed as soon as the execution leaves that block.
- Use Case: Used for temporary calculations, loop counters, or intermediate logic steps that do not need to be remembered later.
Example
// --- Global Variables ---
// Defined at the root, visible everywhere
system_status = "IDLE"
setpoint = 21.5
fn checkTemperature(current_temp) {
// --- Local Variable ---
// 'diff' is created here and exists only inside this function
diff = current_temp - setpoint
// We can access the global 'setpoint' here
if diff > 1.0 {
return "Cooling Needed"
}
return "Stable"
}
// 'diff' is not accessible here; trying to use it would cause an error
Comments
Comments are fragments of code that are ignored by the Volang compiler. They are used to explain the logic, leave notes for other developers, or temporarily disable specific parts of the code during testing.
Single-line Comments
Single-line comments start with two forward slashes //. Any text following these characters up to the end of the line is treated as a comment.
Multi-line Comments
Multi-line (block) comments start with /* and end with */. Everything between these markers is ignored, regardless of how many lines it spans. This is useful for writing detailed descriptions or disabling large blocks of code.
Example:
// This is a single-line comment. It explains the next line.
targetTemp = 21.5 // You can also place comments at the end of a line
/*
This is a multi-line comment.
It is useful for describing complex logic
or block headers.
*/
/* oldLogic = 10
if (oldLogic > 5) {
// This code is currently disabled (commented out)
}
*/
Statements
Volang is designed to minimize visual noise and maximize readability. Unlike languages such as C++ or Java, Volang does not use semicolons (;) to mark the end of an instruction.
The newline character (pressing Enter) serves as the definitive statement terminator. The compiler interprets the end of a line as the completion of the current command. This approach enforces a clean structure where each instruction typically occupies a single line.
- No Semicolons: You do not need to type
;at the end of a line. - One Instruction per Line: To keep the logic clear for automation tasks, the standard practice is to write one specific action per line of code.
Assignments
Assignment operators are used to store a value in a variable. Volang supports the standard basic assignment operator as well as compound assignment operators, which combine an arithmetic operation with an assignment. These compound operators make the code more concise and readable, especially when updating counters or accumulating sensor values.
Basic Assignment
The = operator is the most fundamental operator. It assigns the value on the right-hand side to the variable on the left-hand side. Since Volang uses dynamic typing, the variable’s type is determined by the value assigned to it.
Example:
// Initialize a target temperature
target_temp = 21.0
// Initialize a counter
event_counter = 0
Compound Assignment Operators
Compound operators perform an operation on the current value of the variable and then update the variable with the new result. They serve as a shorthand for common programming patterns.
+=add and assign-=subtract and assign*=multiply and assign/=divide and assign
Key Behaviors
- Initialization Requirement: For compound operators (
+=,-=, etc.), the variable on the left side must already exist (must have been previously assigned a value). Only the basic=operator can create a new variable. - Type Promotion: Compound assignment follows the same type promotion rules as standard arithmetic.
Examples
1. Updating Counters (Increment/Decrement)
Using += and -= is the standard way to count events or adjustments.
// Increment the counter by 1
event_counter += 1
// Decrease temperature setpoint by 0.5 degrees
target_temp -= 0.5
2. Scaling Values
Using *= and /= for dimming lights or averaging data.
brightness = 100
// Dim the brightness to 80% of its current value
brightness *= 0.8 // Result: 80.0 (Promoted to Float)
// Halve the value
brightness /= 2 // Result: 40.0
3. Energy Accumulation
A typical use case in smart metering is accumulating values over time.
total_energy_kwh = 0.0
current_usage = 1.5
// Add current usage to the total accumulator
total_energy_kwh += current_usage
Control Flow
The if statement is the fundamental decision-making structure in Volang. It allows the program to execute a specific block of code only if a specified condition evaluates to true. You can extend this logic using the else keyword to define an alternative path for when the condition is false.
Syntax
1. Basic if
Executes code only when the condition is true.
if (condition) {
// Code to run if condition is true
}
2. if with else
Executes one block if true, and the other if false.
if (condition) {
// Code to run if condition is true
} else {
// Code to run if condition is false
}
3. Chained Conditions (else if)
Used to check multiple conditions in sequence. The system checks conditions from top to bottom and executes the block associated with the first true condition.
if (condition_1) {
// Runs if condition_1 is true
} else if (condition_2) {
// Runs if condition_1 is false AND condition_2 is true
} else {
// Runs if all above conditions are false
}
Examples
1. Simple Security Check
Turning on a siren if a door is opened while the alarm is armed.
is_armed = true
door_open = true
fn triggerSiren() {
...
}
if (is_armed and door_open) {
triggerSiren()
}
2. Thermostat Logic (Using else if)
A classic automation example showing how to handle three different states: heating, cooling, and idle.
current_temp = 19.0
target_temp = 21.0
hysteresis = 0.5
fn setHeating(value) {
...
}
fn setCooling(value) {
...
}
if (current_temp < (target_temp - hysteresis)) {
// Too cold -> Start Heating
setHeating(true)
} else if (current_temp > (target_temp + hysteresis)) {
// Too hot -> Start Cooling
setCooling(true)
} else {
// Temperature is within the optimal range
// Turn off both systems
setHeating(false)
setCooling(false)
}
Loops
Volang streamlines control flow by supporting a single loop construct: the while loop. There are no for or do-while loops in the language. This design choice maintains the simplicity and efficiency of the interpreter while providing a construct versatile enough to handle all iteration scenarios, from simple counters to complex state monitoring. The while loop executes a block of code repeatedly as long as the specified condition evaluates to true.
Syntax
while (condition) {
// Code to execute repeatedly
}
Volang supports the break keyword within loops. When break is encountered, the loop is immediately terminated, and program execution resumes at the first instruction following the loop block. This is particularly useful for exiting a loop early when a specific condition is met (e.g., a sensor trigger or a timeout).
NOTICE
In embedded systems and smart home controllers, infinite loops can be dangerous as they might block other operations. The Volang VM monitors execution time, but it is the users’s responsibility to ensure that the loop condition eventually becomes false or that a break statement is reached.
Examples
1. Basic Counter (Standard Loop)
A simple loop that counts down from 5 to 1.
counter = 5
// Run as long as counter is greater than 0
while (counter > 0) {
logValue(counter)
// Decrement counter to eventually terminate the loop
counter -= 1
}
2. Waiting with Timeout (Using break)
This pattern is very common in automation: trying to perform an action (like connecting to a sensor) but giving up after a certain number of attempts to avoid freezing the system.
attempts = 0
max_retries = 10
success = false
while (attempts < max_retries) {
// Check if connected
if (checkConnection()) {
success = true
// Exit the loop immediately, no need to retry
break
}
attempts += 1
}
// Logic continues here after the loop...
if (success) {
startProcess()
}
Functions
Functions are reusable blocks of code designed to perform a specific task. They help organize logic, reduce code duplication, and improve readability. In Volang, functions are defined using the fn keyword.
Syntax
To define a function, use the fn keyword followed by a unique name, a set of parentheses () for arguments, and curly braces.
fn functionName(argument1, argument2) {
// Code to be executed
}
Key Rules & Behaviors
- Arguments: A function can accept multiple arguments (separated by commas) or no arguments at all. These arguments act as local variables available only inside the function.
- Return Values:
- Returning Data: Use the
returnkeyword to send a value back to the place where the function was called. - Void (No Return): If the
returnstatement is omitted, or ifreturnis used without a value, the function performs its instructions but does not pass any data back (Void).
- Returning Data: Use the
- Naming Constraints: The name of your custom function must be different from the names of functions in the Volang Standard Library. This prevents conflicts with built-in system commands.
- Scope: Variables defined inside a function are local to that function and cannot be accessed from outside.
Expressions
Binary Arithmetic Operators
Volang provides a comprehensive set of binary arithmetic operators essential for processing sensor data, calculating control values, and managing logic loops. These operators take two operands (values) and return a single numerical result.
+addition-subtraction*multiplication/division%modulo
Volang follows standard mathematical rules for operator precedence to ensure calculations are predictable:
- Multiplication (
*), Division (/), and Modulo (%) are evaluated first. - Addition (
+) and Subtraction (-) are evaluated next.
Examples
1. Sensor Calibration (Linear Scaling)
Converting a raw analog sensor value (0-100) to a percentage using multiplication and addition.
raw_value = 50
scale_factor = 1.2
offset = 5.0
// (50 * 1.2) + 5.0 = 60 + 5 = 65.0
calibrated_value = raw_value * scale_factor + offset
2. Complex Logic with Parentheses
Calculating the average temperature from three sensors. Parentheses are required to perform addition before division.
temp_sensor_1 = 20.5
temp_sensor_2 = 21.0
temp_sensor_3 = 19.5
// Without parentheses, only temp_sensor_3 would be divided by 3
average_temp = (temp_sensor_1 + temp_sensor_2 + temp_sensor_3) / 3
3. Using Modulo for Cyclical Events
Modulo is useful for creating repeating patterns (e.g., executing an action every 10th loop iteration).
loop_counter = 23
// 23 / 10 = 2, remainder is 3
remainder = loop_counter % 10
Binary Logical Operators
Binary logical operators allow you to combine multiple boolean expressions into a single logical condition. They are essential for creating complex automation rules where a decision depends on several factors simultaneously (e.g., “turn on the light if motion is detected AND it is night”). Volang uses English keywords and and or for these operations, making the code highly readable and intuitive.
andLogical Conjunction (Returnstrueonly if both operands evaluate totrue. If either operand isfalse, the result isfalse.)orLogical Disjunction (Returnstrueif at least one of the operands evaluates totrue. The result isfalseonly if both operands arefalse.)
Examples
1. Conditional Lighting (AND)
Turn on the light only if motion is detected AND the ambient brightness is low.
motion_active = true
brightness_level = 10
threshold = 30
// Both conditions must be true
if (motion_active and brightness_level < threshold) {
// Action: Turn on light
setLight(true)
}
2. Redundant Triggering (OR)
Activate an alarm if a door contact is opened OR if a window vibration sensor triggers.
door_open = false
window_vibration = true
// Action triggers if either sensor is active
if (door_open or window_vibration) {
triggerAlarm()
}
3. Complex Logic (Grouping)
Using parentheses to mix operators accurately.
is_weekend = true
user_home = true
manual_override = false
// Heating is on if:
// 1. Manual override is active OR
// 2. It is the weekend AND the user is home
heating_on = manual_override or (is_weekend and user_home)