node-blink1-server
Version:
Standalone REST API server for blink(1) USB LED devices
283 lines (209 loc) • 9.16 kB
Markdown
# node-blink1-server
HTTP REST API server for [blink(1)](https://blink1.thingm.com/) USB LED notification devices.
Version 2 — rewritten from the ground up to match the full
[Blink1Control2](https://github.com/todbot/Blink1Control2) API surface, including named color
patterns, multi-device support, and a Node.js library interface.
Supports plug and unplug of blink(1) while the server is running.
## Installation & running
### One-off with npx (no install required)
```
npx node-blink1-server
npx node-blink1-server 8080
npx node-blink1-server --port 8080 --host 0.0.0.0
```
### Global install
```
npm install -g node-blink1-server
blink1-server # starts on localhost:8934
blink1-server 8080
blink1-server --port 8080 --host 0.0.0.0
```
### From source
```
git clone https://github.com/todbot/node-blink1-server.git
cd node-blink1-server
npm install
npm start # localhost:8934
npm start -- 8080 # port 8080
```
### Persistent server with pm2
For production use or boot persistence, use [pm2](https://pm2.keymetrics.io/):
```
npm install -g pm2
pm2 start blink1-server -- --port 8934
pm2 startup # configure to start on boot
pm2 save
```
### Note on native module rebuild
`node-blink1` contains a native C++ addon. If you switch Node.js versions or use this package
inside an Electron app (which has its own ABI), you need to rebuild the native module:
```
npm run clean
npm install
```
When used inside an Electron app via a `file:` reference, rebuild against Electron's ABI instead:
```
./node_modules/.bin/electron-rebuild -f -w node-blink1
```
## CLI usage
```
blink1-server [port]
blink1-server --port <port> --host <host>
```
| Option | Default | Description |
|--------|---------|-------------|
| `port` (positional or `--port`) | `8934` | HTTP port to listen on |
| `--host` | `localhost` | Bind address (`0.0.0.0` for all interfaces) |
## REST API
All endpoints are HTTP GET. Query parameters use standard URL encoding.
### Common query parameters
| Parameter | Alias | Description |
|-----------|-------|-------------|
| `rgb` | | Hex color, e.g. `%23ff00ff` (URL-encoded `#ff00ff`) |
| `time` | | Fade time in **seconds** (e.g. `0.5`) |
| `millis` | | Fade time in **milliseconds** (e.g. `500`) — takes priority over `time` |
| `ledn` | | LED number: `0` = all, `1` = top, `2` = bottom (default: `0`) |
| `blink1_id` | `id` | Device serial number to target (omit for default device) |
---
### Device info
| Endpoint | Description |
|----------|-------------|
| `GET /blink1` | List connected device serial numbers and IFTTT key |
| `GET /blink1/id` | Same as above |
| `GET /blink1/enumerate` | Re-scan for devices, then list |
### Color control
| Endpoint | Description |
|----------|-------------|
| `GET /blink1/fadeToRGB?rgb=<hex>&time=<secs>` | Fade to a specific color |
| `GET /blink1/lastColor` | Return current color state |
| `GET /blink1/on` | Fade to white |
| `GET /blink1/off` | Fade to black (stops any playing pattern) |
| `GET /blink1/red` | Named color shortcuts |
| `GET /blink1/green` | |
| `GET /blink1/blue` | |
| `GET /blink1/cyan` | |
| `GET /blink1/yellow` | |
| `GET /blink1/magenta` | |
| `GET /blink1/random` | Random color |
| `GET /blink1/blink?rgb=<hex>&count=<n>&time=<secs>` | Blink N times |
### Pattern control
A **pattern** is a named sequence of `{color, time, ledn}` steps. System patterns are built in;
user patterns are added via the API and persist in the config file (when a config file is provided).
| Endpoint | Query params | Description |
|----------|-------------|-------------|
| `GET /blink1/patterns` | | List all patterns |
| `GET /blink1/pattern/queue` | | List currently-playing pattern queue |
| `GET /blink1/pattern/play` | `pname` or `name` | Play a pattern by name |
| `GET /blink1/pattern/stop` | `pname` or `name` (optional) | Stop a pattern, or all if omitted |
| `GET /blink1/pattern/add` | `name`, `pattern` | Add a pattern from a pattern string |
| `GET /blink1/pattern/del` | `name` or `id` | Delete a user pattern |
#### Pattern strings
Pattern strings encode a repeating color sequence:
```
repeats,#color1,secs1,ledn1,#color2,secs2,ledn2,...
```
Examples:
```
3,#ff0000,0.3,0,#000000,0.3,0 # red blink 3x
0,#ff0000,0.5,1,#0000ff,0.5,2 # alternating red/blue forever (repeats=0)
```
#### Special (meta) patterns
These pattern names are interpreted directly without needing a pre-defined pattern:
| Name | Example | Description |
|------|---------|-------------|
| `#rrggbb` | `#ff00ff` | Fade to that hex color |
| `~off` | | Stop all patterns and fade to black |
| `~blink:<color>-<count>` | `~blink:#ff0000-5` | Blink color N times |
| `~blink:<color>-<count>-<secs>` | `~blink:#ff0000-5-0.3` | Blink with custom timing |
| `~pattern:<name>:<patternstr>` | `~pattern:mypatt:3,#ff0000,0.3,0` | Play an inline pattern |
## Examples
```sh
$ blink1-server &
# Device info
$ curl 'http://localhost:8934/blink1'
{
"blink1_serialnums": [ "AB0026C1" ],
"blink1_id": "DEADBEEFAB0026C1",
"status": "blink1 id"
}
# Fade to blue over 2.5 seconds on LED 2
$ curl 'http://localhost:8934/blink1/fadeToRGB?rgb=%230000ff&time=2.5&ledn=2'
{
"blink1_serialnums": [ "AB0026C1" ],
"lastColor": "#0000ff",
"lastTime": 2.5,
"lastMillis": 2500,
"lastLedn": 2,
"cmd": "fadeToRGB",
"status": "success"
}
# Blink red 5 times
$ curl 'http://localhost:8934/blink1/blink?rgb=%23ff0000&count=5&time=0.3'
# Play a built-in pattern
$ curl 'http://localhost:8934/blink1/pattern/play?pname=red+flash'
# Add and play a custom pattern
$ curl 'http://localhost:8934/blink1/pattern/add?name=police&pattern=6,%23ff0000,0.3,1,%230000ff,0.3,2,%23000000,0.1,0'
$ curl 'http://localhost:8934/blink1/pattern/play?pname=police'
# Stop all patterns
$ curl 'http://localhost:8934/blink1/pattern/stop'
# Play a meta-pattern inline (no pre-defined pattern needed)
$ curl 'http://localhost:8934/blink1/pattern/play?pname=~blink:%23ff00ff-3-0.5'
```
## Testing
Three test suites, all using the built-in `node:test` runner (no extra dependencies).
```sh
npm test # unit + HTTP integration — no device needed
npm run test:hardware # hardware-in-the-loop — skips if no device found
node --test test/blink1Service.test.js # single file
```
| Suite | File | Requires device |
|-------|------|----------------|
| blink1Service unit | `test/blink1Service.test.js` | No |
| patternsService unit | `test/patternsService.test.js` | No |
| apiServer HTTP integration | `test/apiServer.test.js` | No |
| Hardware-in-the-loop | `test/blink1Hardware.test.js` | Yes (skipped if absent) |
The hardware suite actually lights up the device — run it in an environment where that's acceptable. It covers device detection, `fadeToColor`, pattern playback, `writePatternToBlink1`, and all color/blink/pattern HTTP endpoints end-to-end.
## Node.js library usage
```js
var createBlink1Server = require('node-blink1-server');
var server = createBlink1Server({
apiConfig: { port: 8934, host: 'localhost' },
blink1Config: { deviceRescan: true, enableGamma: false },
patternsConfig: { playingSerialize: false },
patterns: [], // array of saved user pattern objects
});
// The server object is an EventEmitter
server.on('status', function(s) { console.log(s.type, s.text); });
server.on('deviceUpdated', function() { /* blink1 list changed */ });
server.on('patternsChanged', function(patterns) { /* persist patterns array */ });
server.on('configChanged', function(key, val) { /* persist config value */ });
server.start();
// Access underlying service singletons directly if needed
server.blink1Service.fadeToColor(500, '#ff0000', 0);
server.patternsService.playPatternFrom('myapp', 'red flash');
server.apiServer.stop(function() { console.log('stopped'); });
```
### Factory options
| Option | Type | Description |
|--------|------|-------------|
| `apiConfig` | object | `{ port, host }` — HTTP server config |
| `blink1Config` | object | `{ deviceRescan, enableGamma, blink1ToUse, hostId }` |
| `patternsConfig` | object | `{ playingSerialize }` — serialize pattern playback |
| `patterns` | Array | Saved user pattern objects to pre-load |
| `logger` | object | Optional logger with `.msg()` and `.error()` methods |
### Events emitted
| Event | Arguments | Description |
|-------|-----------|-------------|
| `status` | `{type, source, id, text}` | Service status / error updates |
| `deviceUpdated` | | blink(1) device list changed |
| `patternsChanged` | `patternsArray` | User patterns were saved or deleted |
| `configChanged` | `key, value` | A config value changed (e.g. `hostId`) |
## Differences from v1
| Feature | v1 | v2 |
|---------|----|----|
| Named patterns | No | Yes — play, stop, add, delete |
| Multi-device | No | Yes — `blink1_id` on all endpoints |
| Pattern persistence | No | Via `patternsChanged` event |
| Library API | No | Yes — `createBlink1Server()` factory |
| Config system | None | Plain objects / CLI args |
| Events | None | EventEmitter on server object |