Virtual Triggers

While a component can generate triggers, sometimes you might want to trigger something based on an external event, such as a parameter change on one of your switchers. To facilitate this, Reactor can generate a trigger based on:

  • Conditions
  • Time
  • Value Changes

Modes

Virtual triggers can operate in one of the following modes:

Binary: Use a condition, simulates a button press

In Binary mode, a condition is interpreted as a Binary trigger (simulating a button press). When the condition becomes true, a button press is simulated (ActDown trigger), and a release (ActUp trigger) occurs when it becomes false.

Change: Trigger when an external value changes

In Change mode, you can select any parameter. When the parameter changes or updates, the trigger is fired, simulating a button press (similar to Binary mode).

Analog: Simulate a fader move

In Analog mode, the selected parameter becomes an Analog Trigger (simulating a fader). Every time the value changes, a new Analog Value is sent to the defined Behavior.

Schedule: Use a fixed time schedule to trigger something

In Schedule mode, you can configure a schedule for when your trigger will be executed using cron-like syntax. Every time the specified time is reached, the Virtual trigger sends a Binary trigger (simulating a button press) to the defined behavior.

See below for how to configure the time format.

HoldGroup

Special Virtual Trigger type designed for use cases like a Joystick Hold Group with multiple RCPs [^note].

Creating and Configuring Virtual Triggers

Like many other elements, VirtualTriggers are part of the tree and are defined on a layer. To keep things simple, it is recommended to create VirtualTriggers on the base configuration layer of your configuration. Start by clicking anywhere on the blue or black area of your controller. You should now see the root layer of this panel.

To create a VirtualTrigger, follow these steps:

Create Virtual Trigger

After creation, click on the name to edit it. You will see this in your inspector:

Edit Virtual Trigger
  1. When adding a Virtual Trigger, first select its mode using the dropdown.
  2. After creating a trigger, select either a Condition, Parameter Reference, or time schedule.
  3. Click Create to create a Behavior.
  4. Click on the Behavior to configure a behavior that will be used for the Virtual Trigger.

Configuring 'Schedule' Virtual Triggers

Schedule Virtual Triggers use a configuration format called Cron. Cron is a Linux application that schedules commands or scripts to run automatically at specified times and dates.

The syntax works as follows:

******
Field nameSecondsMinutesHoursDay of monthMonthDay of week
Allowed values0-590-590-231-311-12 or
JAN-DEC
0-6 or
SUN-SAT
Allowed special characters* / , -* / , -* / , -* / , - ?* / , -* / , - ?

All six fields are required.

Allowed special characters explained:

  • Asterisk ( * ) The asterisk indicates that the cron expression will match all possible values of the field. For example, using an asterisk in the 5th field (month) would indicate every month.

  • Slash ( / ) Slashes describe increments of ranges. For example, 3-59/15 in the 1st field (minutes) would indicate the 3rd minute of the hour and every 15 minutes thereafter. The form */... is equivalent to first-last/..., meaning an increment over the largest possible range of the field. The form N/... is accepted as meaning N-MAX/..., starting at N and using the increment until the end of the specific range. It does not wrap around.

  • Comma ( , ) Commas separate items in a list. For example, "MON,WED,FRI" in the 6th field (day of the week) would mean Mondays, Wednesdays, and Fridays.

  • Hyphen ( - ) Hyphens define ranges. For example, 9-17 would indicate every hour between 9am and 5pm inclusive.

  • Question mark ( ? ) A question mark may be used instead of * to leave either the day-of-month or day-of-week field blank. Important: When using day-of-week, you must use ? in the day-of-month field, and vice versa.

Alternatively, we also allow the following inputs:

EntryDescriptionEquivalent To
@yearly (or @annually)Run once a year, midnight, Jan. 1st0 0 0 1 1 *
@monthlyRun once a month, midnight, first of month0 0 0 1 * *
@weeklyRun once a week, midnight between Sat/Sun0 0 0 * * 0
@daily (or @midnight)Run once a day, midnight0 0 0 * * *
@hourlyRun once an hour, beginning of hour0 0 * * * *

Common Schedule Examples

Here are some practical examples to help you create your own schedules:

DescriptionSchedule ExpressionExplanation
Every weekday at 8:00 AM0 0 8 ? * MON-FRISeconds=0, Minutes=0, Hours=8, Day-of-month=? (any), Month=* (all), Days=Mon-Fri
Every weekday at 8:00 AM (numeric)0 0 8 ? * 1-5Same as above, using 1=Monday through 5=Friday
Mon, Wed, Fri at 10:30 AM0 30 10 ? * MON,WED,FRISpecific days of the week at 10:30
Every day at 6:00 PM0 0 18 * * ?Day-of-month=* (every day), Day-of-week=? (ignored)
First day of every month at 9:00 AM0 0 9 1 * ?Day-of-month=1, Day-of-week=? (ignored)
Every 15 minutes during business hours0 */15 9-17 * * MON-FRIEvery 15 minutes from 9am-5pm on weekdays
Every Sunday at midnight0 0 0 ? * SUNDay-of-week=Sunday (0 or SUN)
Every Sunday at 10:00 AM0 0 10 ? * SUNDay-of-week=Sunday at 10:00 AM
Every hour on the hour, every day0 0 * * * ?Minutes=0, Hours=* (every hour)
Every 5 seconds*/5 * * * * ?Seconds=every 5 seconds (/5), all other fields= (any time)

``` admonish tip title="Remember: Use ? when specifying day-of-week" collapsible=false When you want to trigger on specific days of the week (like MON-FRI), always use ? in the day-of-month field (4th field). This tells the scheduler to ignore the day-of-month and only pay attention to the day-of-week field.

Example: 0 0 8 ? * MON-FRI - The ? in the 4th position is required!


## Videos

We currently have 3 videos on our YouTube channel documenting Virtual Trigger Configuration:

<!-- {% embed youtube id="_NHMzD10Qzc" loading="lazy" %} -->

<div data-embedify data-app="youtube" data-option-id="_NHMzD10Qzc" data-option-loading="lazy" style="display:none"></div>

<iframe allowfullscreen name="youtube" loading="lazy" src="https://www.youtube.com/embed/_NHMzD10Qzc" style="width: 100%; height: 100%; border: none; aspect-ratio: 16/9; border-radius: 1rem; background: black" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"></iframe>

<!-- {% embed youtube id="k-vT89Z5BUk" loading="lazy" %} -->

<div data-embedify data-app="youtube" data-option-id="k-vT89Z5BUk" data-option-loading="lazy" style="display:none"></div>

<iframe allowfullscreen name="youtube" loading="lazy" src="https://www.youtube.com/embed/k-vT89Z5BUk" style="width: 100%; height: 100%; border: none; aspect-ratio: 16/9; border-radius: 1rem; background: black" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"></iframe>

<!-- {% embed youtube id="PELylIUdY4k" loading="lazy" %} -->

<div data-embedify data-app="youtube" data-option-id="PELylIUdY4k" data-option-loading="lazy" style="display:none"></div>

<iframe allowfullscreen name="youtube" loading="lazy" src="https://www.youtube.com/embed/PELylIUdY4k" style="width: 100%; height: 100%; border: none; aspect-ratio: 16/9; border-radius: 1rem; background: black" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"></iframe>

[^note]: <div class="advanced" ></div> This is an advanced feature.