CCT
The CCT API provides a dedicated handle for a Standard Shelly CCT.
API Reference
| Method | Description |
|---|---|
CCT.get(id) | Returns a CCTHandle for the component, or null if not found. |
cct.getStatus() | Returns the component's current status object. |
cct.getConfig() | Returns the component's config object. |
cct.setConfig(cfg) | Updates the config. Returns true on success. |
cct.set(state) | Writes a new state {on, brightness, ct, transition_duration, toggle_after}. |
cct.on(event, callback) | Registers a listener. Returns a listener id. |
cct.off(id) | Removes a listener by its id. |
cct.setWriteOutputState(callback) | Handles every write to the component. |
The CCT 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.
CCT.get(id)
Look up a CCT component and return a handle to it.
const cct = CCT.get(0);
if (!cct) {
console.log('ERROR: no cct:0');
throw new Error('CCT not available');
}
Parameters:
id— CCT component id (0for the first CCT).
Returns a CCTHandle, or null if the component is not available.
cct.getStatus()
Read the component's current status.
const status = cct.getStatus();
// { output: true, brightness: 75, ct: 3500 }
if (status.output) {
console.log('CCT is on at ' + status.brightness + '%, ' + status.ct + 'K');
}
Returned fields:
output— Boolean on/off state.brightness— Overall brightness (e.g.0–100).ct— Colour temperature in Kelvin, within the configuredct_range.
Every status object also carries id, source, and tag, and may include temperature, transition, and errors when applicable — see the CCT component reference for the full shape.
cct.getConfig()
Read the component's current configuration.
const cfg = cct.getConfig();
console.log('config:', JSON.stringify(cfg));
Returns the configuration object. Notable fields include name and ct_range (a [min, max] pair in Kelvin). See the CCT component reference for the full shape.
cct.setConfig(cfg)
Apply a configuration update. Configs are merged before applying, so you can provide only the values you want to update.
const cfg = cct.getConfig();
cfg.name = 'Reading lamp';
const ok = cct.setConfig(cfg);
if (!ok) {
console.log('Failed to update config');
}
Returns true on success, false if the config was rejected.
cct.set(state)
Write a new state to the CCT. Any of the fields below may be provided; fields you omit are left unchanged.
cct.set({ on: true, brightness: 60, ct: 3000 });
cct.set({ ct: 5000 }); // just shift color temperature
cct.set({ brightness: 30 }); // just dim
cct.set({ on: false }); // just turn off
cct.set({ on: true, brightness: 80, ct: 4000, transition_duration: 2.0 });
Parameters:
on— Boolean on/off state.brightness— Integer0–100. Values outside this range throw"brightness must be 0-100".ct— Color temperature in Kelvin. Must fall within the configuredct_range; out-of-range values are rejected.transition_duration— Optional. Transition time in seconds (float).toggle_after— Optional. Seconds after which the light flips to the opposite state (float).
Calling cct.set triggers the write handler (see below), so this is also how a script pushes hardware-reported state back into the component.
cct.on(event, callback)
Listen for events on the CCT. The callback is passive — it observes changes and its return value is ignored.
const subId = cct.on('change', (status) => {
if (status.output) {
console.log('CCT: ' + status.brightness + '% @ ' + status.ct + 'K');
} else {
console.log('CCT turned off');
}
});
Parameters:
event— Event name. Only"change"is accepted; any other name throws"unsupported event".callback— Function invoked with the CCT's status object (same shape asgetStatus()).
Returns a numeric listener id, which you pass to cct.off to remove the listener.
cct.off(id)
Remove a listener previously registered with cct.on.
cct.off(subId);
Returns true if the listener was found and removed, false otherwise.
cct.setWriteOutputState(callback)
setWriteOutputState installs a handler that runs on every write to the CCT — from the UI, an automation, or another script.
cct.setWriteOutputState((params) => {
// params: { output: boolean, brightness: number, ct: 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 (0–100).ct— Requested color temperature in Kelvin.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 color temperature or block a write while the hardware is busy.
Bidirectional Synchronization
The CCT API sits between the app (UI, RPC calls, other scripts) and your physical hardware. Two directions need to stay in sync:
- Hardware -> CCT: when your hardware reports a state change, call
cct.set(...)so the app sees the new state. - CCT -> Hardware: when a user, RPC caller, or other script changes the CCT, your write handler runs — drive the hardware from there.
Note that your own cct.set(...) calls also fire the handler. Without care, this creates a loop: hardware reports a change -> script calls cct.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.
CCT Examples
Getting a Handle
const cct = CCT.get(0);
if (!cct) {
throw new Error('CCT not available');
}
Reading Status
const status = cct.getStatus();
console.log(status.output
? 'On at ' + status.brightness + '% @ ' + status.ct + 'K'
: 'Off');
Reading Config
const cfg = cct.getConfig();
console.log('Configured name:', cfg.name);
console.log('CT range:', cfg.ct_range);
Updating Config
const cfg = cct.getConfig();
cfg.name = 'Reading lamp';
if (!cct.setConfig(cfg)) {
console.log('Config update rejected');
}
Setting State
// Warm white at 50%
cct.set({ on: true, brightness: 50, ct: 2700 });
// Shift to cool white without changing on/off or brightness
cct.set({ ct: 6000 });
// Dim without changing color temperature
cct.set({ brightness: 20 });
// Turn off
cct.set({ on: false });
// Fade to cool white over 2 seconds
cct.set({ on: true, brightness: 80, ct: 4000, transition_duration: 2.0 });
Listening for Changes
const subId = cct.on('change', (status) => {
console.log('CCT is now', status.output ? 'on' : 'off',
'at', status.ct + 'K');
// Stop listening after the first change:
cct.off(subId);
});
Validation
Use the handler to enforce a policy — here, clamp the usable color temperature to a narrower range:
const cct = CCT.get(0);
if (!cct) {
throw new Error('CCT not available');
}
cct.setWriteOutputState((params) => {
if (params.ct < 3000 || params.ct > 5000) {
return 'ct restricted to 3000-5000K';
}
return null;
});
CCT (UART)
const uart = UART.get(0);
uart.configure({ baud: 115200, mode: '8N1' });
const cct = CCT.get(0);
if (!cct) {
throw new Error('CCT not available');
}
// Device to app
uart.recv((data) => {
const line = data.trim();
if (line.startsWith('STATE:')) {
const on = line.substring(6).toUpperCase() === 'ON';
cct.set({ on });
} else if (line.startsWith('DIM:')) {
const brightness = parseInt(line.substring(4), 10);
if (!isNaN(brightness)) {
cct.set({ brightness, on: brightness > 0 });
}
} else if (line.startsWith('CT:')) {
const ct = parseInt(line.substring(3), 10);
if (!isNaN(ct)) {
cct.set({ ct });
}
}
});
// App to device
cct.setWriteOutputState((params) => {
if (params.source !== 'user' && params.source !== 'api') {
return null;
}
uart.write('STATE:' + (params.output ? 'ON' : 'OFF') + '\n');
uart.write('DIM:' + params.brightness + '\n');
uart.write('CT:' + params.ct + '\n');
return null;
});