Commands
Commands are individual control instructions within a command set. Each command has a name, a template string, and optional arguments that allow dynamic value substitution at runtime.
Overview
The Commands page manages the individual commands within a command set. It is accessed by clicking Edit on a command set row in the Command Sets page, or via the commands icon on a device row in the Devices page.
Viewing Commands
The main grid displays all commands in the selected command set:
- ID - Unique identifier
- Name - Command name (lowercase_with_underscores)
- Template - The command string or template
- Args - Defined argument names
- Source -
DRIVERfor driver-managed (system) commands,USERfor ones you added - Command Set - Parent command set
Header
- Title - Shows the command set name (or the device name, when the page is opened from a device's commands icon)
- Test Device selector - Choose a device for testing commands
- Reload button - Reload the selected test device
- Device button - Open the selected test device in a reference modal over this page. Testing a command routinely turns into a question about the device itself — its address, its driver, an attribute the template interpolates — and opening it here keeps the command set you are filtered to, the test device you picked, and everything the Device Console has collected. Disabled until a test device is selected.
Grid Actions
- Add - Create a new command
- Edit - Open the command editor
- Delete - Remove a command
Quick Actions
- Test (remote icon) - Execute the command on the selected test device
Creating a Command
- Click Add in the grid toolbar
- Fill in the command details:
Command Fields
Name
- Internal identifier (lowercase_with_underscores)
- Auto-formatted on blur
- Examples:
power_on,set_volume,select_input
Template
- Raw command string sent to the device
- Use
[arg_name]for argument placeholders (e.g.,set_volume [level]) — each placeholder is replaced by the value of the argument with that name - When a command targets a zone, GEM auto-fills an
[address]placeholder with that zone's address (or, for a controller's child device, the device's address). So a template ofZONE[address] ONworks on its own — you don't have to declare anaddressargument or wire up anything - Don't type the
[@…]dynamic values directly into the template — they belong on an argument's options, not in the template string (see Dynamic Value Placeholders) - A template may also name one of the device's own attributes —
login [username] [password]is how a driver that authenticates on connect is written, and a secure attribute resolves there because the template is something an administrator wrote (see the warning below)
A [password], [api_key] or other secure attribute token resolves only in the template itself, and only against the device the command is running on. That is the case the feature exists for: the credential goes onto the wire to the equipment that needs it.
The same token in an argument value does not resolve, and is left as literal text. An argument value can come from whoever ran the command — a UI, a macro argument, an outside system — so resolving it there would hand the device's decrypted credential back to the caller and print it in the Request History under whatever argument name it was smuggled in as. If you see a literal [password] reach a device, that is this rule, not a substitution failure; the server also logs "refusing to resolve secure attribute". Put the token in the template.
Rendering a command follows a fixed order: the device's secure attributes, then [$variable] references, then the device's other attributes, and finally the argument values. Two things follow from it:
- An argument wins over a device attribute of the same name. A template of
VOL [volume]on a driver that also caches avolumeattribute still sends the level the caller asked for; the attribute fills the slot only when the command carries novolumeargument. - Nothing expands a token that arrives inside an argument's value. An argument's own
[attribute]token is resolved once, by the server, against the command's device → zone → subsystem (never a secure attribute — see above); after that the text is placed into the template and no further pass looks at it. So a[$variable]reference, or a token only the driver would know, reaches the device as literal text. Write tokens in the template, not in an argument's value.
For a non-elevated session there is a further check on what an argument resolves to — see Roles → Elevated devices and macros.
Arguments
Arguments define named parameters that are substituted into the template at runtime:
- Type an argument name in the Arguments list editor and press Enter
- The argument appears as a selectable item
- Click an argument to configure its options
Argument Options
When an argument is selected, you can define the allowed values:
- Add static values (e.g.,
on,off,toggle) - Add dynamic value placeholders that resolve at runtime using the buttons:
- Zone Address -
[@zone.address]- Resolved to the target zone's address - Device Address -
[@device.address]- Resolved to the device's address - Device ID -
[@device.id]- Resolved to the device's database ID - AV Zone -
[@av_zone.address]- Resolved to the AV zone's address - AV Source -
[@av_source.address]- Resolved to the AV source's address
- Zone Address -
When you add one of these placeholders to an argument's options, GEM expands it into a picker of the matching items — every zone, every device, or every AV zone/source — each labeled with its [id] and showing the address (or ID) it will send. Instead of typing a raw address or ID, you (or whoever later uses the command in the tester or a macro) choose the target from a dropdown, and that value fills the template's [arg_name] placeholder when the command runs.
Command-Backed Options
A second kind of dynamic placeholder has no editor button — drivers supply it in their command definitions (and you can type one by hand):
[@command:<command_name>.<items_path>.<field>]
[@command:<command_name>.<items_path>.<field>.<label_field>]
At pick time GEM runs <command_name> on the command's device, takes the array at <items_path> in the result, and lists each item's <field> as an option — a dropdown of live device state rather than a database table. Items carrying an ip, model, or meta field show it in parentheses after the value as a label hint. For example, the Dante driver's set_zone_source command uses [@command:get_sources.sources.name] so its transmitter argument lists the transmitter devices currently discovered on the network.
Leave <items_path> empty when the command returns the array itself, and add a fourth segment to label each option with a different field than the one it sends. The Samsung TV driver's launch_app, close_app and app_status commands use [@command:get_apps..appId.name]: the app_id picker lists the apps installed on the selected set as Netflix (3201907018807) and sends the Tizen id. Because the picker allows free text, a name the driver knows (netflix) can still be typed instead.
Because the lookup command runs against the argument's own device, the picker stays empty until a concrete device is selected — in the tester, choose a Test Device first.
Testing Commands
To test a command:
- Select a Test Device from the dropdown in the header (if you skip this, clicking Test just warns "Please select a test device")
- Click the Test icon (remote icon) on a command row
- If the command takes arguments — or its template contains a
[…]placeholder — a command tester opens:- Fill in (or pick) the argument values
- Click Test (the play-icon button) to send it
- The result appears in the tester's own Test Console, which reports a verdict, the round-trip time, the resolved payload that was sent, and the reply rendered as what it is — see Command step testing for the full breakdown
- If the command has no arguments and no placeholders, it executes immediately
- Commands fired straight from the grid, and everything the device sends back afterwards, appear in the Device Console below
Device Console
When a test device is selected, a Device Console panel appears. It's a rolling log of the interaction with the device: commands sent from this page, their immediate replies, plus everything the device pushes back asynchronously — attribute changes and raw driver responses. This is where you verify a command actually did something ("I sent set_volume 30, the device echoed the new level") instead of trusting the toast.
The panel is docked to the bottom of the window, so it stays in view while you scroll the command grid and fire the next command. Click its title bar (or Hide) to collapse it down to that bar; the choice is remembered on this browser. While collapsed, a badge counts the lines that have arrived since — that count is how you tell "the device answered and I wasn't looking" from "the device never answered". Clear empties the log.
Prompt Attributes
Some commands may trigger a prompt attribute response from the device, asking for additional input (e.g., a PIN code or confirmation). The console will display a dialog for the user to provide the value.
Command Templates
Templates define what is sent to the device. They support several placeholder syntaxes:
Static Templates
POWER ON
VOL UP
INPUT HDMI1
Argument Placeholders
VOLUME [level] → VOLUME 50
INPUT [input_name] → INPUT HDMI1
PWR [state] → PWR ON
Dynamic Value Placeholders
The [@…] placeholders ([@zone.address], [@device.address], [@device.id], [@av_zone.address], [@av_source.address], and the command-backed [@command:…] form) are not typed into the template. They go into an argument's options — that is what the Dynamic Values buttons in the editor do. At edit time GEM expands the placeholder into a dropdown of the matching items so the value is picked, not typed; the picked value is then substituted into the template's matching [arg_name].
For example, to make a command pick which AV zone to address:
- Add an argument named
address - Select it, then click the AV Zone button to add
[@av_zone.address]to its options - Write the template as
SET [address] ON
When the command runs, the chosen AV zone's address replaces [address] — the device receives e.g. SET 3 ON. (For a command's own target zone you don't even need this — the auto-filled [address] placeholder described above already covers it.)
System Commands
Many device drivers ship with a built-in set of default commands. When a device connects for the first time (or after a GEM update adds new commands to a driver), GEM automatically syncs these system commands into the device's command set.
How System Commands Work
- Auto-created: When a device starts up, any commands defined by the driver that are missing from the command set are automatically inserted.
- System flag: System-managed commands have the
systemfield set totrueand show aDRIVERbadge in the Source column. - Driver is the source of truth: On every boot, GEM resyncs each field on the existing row from the driver definition.
template,args,arg_options, anddescriptionall follow the same fill-empty stop-gap rule: if the driver supplies a value, the driver wins; if the driver leaves the field empty, your local edit is preserved (see below). - Orphan cleanup: If a driver removes a command in an update, the corresponding system command is automatically deleted from the database. User-created commands (where
systemisfalse) are never removed by the sync process.
Editing Driver-Managed Commands
A command with the DRIVER badge is owned by the device's driver, which re-applies its definition every time the device boots. On this page that means most of the command is read-only:
- Name, Template, and Arguments are locked. When you open a DRIVER command in the editor those fields are greyed out, because the driver would rewrite them on the next boot anyway.
- Argument options are the one thing you can fill in — and only as a stop-gap, when the driver hasn't supplied options of its own. This is the escape hatch for patching a missing hint locally: for example, adding a
[@zone.address]option so a driver that shipped before the zone-picker convention still gets a labeled picker instead of a raw number field. If the driver already provides options for that command, the editor won't open it — you'll get a notice telling you to add a separate command instead. - Inline grid edits and deletion are blocked for DRIVER commands; both warn you, because a deleted system command is just re-created and an inline edit is reverted on the next boot.
Behind the scenes, anything the driver leaves unspecified is preserved across reboots, while any field the driver does define is restored to the driver's value — so the argument options you fill in survive until (and unless) the driver starts supplying its own.
If you need a permanent customization that conflicts with the driver, add a separate command with a different name. Custom commands (system = false) are preserved across updates and never overwritten by the sync process.
Identifying System Commands
In the commands grid, the Source column shows DRIVER for system-managed commands and USER for ones you've added. You can test driver-managed commands and use them in macros just like any other command. Deletion and inline edits are blocked, and any field the driver defines is restored on the next boot — the only thing you can fill in on this page is an argument's options, and only while the driver hasn't supplied them itself.
Command Name Aliases
GEM maintains an internal alias system for common command names. When a command is executed and the exact name is not found in the device's command set, GEM tries alternative names from the same alias group. For example:
fast_forward→ triesscan_forward,forward,ffrewind→ triesscan_back,reverse,scan_reverse,rwnext→ triesskip_forward,skip_nextvolume↔set_volumeinput↔set_inputpower_on→ trieson,discrete_onback↔returnselect→ triesok,enter
This means macros and automation commands work across different drivers even when naming conventions differ slightly.
Database Schema
| Column | Type | Description |
|---|---|---|
id | INTEGER | Primary key |
name | STRING | Command name (required) |
command_set_id | INTEGER | FK to parent command_set (required) |
template | TEXT | Command template string |
description | TEXT | Optional description (no length limit) |
args | JSON | Array of argument names |
arg_options | JSON | Object mapping argument names to arrays of allowed values |
system | BOOLEAN | Whether this command is managed by the driver (default: false) |
Import and Export
Exporting Commands
Use the grid Export button to save all commands to a file — CSV by default, or JSON via the checkbox in the export dialog (choose JSON if you plan to re-import the file; the Import button only accepts JSON). To export only specific commands, multi-select them first (click-and-drag or Ctrl+Click) — the same Export button then exports just the selected rows. Either way, GEM asks "Include IDs in export?": answer No when you intend to import the file into a different command set (so the rows come in as new commands rather than colliding with existing IDs).
Importing Commands
- Click the Import button in the grid toolbar
- Select a JSON file containing an array of command objects
- A confirmation dialog appears with the number of commands to import
- Replace existing commands — check this option to delete all existing commands in the set before importing. This is useful when loading a complete replacement command set rather than merging.
- Click OK to proceed
Driver-managed rows (Source: DRIVER) are skipped by the replace sweep, the same way they refuse a single-row delete — the driver would just re-create them on the next device boot. If any row is protected or fails to delete, a summary message reports the counts (deleted 40, 6 protected).
An import file is capped at 10,000 rows. If the file is longer, GEM warns you how many rows were skipped rather than reporting only the imported count.
The replace option permanently deletes all user-created commands in the command set before importing. Use this only when you want a full replacement, not a merge.
Best Practices
- Consistent Naming: Use descriptive command names matching the device protocol (e.g.,
power_on,volume_up) - Match the Protocol Exactly: The template is sent to the device as written, so copy the command's syntax, casing, and delimiters straight from the device's control protocol
- Test After Creating: Always test new commands with a real device before deploying
- Use Dynamic Values: Prefer
[@zone.address]over hardcoded addresses for reusable commands - Group Related Args: Keep argument option lists focused and ordered logically
Related Documentation
- Command Sets - Managing command set collections
- Devices - Device configuration and command set assignment