Skip to main content
Version: 1.0

RGBW

The RGBW API provides a dedicated handle for a Standard Shelly RGBW.

API Reference

MethodDescription
RGBW.get(id)Returns an RGBWHandle for the light, or null if not found.
rgbw.getStatus()Returns the light's current status object.
rgbw.getConfig()Returns the light's config object.
rgbw.setConfig(cfg)Updates the config. Returns true on success.
rgbw.set(state)Writes a new state {on, brightness, rgb, white, transition_duration, toggle_after}.
rgbw.on(event, callback)Registers a listener. Returns a listener id.
rgbw.off(id)Removes a listener by its id.
rgbw.setWriteOutputState(callback)Handles every write to the light.
note

The RGBW API is only available to the component's owner service (identified by the svc property in the script's JWT). Other services cannot obtain a handle for it.

RGBW.get(id)

Look up an RGBW component and return a handle to it.

const rgbw = RGBW.get(0);
if (!rgbw) {
console.log('ERROR: no rgbw:0');
throw new Error('RGBW not available');
}

Parameters:

  • id — RGBW component id (0 for the first RGBW light).

Returns an RGBWHandle, or null if the component is not available.

rgbw.getStatus()

Read the light's current status.

const status = rgbw.getStatus();
// { id: 0, source: "...", output: true, brightness: 75, rgb: [255, 128, 0], white: 50 }

if (status.output) {
const [r, g, b] = status.rgb;
console.log('On at ' + status.brightness + '%, color (' + r + ',' + g + ',' + b + '), white ' + status.white);
}

Returned fields:

  • output — Boolean on/off state.
  • brightness — Overall brightness (e.g. 0100).
  • rgb — Array of three color channel values [r, g, b], each 0255.
  • white — White channel value, 0255 (present only if the hardware supports a white channel).

Every status object also carries id, source, and tag, and may include temperature, transition, and errors when applicable — see the RGBW component reference for the full shape.

rgbw.getConfig()

Read the light's current configuration.

const cfg = rgbw.getConfig();
console.log('config:', JSON.stringify(cfg));

Returns the configuration object. Notable fields include name, in_mode, initial_state, transition_duration, and night_mode. See the RGBW component reference for the full shape.

rgbw.setConfig(cfg)

Apply a configuration update. Configs are merged before applying, so you can provide only the values you want to update.

const cfg = rgbw.getConfig();
cfg.name = 'Living room strip';

const ok = rgbw.setConfig(cfg);
if (!ok) {
console.log('Failed to update config');
}

Returns true on success, false if the config was rejected.

rgbw.set(state)

Write a new state to the light. Any of the fields below may be provided; fields you omit are left unchanged.

rgbw.set({ on: true, brightness: 60, rgb: [255, 128, 0], white: 50 });
rgbw.set({ on: false });
rgbw.set({ white: 100 });
rgbw.set({ on: true, brightness: 80, rgb: [255, 0, 0], transition_duration: 2.0 });

Parameters:

  • on — Boolean on/off state.
  • brightness — Integer 0100. Values outside this range throw "brightness must be 0-100".
  • rgb — Array of exactly three integers [r, g, b], each 0255. Arrays of wrong length or values outside 0255 are rejected.
  • white — Integer 0255 for the white channel. Values outside this range throw "white must be 0-255".
  • transition_duration — Optional. Transition time in seconds (float).
  • toggle_after — Optional. Seconds after which the light flips to the opposite state (float).

Calling rgbw.set triggers the write handler (see below), so this is also how a script pushes hardware-reported state back into the light.

rgbw.on(event, callback)

Listen for events on the light. The callback is passive — it observes changes and its return value is ignored.

const subId = rgbw.on('change', (status) => {
if (status.output) {
const [r, g, b] = status.rgb;
console.log('RGBW now (' + r + ',' + g + ',' + b + '), white ' + status.white);
} else {
console.log('RGBW off');
}
});

Parameters:

  • event — Event name. Only "change" is accepted; any other name throws "unsupported event".
  • callback — Function invoked with the light's status object (same shape as getStatus()).

Returns a numeric listener id, which you pass to rgbw.off to remove the listener.

rgbw.off(id)

Remove a listener previously registered with rgbw.on.

rgbw.off(subId);

Returns true if the listener was found and removed, false otherwise.

rgbw.setWriteOutputState(callback)

setWriteOutputState installs a handler that runs on every write to the light — from the UI, an automation, or another script.

