npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

cc-yeelight

v0.3.1

Published

Claude Code hooks that turn a Yeelight Screen Light Bar into a focus-aware status indicator — solid status colors when you're at the terminal, breathing pulse when you're away.

Readme

cc-yeelight

npm version npm downloads node license

Turn your Yeelight Screen Light Bar into a focus-aware status indicator for Claude Code.

  • When Claude needs your input (or finishes a turn) and you're not at the terminal, the bar does a single breathing pulse to catch your eye — by default it rides the bar's current color/temperature (a warm-white desk lamp just dims and brightens).
  • When you are at the terminal, the pulse becomes a much gentler dim-and-brighten so it doesn't pull focus.
  • When you reply, the breathing stops and the bar returns to its previous brightness.

All over the local network using Yeelight's published LAN protocol. No cloud, no Mi Home runtime, no polling.


Requirements

  • A Yeelight WiFi light. Tested on the Screen Light Bar Proyeelink.light.lamp15. Any single-channel Yeelight bulb/bar that supports start_cf should work.
  • LAN Control enabled on the bar (toggle in the Yeelight app — see Setup).
  • Node.js ≥ 18 on the machine running Claude Code.
  • Claude Code ≥ the version that supports hooks (Notification, Stop, UserPromptSubmit, SessionEnd).
  • macOS for focus detection. On Linux/Windows the tool still works — it just always behaves as "unfocused" (i.e., pulses harder) since lsappinfo/osascript aren't available.

Install

From the npm registry (recommended):

npm install -g cc-yeelight

Or one-shot via npx (no global install — useful for trying it):

npx -y cc-yeelight discover

Or install straight from GitHub if you want the bleeding edge:

npm install -g github:yaroslavkrutiak/cc-yeelight

Verify:

cc-yeelight --version

Setup

1. Enable LAN Control on the bar

  1. Open the Yeelight app (Mi Home works but the Yeelight app is more reliable for this).
  2. Pair the bar / open it from your device list.
  3. Open the bar's settings (three-dot / gear icon).
  4. Toggle LAN Control to ON.

2. Create a config

cc-yeelight init

This writes ~/.config/cc-yeelight/config.json (or $XDG_CONFIG_HOME/cc-yeelight/config.json) with all defaults except host, which is null.

3. Find your bar's IP

cc-yeelight discover

The bar must be on the same network as your machine for SSDP multicast to reach it. Output looks like:

[
  {
    "address": "192.168.1.42:55443",
    "id": "0x000000001234abcd",
    "model": "lamp15",
    "name": "Screen Bar"
  }
]

4. Set the host in config

Open ~/.config/cc-yeelight/config.json and set "host" to the IP (without the port):

{ "host": "192.168.1.42", "port": 55443, ... }

5. Verify it works

cc-yeelight test

The bar will softly pulse for ~4s preserving its current color/temperature, then restore its prior state. The command also prints State before and State after; they should match.

6. Install the hooks

cc-yeelight install-hooks

This merges four hook entries into ~/.claude/settings.json (backing up the existing file first):

| Hook event | Command | Effect | |---|---|---| | Notification | cc-yeelight notify | needs your input | | Stop | cc-yeelight stop-event | turn finished | | UserPromptSubmit | cc-yeelight working | you replied → cancel pulse | | SessionEnd | cc-yeelight session-end | safety net: stop any pulse |

Existing hooks in your settings (e.g. status-line, formatter, other plugins) are left untouched.

To remove the hooks later:

cc-yeelight uninstall-hooks

How it behaves

