Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ dotnet publish src/Claudelk.Cli -c Release
# hooks invoke. ~1.6s cold per command.
```

CLI subcommands: `scan [--debug]`, `pair <id>`, `on`, `off`, `color <#RRGGBB>`, `blink <#RRGGBB> [pulses] [ms] [--end <#RRGGBB>]`, `brightness 0-100`, `speed 0-100`, `effect 0x80-0x9f`, `temp 0-100`. All commands accept `--device <id>` to override the saved default.
CLI subcommands: `scan [--debug]`, `pair <id>`, `ensure [--color <#RRGGBB>]` (verify connection; reconnect/re-pair/power-on as needed, then set colour — default `#ffffff`; used by the `SessionStart` hook), `on`, `off`, `color <#RRGGBB>`, `blink <#RRGGBB> [pulses] [ms] [--end <#RRGGBB>]`, `brightness 0-100`, `speed 0-100`, `effect 0x80-0x9f`, `temp 0-100`. All commands accept `--device <id>` to override the saved default.

## Tests

Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ claudelk scan # add --debug to see all BLE adverts
claudelk pair <device-id>

# 3. Drive it.
claudelk ensure # verify connection (re-pair / power-on if needed), then white
claudelk on
claudelk color "#ff8800"
claudelk blink "#ff0000" # default 4×250ms, holds colour
Expand Down Expand Up @@ -98,11 +99,15 @@ waiting for your next message. Never turns off.
| `Notification` | `permission_prompt` | red `#ff0000`, 5 s blink, ends on white |
| `Notification` | `idle_prompt` | yellow `#ffff00`, 3 s blink, ends on white |

`ensure` is used instead of plain `color` so a fallen-out Windows pairing or
a powered-off strip is repaired on session start (reconnect → idempotent
re-pair → power-on → set white).

```json
"SessionStart": [{
"hooks": [{
"type": "command", "shell": "powershell", "async": true,
"command": "& 'C:\\path\\to\\claudelk.exe' color '#ffffff'"
"command": "& 'C:\\path\\to\\claudelk.exe' ensure"
}]
}],
"Notification": [
Expand Down
26 changes: 26 additions & 0 deletions src/Claudelk.Cli/Dispatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public static async Task<int> RunAsync(string[] args)
{
"scan" => await ScanAsync(args),
"pair" => await PairAsync(args),
"ensure" => await EnsureAsync(args),
"on" => await SimpleCommandAsync(args, d => d.TurnOnAsync()),
"off" => await SimpleCommandAsync(args, d => d.TurnOffAsync()),
"color" => await ColorAsync(args),
Expand Down Expand Up @@ -56,6 +57,8 @@ private static void PrintHelp()
Commands:
scan [--debug] Discover nearby ELK-BLEDOM strips (--debug lists all BLE adverts)
pair <device-id> Save a device as the default target
ensure [--color <#RRGGBB>] Verify connection; reconnect/repair-pair/power-on if needed, then set colour
(default colour: #ffffff)
on Power on the saved device
off Power off the saved device
color <#RRGGBB | R G B> Set RGB color
Expand Down Expand Up @@ -146,6 +149,29 @@ private static async Task<int> PairAsync(string[] args)
return 0;
}

private static async Task<int> EnsureAsync(string[] args)
{
var colorHex = ExtractOption(args, "--color") ?? "#ffffff";
if (!TryParseHex(colorHex, out var r, out var g, out var b))
{
Console.Error.WriteLine("ensure: --color expects '#RRGGBB'.");
return 1;
}

using var device = await ResolveDeviceAsync(args);

// Restore the paired-list fast path if ConnectByIdAsync had to fall back
// to an advertisement scan. PairAsync is a no-op when already paired.
await device.PairWithWindowsAsync();

// Power on explicitly: this firmware silently drops SetColor writes that
// arrive while the strip is in the off state.
await device.TurnOnAsync();
await device.SetColorAsync(r, g, b);
Console.WriteLine($"Ensured {device.Name} ({device.Id}) on, colour #{r:X2}{g:X2}{b:X2}.");
return 0;
}

private static async Task<int> SimpleCommandAsync(string[] args, Func<ElkBledomDevice, Task> action)
{
using var device = await ResolveDeviceAsync(args);
Expand Down
Loading