rgbw.setWriteOutputState((params) => {
// params: { output: boolean, brightness: number, rgb: [r, g, b], white: number, source: string }
// return null to accept, or an error string to reject
return null;
});

The function receives:

  • output — Requested on/off state.
  • brightness — Requested brightness (0100).
  • rgb — Requested color as an array [r, g, b], each 0255.
  • white — Requested white channel value (0255).
  • source — String identifying where the write originated.

Return value:

  • null — Accept the write. The requested state is applied to the component.
  • string — Reject the write. The returned string is surfaced as the error message to the caller (UI, RPC, or script), and the component's state is left unchanged. Use this to enforce policy or validation — for example, refuse an out-of-range value or block a write while the hardware is busy.

Bidirectional Synchronization

The RGBW API sits between the app (UI, RPC calls, other scripts) and your physical hardware. Two directions need to stay in sync:

  • Hardware -> Light: when your hardware reports a state change, call rgbw.set(...) so the app sees the new state.
  • Light -> Hardware: when a user, RPC caller, or other script changes the light, your write handler runs — drive the hardware from there.

Note that your own rgbw.set(...) calls also fire the handler. Without care, this creates a loop: hardware reports a change -> script calls rgbw.set -> handler drives hardware -> hardware reports again -> repeat. Break the loop by inspecting params.source inside the handler and only driving hardware for writes that came from outside your script. The UART example below shows one common pattern.

RGBW Examples

Getting a Handle

const rgbw = RGBW.get(0);
if (!rgbw) {
throw new Error('RGBW not available');
}

Reading Status

const status = rgbw.getStatus();
const [r, g, b] = status.rgb;
console.log(status.output
? 'On at ' + status.brightness + '%, (' + r + ',' + g + ',' + b + '), white ' + status.white
: 'Off');

Reading Config

const cfg = rgbw.getConfig();
console.log('Configured name:', cfg.name);

Updating Config

const cfg = rgbw.getConfig();
cfg.name = 'Living room strip';

if (!rgbw.setConfig(cfg)) {
console.log('Config update rejected');
}

Setting State

// Turn on, orange, some white
rgbw.set({ on: true, brightness: 60, rgb: [255, 128, 0], white: 50 });

// Boost the white channel only
rgbw.set({ white: 100 });

// Turn off
rgbw.set({ on: false });

// Fade to red over 2 seconds
rgbw.set({ on: true, brightness: 80, rgb: [255, 0, 0], transition_duration: 2.0 });

Listening for Changes

const subId = rgbw.on('change', (status) => {
console.log('RGBW is now', status.output ? 'on' : 'off');

// Stop listening after the first change:
rgbw.off(subId);
});

Validation

Use the handler to enforce a policy — here, cap brightness at 80:

const rgbw = RGBW.get(0);
if (!rgbw) {
throw new Error('RGBW not available');
}

rgbw.setWriteOutputState((params) => {
if (params.brightness > 80) {
return 'brightness limited to 80';
}
return null;
});

RGBW (UART)

const uart = UART.get(0);
uart.configure({ baud: 115200, mode: '8N1' });

const rgbw = RGBW.get(0);
if (!rgbw) {
throw new Error('RGBW not available');
}

// Device to app
uart.recv((data) => {
const line = data.trim();
if (line.startsWith('STATE:')) {
const on = line.substring(6).toUpperCase() === 'ON';
rgbw.set({ on });
} else if (line.startsWith('DIM:')) {
const brightness = parseInt(line.substring(4), 10);
if (!isNaN(brightness)) {
rgbw.set({ brightness });
}
} else if (line.startsWith('RGB:')) {
const parts = line.substring(4).split(',');
if (parts.length === 3) {
const r = parseInt(parts[0], 10);
const g = parseInt(parts[1], 10);
const b = parseInt(parts[2], 10);
if (!isNaN(r) && !isNaN(g) && !isNaN(b)) {
rgbw.set({ rgb: [r, g, b] });
}
}
} else if (line.startsWith('W:')) {
const w = parseInt(line.substring(2), 10);
if (!isNaN(w)) {
rgbw.set({ white: w });
}
}
});

// App to device
rgbw.setWriteOutputState((params) => {
if (params.source !== 'user' && params.source !== 'api') {
return null;
}
const [r, g, b] = params.rgb;
uart.write('STATE:' + (params.output ? 'ON' : 'OFF') + '\n');
uart.write('DIM:' + params.brightness + '\n');
uart.write('RGB:' + r + ',' + g + ',' + b + '\n');
uart.write('W:' + params.white + '\n');
return null;
});