Control4 programming is event driven: when something happens (an event), run a script of actions, optionally gated by conditionals and loops. All of it is built in the Programming view.
Programming view layout
Open with Ctrl+5. Four panes:
- Device Events (top left): the project tree of everything that fires events. Pick a device, room, or agent.
- Events list (bottom left): the selected item's events as radio buttons (for a keypad button: Press, Release, Hold, Double Tap, Triple Tap, depending on the device).
- Script (center): the program that runs for the selected event. It has an Execute button to test the whole script.
- Device Actions (top right): the tree of everything you can command or check. Below it are three tabs: Commands, Conditionals, Loops.
To add an action: select the event on the left, select the target device on the right, choose a command and its parameters on the Commands tab, then drag the green arrow into the Script pane. Conditionals use a blue question mark the same way. Drag the icon, not the text.
Where you drop matters: on empty Script space it appends at the top level. Onto a conditional or loop it nests inside. Between two lines it inserts there.
Events
Common event sources:
- Device events: keypad and dimmer buttons (Press, Release, Hold, multi-tap), light turned on or off, contact opened or closed, motion detected, doorbell pressed, lock locked or unlocked, thermostat changes, security panel arm and alarm states.
- Room events: room turned off, source changed, volume changed, and media events.
- Agent events: Scheduler times, timer expired, macro executed, variable changed, custom button pressed, wakeup and goodnight, lighting scene activated or deactivated.
- System events: on the project root, such as system startup. Use it to initialize variables after a controller reboot.
Driver events: third-party drivers can add their own events (for example "Device Online" or "Pairing Complete"). They show under that driver in Device Events.
Actions
Commands are the "do this" lines. Every device exposes its own: lights (Set Level, Ramp, Toggle), rooms (Select Video Device, Select Audio Device, Room Off, Volume Up/Down, Set Volume, Mute), media sources, thermostats, locks, relays (Open, Close, Toggle, Pulse), keypad LEDs, and so on.
Agents also take commands: activate a lighting scene, execute a macro, start a timer, set a variable, play an announcement, send a push notification.
Most room-level AV programming should go through the room ("Select Video Device: Apple TV") rather than commanding each device. Selecting through the room powers everything in the path, sets inputs, and keeps the Navigator in sync. Commanding a receiver input directly leaves Director thinking the room is on something else.
Conditionals, And, Or, Else
Conditionals are "if" lines. Find them on the Conditionals tab for the selected device or agent:
- Device state: light is on, light level greater than, room is off, room's current source is, lock is locked, contact is open.
- Variables: Boolean is true, Number compared with =, !=, <, >, String equals.
- Time and schedule: conditions from the Scheduler (for example whether it is day or night, or a day of the week).
To build compound logic, use the Programming Controls strip next to the Script pane:
- And / Or: drop onto an existing conditional to add a second test.
- Else: drop onto a conditional; its children run when the test is false.
- Nest a command by dragging it onto the conditional line, so it runs only when true.
Keep conditionals shallow. Two or three levels deep is readable. Beyond that, move logic into a macro or use a variable as a mode flag.
Loops, delays, Stop, and Break
From the Programming Controls strip and the Loops tab:
- While loop: repeats while a conditional is true. Always put a delay inside and make sure the condition can become false. A tight loop with no delay can bog down Director.
- Delay: pauses the rest of this script for a set time. It does not pause other scripts. A delay cannot be cancelled once started.
- Stop: ends the script.
- Break: exits the current loop.
- Comment: a note in the script with no effect. Use them.
Cancellable timing belongs in the Timer agent, not a Delay. Example: "turn off the closet light 10 minutes after the door opens, but restart the countdown if it opens again" needs a timer you can restart. A delay would stack several shut-off actions.
Variables: device, room, and custom
Three kinds:
- Device variables: read-only state a driver publishes (light level, power state, current input, online status). They appear under each device in Device Actions and can be compared in conditionals.
- Room variables: room state such as power, current selected device, and volume.
- Custom variables: made in the Variables agent. Boolean, Number, and String are the common types (some versions add more). Set, toggle, increment, or decrement them in programming, and compare them in conditionals. A change to a custom variable is also an event you can program.
Custom variables survive normal restarts but should not be trusted after an abnormal power loss. If a variable drives important behavior, set it in the system startup event.
Good uses: house mode (Home, Away, Night, Vacation), "guest mode" flags, a counter for multi-press logic, "movie in progress" so the doorbell does not interrupt.
Macros and custom buttons
Macros agent: a named, reusable script. Program it once (Programming > Device Events > the macro > When executed), then call "Execute macro" from any keypad, schedule, or other script. Change the behavior in one place.
Custom Buttons agent: buttons that appear on Navigators (app and touch screens) in a room's menus. Each button fires an event you program. Use them for things that have no device of their own: "Movie Time", "All Off", "Open Pool Gate". Set their visibility in the room's Navigator menus and refresh Navigators.
Pattern: put the real logic in a macro, then call the macro from the custom button, the keypad, and the schedule. All three stay in sync.
Scheduling
Scheduler agent events fire at a time of day, at sunrise or sunset with an offset, once or repeating daily, weekly, monthly, or yearly. Randomize adds a variation for "lived in" lighting. The project's location and time zone must be correct or sunrise and sunset will be wrong.
Program a schedule event like any other: Programming view > Scheduler under Device Events > pick the event > add actions.
Tips:
- Add a conditional for house mode inside scheduled scripts so vacation and guest modes can change behavior without editing schedules.
- Homeowners can edit some schedules and routines from the app on newer OS versions. Confirm with the customer which ones are theirs.
- After an OS update, spot check that scheduled events still fire.
Pattern: room on, room off, and wake on watch
Room off from a keypad: on the button Press, command the room "Room Off". It powers off the devices in the room's current path.
One-button "Watch TV": on Press, if the room is off, Select Video Device: the main source. Else Room Off. This gives a toggle that behaves well even if someone turned things on from the app.
Wake on watch (lights respond to AV): event room source changed (or room on). If it is night, set the room's lights to a viewing scene. When the room turns off, raise lights to a walking level and start a timer to turn them off.
Do not program "TV on" by commanding the TV directly. Use the room selection so volume, inputs, and the Navigator state stay right.
Pattern: lighting scenes and keypad LEDs
Build scenes in the Advanced Lighting agent, not as lists of per-light commands in a script. Benefits: one command activates the scene, keypad LEDs can track whether the scene is active, toggle behavior is built in, and loads ramp together.
Keypad button for a scene: in Advanced Lighting, the scene can be assigned to a keypad button with tracking, or in Programming: button Press > Advanced Lighting > Toggle (or Activate) scene.
LED tracking: let the scene drive the LED. Avoid programming LEDs manually except for non-lighting status (for example garage door open), and if you do, set the LED from the device's state change event, not the button press.
Pattern: presence, occupancy, and modes
Occupancy lighting: motion sensor "motion detected" turns lights on if it is dark and the lights are off. "No motion" (or a timer restarted by each motion event) turns them off. Add a variable so a manual keypad press can suspend the automation.
Away mode: a Boolean or String variable for mode. Arming the security panel away, a "Leaving" button, or a geofence-driven routine sets Away. Scripts check the mode: lights off, thermostats to setback, shades closed, music off. Disarm sets Home.
Doorbell and media: doorbell pressed, if "movie in progress" is true, show a notification instead of pausing; else pause the room and pop up the camera.
Keep mode logic in one place (one macro per mode change) so every trigger produces the same result.
Copying, organizing, and documenting programming
Right-click in the Script pane to copy a script and paste it onto another event. This copies the full tree including nested conditionals. Re-point the target devices after pasting.
Organize:
- Use Comments at the top of every non-trivial script saying what it does and why.
- Put shared logic in macros.
- Name variables clearly (House Mode, Movie Active).
- Avoid having two triggers fight over the same devices.
Debugging programming that does not work
- Select the event and click Execute. If the actions work now, the problem is the trigger, not the actions.
- If the trigger is the problem: confirm the event actually fires (a keypad button has the right event selected: Press vs Release vs Tap; a scheduled time and time zone are right; a sensor reports state changes in its driver).
- Walk the conditionals. Check variable values in the Variables agent and device states in Properties. A conditional that is always false hides everything under it.
- Look for Delays and loops that have not finished or are stacking.
- Look for another script undoing the change (a schedule, a motion sensor, a room-off).
- For third-party driver actions, open the driver's Lua tab and watch the output while you Execute. Turn up the driver's debug or log level if it has one.
- After a driver update, confirm the event and actions still point to the right items. Events or commands a driver renamed or removed will drop out of scripts.
- If the controller recently rebooted, check variables that should have been initialized at system startup.