Macros: Event-driven system (SMART)

Macros (or scripts) are the most powerful internal automation mechanism of SIM Roulette aggregators. With macros, absolutely any work scenarios can be implemented. It is very important that writing macros is available even to users unfamiliar with programming. Macros do not require compilation (pre-translation into machine code), are always ready for execution, and are available for editing.

Macro structure
Syntax
First steps
Useful techniques
Examples

Macro structure

Lines in a macro are executed sequentially from top to bottom. To change the order of execution, labels, branching operators, and unconditional jumps are used.

Label – any Latin word enclosed in square brackets, placed above the line to which the jump should be made.
Example of a label: [next]

The macro text must not contain empty lines. Each macro command can be accompanied by a comment separated from the command by symbols.

When launching a macro through the Terminal, you can pass input parameters: m.my_macros:Text_to_pass_to_macro The string after : will be placed into the buffer and can then be parsed by standard buffer processing methods.

Macros are placed in the /m folder of the file system.

Complex macros are sets of files; in this case, it makes sense to store them in one folder. When launching a macro through the terminal, it is enough to specify the folder name, SR will find and launch the file of the same name inside the specified folder. If there is a macro m/scanner/scanner, it can be launched either with the command m.scanner/scanner or m.scanner.

It is also useful to provide complex macro sets with a guide file in macro format (there is a sample among the examples) named help, and a menu file if launching different macros from the set is expected (there is also an example).

Syntax


Branching operators

if — Checks the result of the previous command. If the result equals 1, a jump to the specified label is performed.

var:a<10
if next
...
[next]
...

unless — Checks the result of the previous command. If the result equals 0 (NULL/Empty), a jump to the specified label is performed.

unless next


Unconditional jump operator

goto — Performs a jump to the specified label.

goto next
...
[next]
...


Program termination operators

stop — Ends macro execution.

stop

return — Performs an early return from a nested macro (by default, the return occurs after executing the last line in the nested macro).

return

return:event — Performs an early return from a macro invoked by an event.

return:event


Input operator

input — Requests an operator’s action in the Operator section of the web interface. Parameters: maximum input length; text to display above the input field.

input 1;Select action:<br>1-first option<br>2-second option


Operators for executing a command from the buffer

exec Executes a command from the buffer (the text placed in the buffer will be executed as a command).

buffer>write=sound:beep
exec

exec2 Executes a command from a copy of the buffer in the stack.

buffer>write=sound:beep
buffer>push
exec2


Operator for including a nested macro

include Includes the specified macro and transfers control to it (macro nesting depth is unlimited). Upon completion, control returns to the current macro.

include macrosname


Pause

pause Pause (program suspension) in milliseconds (1/1000 sec).

pause 1000

First steps

So, what is a macro or script? It is a set of commands for the aggregator (all device commands are described in detail in the corresponding Documentation section). For example, you can output Hello world! in the Terminal with the command @echo:Hello world!. But we are interested in another command: .ussd:{"number":"*102#","modem":"1"} — a command for sending a USSD request (in this case, a balance check) for one of the SIM cards of the first modem. The same command may look like: .ussd:*102#,1, but later we will use JSON format. It is clearer this way.

Let's try writing our first macro. To do this, go to the Macros section of your device’s web interface and click “Create macro”. In the “Name” field, enter my_macro. And in the “Content” field:

.ussd:{"number":"*102#","modem":"1"}

The macro is ready. The command sends a USSD request to modem 1 to check the balance. This way, we have already performed a small automation. Now save the macro by clicking the corresponding button.

You must place a dot before the ussd command if you want to receive an execution response in the output stream (to the Terminal and the control server). If no dot is placed, the command still executes, but without a response.

How to launch (execute) a macro. Through the web interface, you can launch a macro in two ways:

  1. In the Macros section, find the row with the macro name in the list and click the execute icon. The executed macro line will be highlighted, and a new icon will appear — the stop icon.
  2. In the Terminal section, enter the command macro: and the macro name — macro:my_macro. There is also a shortcut command m — m:my_macro. Running via terminal allows you to pass input data to the macro. This is done like this: m:my_macro:Text_to_pass_to_macro.

How to stop a macro. There are also two ways to interrupt a macro:

  1. In the Macros section, find the row with the running macro and click the stop icon.
  2. In the Terminal section, enter the command macro.stop. There is also a shortcut command m.stop.

So, select one of the SIM cards installed in the device, wait until it becomes active, and launch

Two new lines have been added.
The command in the first line forces our aggregator to constantly track the modemState event (change of modem states) and, whenever it changes, to launch our specified macro my_macro starting from the test label [the word in square brackets from the second line]. After the comma, action=add means that we are adding a new listener for the modemState event. If we later decide to stop tracking the event, we need to issue the command m.event:event=modemState,macro=my_macro:test,action=delete.

How does this work? By selecting a SIM card from the list, you disconnect the current card from the modem and connect the chosen one. At this point, the device restarts the modem so it can begin working with the new SIM card. The modem status changes to -1. It is exactly this status change that we instruct our aggregator to track.

As soon as the status changes, the device looks through the list of listeners for this event and calls the specified macros one by one. Eventually it reaches our macro. And so our macro is launched. But the command executed is the one following the test label. Let’s now add this and the following commands.

m.event:event=modemState,macro=my_macro:test,action=add
[test]
buffer.test:-1
if end // If status is -1 (SIM card not yet active) – terminate event handling
buffer.test:1
if task // If status is 1 (OK, SIM card is registered) – perform the action
buffer.test:3
if task // If status is 3 (no network registration) – assume registration will succeed and perform the action
buffer.test:5
if task // If status is 5 – perform the action
buffer.test:6
unless end // If status is (0,4), meaning not equal to 6 (card absent) – terminate event handling
[end]
@echo: Connecting to the network...
return:event // Terminate event handling
[task] buffer.event.dev buffer.test:1 // Check if it was the first modem that returned the status
unless end // If not the first modem, terminate event handling .ussd:{"number":"*102#","modem":"1"}