| Event | Focused (terminal is frontmost) | Unfocused (you're elsewhere) | |---|---|---| | You typed a message | pulse cancelled, bar restored | pulse cancelled, bar restored | | Claude wants input | gentle pulse (25–55% range) | breathing (20–90%) | | Claude finished | bar restored (no extra notice) | breathing (20–90%) |

Focus is checked per event — switching apps mid-turn is handled correctly on the next event.

Each pulse defaults to a single breath that preserves the bar's current color temperature — a 3000K warm-white desk lamp just dims and brightens, it does not jump to RGB orange/green. Set preserveColor: false on any event to opt into a colored pulse using the configured color. Tune the count per event with cc-yeelight config set notification.breaths N (or stop.breaths, focused.softPulse.breaths). Use 0 for an infinite pulse that only stops on the next event.

State restoration

Each pulse snapshots the bar's state (color mode, CT/RGB, brightness, power) before changing it, then a detached worker explicitly restores set_ct_abx/set_rgb/set_bright after the breath count completes — so color temperature is preserved exactly across notifications. SessionEnd is also wired up as a safety net to clear any in-flight pulse if Claude exits unexpectedly.


Configuration reference

~/.config/cc-yeelight/config.json:

{
  "host": "192.168.1.42",
  "port": 55443,
  "notification": {
    "color": 16753920,
    "preserveColor": true,
    "minBright": 20,
    "maxBright": 90,
    "durationMs": 1500,
    "breaths": 1
  },
  "stop": {
    "color": 65280,
    "preserveColor": true,
    "minBright": 20,
    "maxBright": 90,
    "durationMs": 1000,
    "breaths": 1
  },
  "focused": {
    "softPulse": {
      "color": 16753920,
      "preserveColor": true,
      "minBright": 25,
      "maxBright": 55,
      "durationMs": 2000,
      "breaths": 1
    }
  }
}

breaths

The number of breath cycles each pulse plays before the front bar auto-restores to its prior state (CT, brightness, color mode all preserved).

  • breaths: 1 (default) — single breath, ~durationMs × 2 long, then back to normal.
  • breaths: 0 — infinite, keeps breathing until the next event cancels it (UserPromptSubmit for Notification pulses, manual cc-yeelight cancel otherwise).
  • breaths: N — N full breath cycles, then restore.

The restore happens via a detached background process, so the hook itself returns within ~1.5s — Claude is not blocked.

preserveColor

Controls whether a breath rides on the bar's current color/temperature or switches to the configured RGB color.

  • preserveColor: true (default) — the breath uses the bar's current color_mode and value, so a warm-white CT desk lamp just dims and brightens. color is ignored. (If the bar is in HSV mode, it falls back to the configured RGB.)
  • preserveColor: false — the breath uses the configured color (24-bit RGB) for a deliberately tinted alert.

Per-event, e.g.:

cc-yeelight config set notification.preserveColor false   # amber alert pulse
cc-yeelight config set stop.preserveColor true            # green-on-warm-white off

Editing config from the CLI

You can edit any value without opening the file:

cc-yeelight config list
cc-yeelight config get notification.color
cc-yeelight config set notification.breaths 3
cc-yeelight config set focused.softPulse.color 0xff8800
cc-yeelight config set notification.preserveColor false
cc-yeelight config unset focused.softPulse.preserveColor
cc-yeelight config path

Hex (0xRRGGBB or #RRGGBB), numbers, true/false, and null are all parsed automatically.

color is a 24-bit RGB integer. Some helpful values:

| Color | Hex | Decimal | |---|---|---| | Red | 0xFF0000 | 16711680 | | Orange | 0xFFA500 | 16753920 | | Yellow | 0xFFFF00 | 16776960 | | Green | 0x00FF00 | 65280 | | Blue | 0x0000FF | 255 | | Cyan | 0x00FFFF | 65535 | | Purple | 0x800080 | 8388736 | | White | 0xFFFFFF | 16777215 |

bright / minBright / maxBright are 1–100. 0 is not legal in Yeelight color flow; the bar would treat it as "no brightness change".

Environment variables

| Variable | Default | Purpose | |---|---|---| | CC_YEELIGHT_CONFIG | $XDG_CONFIG_HOME/cc-yeelight/config.json | Override config file path | | CC_YEELIGHT_HOST | (from config) | Override bar IP | | CC_YEELIGHT_PORT | 55443 | Override bar TCP port | | CC_YEELIGHT_FRONT_STATE | $TMPDIR/cc-yeelight-front.json | Override state-snapshot file |


Command reference

Setup:
  init                              Create config at ~/.config/cc-yeelight/config.json
  discover                          SSDP scan for Yeelight devices on the LAN
  install-hooks                     Wire into ~/.claude/settings.json
  uninstall-hooks                   Remove cc-yeelight entries from ~/.claude/settings.json

Configuration:
  config list                       Print full config
  config get <dotted.key>           Print a single value
  config set <dotted.key> <value>   Update a value (hex colors accepted)
  config unset <dotted.key>         Remove a value
  config path                       Print the config file path

Diagnostics:
  state                             Print the bar's current state
  focus                             Print terminal-focus detection result
  test                              Soft pulse for 4s, then restore

Hook commands (also invokable manually):
  notify                            Notification event (Claude needs your input)
  stop-event                        Stop event (Claude finished a turn)
  working                           UserPromptSubmit event (you replied)
  session-end                       SessionEnd event (safety net: clear pulse)
  cancel                            Manual: stop any pulse, restore the bar

Troubleshooting

No host configured Run cc-yeelight init, then cc-yeelight discover to find the IP, then edit ~/.config/cc-yeelight/config.json and paste it into host.

Yeelight timeout Either the bar isn't reachable from your machine or LAN Control is disabled. Check:

  • Your machine and the bar are on the same subnet.
  • The bar is powered on.
  • LAN Control is ON in the Yeelight app (it gets reset after some firmware updates).
  • Your firewall isn't blocking outbound TCP/55443.

Quick reachability test: nc -z -w 2 <bar-ip> 55443 && echo OK || echo unreachable.

cc-yeelight discover returns nothing SSDP multicast doesn't cross subnets. Make sure you're on the same Wi-Fi/SSID/VLAN as the bar. If you are and it still returns nothing, try a wired connection or another machine on the same network — some routers' "AP isolation" blocks multicast between clients. If you already know the bar's IP, you can skip discovery entirely.

Bar starts breathing but never stops Run cc-yeelight cancel to clear it manually. If it happens repeatedly, your ~/.claude/settings.json may have old hooks pointing at an absolute path from a previous install — run cc-yeelight uninstall-hooks then cc-yeelight install-hooks to refresh them.

Focus detection always says false Either you're not on macOS, or $TERM_PROGRAM isn't being set by your terminal. Run cc-yeelight focus to see what's detected — if term_program is <unset>, your terminal isn't exporting it. tmux's default-terminal setting sometimes overwrites it; ensure your .tmux.conf does not clobber $TERM_PROGRAM.


How it talks to the bar

The Yeelight LAN protocol is a newline-delimited JSON-RPC over TCP/55443. Each request looks like:

{"id":1,"method":"set_bright","params":[50,"smooth",500]}

cc-yeelight uses these methods:

  • get_prop — read current state (power, color mode, CT, RGB, brightness)
  • set_power, set_rgb, set_ct_abx, set_hsv, set_bright — restore prior state
  • start_cf / stop_cf — color flow (the breathing animation)

Discovery uses SSDP on UDP multicast 239.255.255.250:1982 (note: 1982, not the standard 1900) with ST: wifi_bulb.

Reference: Yeelight Inter-Operation Spec.


Compatibility

| Surface | Status | |---|---| | Claude Code hooks | Notification, Stop, UserPromptSubmit, SessionEnd — installed via cc-yeelight install-hooks. | | Yeelight device | Tested on Yeelight Screen Light Bar Pro (yeelink.light.lamp15). Any Yeelight WiFi light that exposes start_cf / set_ct_abx / set_rgb over the LAN protocol should work — color/temperature breath rides whichever color_mode the bar reports. | | Node.js | ≥ 18 (uses node:net, node:dgram, node:fs, node:child_process — no native modules, no transitive dependencies). | | macOS | Focus detection (isTerminalFocused) uses lsappinfo / osascript. Tested on macOS 13+. | | Linux / Windows | The tool runs and pulses the bar correctly, but focus detection is unavailable — it always behaves as "unfocused" (the louder pulse). Configure notification / stop profiles accordingly. | | Tested terminals | Apple Terminal, iTerm2, WezTerm, Ghostty, Warp, Alacritty, kitty, Tabby, Hyper, VS Code / Cursor integrated terminals. Detection keys off $TERM_PROGRAM. |

Known limitations

  • Overlapping pulses can briefly desync state. Each pulse schedules a detached restore by durationMs × breaths × 2 + 250 ms. If a second event fires inside that window (notify → notify, or notify → stop), the older restore may complete after the newer one and leave the bar slightly dim until the next event corrects it. Run cc-yeelight cancel if it ever sticks. A generation-token fix is on the roadmap.
  • macOS-only focus detection. Linux/Windows users get the unfocused profile for every event. Contributions adding xdotool / wmctrl / Windows focus detection are welcome.
  • State snapshot lives in $TMPDIR. On macOS this is /var/folders/... and survives reboots but can be purged on low disk. If the bar is reset or its state changes outside cc-yeelight, the next pulse may restore stale values; trigger a fresh cc-yeelight cancel to clean up.
  • Hook timeout budget is 8 s. Focus detection probes have a combined ~2.7 s synchronous worst case (lsappinfo 1.2 s + osascript 1.5 s) before TCP work begins. On a slow LAN this can approach the limit.
  • HSV bars fall back to RGB. Yeelight's start_cf color flow doesn't support HSV mode (3) directly; if your bar reports color_mode: 3, the breath uses the configured RGB color instead.

Development & contributing

git clone https://github.com/yaroslavkrutiak/cc-yeelight.git
cd cc-yeelight
node bin/cc-yeelight.js --version

There are no build steps and no runtime dependencies. To exercise against a real bar, point CC_YEELIGHT_HOST at it and run:

CC_YEELIGHT_HOST=192.168.1.42 node bin/cc-yeelight.js test

Pull requests are welcome — please keep the package dependency-free and the CLI under one file. For bigger changes (new platforms, new device families) open an issue first so we can sketch the surface together.

Bug reports and feature requests: https://github.com/yaroslavkrutiak/cc-yeelight/issues.

Changelog

See CHANGELOG.md for the version history.


Uninstall

cc-yeelight uninstall-hooks
npm uninstall -g cc-yeelight
rm -rf ~/.config/cc-yeelight

License

MIT — see LICENSE.