Signals

Other programs tell Candeo what happened — a build that failed, the doorbell, a call starting — and you decide what it lights. The sender never names a device, an effect or a colour: it states a fact, and what that fact changes is decided in Candeo.

A signal is a name and a value

build is failed, volume is 0.4, muted is true. Names are yours to choose — letters, digits and _ - . : — and values are flat: text, a number, true or false.

A value lasts 60 seconds unless the sender says otherwise, so a script that stops does not leave a keyboard lit for the rest of the day. A state sent once — a build that stays broken — is sent with no lifetime, and lasts until it changes or is erased. Values are kept in memory only: never written to disk, never written to the log.

Turning it on

In Settings › Signals, turn on Receive signals from other software. Candeo then listens on this computer, at http://127.0.0.1:7317/signals, and shows the token every request carries — Copy it into your script; New token refuses every sender still holding the old one.

For another machine — Home Assistant, most often — tick the network interface it reaches this computer through: Wi-Fi, Ethernet. Windows asks once whether to let Candeo through its firewall; allow it on private networks only. The port is 7317, and can be changed there.

Sending one

PowerShell, at the end of a build script:

$headers = @{ Authorization = "Bearer $env:CANDEO_TOKEN" }
Invoke-RestMethod -Method Post -Uri "http://127.0.0.1:7317/signals?ttl=0" `
  -Headers $headers -ContentType "application/json" -Body '{"build":"failed"}'

curl, which Windows 10 and later ship, and every Linux:

curl -H "Authorization: Bearer $CANDEO_TOKEN" -H "Content-Type: application/json" \
  -d '{"build":"ok","tests":"running"}' http://127.0.0.1:7317/signals

The answer says what was taken and when it expires — ?ttl= sets the lifetime in seconds, 0 for none, and an empty string erases a name. A request is taken whole or not at all, and a refused one says why. Settings lists the signals held, with their time left, and Send a test signal tries a rule before any sender exists.

Which to use

A signal can change which effect runs — a rule, in the Automations tab — or set a value of the effect already running — a setting following it, in the gallery. The first takes the device over for a while and gives it back; the second never changes the effect, only one of its settings.

You want Use Where
the keyboard red while the build is broken, then your effect back a rule, while it holds Automations
a flash when the doorbell rings a rule, for a few seconds Automations
your gradient's colour, or an effect's speed, set by a script a setting following the signal the gallery
several signals on the keyboard at once Status row, or an effect reading them the gallery

A word someone chose — failed, ring — is for a rule, which decides what it means. A value the sender computed — a colour, a level — is for a setting.

Three ways to use it

A rule waits for it

When build is failed · on Alienware m18 R1 · show Fixed gradient · while it holds

While it holds suits a state: the rule lasts from the moment the value arrives until it changes, expires or is erased. For 5 seconds suits an event: a flash at each receipt, so a doorbell rung again flashes again. Rules keep their priority and their Try — see automations.

A setting follows it

In the gallery, any setting of any effect — a colour, a speed, a choice — has a Value | Signal switch. On Signal, it takes the value sent under the name you type, at every frame, and keeps its own while nothing arrives. The effect needs no code for it: every effect that ships with Candeo, and every one you write, follows a signal this way. A rule's settings bind the same way.

A device's Brightness follows one the same way, under its slider: send a number from 0 to 100, {"lux": 40}, and the keyboard dims to that percent of the slider, whatever effect runs.

Setting Takes
Colour #rrggbb or #rgb, the # optional
Number a number or its text, kept within the setting's range
On / off true, false, 1, 0
Choice one of its choices
Text any value, as its text, up to the setting's length

Anything else leaves the setting as it was. So a script computing a colour sends {"status": "#ff0000"} and the colour follows; a word like failed is for a rule, or for Scrolling text, which shows on the keys whatever its Text setting receives. The effects the device's firmware runs are the exception: their colour is written into the device once, not drawn frame by frame, and they have no switch.

An effect reads them all

Status row, shipped with Candeo, gives each signal a key of the top row, in the order of their names, green, amber or red by what it says — ok, running, failed — and a number as a level. A prefix keeps only the names starting with it, ci. for ci.build and ci.tests. An effect you write does the same by declaring inputs: ['signals']; the signals documentation has one to copy, drawing each number received as a bar.

Naming: facts, not devices

The sender only knows the names you chose. The link between a name and a device is made in Candeo, when you bind a setting on that device or write a rule for it.

Settings › Signals shows, under each name, what it drives — a setting on a device, a rule — and lists the names something waits for that have not arrived.

Recipe: your computer's load on the keys

A few lines send the processor's load and the memory in use, as percentages, every couple of seconds. PowerShell, left running in a window or started with your session:

Send-Load.ps1
# The processor's load and the memory in use, 0 to 100, as pc.cpu and pc.ram.
$headers = @{ Authorization = "Bearer $env:CANDEO_TOKEN" }
while ($true) {
  $cpu = (Get-CimInstance Win32_Processor | Measure-Object LoadPercentage -Average).Average
  $os = Get-CimInstance Win32_OperatingSystem
  $ram = 100 * (1 - $os.FreePhysicalMemory / $os.TotalVisibleMemorySize)
  $body = @{ 'pc.cpu' = [int]$cpu; 'pc.ram' = [int]$ram } | ConvertTo-Json -Compress
  # Ten seconds of life: when the script stops, the keys go back to what they were.
  Invoke-RestMethod -Method Post -Uri "http://127.0.0.1:7317/signals?ttl=10" `
    -Headers $headers -ContentType "application/json" -Body $body | Out-Null
  Start-Sleep -Seconds 2
}

The same on Linux:

while true; do
  cpu=$(vmstat 1 2 | awk 'END {print 100 - $15}')
  ram=$(free | awk '/^Mem:/ {printf "%d", 100 * $3 / $2}')
  curl -s -H "Authorization: Bearer $CANDEO_TOKEN" -H "Content-Type: application/json" \
    -d "{\"pc.cpu\":$cpu,\"pc.ram\":$ram}" "http://127.0.0.1:7317/signals?ttl=10" > /dev/null
  sleep 1
done

Then choose what shows it:

Temperatures and the graphics card are not in Windows' standard counters. A monitoring tool that serves its readings locally — LibreHardwareMonitor's web server, for one — can be read by the same loop and its numbers sent the same way.

Home Assistant

Once the interface is ticked, a rest_command in configuration.yaml, with the address of the computer running Candeo:

configuration.yaml
rest_command:
  candeo_signal:
    url: "http://192.0.2.23:7317/signals"
    method: post
    headers:
      Authorization: !secret candeo_bearer
    content_type: "application/json"
    payload: '{"{{ name }}": "{{ value }}"}'

with candeo_bearer: "Bearer <token>" in secrets.yaml, and an automation calling it:

action:
  - action: rest_command.candeo_signal
    data:
      name: doorbell
      value: ring

What the token protects

Signals are off until you turn them on. Every request carries the token, this computer's own included, and a request coming from a web page is refused: a page can reach any port of this computer, and the token is what it does not have. Whoever holds it can set values, and nothing else — not choose a device or an effect, not read your settings. Keep it out of shared scripts all the same, and make a new one if it leaks. The rest is in the privacy policy.

Further

The request in full — every answer and every bound — is in docs/api/signals.md, and the reasons behind these choices in docs/design/inputs-and-automations.md.