Let’s break down the new lines:

First, pay attention to phrases separated by “//” from the commands — these are comments. Everything after the slashes is not part of the program, is not interpreted by the macro handler, and serves only as code explanation.

Second — branching operators if and unless. How do they work?

Branching operators process the response of the previous command. If the response is positive (1 or some text), the if operator condition is satisfied, and control jumps to the label specified after it. For the unless operator, the opposite: if the response is not positive, the jump occurs. If the response is negative (0 (NULL)), then the if condition is not satisfied, and execution continues with the next line, while unless is satisfied, and execution jumps to its label.

Now you can analyze this piece of code yourself and trace under which statuses the macro continues from the task label and under which it stops at end. Note that exit from a macro launched by an event handler is performed with the return:event command. Next time, our macro will be called again from the test label upon a new status change.

We could already launch the balance check command. But! Remember, our device has 3 active modems. In the code above, we checked the status but did not check which modem returned it. So, let’s check. We obtain the device with the command buffer.event.dev and, with a branching operator, decide the next course of action.

So, upon receiving the status relevant to our task and the modem number, we finally launch the balance check command return:event. Halfway done. Now we need to set a listener for USSD responses. We do this with m.event:event=ussd,macro=my_macro:ussd,action=add. Since this mechanism is already familiar, we immediately add an exit return:event, the ussd label, and write the next part of the program:

m.event:event=modemState,macro=my_macro:test,action=add
[test]
buffer.test:-1
if end // If status is -1 (SIM card not yet active) – terminate event handling
buffer.test:1
if task // If status is 1 (OK, SIM card registered) – perform the action
buffer.test:3
if task // If status is 3 (no network registration) – assume registration will succeed and perform the action
buffer.test:5
if task // If status is 5 – perform the action
buffer.test:6
unless end // If status (0,4), meaning not 6 (card absent) – terminate event handling
[end]
@echo: Connecting to the network...
return:event // Terminate event handling
[task]
buffer.event.dev // Get the device number that returned the result
buffer.test:1 // Check if it was the first modem
unless end // If not the first modem, terminate event handling
.ussd:{"number":"*102#","modem":"1"}
m.event:event=ussd,macro=my_macro:ussd,action=add
return:event
[ussd]
buffer.find:*rub
if next // Look for substring in USSD response, e.g. " 53 rub"
return:event
[next]
@buffer // If found, output the balance
m.event:event=ussd,macro=my_macro:ussd,action=delete // Remove listener
return:event

New lines explained:

The USSD event invokes our macro at the ussd label. The macro searches the buffer for the balance amount. If not found, we terminate but leave the USSD listener, even though it’s no longer relevant. Homework: improve this macro ;)

If the balance is found, we output it to the stream with @buffer, remove the USSD listener, but keep the modemState listener. This way, when switching any SIM card in row A, our balance request will run again.

That’s it — now you know how to write macros for SIM Roulette. Review the macro syntax, read the documentation for SR-Organizer-Smart, check out macro examples. And when you write a useful macro, you can share it with others for free or sell it.


Useful techniques


Text formatting

In text output to the Terminal stream, you can use the following mnemonics:
[br] — line break
[b]bold text[/b]
[i]italic[/i]


Tables

Data in the Terminal stream can be output in table form using this syntax (shown line by line for readability, but in the macro it must be one line):

[table]
     [tr]
          [th]Header of column 1[/th]
          [th]Header of column 2[/th]
     [/tr]
     [tr]
          [td]Content of column 1[/td]
          [td]Content of column 2[/td]
     [/tr]
[/table]


Redirecting to another web page / Reload

To redirect to any _URL_ in Terminal output (via echo or buffer), insert sr>reload_URL_;. Don’t forget the “.” and “@” at the start.

buffer.write:sr>reload/terminal;
@buffer
@echo:sr>reloadhttps://google.com;


Clearing Terminal output

To clear Terminal output, insert sr>clear;.

buffer.write:sr>clear;
@buffer
@echo:sr>clear;


Emulating manual input

To execute any command (_COM_) by simulating manual input, insert sr>com:_COM_; in Terminal output text (echo or buffer). The command will execute and stay in the log.

buffer:sr>com:version;
@buffer
@echo:version;


Outputting text to the Terminal input field

To place arbitrary text (_TEXT_) into the Terminal input field and move browser focus there, insert sr>enter:_TEXT_;.

buffer:sr>version;
@buffer
@echo:sr>text:version;


Hyperlinks

For navigation convenience, instead of or alongside command lists you can output ready hyperlinks containing commands. Use: [link]_COMMAND_[name]_TITLE_[/name][/link]

buffer:sr>Command [link]version[name]Version[/name][/link]
@buffer
@echo>sr>Command [link]version[name]Version[/name][/link]


Examples

autoexec
Auto-start of background SIM card number retrieval.

m.event:event=modemState,macro=scanner:test,action=add // Add listener for modemState event

com_add
Macro for adding quick link buttons in the Terminal.

buffer.swap // Swap buffer and copy
buffer.file.load:/terminal.dat // Load current buttons into buffer
buffer.merge: // Merge buffer and its copy
buffer.postfix:; // Add semicolon
buffer.file.save:/terminal.dat // Save edited file
buffer.write:Command added // Prepare notification
@buffer // Output notification
pause 1000 // Wait one second
@echo:sr>reload/terminal; // Reload Terminal window

Новости SIM Roulette

Анонсы новых девайсов и софта, информацию об акциях и скидках мы публикуем только в соцсетях: