Attributes
Attributes are key-value pairs that store configuration, state, and metadata for all entities in GEM. They provide a flexible, extensible way to add custom properties to subsystems, devices, zones, and other system objects.
Overview
The Attributes page provides a unified interface for viewing and managing attributes across all system targets. Attributes can store configuration values, runtime state, device settings, and custom metadata.
Attribute Registry
GEM maintains an Attribute Registry — a catalog of known attributes for each system target and driver. When editing attributes on a device or zone, the registry provides:
- Auto-complete — the attribute name field suggests registered attributes relevant to the entity's driver.
- Auto-fill — selecting a registered attribute auto-fills the value type, history, secure, and readonly flags.
- Descriptions — registered attributes show a description hint and unit below the name.
- Driver context — attributes are filtered by the entity's driver inheritance chain (e.g., a
lutron_qsxdevice sees attributes fromlutron_qsx,lutron_base, andcore). Cross-cutting targets without a driver (e.g.,ui,site,site_space) accept attributes from any integration and are shown the full registry for that target.
The registry is auto-generated from a codebase scan (scripts/register_attributes.js) and seeded into the database on startup. Manual edits to descriptions, history flags, and secure flags in the seed file are preserved across re-scans.
Viewing Attributes
The main grid displays attributes filtered by system target with the following columns:
- ID - Unique identifier
- Target - System target type (zone, device, subsystem, etc.)
- Target ID - ID of the specific entity
- Target - Human-readable name of the target entity (computed)
- Name - Attribute name
- Value - Current value
- Previous Value - Last value before most recent change
- Value Type - Data type (string, bool, int, float, date, json)
- Read Only - Whether the attribute can be modified
- History - Whether value changes are logged
Filtering by System Target
Before viewing attributes, select a System Target from the dropdown:
subsystem- Subsystem attributesdevice- Device attributeszone- Zone attributesmacro- Macro attributesui- UI attributesuser- User attributessystem- System-wide settingsvariable- Virtual variables- And many more...
The grid automatically loads attributes for the selected target type.
Grid Actions
- Add - Create a new attribute
- Edit - Modify an existing attribute
- Delete - Remove an attribute (warning: may affect functionality)
Note: Some system attributes are read-only and cannot be modified or deleted.
Attribute Editor
The inline attribute editor (used on device, zone, and other detail pages) uses a click-to-edit pattern:
- Attributes display in a compact read-only table with name, truncated value, type badge, and toggle switches for Read Only, History, and Secure.
- Double-click any row (or click the edit icon) to enter edit mode for that row. The row highlights and expands with full input fields.
- Click Save (checkmark) to save that single row, or Cancel to discard changes.
- The Read Only, History, and Secure toggles auto-save immediately when clicked — no need to enter edit mode.
- Click Add to add a new attribute (opens in edit mode with the registry-aware name selector).
- Click Save All to batch-save all pending changes across rows.
When Live Values polling is active, all rows are locked to read-only mode. Disable live values to edit attributes.
Creating an Attribute
To create a new attribute:
- Select the System Target (e.g.,
zone) - Click Add in the grid toolbar
- Configure the attribute:
- System Target - Auto-filled from selection
- Target ID - Select the specific entity
- Name - Attribute name (lowercase_with_underscores). The dropdown suggests registered attributes for the entity's driver.
- Value - Initial value (auto-filled from registry default if available)
- Value Type - Data type (auto-filled from registry)
- Read Only - Check to prevent modifications
- History - Check to log all value changes
- Click Save
Attribute Properties
Name
Attribute names follow the GEM naming convention:
- Lowercase letters, numbers, and underscores only
- Descriptive and clear
- Examples:
ip_address,default_level,color_temp
Value Types
GEM supports six data types:
string
- Text values
- Examples: "Living Room", "192.168.1.100", "HDMI 1"
- No type conversion
bool
- Boolean values
- Stored as: true/false, 1/0, on/off
- Normalized to
trueorfalse
int
- Integer numbers
- Examples: 100, -50, 0
- No decimal places
float
- Decimal numbers
- Examples: 72.5, -10.25, 3.14159
- Supports scientific notation
date
- Timestamps
- Stored in ISO 8601 format
- Examples: "2024-01-15T14:30:00Z"
- Automatically converted to local timezone
json
- Complex structured data
- Stored as JSON objects or arrays
- Examples:
{"red": 255, "green": 128, "blue": 0}["HDMI1", "HDMI2", "Component"]
Read Only
Read-only attributes cannot be modified through the admin interface:
- System-calculated values
- Device-reported states
- Protected configuration
Examples:
firmware_versionconnection_statelast_seen
Value Options
Define a fixed set of allowed values for an attribute. When value options are set, the attribute editor and trigger condition builder show a dropdown instead of a free-text input.
- Available for all types except
bool(which already has true/false) - Add options one at a time in the attribute creator
- Each option appears as a removable tag
- Users can still enter custom values (fill-in allowed)
Use cases:
- Restrict a
modeattribute tohome,away,vacation - Limit an
inputattribute to known source names - Constrain a
color_tempattribute to preset values
Dynamic Option Source
Some registered attributes pull their dropdown choices live from elsewhere in the system instead of from a static value_options list. When a registry entry declares an option_source, the attribute editor replaces the free-text input with a SelectSearch picker whose items are resolved on demand and the read-only display shows the matching label instead of the raw stored id.
Sources are one of:
command— runs a command on a device (located bydevice_id, by a sibling attribute, or by adevice_filterlike{driver: 'lutron_qsx'}) and maps the response into picker items.query— queries a model (zone,device,macro,ui_page, …) with optional filters.settings— reads a list/object from the global settings tree.static— a fixed array embedded in the registry entry.
Option sources are resolved client-side, cached for the session, and shared between the AttributeEditor and the widget configuration form so the same descriptor works in both places. They're defined in lib/attribute_registry_seed.js and survive re-scans.
Metric Role
Power and energy attributes can carry a metric_role tag (e.g. power.solar, power.grid, power.battery, power.load, battery.soc) that maps a driver's vendor-specific attribute name onto a shared, canonical concept. This lets a single generic dashboard read heterogeneous drivers — production_now_w (Enphase), pv_power (SolarEdge) and solar_power_w (Tesla) all resolve to power.solar — without per-driver code. A companion metric_sign: 'inverted' flag flips the value when a driver's native sign is the opposite of the canonical convention.
These tags are set in the seed file (not the attribute editor) and are consumed by the Power Flow widget. See Power Management for the full role list, sign conventions, and how to tag a new driver.
History
Enable history to log all value changes:
-
Use cases:
- Security and compliance
- Troubleshooting
- Analytics and reporting
- Behavior analysis
-
Performance impact:
- Minimal for infrequent changes
- Moderate for frequent changes (e.g., temperature sensors)
- High for very frequent changes (e.g., motion sensors)
Enable history only for attributes where historical data is valuable.
View attribute history at Insights > Attribute History.
Common Attribute Categories
Device Attributes
Network Configuration:
ip_address- Device IP address (string)port- Network port (int)mac_address- Physical MAC address (string)hostname- DNS hostname (string)
Authentication:
username- Login username (string)password- Login password (string, auto-encrypted)api_key- API authentication key (string, auto-encrypted)token- Access token (string, auto-encrypted)
Serial Configuration:
baud_rate- Serial baud rate (int)parity- Parity setting (string: none/even/odd)data_bits- Data bits (int: 7 or 8)stop_bits- Stop bits (int: 1 or 2)delimiter- Message delimiter (string)
Runtime State:
power_state- Current power status (string: on/off)connection_state- Connection status (string: connected/disconnected)firmware_version- Device firmware version (string, read-only)uptime- Device uptime in seconds (int, read-only)
Zone Attributes
State:
power- Power state (string: on/off)level- Brightness/volume/position (int: 0-100)input- Selected input source (string)mute- Mute state (bool)temperature- Current temperature (float)setpoint- Target temperature (float)
Color (RGB/RGBW):
color- RGB hex color (string: #RRGGBB)hue- Hue (int: 0-360)saturation- Saturation (int: 0-100)brightness- Brightness (int: 0-100)color_temp- Color temperature in Kelvin (int: 2000-6500)
Limits:
min_level- Minimum level (int: 0-100)max_level- Maximum level (int: 0-100)default_level- Default on level (int: 0-100)
Behavior:
ramp_rate- Fade/transition time in seconds (float)on_delay- Delay before turning on (int, milliseconds)off_delay- Delay before turning off (int, milliseconds)
Relay Control:
normally_closed- Invert relay logic (bool)relay_states- Custom state mapping (json)
Subsystem Attributes
UI Configuration:
icon- Icon name for UI display (string)color- Theme color (string: #RRGGBB)sort_index- Display order (int)
Behavior:
enabled- Subsystem active state (bool)default_level- Default level for all zones (int)
System Attributes
System-wide settings stored on the system target:
system_name- Installation name (string)timezone- System timezone (string)geo_latitude- Latitude for astronomical calculations (float)geo_longitude- Longitude for astronomical calculations (float)db_maintenance_enabled- Enable automatic database cleanup (bool)db_retention_days- Days to retain historical data (int)
UI Attributes
User interface configuration:
theme- UI theme name (string)layout- Layout configuration (json)widgets- Widget configuration (json)
User Attributes
User preferences and settings:
email- Email address (string)phone- Phone number (string)notification_preferences- Notification settings (json)language- Preferred language (string)
Secure Attributes
Certain attributes are automatically encrypted for security:
Auto-Detected Names
Attributes with these names are encrypted:
passwordapi_keyprivate_keysecrettokenpassphrase
Encryption Details
- Algorithm: AES-256-GCM
- Key Storage:
.encryption_keyfile (not in version control) - Format:
gem-crypt:iv:authTag:encrypted - Transparency: Automatic encryption/decryption via Sequelize hooks
Manual Encryption
Force encryption for custom sensitive attributes:
await gem.setAttribute('device', deviceId, 'my_secret', 'sensitive_value', 'string', {secure: true});
Key Management
Encryption Key Backup:
- Navigate to System > Backup & Restore
- Download encryption keys
- Store securely (password manager, encrypted cloud storage)
Key Restoration: Required when restoring backups to new hardware. Upload encryption keys file in the Backup & Restore page.
Accessing Attributes Programmatically
JavaScript (Macros, Drivers)
// Get attribute value
let value = await gem.getAttribute('zone', zoneId, 'brightness');
// Set attribute value
await gem.setAttribute('zone', zoneId, 'brightness', 75, 'int');
// Get multiple attributes
let attrs = await gem.getAttributes('device', deviceId);
console.log(attrs.ip_address, attrs.port);
// Delete attribute
await gem.deleteAttribute('zone', zoneId, 'old_attribute');
REST API
# Get attribute
GET /api/data/attribute?system_target=zone&target_id=123&name=brightness
# Set attribute
POST /api/data/attribute
{
"system_target": "zone",
"target_id": 123,
"name": "brightness",
"value": 75,
"value_type": "int"
}
# Delete attribute
DELETE /api/data/attribute/{id}
WebSocket
// Get attribute
socket.emit('get_attribute', {
system_target: 'zone',
target_id: 123,
name: 'brightness'
}, (result) => {
console.log('Brightness:', result.value);
});
// Set attribute
socket.emit('set_attribute', {
system_target: 'zone',
target_id: 123,
name: 'brightness',
value: 75,
value_type: 'int'
}, (result) => {
if (result.error) {
console.error('Error:', result.error);
}
});
Attribute Triggers
Attributes can trigger automation when their values change. See Triggers for details.
Example use cases:
- Turn on lights when motion sensor goes high
- Send alert when temperature exceeds threshold
- Activate scene when security system arms
- Sync attributes across multiple devices
Best Practices
-
Naming: Use clear, descriptive names that indicate purpose
-
Type Selection: Choose the most specific type:
- Use
intfor whole numbers - Use
floatfor decimals - Use
boolfor true/false - Use
jsonfor structured data
- Use
-
History: Enable only when needed:
- Enable for security-critical attributes
- Enable for troubleshooting key values
- Disable for high-frequency updates
-
Read-Only: Mark calculated or device-reported attributes as read-only
-
Documentation: Use descriptive names instead of needing separate documentation
-
Defaults: Store default values as attributes rather than hardcoding
-
Organization: Group related attributes with common prefixes:
color_*for color propertieslimit_*for constraintsstate_*for runtime state
-
Validation: Validate values before setting:
if (level >= 0 && level <= 100) {await gem.setAttribute('zone', zoneId, 'level', level, 'int');} -
Security: Never log or expose secure attributes
-
Cleanup: Delete unused attributes periodically
Dynamic Attribute Values
Dynamic attribute values use bracket expressions to reference other attributes, variables, or cross-entity values at runtime. They are resolved in macro steps, trigger conditions, and scheduled macros.
Syntax
All dynamic values start with [ and end with ], optionally followed by arithmetic and clamping.
Local Attribute Reference
[level] → value of 'level' on the current entity
[brightness]+20 → brightness + 20
Variable Reference
[$sunrise] → value of the 'sunrise' system variable
[$brightness]+10 → brightness variable + 10
Cross-Entity Reference
[zone.5.level] → level attribute of zone 5
[device.12.temperature] → temperature attribute of device 12
Arithmetic Operators
Append an operator and operand after the closing bracket:
| Operator | Example | Result |
|---|---|---|
+ | [level]+20 | level + 20 |
- | [level]-10 | level - 10 |
* | [level]*2 | level × 2 |
/ | [level]/3 | level ÷ 3 |
Clamping
Constrain the result to a range with :min:max:
[level]+50:0:100 → add 50, clamp between 0 and 100
[level]*3:0:200 → multiply by 3, clamp between 0 and 200
[level]:0:50 → clamp raw value between 0 and 50 (no operator)
Date Arithmetic
For date type values, the offset is in minutes:
[$sunrise]+30 → 30 minutes after sunrise
[$sunset]-15 → 15 minutes before sunset
Dynamic Value Builder
The macro step editor includes a Dynamic Value Builder dialog for constructing dynamic expressions visually. Click the settings icon next to a dynamic parameter to open it. The builder supports:
- Static — plain literal value
- Local Attribute — reference an attribute on the current entity
- Cross-Entity — pick a zone or device and its attribute
- Variable — select a system variable
Configure optional arithmetic operator, operand, and clamping, then click Apply to insert the expression.
Advanced Topics
Computed Attributes
Create virtual attributes calculated from other values:
// In a driver or macro
let totalPower = zone1.power + zone2.power + zone3.power;
await gem.setAttribute('variable', 0, 'total_power', totalPower, 'float');
Attribute Synchronization
Keep attributes in sync across entities:
// Trigger when master zone level changes
socket.on('attribute_change', async (data) => {
if (data.name === 'level' && data.target_id === masterZoneId) {
// Sync to slave zones
for (let slaveId of slaveZones) {
await gem.setAttribute('zone', slaveId, 'level', data.value, 'int');
}
}
});
JSON Attribute Schemas
Structure complex JSON attributes:
{
"scenes": {
"movie": {"brightness": 20, "color_temp": 2700},
"reading": {"brightness": 80, "color_temp": 4000},
"party": {"brightness": 100, "color": "#FF00FF"}
}
}
Access with:
let attrs = await gem.getAttributes('zone', zoneId);
let scenes = JSON.parse(attrs.scenes || '{}');
let movieScene = scenes.movie;
Attribute Migrations
When restructuring attributes:
- Create new attributes with new naming
- Copy values from old to new
- Update all references
- Delete old attributes
- Document the change
Troubleshooting
Attribute Not Saving
- Check Type: Verify value matches the value_type
- Check Read-Only: Read-only attributes cannot be modified
- Check Permissions: Verify user has attribute write permission
- Review Logs: Check for validation errors
Attribute Not Updating
- Check Subscriptions: Ensure clients are subscribed to updates
- Review Triggers: Conflicting triggers may override values
- Check Device: Device may not be sending feedback
- Verify Response Set: Response parsing may be incorrect
History Not Saving
- Check History Flag: Verify history is enabled for the attribute
- Check Retention: Old history may be pruned based on retention settings
- Review Performance: High-frequency updates may be throttled
Encryption Issues
- Missing Key: Encryption key file must exist
- Wrong Key: Restoring backups requires original encryption key
- Key Permissions: Verify file permissions on
.encryption_key
Related Documentation
- Devices - Device attribute management
- Zones - Zone attribute configuration
- Subsystems - Subsystem attributes
- Triggers - Attribute-based automation
- Attribute History - Viewing historical data
- Backup & Restore - Encryption key management