Advanced Configuration Information and references which are useful for integrators using Reactor to create advanced configurations in Config tab or JSON editor. Advanced Fader Range Setup This is an example of an eventhandler that makes it so moving the full range of the physical fader only moved the top half of the actual "fader" parameter, using the "Analog to Analog" conversion and Map1: "EventHandlers": { "change": { "EventPreProc": { "A2A": { "InputMapping": { "Default": { "Map1": { "InputEnd": 1000, "OutputStart": 500, "OutputEnd": 1000 } } } } }, "BinarySetValues": {}, "IOReference": {} } }, This does however completly remove the option to go to 0 on the param, if you want that you might want this kind of code instead where Map2 makes it so the last 5% of the physical fader makes it jump down to 0 or "inf" when talking audio faders, while still keeping the scaling on the rest of the fader positions: "EventHandlers": { "change": { "EventPreProc": { "A2A": { "InputMapping": { "Default": { "Map1": {}, "Map2": { "InputStart": 50, "InputEnd": 1000, "OutputStart": 500, "OutputEnd": 1000 } } } } }, "BinarySetValues": {}, "IOReference": {} } }, All Values here are set in a "Normalized" range, so this will always be a number between 0 and 1000, think of it as a % of the values on your param where 1000 = 100%, 500 = 50% and so on. InputStart and InputEnd controls the max movement of the physical fader, so setting these can give you "deadzones" at the ends of a fader, if you'r newer quite able to hit the max and min values. by default you would always want them on 0 or 1000 on a fader when using map1. If you're using map2 you might want the lower one to be a certain % above 0, so our recommendation would be to start at 50 (5%) or 100 (10%) and experiment from there, and keeping the max on 1000. OutputStart is the minimum value you want the fader to set OutputEnd is the maximum value you want the fader to set In the example above you will see them limit the output values to only cover the upper 50% of the parameter by using the values 500 and 1000. play around with these to fit your desired outcome range. Double click button You may wish to detect 'double-click' or 'triple-click' on buttons. This would be useful for showing special menus (layers) - or sending commands that need a little more security. To detect 'multiple-clicks' we use 'Event handler - preprocessor'. In this simple guide we want to control a variable called "ShowMenu". It has just two options: off / on. We create a behavior on a button - It must be double-clicked in order to set "ShowMenu" to 'on' Create button behavior • right-click desired button and select 'Create behavior'   - Parameter:                Var:ShowMenu   - Settings template:   [leave blank] Create Event handler • click 'Show more' > locate Event handler section > enter name "DblPress" and click 'Create' > click new Event handler to open it   - Handler type:           Binary   - Set values:               'on' Create preprocessor (from binary) • Locate the Event Preprocessor section > next to From binary > click '+' to create > click it to edit • select 'Default' > click '+' to create > click 'ActDown' to open settings   - Repeat:                    Delayed      <-- start a timer   - Repeat delay:          300             <-- timer lenght in milleseconds   - Burst count:            2                  <-- number of required button presses to proceed with output trigger   - Output trigger:       ActDown With the above, you must press button 2 times within 300ms for the action to proceed (ie. set ShowMenu to 'on') IO References In this article we explain the internal structure, of the IOReference string. This will help you, if you to find the correct modifiers to use in Configuration tab, and when using the JSON editor What is an IO Reference (aka IOref aka Parameter)? An IO Reference is a unified text reference to a data point that can be read and/or written. This data point might represent a device parameter, a variable within the Reactor, or a read-only constant. A device parameter allows users to both set and fetch its value. In contrast, a variable within Reactor can also be adjusted and retrieved, whereas a constant can only be read. Reactor employs IO References extensively to facilitate data-driven configurations. These configurations are used for a range of functions, including enabling or disabling features, displaying information, defining button colors in response to data, and identifying the next value for a parameter. In many ways, IO References are similar to URLs as encountered on websites, providing a consistent method for accessing and manipulating data. Examples Example Comment DC:bmd-atem/1/ProgramInputVideoSource/2/ This IO Reference points to the "Program Input Video Source" parameter in an ATEM switcher from Blackmagic Design, accessed through the device core labeled "bmd-atem". The segment "/1" identifies the specific ATEM switcher with ID=1 in the system, and "/2" refers to the dimension of the parameter, in this case, the Mix Effect row for the program video input. A comprehensive list of parameters for ATEM switchers can be found at: https://cloud.skaarhoj.com/coremanual/core-bmd-atem/pre. Var:MyMenu This IO Reference pertains to the nearest variable named "MyMenu" in Reactor's tree. It's a read/write reference, meaning it can be both modified and retrieved. Behavior:Const:Input This IO Reference refers to the read-only value of the constant "Input" from the behavior of a specific context (as most IO References are utilized within a Behavior context). This reference is read-only, indicating it can only be fetched but not modified. Datatypes IO References can encapsulate different types of data such as strings, integers, floating point numbers, and booleans. For booleans, the values are represented as the strings "true" or "false". In essence, every IO Reference is treated as a one-dimensional array, allowing for the possibility of holding multiple values or even none at all. For instance, a constant might contain anywhere from zero to many values, though typically it holds a single value. When interacting with Device Cores, single values are exclusively employed. Variables usually operate with single values too but may occasionally use multiple values. This multi-value support is inherently built into the IO Reference system. IO Reference characters and structure IO Reference strings shall only contain the characters [a-zA-Z0-9_,\.-:/{}] where ":" and "/" are generally used to tokenize the IO Reference Curly braces, {}, are used to nest other IO References where allowed. Encapsulation is not always necessary, but if the nested IO Reference contains the same characters as used for tokenization, it will be necessary. The safe and consistent choice would be to always encapsulate.  [] is a part of literal values when they contain multiple values in the set. IO References are generally tokenized using a combination of colons and forward slashes. Specifically, an IO Reference's basic type is always indicated by a prefix that precedes the first colon in the string. If there's no colon, it's considered a literal value (or a set thereof). The different types are outlined below. Literal values Literal values, such as "3", "true", or "page1", are utilized to assign a fixed value instead of referencing another data source. Frequently, it may be desirable to define literal values as sets with multiple elements. In such cases, these values are enclosed in brackets, with individual values separated by commas, for example, "[red, green, blue]". Format: Any IO Reference without a colon is categorized as a literal value. Example Comment 3 The value "3" red The value "red" [red,blue] The values "red" and "blue" (two values in the array) [] No values (empty array) (empty string) No values (empty array) [''] [""] A single empty string Strings ("String") The "String:" prefix allows complex strings to be treated as literal values, particularly when they contain special characters. Without this prefix, these strings might otherwise be parsed due to the presence of such characters. Format: String:[Value] Example Comment String:{"Outputs": [{"ID": \d1,"Action": \d2}]} Strings such as JSON code must be embedded in an IO Reference using the "String:" prefix. Without this prefix, the string would be parsed, and the presence of colons could lead to unexpected outcomes. Variables ("Var") Variables in Reactor are used to hold data that can be manipulated. You can think of them as containers that store information. Their value can be changed, and they can be reused many times at different places in a configuration. The purpose of a variable is never predefined by Reactor but depends on how it's being used in the configuration.  Usually variable either spans an integer range (eg. from -10 to 10) or has specific options with names (eg. "page1", "page2", "page3" with the names "Home", "Setup", "Color") Format: Var:[Variable reference]:[Mod 1]:[Mod 2]:...:[Mod n] Examples Examples Comment Var:Shift Value of variable "Shift" Var:Shift:Current:Name Label of variable "Shift" Modifiers See the General Modifiers table Device Cores ("DC") IO References that are prefixed with "DC:" correspond to parameters in devices that are managed by device cores. These are the devices that have been added to Reactors current project for control purposes and/or to serve as data sources. Format: DC:[Seg 0]/[Seg 1]/[Seg 2]/[Seg 3]/[Seg 4]/[Seg 5]/:[Mod 1]:[Mod 2]:...:[Mod n] The string following "DC:" is divided by "/", with each resulting segment signifying the following: Segment 0: Device Core reference, eg "bmd-atem" Segment 1: Device Index, aka Device ID (IO Reference, optionally encapsulated in {}) Segment 2: Parameter reference Segment 3-5: Parameter Dimensions (0-3 , depends on parameter). (IO References, optionally encapsulated in {}) Modifiers See the General Modifiers table Examples Examples Comment DC:bmd-atem/4/AuxSource/2/ Reference to the current source on AUX Channel 2 on the ATEM switcher with Device ID 4. DC:bmd-atem/4/AuxSource/2/:Current:Name Name of the current source on AUX Channel 2 on the ATEM switcher with Device ID 4. DC:bmd-atem/Var:DeviceIndex/AuxSource/Var:AuxChannel/ DC:bmd-atem/{Var:DeviceIndex}/AuxSource/{Var:AuxChannel}/ Reference to AUX sources on the ATEM switcher with Device ID found in "Var:DeviceIndex" (idiomatic) and in the AUX Channel denoted by "Var:AuxChannel". Both examples provided are identical. In this scenario, encapsulating the nested IO Reference is not required, as there are no forward slashes included that might interfere with the parsing process. DC:dreamchip-cam/Var:DeviceIndex/PlayAtSpeed/{DC:dreamchip-cam/Var:DeviceIndex/CurrentBuffer/}/:Current:Name This complex IO Reference extracts the formatted "Name" (suitable for display) of the play speed in a Dream Chip camera. The camera's ID is derived from "Var:DeviceIndex", and the buffer is retrieved from another IO Reference pointing to the CurrentBuffer parameter from the same camera. This is an example of an embedded IO Reference, encapsulated in curly braces, required because the reference itself contains the character ("/") used for tokenization. Constant Sets ("Const") Reactor's constants are unchangeable values embedded in the configuration code, used for various purposes within the configuration. Unlike variables, constants can only be modified by altering the configuration. Constant Sets in Reactor are essentially tables of related constants. Each row forms a set of constant values, and columns represent different constants within these sets. They can be utilized for a variety of functions, from setting switcher inputs to configuring a PTZ controller's camera selector. Even simpler uses might include single-row constant sets for configuring specific aspects of a configuration, such as the number of cameras on a page. Format: Const:[Seg 0]/[Seg 1]/[Seg 2]/ The string following "Const:" is divided by "/", with each resulting segment signifying the following: Segment 0: Constant Set reference. The name of the closest constant set in the layer tree. Segment 1: Constant Set row index (IO Reference, optionally encapsulated in {}) Segment 2: Constant Name Modifiers See the General Modifiers table Examples Examples Comment Const:CameraSelector/0/CameraName Returns the value of the constant called "CameraName" in the first row (index 0) of the constant set "CameraSelector". Const:CameraSelector/{Var:CameraIndex:Current:Offset:-1}/CameraName This retrieves the value of the constant "CameraName" from the row in the "CameraSelector" constant set, as indicated by the "CameraIndex" variable. If the "CameraIndex" variable is set to 1, it refers to the first row (index 0). This occurs because the variable's value is reduced by one when inserted as the value for Segment 1. (:Current:Offset:-1) Behaviors ("Behavior") Behaviors dictate how a controller's hardware component typically interacts, including the actions it can execute on devices and other IO References, as well as how system feedback is displayed on its screens and LEDs. IO References of the "Behavior" type facilitates reading and writing of the behavior aspects, which are often the context of an IO Reference. Format: Behavior:[Seg 0]/[Seg 0a]:[Seg 1]:...[Seg n] The string following "Behavior:" is divided by ":", with each resulting segment signifying the following: Segment 0: The sub-type. This string value is further divided by "/" to provide a further refinement of the sub-type. Segment 1-n: Depends on the sub-type. See the overview below Basic Info This is basic info about the behavior: IO Reference I/O Comment Behavior:Path Read Returns the "path" of the behavior in the layer tree. Eg. "0/3/0/A3" Behavior:Name Read Returns the name of the behavior (value of the Name field) Behavior:Id Read Returns the behaviors main mapping to a panel, for example "_p4.32" (panel Id 4, Hardware Component (HWC) 32) Behavior:Panels Read An array of all Panel Ids this behavior is mapped to Access to the Behaviors IO Reference ("IOReference" sub-type) Behaviors usually modify a single parameter - a single IO Reference - in a device or within Reactor. To simplify this, behaviors have a master IO Reference field set by the user, which can be referred to throughout the behavior's configuration as "Behavior:IOReference". For example, if the behavior's IO Reference is set as "DC:bmd-atem/4/AuxSource/2/", "Behavior:IOReference" can be used as a substitute anywhere within the behavior where IO References are applicable. If the label of a value is needed instead, "Behavior:IOReference:Current:Name" serves the same function as "DC:bmd-atem/4/AuxSource/2/:Current:Name". With "Behavior:IOReference", the "main" IO Reference is derived from the master IO Reference, while modifiers in segments 1-n are taken from "Behavior:IOReference". Examples For these example, assume that the IO Reference of the behavior is "Var:Menu" with the name "Page" and with the current value being "page1" with the label "Background" Examples Comment Behavior:IOReference Behavior:IOReference:Current Returns the current value of Var:Menu ("page1") Behavior:IOReference:Name Returns what corresponds to "Var:Menu:Name" which is the name of the variable ("Page") Behavior:IOReference:Current:Name Returns what corresponds to "Var:Menu:Current:Name" which is the label of the current value ("Background") Script State ("Script" sub-type) These are read and write registers of a behaviors script. IO Reference I/O Comment Behavior:Script Read Returns "true" if there is an event script defined. Behavior:Script:IsRunning Read Returns "true" if there is a script and if script is running. Behavior:Script:Stop Write Trigger action to stop a script if it's running. Last Event ("LastEvent" sub-type) These are read registers of the last event received by the behavior. IO Reference I/O Comment Behavior:LastEvent:Type Read Type of last event. Options: Binary, Pulsed, Analog, Speed Behavior:LastEvent:TimeToNow:[Limit] Read This returns the time in milliseconds that has passed since the last event. Although setting a limit is optional, it's recommended to set it at or above the comparison value for performance efficiency. If a limit is set, it designates the maximum returned value. Examples: Behavior:LastEvent:TimeToNow < 500 Behavior:LastEvent:TimeToNow:500 < 500 (better) Behavior:LastEvent/Binary:Pressed Read Returns whether the last binary trigger was pressed (ActDown) or not (ActUp). Options: true, false Behavior:LastEvent/Binary:Edge Read Returns which edge the last binary trigger sent. Options: NoEdge, Top, Left, Bottom, Right, Encoder Behavior:LastEvent/Pulsed:Direction Read Returns the direction of the last pulsed trigger received Options: Up, Down Behavior:LastEvent/Pulsed:Value Read Returns the value of the last pulsed trigger Behavior:LastEvent/Analog:Value Read Returns the value of the last analog trigger (0 to 1000) Behavior:LastEvent/Speed:Value Read Returns the value of the last intensity trigger (-500 to 500) Event Handlers ("Event" sub-type) These registers are associated with the various event handlers that can be established for a behavior. The term "eventHandlerKey" in the subsequent table refers to the specific event handler's name being referred to. IO Reference I/O Comment Behavior:Events/[eventHandlerKey]:TimeToNow:[Limit] Read This returns the time in milliseconds that has passed since the last accepted trigger for this event handler. Although setting a limit is optional, it's recommended to set it at or above the comparison value for performance efficiency. If a limit is set, it designates the maximum returned value.   Example:  Behavior:Events/myTrigger:TimeToNow < 500 Behavior:Events/myTrigger:TimeToNow:500 < 500 (better) Behavior:Events/[eventHandlerKey]:SequenceStep Read The current sequence step being executed Behavior Constants ("Const" sub-type) Registers related to accessing constants in a behavior IO Reference I/O Comment Behavior:Const:[constant] Read Value of constant (one or more values) Behavior:Const:[constant]:Name Read Provides the name of the constant. Behavior:Const:[constant]:Label Read Offers the label of the constant value(s). If no labels are defined, it will return the values. Behavior:Const:[constant]:ASCIIOnly Read (Refer to detailed description below.) Behavior:Const:[constant]:Index:[index number] Read Provides the value at a specific index. Index numbers are integers, starting from zero. Negative numbers like -1 point to the last element, -2 to the second last, and so on. The special index value "Last" equates to "-1" (the last element). Behavior:Const:[constant]:Index:[index number]:Exists Read Returns true or false, depending on whether the constant value exists for this index Behavior:Const:[constant]:Index:[index number]:Label Read Returns the value label if it differs from an empty string; otherwise, it returns the value itself. Behavior:Const:[constant]:Offset:[int] Read Returns the value with [int] added to it. For example, "Behavior:Const:[constant]:Offset:1" increases values by one. Sub Behaviors ("Sub" sub-type) (Future - may not be in Reactor yet or ever...) Registers related to the execution of a sequence of sub behaviors, what may be referred to as a Multi Behavior or Multi Action. IO Reference I/O Comment Behavior:Sub:TotalSteps Read Total number of steps in a multi-behavior sequence. Behavior:Sub:TotalTime Read Total time (in milliseconds) required for the multi-behavior sequence. Behavior:Sub:CurrentStep Read Current execution step in the multi-behavior sequence. Zero indicates no execution, while the first step is denoted as 1. Behavior:Sub:CurrentDelay Read Delay duration for the current step. Behavior:Sub:CurrentName Read Name of the behavior being executed in the current step. Presets ("Preset") String after "Preset:” is split by “/“ and the parts will mean this: - Index 0: Preset reference (like variables) - Index 1: Command - Index 2: Preset number (IOreference, (Optionally encapsulated in {})) - Index 3: Device Index (IOreference, (Optionally encapsulated in {})) - Index 4-…: Dimensions (After the last trailing slash, if “:” is found the following will be used as modifiers) Flags ("Flag") (TODO) System System:Lock System:IPAddress Reactor Reactor:ProjectTitle - Current Project title Reactor:ConfigTitle - Current name of root layer Reactor:CurrentProject - Allows to trigger a change of projects using SetValue Reactor:RestartEngine - Perform a restart of reactors engine, like switching projects Reactor:UptimeFormatted - Uptime since Reactor was started Reactor:Panels:Connected - Number of connected panels  Reactor:Panels:Warnings - Number of panels with warnings Reactor:Panels:Unconnected - Number of unconnected panels (errors) Reactor:Panels:LastEvent - Last event as a string, eg. "Down (T)" if a four way buttons top edge was pressed down. Reactor:Panels:LastEventSource - The HWC source of last event, including panel ID, on form "P[panel id]#[HWC id]", for example "P1#43" Reactor:Devices:Connected - Number of connected devices Reactor:Devices:Warnings - Number of devices with warnings Reactor:Devices:Unconnected - Number of unconnected devices (errors) Reactor:Devices/[idx]:Name - Name of devices. idx is just an index. Reactor:Warnings - Total number of warnings in Reactor Reactor:Errors - Total number of errors in Reactor Panels String after Panels: is split by “/“ and the parts will mean this: Index 0: Target of ID: "Canvas", "Panel", "CanvasOfPanel" Canvas/[id]/ - forcing a given canvas id. Normally not used Panel/[id]/ - forcing a particular panel only. May be used when canvas is not used to represent a coherent panel CanvasOfPanel/[id]/ - normally the one used because Reactor knows the panel ID, but usually desires to target the canvas of the panel (Usual source of id is: Behavior:Panels) Index 1: ID of target (IOreference, (Optionally encapsulated in {})) Index 2: Parameter SleepTime, minutes DimTime, minutes DisplayBrightness, 0-8 LEDBrightness, 0-8 GlobalBrightness, 0-8 Sleep (binary: "on", "off") - Is reset by the panel when it wakes up again. It's probed once a second, so it can feel a little like a "hold down" function ResetSleepTimer (trigger) Name (read only) Model (read only) General Modifiers General modifiers are used by certain IO References due to their shared data source framework. However, not all IO Reference types support or need these modifiers. The table below indicates which primary types - Device Cores (DC), Variables (Var), and Constants (Const) - support these general modifiers. Modifier Comment DC Var Const (no modifiers) Without any modifiers, returns the values. Yes Yes Yes Name Returns the name of the reference, such as a parameter or variable name. Yes Yes Yes Default Returns the default values of the IO reference. Yes Yes N/A Default:Name Returns the names/labels of the default values of the IO reference. Yes Yes N/A All Returns all option values for a parameter. For an integer range with less than 100 values, it will calculate and return that as an option list. (June 23) Yes Yes No Exists Returns "true" if the reference exists. Yes Yes Yes Mutable Returns "true" if the reference can be changed. Yes Yes Yes Assumed Returns "true" if a parameter is assumed in the core. Yes N/A N/A FineSteps Returns the recommended fine steps for a Device Core; otherwise, returns 1. Yes N/A N/A CoarseSteps Returns the recommended coarse steps for a Device Core; otherwise, returns 10. Yes N/A N/A ASCIIOnly Returns true if only ASCII characters are found in the return value. Yes Yes Yes Current Current Returns the current values. Same as not using "Current" modifier. Yes Yes Yes Current:Name Returns the names of the current values. Yes Yes Yes Current:Normalized Returns the normalized value in the range of 0-1000. Yes Yes No Current:NormalizedInverted Returns the normalized value in the inverted range of 1000-0. Yes Yes No Current:Percent Returns the normalized value in the range of 0-100. Yes Yes No Current:Remap:[low int]:[high int]:[optional integer divisor, default to 1] Returns the value normalized to the range [low int]-[high int], divided by the divisor. Yes Yes No Current:Index Returns the index of the current value from the option list, starting with zero. Works only for parameters and variables with Option Lists, not ranges. Yes Yes No Current:Count Returns the number of values in the IO reference array. Yes Yes Yes Current:Join:Token Returns an IO reference with a single value, a concatenation of all values it had, separated by the Token string. Yes Yes Yes Current:Offset:[int] Returns the value with [int] added to it. Yes Yes Yes Current:BufferTimeToNow Returns the time in milliseconds from the buffered value time out to the current time. Yes N/A N/A Confirm:[int] Changes the value only locally, waiting to be confirmed for [int] milliseconds. Yes N/A N/A Wait:[int] Similar to Confirm, but the change is automatically accepted after the time expires. Yes N/A N/A Index (some changes March 25) Index:[integer comma list incl. "Last" keyword / HWC behavior constant ref] Returns option value with index [int] or if it's a string, the constant of the HWC Behavior. Yes Yes No Index:[integer comma list incl. "Last" keyword / HWC behavior constant ref]:Name Returns the name of the option value with index [int]. Yes Yes No Index:[integer comma list incl. "Last" keyword/HWC behavior constant ref]:Exists Returns true if the index exists. Yes Yes No Index:[integer comma list incl. "Last" keyword / HWC behavior constant ref]:Raw Returns the raw index value of the option value with index [int]. Yes Yes No DimensionName (some changes January 24) DimensionName:[int]:[optional int] Returns the name of the specified diemsion (0 index) if 2 ints are specified returns the name of the specified Element Yes No No Index (some changes March 24) ID:[integer / HWC behavior constant ref] Return an option by its value ID (different than index on some cores) also supports :Name and :Exists Yes Yes No Page Layers This guide show you how to make Page Layers. Say you wish control 8 parameters on a camera, but only have 4 encoders on your controller. In this case you make two 'Pages' - or layers - and assign different commands in each 'Page'. You then assign a button to toggle between the pages. Pan/Tilt/Zoom Rocker Sensitivity and Curve Setup For all our default PTZ configs we have pre-defined up to 4 variables for you to use to tune the feeling of the joystick and zoom rocker to fit the feeling between multiple cameras to your liking. Before in time, you would find one param that would control all 3 axes on the joystick (L/R, U/D & Rotate) as well as the Zoom rocker. This has now been split out by default to these for options that can be set pr. camera and that will remember their values between reboots, so you don't have to remember them. The for new params are: PT_Speed -> Controls the scale on U/D, L/R Joystick movements (Max Speed) PT_Curve -> Controls the curve for U/D, L/R Joystick movements (Expo) Z_Speed -> Controls the scale of the Zoom rocker and Joystick rotation (Max Speed) Z_Curve -> Controls the curve of the Zoom rocker and Joystick rotation (Expo) The two-speed variables are direct replacements for the old JoystickSens variable and they go just like the old variable from 1-10, with the default being 10. This correlates to a min setting of 10% of the max speed and up to the full 100% speed. So in short 1 = 10%, 2 = 20%, 5 = 50% and 10 = 100% speed on a fully deflected joystick/rocker. Then we have the Two new curve variables that actually replace the old JoystickExpo variable. The chances of you having seen that variable in Reactor are very slim as it's not been part of the configs before, even though it has been set as an option in all configs. The new PT_Curve and Z_Curve replace this old variable with a name that makes more sense as well. What we control here is the amount of Expo we want on the joystick. These variables go from 0-25 giving us a wide selection from 0 = linear to 25 = 75% expo. The reason we have limited it by default to a max of 75% is that otherwise it gets so sharp that it's basically just an on/off behaviour. Adjusting this variable will give you more resolution in the middle of the joystick without losing max speed at the ends. combine this with the speed above to get the best feeling for you. In order to visualise what the curve variables do, please take a look at this image, if you have the curve set to 0 then your joystick will follow the green curve, then as you move up towards 25 you will get closer and closer to the red curve. Thus giving you more resolution in the middle. Image showing these four variables mapped to the encoders in a config: Example of the P/T Speed and P/T Curve variable setup (Note that they also have "Persistent" = true): Here is the full definition of the 4 variables in their full JSON setup: This snippet is taken from the file: SKAARHOJ.Devices.Canon-XC.ProClass.Basic "PT_Curve": { "Name": "P/T Curve", "Description": "Pan/Tilt Joystick Curve (0 = Linear, 25 = Exponential)", "MinMaxCenterValue": [ 0, 25 ], "Default": [ "0" ], "Persistent": true }, "PT_Speed": { "Name": "P/T Speed", "Description": "Pan/Tilt Joystick Max Speed (1 = 10%, 10 = 100%)", "MinMaxCenterValue": [ 1, 10 ], "Default": [ "10" ], "Persistent": true }, "Z_Curve": { "Name": "Z Curve", "Description": "Zoom Rocker Curve (0 = Linear, 25 = Exponential)", "MinMaxCenterValue": [ 0, 25 ], "Default": [ "0" ], "Persistent": true }, "Z_Speed": { "Name": "Z Speed", "Description": "Zoom Rocker Max Speed (1 = 10%, 10 = 100%)", "MinMaxCenterValue": [ 1, 10 ], "Default": [ "10" ], "Persistent": true } One thing you will notice when you go to look at a PTZ behaviour is that you cant find where these variables are actually used, as for the moment this is not visible in the UI, so, for now, you will have to add or change this via the JSON editor. If we go take a look at where this would become visible at some point in the future, then that would be here inside the blue box, inside a part called "ValueMapping": If we then Click the "Show JSON" button then, we will see the full config for the Pan joystick setup for Canon XC: { "ParentID": "SKAARHOJ:SpeedControl", "Name": "Pan", "EventHandlers": { "value": { "ValueMapping": { "ScalingDivisor": 10, "ScalingIOReference": { "Raw": "Var:PT_Speed" }, "ExpoDivisor": 30, "ExpoIOReference": { "Raw": "Var:PT_Curve" } }, "InvertCondition": "Var:InvertPan == true" } }, "IOReference": { "Raw": "DC:canon-xc/Var:DeviceIndex/panSpeed" } } What we look for is again the "ValueMapping" field where we can see a ScallingDivisor so that's how we get the 10% steps on the PT_Speed variable 100%/10 = 10% step size Then the same for the Curve, we have an ExpoDivisor that is set to 30 so 100%/30 = 3% expo pr step and by having our PT_Curve variable limited to only 25 limits the max setting on the controller to 25 * 3% = 75% expo. Thus preventing the curve from becoming unusably sharp. Don't hesitate to play around with these options to get the tunning fully right to your liking. Presets Functions Presets - Generally speaking, presets store and recall parameter values and triggers in Reactor - The values to be stored can be predefined in a preset profile or they can be completely arbitrary (recording what changes) - A preset profile can define values across device cores, device IDs and parameter dimensions - or they can be narrowed down - With narrow preset profiles, there are inherent opportunities to use presets to copy/paste sets of values between devices and dimensions - Reactor supports an infinite amount of preset profiles (re-)defined anywhere in the layer tree - Storage and recall of a preset can work either instantaneously or played back over time - When recorded and played back over time, values are organized in multiple segments. Each segment is essentially a time line and at the end of a timeline, playback will continue to the next segment either automatically or by user invocation (waiting for user input). - Playback order of segments can be shuffled and waiting time between segments can be randomized. Playback for a timeline can be looped - Recording and playback allows cancellation which will restore the state before recording or playback. - Support for ganged recording and playback of multiple preset numbers, device ids, and dimensions (fairly exotic, honestly) - Prepared for parameter animation (must be implemented in devicecollection) Commands: NextSegment (trigger) During recording, this will end the segment and start a new one assuming there has been values added. During playback, this will skip to next segment AddUserWait (trigger) Like NextSegment, but when used during recording it will insert a User Wait at the end and cap the segment length to the last added value. Play (trigger) Starts playback PlayToggle (trigger) Starts or stops playback PlayToggleNext (trigger) Starts or stops playback, except if waiting in which case it will PlaySkip (trigger) Starts playback, and skips to next segment if already playing (which may result in stopping altogether) PlayPause Starts playback and toggles pause if already playing PlayPauseNext Starts playback and toggles pause if already playing, unless at a user wait in which case it skips to next. Record (trigger) Starts recording RecordToggle (trigger) Starts or stops recording RecordNewSegment (trigger) Starts recording, and creates new segment on second press RecordAddUserWait (trigger) Starts recording, and creates new segment with user wait on second press Stop (trigger) Stops recording or playing Delete (trigger) Deletes preset if it is not recording or playing Cancel (trigger) If recording, it will stop recording, recall the values from before recording and reinstate the previous content for the preset If playing, it will stop playing and recall the values from before playing. Recall (trigger) Instantly recalls the final values of the first segment of a preset. RecallStateFromBeforeRecording (trigger) Recalls the initially stored state of a given preset DurationRandomExtension (Integer value, ms) Sets the Random extension value for a given recorded preset. Loop (bool) Enable looping for a preset Shuffle (bool) Enable shuffle for a preset Separating Zoom Sensitivity from Pan and Tilt Sensitivity By default Pan, Tilt, and Zoom are all controlled by the setting Joystick Sensitivity. This is not ideal for some users who would like Zoom to be controllable at its own sensitivity. To separate this out it is necessary to dive into the configuration and the json editor, but don't worry, it isn't too difficult.  This example will be done on the PTZ Extreme but can be applied to all configurations.  Joystick sensitivity is not set for the whole controller, but per camera configuration based on device core. The configuration in this instance is different from the panel mapping. There will be a different configuration added for each device core used.  Different brands might use the same device core, example: BirdDog, NewTek, Marshall all use the Visca device core.  This example was made on a Blue Pill running the following version: SkaarOS  v1.0 Reactor v1.0.5-pre11 System Manager v1.0.1-pre1 Hardware Manager v1.0.2-pre3 Using earlier versions can affect the look of the interface causing it to appear different. We recommend using at least the indicated versions or newer.  1. Determine the number of PTZ configuration in your set up. In the set up example there are 3 different device cores used as indicated in the Devices section. All devices will be grouped by the used core in this section. 2. Navigate to the Configuration Tab. 3. Open the Tree to navigate to the Device Core Configurations. The tree expands from the bottom up, like a tree starting at the roots. (This is shown in a collapsed view) 4. Navigate to the Joystick Sensitivity variable for a Device Core Configuration.  5. Open the Tree Layer with the Joystick Sensitivity Variable. When selected the layer will change color and you will see it open in the Inspector on the right side of the screen.  6. Create a new variable called ZoomSens . This could be named anything, but it is best to use something that will be easy to remember and type later.  7. Open the ZoomSens variable to edit the details. Use the same details as the JoystickSens variable.  The name is however you would like it to display on the controller.  The type is Range The Min is 1 The Max is 10 (The scale should not exceed 10) Default value is 10 8. Go back to the Tree and select the JoyRot box in the same layer you are working in. This will open the joystick rotation (zoom) in the inspector.  9. DON'T PANIC 10. Open Show JSON for the JoyROT component.  11. Select Format 12. Select Show Parent Behavior 13. In the Window the pops up, select Format again. This will expand the data to see the whole JSON text.  14. Scroll down to the Joystick Sensitivity commands and copy the whole command set as seen below, every part is essential: "ValueMapping": { "ScalingDivisor": 10, "ScalingIOReference": { "Raw": "Var:JoystickSens" } }, 15. Close this window.  16. In the Inspector, find the BehaviorSetValues text in the JSON editor. Click after the comma and hit enter. 17. Paste in the text you copied from the Parent Behavior pop up.  18. Find the text in the pasted in command that say: "Var:JoystickSens" 19. Edit this text to say: "Var:ZoomSens" then press Save     If you did not name the variable you created earlier ZoomSens, you will type the name you did create here after the : 20. If there is a Zoom Rocker, you will need to do steps 18 + 19 for that hardware component as well.  21. The variable and command have now both been set up. The last step is to assign control of the variable to an encoder.  22. Go back to the tree view and navigate deeper in that device core configuration to the Camera Adjustment layer and open it. This will open the tree to see the different page layers of settings for the camera. They should be named based on the what they contain. 23. Navigate to and select the System layer, this is where the Joystick Sensitivity control is usually located but may also be on the Home page. This should be the same across all device core configurations. The number and names of layers will be different for each device core configuration. On each of them should be a Home and a System page. 24. Find one of the encoders that does not currently have something assigned to it, you can see this by going to that page on your physical panel and seeing where a good place to put the Zoom Sensitivity Control would be. In this example I am using Encoder 4 (from right to left on the physical panel). 25. In the Inspector select Edit next to the Parameter. 26. In the pop up window, select Variable.  27. In the drop down next to Variable Name, select ZoomSens and press Submit. 28. After submitting the Behavior should either suggest or auto-assign Step Change. This is the correct behavior.  29. You should now be able to control Zoom Sensitivity separately from Pan and Tilt.  30. If you are combining cameras that use multiple device cores on your panel, you will need to go in and do the same in each device core configuration. In the example below there are 3 different configurations and we made the changes to the Canon configuration.  Setting Blue Pill Panel into Raw Panel Mode To set up multiple Blue Pill Inside units to work in a group together, it is necessary to change some settings in the connected panels from the Packages page to allow them to be controlled by the main Blue Pill panel.  The connected unit needs the following changes: Stop the Reactor package. Disable Auto-Start in the Reactor package details. Enable Listen on Port in Hardware Manager package details and disable Listen On Socket. 4. Set the Protocol Mode needed for the project in the Hardware Manager package details.  5. Set additional settings as needed/desired in the Hardware Manager package.  6. Save and restart the Hardware Manager package.  Understanding Flags Inside reactor we have support for setting flags true or false. These flags are based around how TSL works to some extent and provides a list of true/false states with 4 "bits" per index. Flags in reactor consists of 4 bits per index, that can be set true or false individually, each of these flag bits can be used for different things but you will mainly see them used for providing tally and routing support in configs as they are a great way to store the intermediate state that comes in from a GPI pin or from a device like an ATEM switcher. Then use the stored state to send the Tally to the cameras The 4 "colors/bits" main use cases in the default configs for each color are: Red Mainly used for Program Tally Green Mainly used for Preview Tally  Blue Not widely used in default configs yet White Mainly used for storing what camera to Route on a mixer/videohub These are not limited to this use case, but this is the current convention we use internally.  Define flag groups and sync them up When you use flags you will need to add them to your project as "Flaggroups" with a name and an Index. One Important note is that ANY config that you have loaded in the project that uses the same Group ID will be synced even if you have different names for them. This means that you can "sync" up tally or routing between 2 or more controllers running in the same reactor just by changing the flaggroup ID for "Tally" to be the same on both panels. This is also a very simple way to add a GPIO or Tally box to an existing system/setup. This also brings the problem of if you happen to set your Routing group to the same ID as your Tally group, funny stuff might happen. But since we are using different Bit's for it, you should be fine and not risk having "crosstalk" between them. For simplicity we have made sure to make them on different group ID's in all default configs. Here is an example from a PTZ Extreme about how these groups are defined on Index 10 and 11: How to set a flag The simplest way to set a flag is to use the normal "SKAARHOJ:HoldDown" behavior on a button/GPI pin or in a virtual trigger where you might use the tally status reported by your switcher. When setting flags we are looking at an IORefrence structure that looks like this: Flag:Tally/Red/Var:TallyIndex/ So to break it down we have: Flag:[Group Name]/[Color]/[Index] Often you will see us use a variable for the index. In most cases we use it together with a constant set where the user can fill in what routing input or tally index it should follow. For a mixer setup the tally index would correlate with what input number is active on the mixer. Some examples of this in use Here is a couple of examples of how these setups look, they are posted here in json format as it's the simplest way to get a condensed look at the setup: From an RCP using the GPI input to set the red tally flag on the selected camera: { "ParentID": "SKAARHOJ:HoldDown", "IOReference": { "Raw": "Flag:Tally/Red/Var:TallyIndex" } } And here for the preview button (same as pressing the joystick) on the same RCP, that sets a Routing Flag instead: { "ParentID": "SKAARHOJ:HoldDown", "IOReference": { "Raw": "Flag:RoutingSource/White/Var:RouteIndex" }, "FeedbackDefault": { "DisplayText": { "Title": "Preview" } } } A more extreme setup of using a Virtual Trigger for setting the tally flags with tally status from an ATEM mixer: This is the file you load when you select a tally device in the constant set: Inside it has two Virtual Triggers that set the Program and Preview Tally and they do the same thing where they read the state of the tally parameter on the ATEM and store it into the flags on either the color red or green, depending on if it's the program or preview tigger. Full code for the ATEM Tally snippet: { "Name": "ATEM - Global Tally By Source Tally Flags", "MetaData": { "DeviceCoresInside": { "bmd-atem": [] }, "Priority": 150 }, "VirtualTriggers": [ { "Name": "Set Program Tally", "ConstantSetReference": "CameraSelector", "Mode": "Binary", "BinaryActiveIf": "DC:bmd-atem/Var:TallyDeviceIndex/TallyBySourceTallyFlags/Behavior:Const:TallyIndex/1 == true", "AnalogIOref": {}, "HoldGroupFinalValue": {}, "Behavior": { "ParentID": "SKAARHOJ:HoldDown", "IOReference": { "Raw": "Flag:Tally/Red/Behavior:Const:TallyIndex" } } }, { "Name": "Set Preview Tally", "ConstantSetReference": "CameraSelector", "Mode": "Binary", "BinaryActiveIf": "DC:bmd-atem/Var:TallyDeviceIndex/TallyBySourceTallyFlags/Behavior:Const:TallyIndex/2 == true", "AnalogIOref": {}, "HoldGroupFinalValue": {}, "Behavior": { "ParentID": "SKAARHOJ:HoldDown", "IOReference": { "Raw": "Flag:Tally/Green/Behavior:Const:TallyIndex" } } } ] } How to read a flag Reading a flag is very similar to setting a flag, but now we look at the feedback value of it instead and compare it against if it's true or false. The simplest way to read a flag is to use the "SKAARHOJ:Output" behavior on a LED or GPO pin or in a virtual trigger where you might use the tally status reported by your switcher. Some examples of this in use Here is a couple of examples of how these setups look, they are posted here in json format as it's the simplest way to get a condensed look at the setup: First the simplest way of reading it is for a GPO pin on an RCP for providing routing support externally: { "ParentID": "SKAARHOJ:Output", "IOReference": { "Raw": "Flag:RoutingSource/White/Var:RouteIndex" } } Another use case for a Tally LED on a Blue Pill or RCP where you will see a setup like this, where it reads both the Red and Green Tally bit: { "Name": "Tally LED - Selected", "Description": "Show tally for selected tally index (in TallyIndex variable, using Tally Flag group)", "IOReference": {}, "FeedbackDefault": { "Intensity": "Dimmed" }, "FeedbackConditional": { "20": { "ActiveIf": "Flag:Tally/Green/Var:TallyIndex == true", "Color": { "ColorCode": "GREEN" }, "Intensity": "On" }, "30": { "ActiveIf": "Flag:Tally/Red/Var:TallyIndex == true", "Color": { "ColorCode": "RED" }, "Intensity": "On" } } } The last two main use cases that we will look at is reading a flag and then sending some command to a BMD Video hub for providing routing control, this is using virtual triggers: This is the file you load when you select a routing device in the constant set: Inside it has virtual triggers that read the stored routing flag and sends the routing selected and sends it to the parameter on the Videohub. { "Name": "Basic V-Trigger", "ActiveIf": "Var:UseHoldGroup:Current == false", "VirtualTriggers": [ { "Name": "Set Routing Source", "ConstantSetReference": "CameraSelector", "Mode": "Binary", "BinaryActiveIf": "Flag:RoutingSource/White/Behavior:Const:RouteIndex == true", "AnalogIOref": {}, "HoldGroupFinalValue": {}, "Behavior": { "ParentID": "SKAARHOJ:SetValue", "IOReference": { "Raw": "DC:bmd-videohub/Var:RoutingDeviceIndex/routeInputToOutput/Var:RoutingBusSelect" }, "EventHandlers": { "trigger": { "BinarySetValues": { "Raw": "Behavior:Const:RouteIndex" }, "IOReference": {} } } } } ] } The last main use case to show of here is doing the same as for routing but for forwarding the tally states directly to any camera connected. We will look at the setup for the Canon PTZ cameras in this example: This example is taken from an ETH-GPIO Link, but it's the exact same code used on all PTZ controllers. Inside the Camera Selector you will have a field for the "Tally Forward Config", this should auto fill for you and look something like this:  This will then load up like this inside the config, in something that should look familiar by now, as it's the same setup as for the tally and routing setups mentioned above. The big difference here is that this setup will very a lot depending on how the cameras handle tally, but one thing they will all have in common is that they have a trigger for disabling tally altogether, and that they all use the same flags, red and green for the tally feedback. On the Canon PTZ's you will see a total of 4 Virtual triggers. We will only look at the "Set Red" trigger. { "Name": "Set Red Tally", "ActiveIf": "Var:TallyToCam == true", "Mode": "Binary", "BinaryActiveIf": "Flag:Tally/Red/Var:TallyIndex == true", "AnalogIOref": {}, "HoldGroupFinalValue": {}, "Behavior": { "ParentID": "SKAARHOJ:SetValue", "Constants": { "MatchValue": { "Values": [ "1" ] } }, "IOReference": { "Raw": "DC:canon-xc/Var:DeviceIndex/f.tally.mode" } } } As seen above, it's basically the same thing the difference is only that here we have a "MatchValue" to select that it's the program we want to set on the Canon PTZ, if we want to swap the colors on the PTZ, then it's just a matter of changing this from a 1 to a number 2 and the reverse on the preview trigger. This is because of the options available on the camera, to look more at these option please refer to the parameter list for the Canon-XC core. Here is the full JSON for this layer with all 4 virtual triggers, in their current form: { "Name": "Canon XC - Tally", "MetaData": { "DeviceCoresInside": { "canon-xc": [] }, "Priority": 100 }, "VirtualTriggers": [ { "Name": "Disable Tally To Cam", "Mode": "Binary", "BinaryActiveIf": "Var:TallyToCam == false", "AnalogIOref": {}, "HoldGroupFinalValue": {}, "Behavior": { "ParentID": "SKAARHOJ:SetValue", "Constants": { "MatchValue": { "Values": [ "0" ] } }, "IOReference": { "Raw": "DC:canon-xc/Var:DeviceIndex/f.tally" } } }, { "Name": "Toggle Tally lamp", "ActiveIf": "Var:TallyToCam == true", "Mode": "Binary", "BinaryActiveIf": "Flag:Tally/Red/Var:TallyIndex == true || Flag:Tally/Green/Var:TallyIndex == true", "AnalogIOref": {}, "HoldGroupFinalValue": {}, "Behavior": { "ParentID": "SKAARHOJ:HoldDown", "IOReference": { "Raw": "DC:canon-xc/Var:DeviceIndex/f.tally" } } }, { "Name": "Set Red Tally", "ActiveIf": "Var:TallyToCam == true", "Mode": "Binary", "BinaryActiveIf": "Flag:Tally/Red/Var:TallyIndex == true", "AnalogIOref": {}, "HoldGroupFinalValue": {}, "Behavior": { "ParentID": "SKAARHOJ:SetValue", "Constants": { "MatchValue": { "Values": [ "1" ] } }, "IOReference": { "Raw": "DC:canon-xc/Var:DeviceIndex/f.tally.mode" } } }, { "Name": "Set Green Tally", "ActiveIf": "Var:TallyToCam == true", "Mode": "Binary", "BinaryActiveIf": "Flag:Tally/Green/Var:TallyIndex == true && Flag:Tally/Red/Var:TallyIndex == false", "AnalogIOref": {}, "HoldGroupFinalValue": {}, "Behavior": { "ParentID": "SKAARHOJ:SetValue", "Constants": { "MatchValue": { "Values": [ "0" ] } }, "IOReference": { "Raw": "DC:canon-xc/Var:DeviceIndex/f.tally.mode" } } } ] } Flags used in Routing Triggers Routing Triggers in SKAARHOJ PTZ Controller configurations utilize a flag named "RoutingSource". This flag is used to communicate between the camera selector and a virtual trigger, providing information about the input source to be routed to a specific video switcher or hub when a camera is selected. The "RoutingSource" flag index varies across different controller configurations. For instance, the index for a PTZ Extreme is set to "11": When managing multiple PTZ Extremes with the same configuration, a conflict arises as each controller's Routing Trigger setup uses the same index (11). This duplication can lead to operational issues. That same is true for other controllers and their configurations being shared. Solution to the Index Conflict The solution lies in creating a flag by the same name in the tree on a parent layer to the shared configuration. The "RoutingSource" flag (a "TreeDweller" property in the configuration tree) is not set with the "Always Define" flag. This allows for an alternate definition of "RoutingSource" on the parent layer of the configuration. When defined on this parent layer, it prevents the value inside the PTZ Extreme Configuration from overriding. Thus, for any additional configurations of similar controllers, you can assign a different index number to the "RoutingSource" flag by setting it on the parent layer. See below: Adding a new flag is done from the tree by clicking the layer name and adding a flag in the Inspector:     Variables For increased flexibility in a configuration Variables are often applied. A variable works as an adjustable value which can be numerical or text based that can be used in place of a set value.  Variables live on the layers of the tree but are used in lots of different places and in different ways. It is on the layers that you can see what Variables are available, their names, and their current value. In the example below you can see how: Please note, Variables in the tree are only accessible to the same layer they are created on or higher in the tree, unless defined otherwise in the variable set up. Additionally, layers limited by Active If conditions also set limits on the variable.  There are two types of variables; range or options.  Range Variables Name Variable's name Description Easy to understand description to help identify the variable's purpose Type Range or Options  Min Minimum value for the variable (must be lower than the max). Can be set to 0 Max Maximum value for the variable (must be higher than the max) Center (or Default) Default value for the variable if not saved on panel reboot.  Default to first Sets default value to the first. Default Value Define the default value if not first. Always define Separates this instance of the variable from ones of the same name lower in the tree (overridden by Expand Scope). Accept Any Value Allows any value to be accepted during conformity check.  Expand Scope Allows the variable to be used lower in the tree towards the root. Capture Allows a variable to capture any expanded-scope variable from a branch. Persistent Saves value between panel reboots. Zero set Allow Allows for the variable to be blank  Force value (test) Force set a value for the variable, use for testing Variable Use Case Range variables are great for creating dynamic device parameters. In the example below, the variable is set within a parameter.  This is only a single example of the use of Options Variables.  First looking at a breakdown of the parameter the variable is going to be added into: Atem Program Input Video Source  DC:bmd-atem/1/ProgramInputVideoSource/1/1 DC (DEVICE CORE) :bmd-atem (BMD-ATEM) /1 (DEVICE ID 1) /ProgramInputVideoSource (Specified parameter) /1  (M/E number) /1  (Input Number) A range variable would most naturally be put in place of numerical values, making Device ID, M/E Number, or Input Number great places use a variable. Using the variable in place of the Device ID will allow for switching instances of the Atem.  It is possible to find the specific Device ID number of the Atems in the Device Details on the Home Page of Reactor.  When creating the variable it is best to name is something easy to identify the place it should be used. Insetting that variable in the command would look like this:  DC:bmd-atem/ Var:DeviceIndex /ProgramInputVideoSource/1/1 Please note: Formatting is important for the variable name.   After setting the variable into a parameter it is important to set up a way to control the variable.  The feedback will look like: Confirm the variable is changing properly in the tree: Options Variable Variable Set Up Name Variable's name Description Easy to understand description to help identify the variable's purpose Type Range or Options  Value Value string for the variable Label Easy to understand label used for feedback Flag Sets the option as the default value Trash Deletes the Option Duplicate Duplicates the line +1 Increments the line by +1 Add option Default value for the variable if not saved on panel reboot.    Default to first Sets default value to the first. Default Value Define the default value if not first. Always define Separates this instance of the variable from ones of the same name lower in the tree (overridden by Expand Scope). Accept Any Value Allows any value to be accepted during conformity check.  Expand Scope Allows the variable to be used lower in the tree towards the root. Capture Allows a variable to capture any expanded-scope variable from a branch. Persistent Saves value between panel reboots. Zero set Allow Allows for the variable to be blank  Force value (test) Force set a value for the variable, use for testing Variable Use Case Options variables are great for Active If conditions on layers of the tree. In the example below, the variable is set up to make active different page layers in the tree.  This is only a single example of the use of Options Variables.  In the tree it would look like this. The blue line next to a layer indicates it is the active layer. In the layer where the variable lives, the variable also shows what its current value is.  On the page layer, the variable is set as the Active If conditions, meaning that need to be true for the layer to be the active layer.    There are two main way to select the value of the parameter.  To set a specific value, mainly on key press or by trigger, select the variable as a parameter with the Settings Template as Set a Value Directly.  The feedback will look like this: To cycle through the values, mainly on the pulse of an encoder, select the variable as a parameter with the Settings Template as Change Variable.  The feedback will look like this: Virtual Triggers Sometimes you want to create interaction between devices using Reactor, or use the state of a device to change Variables in the system (eg changing the visible layer depending on the currently active camera) This is what Virtual Triggers are for, providing the ability to use Conditions and Values of Devicecores to change other values in the system or even on other devices Virtual Triggers in Reactor can be created on Layers in the Configurator and have 3 modes: Binary Analog Schedule In Binary mode a condition will be interpreted like a Binary trigger (basically imitating a button). While the condition is true the VirtualTrigger is "pressed" and while its false it is "released" In Analog mode the selected parameter becomes an Analog Trigger (basically imitating a fader). Everytime the value is changed a new Analog Value is sent to the defined Behavior In Schedule mode you can configure a schedule of when your trigger will be executed using cron like syntax. Everytime the specified time is reached the Virtual trigger will send a Binary trigger (basically a buttonpress) to the defined behavior Cron explained Cron is a UNIX utility that schedules a command or script on your server to run automatically at a specified time and date.  Not only does it allow for precise timing of unattended events, but it has a straightforward syntax * * * * * * Field name Seconds Minutes Hours Day of month Month Day of week  Allowed values 0-59 0-59 0-23 1-31  1-12 or JAN-DEC 0-6 or SUN-SAT Allowed special characters * / , - * / , - * / , - * / , - ? * / , - * / , - ? All 6 points of information is required.  Allowed special characters explained:     Asterisk ( * ) The asterisk indicates that the cron expression will match for all values of the field; e.g., using an asterisk in the 5th field (month) would indicate every month.     Slash ( / ) Slashes are used to 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 the form "first-last/...", that is, an increment over the largest possible range of the field. The form "N/..." is accepted as meaning "N-MAX/...", that is, starting at N, use the increment until the end of that specific range. It does not wrap around.     Comma ( , ) Commas are used to separate items of a list. For example, using "MON,WED,FRI" in the 5th field (day of week) would mean Mondays, Wednesdays and Fridays.     Hyphen ( - ) Hyphens are used to define ranges. For example, 9-17 would indicate every hour between 9am and 5pm inclusive.     Question mark ( ? ) Question mark may be used instead of '*' for leaving either day-of-month or day-of-week blank. Alternatively we also allow these inputs: Entry  Description Equivalent To @yearly (or @annually) Run once a year, midnight, Jan. 1st 0 0 0 1 1 * @monthly Run once a month, midnight, first of month 0 0 0 1 * * @weekly Run once a week, midnight between Sat/Sun 0 0 0 * * 0 @daily (or @midnight)  Run once a day, midnight 0 0 0 * * * @hourly Run once an hour, beginning of hour  0 0 * * * *   Videos In these videos Kasper shows some examples of how to work with Virtual Triggers.