@switchbot/openapi-cli 3.3.3 → 3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +127 -575
- package/dist/index.js +1361 -373
- package/dist/policy/schema/v0.2.json +59 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,44 +45,14 @@ Under the hood every surface shares the same catalog, cache, and HMAC client —
|
|
|
45
45
|
|
|
46
46
|
## Table of contents
|
|
47
47
|
|
|
48
|
-
- [Features](#features)
|
|
49
|
-
- [Requirements](#requirements)
|
|
50
|
-
- [Installation](#installation)
|
|
48
|
+
- [Features](#features) · [Requirements](#requirements) · [Installation](#installation)
|
|
51
49
|
- [Quick start](#quick-start)
|
|
52
50
|
- [Credentials](#credentials)
|
|
53
|
-
- [Policy](#policy)
|
|
51
|
+
- [Policy](#policy) · [Rules engine](#rules-engine)
|
|
54
52
|
- [Global options](#global-options)
|
|
55
|
-
- [Commands](#commands)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
- [`devices batch`](#devices-batch--bulk-commands)
|
|
59
|
-
- [`devices watch`](#devices-watch--poll-status)
|
|
60
|
-
- [`scenes`](#scenes--run-manual-scenes)
|
|
61
|
-
- [`webhook`](#webhook--receive-device-events-over-http)
|
|
62
|
-
- [`events`](#events--receive-device-events)
|
|
63
|
-
- [`status-sync`](#status-sync--mqttopenclaw-bridge)
|
|
64
|
-
- [`plan`](#plan--declarative-batch-operations)
|
|
65
|
-
- [`mcp`](#mcp--model-context-protocol-server)
|
|
66
|
-
- [`doctor`](#doctor--self-check)
|
|
67
|
-
- [`health`](#health--runtime-health-report)
|
|
68
|
-
- [`upgrade-check`](#upgrade-check--version-check)
|
|
69
|
-
- [`quota`](#quota--api-request-counter)
|
|
70
|
-
- [`history`](#history--audit-log)
|
|
71
|
-
- [`catalog`](#catalog--device-type-catalog)
|
|
72
|
-
- [`schema`](#schema--export-catalog-as-json)
|
|
73
|
-
- [`capabilities`](#capabilities--cli-manifest)
|
|
74
|
-
- [`cache`](#cache--inspect-and-clear-local-cache)
|
|
75
|
-
- [`policy`](#policy--validate-scaffold-and-migrate-policyyaml)
|
|
76
|
-
- [`daemon`](#daemon--background-rules-engine-process)
|
|
77
|
-
- [`completion`](#completion--shell-tab-completion)
|
|
78
|
-
- [Output modes](#output-modes)
|
|
79
|
-
- [Cache](#cache)
|
|
80
|
-
- [Exit codes & error codes](#exit-codes--error-codes)
|
|
81
|
-
- [Environment variables](#environment-variables)
|
|
82
|
-
- [Scripting examples](#scripting-examples)
|
|
83
|
-
- [Development](#development)
|
|
84
|
-
- [License](#license)
|
|
85
|
-
- [References](#references)
|
|
53
|
+
- [Commands](#commands): [config](#config--credential-management) · [devices](#devices--list-status-control) · [scenes](#scenes--run-manual-scenes) · [webhook](#webhook--receive-device-events-over-http) · [events](#events--receive-device-events) · [status-sync](#status-sync--mqttopenclaw-bridge) · [daemon](#daemon--background-rules-engine-process) · [plan](#plan--declarative-batch-operations) · [mcp](#mcp--model-context-protocol-server) · [doctor](#doctor--self-check) · [health](#health--runtime-health-report) · [upgrade-check](#upgrade-check--version-check) · [quota](#quota--api-request-counter) · [history](#history--audit-log) · [catalog](#catalog--device-type-catalog) · [schema](#schema--export-catalog-as-json) · [capabilities](#capabilities--cli-manifest) · [cache](#cache--inspect-and-clear-local-cache) · [policy cmd](#policy--validate-scaffold-and-migrate-policyyaml) · [completion](#completion--shell-tab-completion)
|
|
54
|
+
- [Output modes](#output-modes) · [Cache](#cache) · [Exit codes](#exit-codes--error-codes) · [Environment variables](#environment-variables)
|
|
55
|
+
- [Scripting examples](#scripting-examples) · [Development](#development) · [License](#license)
|
|
86
56
|
|
|
87
57
|
---
|
|
88
58
|
|
|
@@ -93,7 +63,7 @@ Under the hood every surface shares the same catalog, cache, and HMAC client —
|
|
|
93
63
|
- 🎨 **Dual output modes** — colorized tables by default; `--json` passthrough for `jq` and scripting
|
|
94
64
|
- 🔐 **Secure credentials** — HMAC-SHA256 signed requests; config file written with `0600`; env-var override for CI
|
|
95
65
|
- 🔍 **Dry-run mode** — preview every mutating request before it hits the API
|
|
96
|
-
- 🧪 **Fully tested** —
|
|
66
|
+
- 🧪 **Fully tested** — 2225 Vitest tests, mocked axios, zero network in CI
|
|
97
67
|
- ⚡ **Shell completion** — Bash / Zsh / Fish / PowerShell
|
|
98
68
|
|
|
99
69
|
## Requirements
|
|
@@ -273,9 +243,9 @@ Five annotated starter files covering common setups live in
|
|
|
273
243
|
With a policy.yaml (v0.2) you can declare automations that the CLI
|
|
274
244
|
executes for you. Supported triggers: **MQTT** (device events),
|
|
275
245
|
**cron** (schedule-driven), and **webhook** (local HTTP POST).
|
|
276
|
-
Supported conditions: `time_between` (quiet hours)
|
|
277
|
-
(live API check with per-tick dedup)
|
|
278
|
-
`~/.switchbot/audit.log`. `rules run` is long-running; use
|
|
246
|
+
Supported conditions: `time_between` (quiet hours), `device_state`
|
|
247
|
+
(live API check with per-tick dedup), and `llm` (AI decision — see
|
|
248
|
+
below). Every fire is recorded in `~/.switchbot/audit.log`. `rules run` is long-running; use
|
|
279
249
|
`daemon start` / `daemon reload` for the managed background mode.
|
|
280
250
|
|
|
281
251
|
**Actions** — each rule's `then` array accepts two action types:
|
|
@@ -296,71 +266,44 @@ then:
|
|
|
296
266
|
template: '{"rule":"{{ rule.name }}","fired":"{{ rule.fired_at }}"}'
|
|
297
267
|
```
|
|
298
268
|
|
|
299
|
-
|
|
300
|
-
# 1. Author rules under `automation.rules`. See examples/policies/automation.yaml
|
|
301
|
-
# for a walkthrough covering the three trigger sources.
|
|
302
|
-
|
|
303
|
-
# 2. Static-check before running.
|
|
304
|
-
switchbot rules lint # exit 0 valid, 1 error
|
|
305
|
-
switchbot rules list --json | jq . # structured summary
|
|
269
|
+
**LLM condition** — add an AI judgement step before actions fire:
|
|
306
270
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
271
|
+
```yaml
|
|
272
|
+
conditions:
|
|
273
|
+
- llm:
|
|
274
|
+
prompt: "Is the temperature above normal comfort range?"
|
|
275
|
+
provider: auto # auto | openai | anthropic
|
|
276
|
+
cache_ttl: 5m
|
|
277
|
+
budget:
|
|
278
|
+
max_calls_per_hour: 20
|
|
279
|
+
on_error: pass # fail | pass | skip
|
|
280
|
+
```
|
|
311
281
|
|
|
312
|
-
|
|
313
|
-
# --max-firings bounds a demo session.
|
|
314
|
-
switchbot rules run --dry-run --max-firings 5
|
|
282
|
+
Set `OPENAI_API_KEY` or `ANTHROPIC_API_KEY`. `rules lint` flags misconfigured LLM conditions.
|
|
315
283
|
|
|
316
|
-
|
|
317
|
-
switchbot daemon reload # managed daemon reload
|
|
284
|
+
**Decision trace** — set `automation.audit.evaluate_trace: sampled` (or `full`) in `policy.yaml` to record every evaluation decision.
|
|
318
285
|
|
|
319
|
-
|
|
320
|
-
switchbot rules
|
|
321
|
-
switchbot rules
|
|
322
|
-
switchbot rules
|
|
323
|
-
switchbot rules
|
|
286
|
+
```bash
|
|
287
|
+
switchbot rules lint # static check: exit 0 valid, 1 error
|
|
288
|
+
switchbot rules list --json | jq . # structured rule summary
|
|
289
|
+
switchbot rules explain "motion on" # trigger, conditions, actions, last fired
|
|
290
|
+
switchbot rules run --dry-run --max-firings 5 # run engine; --dry-run = audit only
|
|
291
|
+
switchbot daemon reload # hot-reload policy without restart
|
|
324
292
|
|
|
325
|
-
#
|
|
326
|
-
switchbot rules
|
|
327
|
-
|
|
328
|
-
switchbot rules
|
|
293
|
+
switchbot rules tail --follow # stream rule-* audit lines
|
|
294
|
+
switchbot rules replay --since 1h --json # per-rule fires/dries/throttled/errors
|
|
295
|
+
switchbot rules summary # aggregate fires/errors (24h)
|
|
296
|
+
switchbot rules conflicts # opposing actions, destructive cmds, quiet-hours gaps
|
|
297
|
+
switchbot rules doctor --json # lint + conflicts; exit 0 when clean
|
|
329
298
|
|
|
330
|
-
# 8. Scaffold a new rule from natural language (heuristic or LLM-backed).
|
|
331
299
|
switchbot rules suggest --intent "turn off AC at 11pm"
|
|
332
|
-
switchbot rules suggest --intent "
|
|
333
|
-
--llm auto # routes complex intents to LLM automatically
|
|
334
|
-
switchbot rules suggest --intent "..." --llm openai # explicit backend
|
|
335
|
-
# Set OPENAI_API_KEY or ANTHROPIC_API_KEY; auto mode falls back to heuristic on failure
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
`rules suggest` enforces several guardrails on LLM output so a model can't quietly arm
|
|
339
|
-
something unsafe:
|
|
340
|
-
|
|
341
|
-
- **`dry_run` is forced to `true`** on every LLM-generated rule. Review the output and
|
|
342
|
-
flip it yourself before running the engine without `--dry-run`.
|
|
343
|
-
- **Explicit overrides always win.** If you pass `--trigger`, the LLM's answer must match;
|
|
344
|
-
a mismatch fails fast. Within the same trigger, mismatched `--event` / `--schedule` /
|
|
345
|
-
`--days` / `--webhook-path` are rewritten to your value with a warning.
|
|
346
|
-
- **`--llm` is enum-validated at the CLI** (`auto | openai | anthropic`) — junk values
|
|
347
|
-
exit non-zero instead of falling through.
|
|
348
|
-
- **Notify URLs must be `http://` or `https://`.** `rules lint` and the runtime both
|
|
349
|
-
reject `file://`, `ftp://`, etc., so a generated webhook can't smuggle in a non-HTTP
|
|
350
|
-
scheme.
|
|
300
|
+
switchbot rules suggest --intent "..." --llm auto # LLM-backed (OPENAI_API_KEY or ANTHROPIC_API_KEY)
|
|
351
301
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
Webhook trigger token management:
|
|
355
|
-
|
|
356
|
-
```bash
|
|
357
|
-
switchbot rules webhook-rotate-token # rotate the bearer token for webhook triggers
|
|
358
|
-
switchbot rules webhook-show-token # print current token (creates one if absent)
|
|
302
|
+
switchbot rules trace-explain --rule "motion on" --last # why a rule fired/was blocked
|
|
303
|
+
switchbot rules simulate "motion on" --since 7d --json # replay without running the engine
|
|
359
304
|
```
|
|
360
305
|
|
|
361
|
-
See [`docs/design/phase4-rules.md`](./docs/design/phase4-rules.md) for
|
|
362
|
-
the engine's pipeline (subscribe → classify → match → conditions →
|
|
363
|
-
throttle → action → audit).
|
|
306
|
+
LLM-generated rules always have `dry_run: true` — flip it yourself after review. Notify URLs must be `http://` or `https://`. See [`docs/design/phase4-rules.md`](./docs/design/phase4-rules.md) for the full pipeline.
|
|
364
307
|
|
|
365
308
|
## Global options
|
|
366
309
|
|
|
@@ -385,23 +328,11 @@ throttle → action → audit).
|
|
|
385
328
|
- `-V`, `--version`: Print the CLI version.
|
|
386
329
|
- `-h`, `--help`: Show help for any command or subcommand.
|
|
387
330
|
|
|
388
|
-
Every subcommand supports `--help
|
|
389
|
-
|
|
390
|
-
```bash
|
|
391
|
-
switchbot --help
|
|
392
|
-
switchbot devices command --help
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
> **Tip — required-value flags and subcommands.** Flags like `--profile`, `--timeout`, `--max`, and `--interval` take a value. If you omit it, Commander will happily consume the next token — including a subcommand name. Since v2.2.1 the CLI rejects that eagerly (exit 2 with a clear error), but if you ever hit `unknown command 'list'` after something like `switchbot --profile list`, use the `--flag=value` form: `switchbot --profile=home devices list`.
|
|
331
|
+
Every subcommand supports `--help`. Use `--flag=value` form when a flag takes a value and is followed by a subcommand (e.g. `switchbot --profile=home devices list`).
|
|
396
332
|
|
|
397
333
|
### `--dry-run`
|
|
398
334
|
|
|
399
|
-
Intercepts every non-GET request:
|
|
400
|
-
sent, then exits `0` without contacting the API. `GET` requests (list, status,
|
|
401
|
-
query) are still executed so you can preview the state involved. Dry-run also
|
|
402
|
-
validates command names against the device catalog and rejects unknown commands
|
|
403
|
-
(exit 2) when the device type has a known catalog entry. Commands sent to
|
|
404
|
-
read-only sensors (e.g. Meter) are likewise rejected.
|
|
335
|
+
Intercepts every non-GET request: prints the URL/body it would have sent, then exits `0`. GET requests still execute. Also validates command names against the device catalog (exit 2 on unknown commands or read-only sensors).
|
|
405
336
|
|
|
406
337
|
```bash
|
|
407
338
|
switchbot devices command ABC123 turnOn --dry-run
|
|
@@ -417,127 +348,44 @@ switchbot devices command ABC123 turnOn --dry-run
|
|
|
417
348
|
switchbot config set-token <token> <secret> # Save to ~/.switchbot/config.json
|
|
418
349
|
switchbot config show # Print current source + masked secret
|
|
419
350
|
switchbot config list-profiles # List saved profiles
|
|
420
|
-
|
|
421
|
-
# Print (or write) the recommended AI-agent profile template
|
|
422
|
-
switchbot config agent-profile # print to stdout
|
|
423
|
-
switchbot config agent-profile --write # write to ~/.switchbot/profiles/agent.json (mode 0600)
|
|
424
|
-
switchbot config agent-profile --write --force # overwrite if it already exists
|
|
425
|
-
switchbot config agent-profile --json # structured JSON envelope
|
|
351
|
+
switchbot config agent-profile --write # write recommended AI-agent profile (mode 0600)
|
|
426
352
|
```
|
|
427
353
|
|
|
428
354
|
### `devices` — list, status, control
|
|
429
355
|
|
|
430
356
|
```bash
|
|
431
357
|
# List all physical devices and IR remote devices
|
|
432
|
-
#
|
|
433
|
-
|
|
434
|
-
switchbot devices list
|
|
435
|
-
switchbot devices ls # short alias for 'list'
|
|
436
|
-
switchbot devices list --wide
|
|
358
|
+
switchbot devices list # default 4 columns: deviceId, deviceName, type, category
|
|
359
|
+
switchbot devices list --wide # full 10-column operator view
|
|
437
360
|
switchbot devices list --json | jq '.deviceList[].deviceId'
|
|
438
|
-
|
|
439
|
-
# IR remotes: type = remoteType (e.g. "TV"), category = "ir"
|
|
440
|
-
# Physical: category = "physical"
|
|
441
361
|
switchbot devices list --format=tsv --fields=deviceId,type,category
|
|
442
362
|
|
|
443
|
-
# Filter
|
|
444
|
-
|
|
445
|
-
switchbot devices list --filter type=Bot
|
|
446
|
-
switchbot devices list --filter name
|
|
447
|
-
|
|
448
|
-
# Filter operators: = (substring; exact for `category`), ~ (substring),
|
|
449
|
-
# =/regex/ (case-insensitive regex). Clauses are AND-ed.
|
|
450
|
-
switchbot devices list --filter 'name~living'
|
|
451
|
-
switchbot devices list --filter 'type=/Hub.*/'
|
|
452
|
-
switchbot devices list --filter 'name~office,type=/Bulb|Strip/'
|
|
363
|
+
# Filter by type / name / category / room
|
|
364
|
+
# Operators: = (substring; exact for category), ~ (substring), =/regex/; clauses AND-ed
|
|
365
|
+
switchbot devices list --filter 'type=Bot'
|
|
366
|
+
switchbot devices list --filter 'name~living,type=/Bulb|Strip/'
|
|
367
|
+
switchbot devices list --filter 'category=physical'
|
|
453
368
|
|
|
454
|
-
#
|
|
455
|
-
# header, which this CLI sends on every request)
|
|
456
|
-
switchbot devices list --json | jq '.deviceList[] | select(.familyName == "Home")'
|
|
457
|
-
switchbot devices list --json | jq '[.deviceList[], .infraredRemoteList[]] | group_by(.familyName)'
|
|
458
|
-
|
|
459
|
-
# Query real-time status of a physical device
|
|
369
|
+
# Query real-time status
|
|
460
370
|
switchbot devices status <deviceId>
|
|
461
|
-
switchbot devices status
|
|
371
|
+
switchbot devices status --ids ABC,DEF,GHI # batch status
|
|
372
|
+
switchbot devices status --ids ABC,DEF --fields power,battery --format jsonl
|
|
462
373
|
|
|
463
374
|
# Resolve device by fuzzy name instead of ID (status, command, describe, expand, watch)
|
|
464
375
|
switchbot devices status --name "Living Room AC"
|
|
465
376
|
switchbot devices command --name "Office Light" turnOn
|
|
466
|
-
switchbot devices describe --name "Kitchen Bot"
|
|
467
|
-
|
|
468
|
-
# Batch status across multiple devices
|
|
469
|
-
switchbot devices status --ids ABC,DEF,GHI
|
|
470
|
-
switchbot devices status --ids ABC,DEF --fields power,battery # only show specific fields
|
|
471
|
-
switchbot devices status --ids ABC,DEF --format jsonl # one JSON line per device
|
|
472
377
|
|
|
473
378
|
# Send a control command
|
|
474
379
|
switchbot devices command <deviceId> <cmd> [parameter] [--type command|customize]
|
|
475
380
|
|
|
476
|
-
#
|
|
477
|
-
switchbot devices
|
|
478
|
-
switchbot devices
|
|
479
|
-
|
|
480
|
-
# Discover what's supported (offline reference, no API call)
|
|
481
|
-
switchbot devices types # List all device types + IR remote types (incl. role column)
|
|
482
|
-
switchbot devices commands <type> # Show commands, parameter formats, and status fields
|
|
483
|
-
switchbot devices commands Bot
|
|
484
|
-
switchbot devices commands "Smart Lock"
|
|
485
|
-
switchbot devices commands curtain # Case-insensitive, substring match
|
|
381
|
+
# Offline reference (no API call)
|
|
382
|
+
switchbot devices types # all device types
|
|
383
|
+
switchbot devices commands <type> # commands, parameter formats, status fields
|
|
486
384
|
```
|
|
487
385
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
Three commands accept `--filter`. They share one four-operator grammar,
|
|
491
|
-
but each exposes its own key set:
|
|
386
|
+
Parameters for `setAll`, `setPosition`, `setMode`, `setBrightness`, and `setColor` are validated client-side (exit 2 on bad input). `setColor` accepts `R:G:B`, `#RRGGBB`, `#RGB`, and CSS names — all normalize to `R:G:B`. Pass `--skip-param-validation` to bypass. Unknown deviceIds exit 2 by default; pass `--allow-unknown-device` for scripted pass-through.
|
|
492
387
|
|
|
493
|
-
- `devices
|
|
494
|
-
Operators: `=` (substring; **exact** for `category`), `!=` (negated),
|
|
495
|
-
`~` (substring), `=/regex/` (case-insensitive regex).
|
|
496
|
-
Keys: `type`, `name`, `category`, `room`.
|
|
497
|
-
- `devices batch`
|
|
498
|
-
Operators: same as `devices list`.
|
|
499
|
-
Keys: `type`, `family`, `room`, `category`.
|
|
500
|
-
- `events tail` / `events mqtt-tail`
|
|
501
|
-
Operators: same (tail only; mqtt-tail uses `--topic` instead).
|
|
502
|
-
Keys: `deviceId`, `type`.
|
|
503
|
-
|
|
504
|
-
Clauses are comma-separated and AND-ed. No OR across clauses — use regex
|
|
505
|
-
alternation (`=/A|B/`) for that. `category` is the one key that stays exact
|
|
506
|
-
under `=` / `!=` to preserve `category=physical` / `category!=ir` semantics.
|
|
507
|
-
A clause with an empty value (e.g. `name~`, `type=`) is rejected with exit 2 —
|
|
508
|
-
the parser refuses to guess whether an empty value means "no constraint" or
|
|
509
|
-
"match empty string". Drop the clause outright to remove the constraint.
|
|
510
|
-
|
|
511
|
-
#### Parameter formats
|
|
512
|
-
|
|
513
|
-
`parameter` is optional — omit it for commands like `turnOn`/`turnOff` (auto-defaults to `"default"`).
|
|
514
|
-
Numeric-only and JSON-object parameters are auto-parsed; strings with colons / commas / semicolons pass through as-is.
|
|
515
|
-
|
|
516
|
-
For the exact commands and parameter formats a specific device supports, query the built-in catalog:
|
|
517
|
-
|
|
518
|
-
```bash
|
|
519
|
-
switchbot devices commands <type> # e.g. Bot, Curtain, "Smart Lock", "Robot Vacuum Cleaner S10"
|
|
520
|
-
```
|
|
521
|
-
|
|
522
|
-
Generic parameter shapes (which one applies is decided by the device — see the catalog):
|
|
523
|
-
|
|
524
|
-
| Shape | Example |
|
|
525
|
-
| ------------------- | -------------------------------------------------------- |
|
|
526
|
-
| _(none)_ | `devices command <id> turnOn` |
|
|
527
|
-
| `<integer>` | `devices command <id> setBrightness 75` |
|
|
528
|
-
| `<R:G:B>` | `devices command <id> setColor "255:0:0"` |
|
|
529
|
-
| `<direction;angle>` | `devices command <id> setPosition "up;60"` |
|
|
530
|
-
| `<a,b,c,…>` | `devices command <id> setAll "26,1,3,on"` |
|
|
531
|
-
| `<json object>` | `'{"action":"sweep","param":{"fanLevel":2,"times":1}}'` |
|
|
532
|
-
| Custom IR button | `devices command <id> MyButton --type customize` |
|
|
533
|
-
|
|
534
|
-
Parameters for `setAll` (Air Conditioner), `setPosition` (Curtain / Blind Tilt), `setMode` (Relay Switch), `setBrightness` (dimmable lights), and `setColor` (Color Bulb / Strip Light / Ceiling Light) are validated client-side before the request — malformed shapes, out-of-range values, and JSON for CSV fields all fail fast with exit 2. `setColor` accepts `R:G:B`, `R,G,B`, `#RRGGBB`, `#RGB`, and CSS named colors (`red`, `blue`, …); all normalize to `R:G:B` before hitting the API. Pass `--skip-param-validation` to bypass (escape hatch — prefer fixing the argument). Command names are also case-normalized against the catalog (e.g. `turnon` is auto-corrected to `turnOn` with a stderr warning); unknown names still exit 2 with the supported-commands list.
|
|
535
|
-
|
|
536
|
-
Unknown deviceIds (not in the local cache) exit 2 by default so `--dry-run` is a reliable pre-flight gate. Unknown command names and commands on read-only sensors are also rejected during dry-run when the device type has a catalog entry. Run `switchbot devices list` first, or pass `--allow-unknown-device` for scripted pass-through.
|
|
537
|
-
|
|
538
|
-
Negative numeric parameters (e.g. `setBrightness -1` for a probe) are passed through to the command validator instead of being swallowed by the flag parser as an unknown option.
|
|
539
|
-
|
|
540
|
-
For the complete per-device command reference, see the [SwitchBot API docs](https://github.com/OpenWonderLabs/SwitchBotAPI#send-device-control-commands).
|
|
388
|
+
For per-device command and parameter details: `switchbot devices commands <type>` or the [SwitchBot API docs](https://github.com/OpenWonderLabs/SwitchBotAPI#send-device-control-commands).
|
|
541
389
|
|
|
542
390
|
#### `devices expand` — named flags for packed parameters
|
|
543
391
|
|
|
@@ -557,61 +405,46 @@ switchbot devices expand <blindId> setPosition --direction up --angle 50
|
|
|
557
405
|
|
|
558
406
|
# Relay Switch — setMode
|
|
559
407
|
switchbot devices expand <relayId> setMode --channel 1 --mode edge
|
|
408
|
+
|
|
409
|
+
# Color Bulb / Strip Light / Floor Lamp / Ceiling Light — setBrightness / setColor / setColorTemperature
|
|
410
|
+
switchbot devices expand <bulbId> setBrightness --brightness 80
|
|
411
|
+
switchbot devices expand <bulbId> setColor --color "#FF0000"
|
|
412
|
+
switchbot devices expand <bulbId> setColorTemperature --color-temp 4000
|
|
560
413
|
```
|
|
561
414
|
|
|
562
|
-
Run `switchbot devices expand <id> <command> --help` to see the available flags for any device command.
|
|
415
|
+
Run `switchbot devices expand <id> <command> --help` to see the available flags for any device command.
|
|
563
416
|
|
|
564
417
|
#### `devices explain` — one-shot device summary
|
|
565
418
|
|
|
566
419
|
```bash
|
|
567
|
-
#
|
|
568
|
-
switchbot devices explain <deviceId>
|
|
569
|
-
|
|
570
|
-
# Skip live status fetch (catalog-only output, no API call)
|
|
571
|
-
switchbot devices explain <deviceId> --no-live
|
|
420
|
+
switchbot devices explain <deviceId> # metadata + commands + live status
|
|
421
|
+
switchbot devices explain <deviceId> --no-live # catalog-only, no API call
|
|
572
422
|
```
|
|
573
423
|
|
|
574
|
-
Returns a combined view: static catalog info (commands, parameters, status fields) merged with the current live status. For Hub devices, also lists connected child devices. Prefer this over separate `status` + `describe` calls.
|
|
575
|
-
|
|
576
424
|
#### `devices meta` — local device metadata
|
|
577
425
|
|
|
578
426
|
```bash
|
|
579
427
|
switchbot devices meta set <deviceId> --alias "Office Light"
|
|
580
|
-
switchbot devices meta set <deviceId> --hide
|
|
428
|
+
switchbot devices meta set <deviceId> --hide # hide from `devices list`
|
|
581
429
|
switchbot devices meta get <deviceId>
|
|
582
|
-
switchbot devices meta list
|
|
430
|
+
switchbot devices meta list
|
|
583
431
|
switchbot devices meta clear <deviceId>
|
|
584
432
|
```
|
|
585
433
|
|
|
586
|
-
Stores local annotations
|
|
434
|
+
Stores local annotations in `~/.switchbot/device-meta.json`. `--show-hidden` on `devices list` reveals hidden devices.
|
|
587
435
|
|
|
588
436
|
#### `devices batch` — bulk commands
|
|
589
437
|
|
|
590
438
|
```bash
|
|
591
|
-
#
|
|
439
|
+
# Same command to every matching device
|
|
592
440
|
switchbot devices batch turnOff --filter 'type=Bot'
|
|
593
441
|
switchbot devices batch setBrightness 50 --filter 'type~Light,family=Living'
|
|
594
|
-
|
|
595
|
-
# Explicit device IDs (comma-separated)
|
|
596
442
|
switchbot devices batch turnOn --ids ID1,ID2,ID3
|
|
597
|
-
|
|
598
|
-
# Pipe device IDs from `devices list`
|
|
599
443
|
switchbot devices list --format=id --filter 'type=Bot' | switchbot devices batch toggle -
|
|
600
|
-
|
|
601
|
-
# Destructive commands require --yes
|
|
602
|
-
switchbot devices batch unlock --filter 'type=Smart Lock' --yes
|
|
603
|
-
|
|
604
|
-
# Skip devices whose cached status is offline (default: off)
|
|
605
|
-
switchbot devices batch turnOn --ids ID1,ID2 --skip-offline
|
|
606
|
-
|
|
607
|
-
# --idempotency-key is an alias for --idempotency-key-prefix; both append -<deviceId>
|
|
608
|
-
switchbot devices batch turnOn --ids ID1,ID2 --idempotency-key morning-lights
|
|
444
|
+
switchbot devices batch unlock --filter 'type=Smart Lock' --yes # destructive: requires --yes
|
|
609
445
|
```
|
|
610
446
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
`--skip-offline` reads from the local status cache only (no new API calls);
|
|
614
|
-
skipped devices appear under `summary.skipped` with `skippedReason:'offline'`.
|
|
447
|
+
Filter keys: `type`, `family`, `room`, `category`. Skipped-offline devices appear under `summary.skipped` when `--skip-offline` is passed.
|
|
615
448
|
|
|
616
449
|
### `scenes` — run manual scenes
|
|
617
450
|
|
|
@@ -646,140 +479,39 @@ The CLI validates that `<url>` is an absolute `http://` or `https://` URL before
|
|
|
646
479
|
|
|
647
480
|
### `events` — receive device events
|
|
648
481
|
|
|
649
|
-
Two subcommands cover the two ways SwitchBot can push state changes to you.
|
|
650
|
-
|
|
651
482
|
#### `events tail` — local webhook receiver
|
|
652
483
|
|
|
653
484
|
```bash
|
|
654
|
-
#
|
|
655
|
-
switchbot events tail
|
|
656
|
-
|
|
657
|
-
# Filter to one device
|
|
658
|
-
switchbot events tail --filter deviceId=ABC123
|
|
659
|
-
|
|
660
|
-
# Stop after 5 matching events
|
|
661
|
-
switchbot events tail --filter 'type=WoMeter' --max 5
|
|
662
|
-
|
|
663
|
-
# Stop after 10 minutes regardless of event count
|
|
664
|
-
switchbot events tail --for 10m
|
|
665
|
-
|
|
666
|
-
# Custom port / path
|
|
485
|
+
switchbot events tail # listen on port 3000
|
|
486
|
+
switchbot events tail --filter deviceId=ABC123 # filter to one device
|
|
487
|
+
switchbot events tail --filter 'type=WoMeter' --max 5 --for 10m
|
|
667
488
|
switchbot events tail --port 8080 --path /hook --json
|
|
668
489
|
```
|
|
669
490
|
|
|
670
|
-
Run `switchbot webhook setup https://your.host/hook` first
|
|
671
|
-
|
|
672
|
-
Output (one JSON line per matched event):
|
|
673
|
-
|
|
674
|
-
```json
|
|
675
|
-
{ "t": "2024-01-01T12:00:00.000Z", "remote": "1.2.3.4:54321", "path": "/", "body": {...}, "matched": true }
|
|
676
|
-
```
|
|
677
|
-
|
|
678
|
-
Filter keys: `deviceId`, `type`. Operators: `=` (substring), `~` (substring), `=/regex/` (case-insensitive regex). Clauses comma-separated and AND-ed.
|
|
491
|
+
Run `switchbot webhook setup https://your.host/hook` first. `events tail` only runs the local receiver — tunnelling (ngrok/cloudflared) is up to you.
|
|
679
492
|
|
|
680
493
|
#### `events mqtt-tail` — real-time MQTT stream
|
|
681
494
|
|
|
682
495
|
```bash
|
|
683
|
-
#
|
|
684
|
-
switchbot events mqtt-tail
|
|
685
|
-
|
|
686
|
-
# Filter to a topic subtree
|
|
687
|
-
switchbot events mqtt-tail --topic 'switchbot/#'
|
|
688
|
-
|
|
689
|
-
# Stop after 10 events
|
|
690
|
-
switchbot events mqtt-tail --max 10 --json
|
|
691
|
-
|
|
692
|
-
# Stop after a fixed duration (emits __session_start under --json before connect)
|
|
693
|
-
switchbot events mqtt-tail --for 30s --json
|
|
694
|
-
```
|
|
695
|
-
|
|
696
|
-
Connects to the SwitchBot MQTT service automatically using the same credentials configured for the REST API (`SWITCHBOT_TOKEN` + `SWITCHBOT_SECRET`). No additional MQTT configuration is required — the client certificates are provisioned on first use.
|
|
697
|
-
|
|
698
|
-
Output (one JSON line per message):
|
|
699
|
-
|
|
700
|
-
```json
|
|
701
|
-
{ "t": "2024-01-01T12:00:00.000Z", "topic": "switchbot/abc123/status", "payload": {...} }
|
|
702
|
-
```
|
|
703
|
-
|
|
704
|
-
This command runs in the foreground and streams events until you press Ctrl-C. To run it persistently in the background, use a process manager:
|
|
705
|
-
|
|
706
|
-
```bash
|
|
707
|
-
# pm2
|
|
708
|
-
pm2 start "switchbot events mqtt-tail --json" --name switchbot-events
|
|
709
|
-
|
|
710
|
-
# nohup
|
|
711
|
-
nohup switchbot events mqtt-tail --json >> ~/switchbot-events.log 2>&1 &
|
|
496
|
+
switchbot events mqtt-tail # stream all shadow events (Ctrl-C to stop)
|
|
497
|
+
switchbot events mqtt-tail --topic 'switchbot/#' # filter to topic subtree
|
|
498
|
+
switchbot events mqtt-tail --max 10 --for 30s --json
|
|
712
499
|
```
|
|
713
500
|
|
|
714
|
-
|
|
501
|
+
Credentials are provisioned automatically from the REST API config. Use `--sink` to route events to external services (`file`, `webhook`, `telegram`, `homeassistant`, `openclaw`) — see `switchbot events mqtt-tail --help` for details.
|
|
715
502
|
|
|
716
503
|
### `status-sync` — MQTT/OpenClaw bridge
|
|
717
504
|
|
|
718
|
-
|
|
719
|
-
long-running bridge that forwards SwitchBot MQTT shadow events into an OpenClaw
|
|
720
|
-
gateway. Internally it reuses `events mqtt-tail --sink openclaw`, but adds a
|
|
721
|
-
stable command surface for foreground execution, background startup, status
|
|
722
|
-
inspection, and shutdown.
|
|
505
|
+
Forwards SwitchBot MQTT shadow events into an OpenClaw gateway with stable lifecycle management.
|
|
723
506
|
|
|
724
507
|
```bash
|
|
725
|
-
#
|
|
726
|
-
switchbot status-sync
|
|
727
|
-
|
|
728
|
-
# Background mode for a normal shell session
|
|
729
|
-
switchbot status-sync start --openclaw-model home-agent
|
|
730
|
-
|
|
731
|
-
# Inspect the current bridge
|
|
508
|
+
switchbot status-sync run --openclaw-model home-agent # foreground (for supervisors)
|
|
509
|
+
switchbot status-sync start --openclaw-model home-agent # background
|
|
732
510
|
switchbot status-sync status --json
|
|
733
|
-
|
|
734
|
-
# Stop the running bridge
|
|
735
511
|
switchbot status-sync stop
|
|
736
512
|
```
|
|
737
513
|
|
|
738
|
-
Required
|
|
739
|
-
|
|
740
|
-
- `OPENCLAW_MODEL` or `--openclaw-model <id>`
|
|
741
|
-
- `OPENCLAW_TOKEN` or `--openclaw-token <token>`
|
|
742
|
-
|
|
743
|
-
Optional input:
|
|
744
|
-
|
|
745
|
-
- `OPENCLAW_URL` or `--openclaw-url <url>`
|
|
746
|
-
- `--topic <pattern>` to narrow the MQTT subscription
|
|
747
|
-
- `SWITCHBOT_STATUS_SYNC_HOME` or `--state-dir <path>` for custom runtime state
|
|
748
|
-
|
|
749
|
-
Background mode writes these files under the state directory:
|
|
750
|
-
|
|
751
|
-
- `state.json` — current pid, start time, effective command
|
|
752
|
-
- `stdout.log` — child stdout
|
|
753
|
-
- `stderr.log` — child stderr
|
|
754
|
-
|
|
755
|
-
Foreground vs background:
|
|
756
|
-
|
|
757
|
-
- `status-sync run` keeps the bridge attached to the current terminal
|
|
758
|
-
- `status-sync start` detaches the bridge and returns immediately
|
|
759
|
-
- `status-sync status` reports whether the bridge is alive plus paths/logs
|
|
760
|
-
- `status-sync stop` terminates the managed bridge process tree
|
|
761
|
-
|
|
762
|
-
#### `mqtt-tail` sinks — route events to external services
|
|
763
|
-
|
|
764
|
-
By default `mqtt-tail` prints JSONL to stdout. Use `--sink` (repeatable) to route events to one or more destinations instead:
|
|
765
|
-
|
|
766
|
-
| Sink | Required flags |
|
|
767
|
-
| --- | --- |
|
|
768
|
-
| `stdout` | (default when no `--sink` given) |
|
|
769
|
-
| `file` | `--sink-file <path>` — append JSONL |
|
|
770
|
-
| `webhook` | `--webhook-url <url>` — HTTP POST each event |
|
|
771
|
-
| `telegram` | `--telegram-token` (or `$TELEGRAM_TOKEN`), `--telegram-chat <chatId>` |
|
|
772
|
-
| `homeassistant` | `--ha-url <url>` + `--ha-webhook-id` (no auth) or `--ha-token` (REST event API) |
|
|
773
|
-
|
|
774
|
-
```bash
|
|
775
|
-
# Generic webhook (n8n, Make, etc.)
|
|
776
|
-
switchbot events mqtt-tail --sink webhook --webhook-url https://n8n.local/hook/abc
|
|
777
|
-
|
|
778
|
-
# Forward to Home Assistant via webhook trigger
|
|
779
|
-
switchbot events mqtt-tail --sink homeassistant --ha-url http://homeassistant.local:8123 --ha-webhook-id switchbot
|
|
780
|
-
```
|
|
781
|
-
|
|
782
|
-
Device state is also persisted to `~/.switchbot/device-history/<deviceId>.json` (latest + 100-entry ring buffer) regardless of sink configuration. This enables the `get_device_history` MCP tool to answer state queries without an API call.
|
|
514
|
+
Required: `OPENCLAW_MODEL` (or `--openclaw-model`) and `OPENCLAW_TOKEN`. Optional: `OPENCLAW_URL`, `--topic`, `--state-dir`. Background mode writes `state.json`, `stdout.log`, and `stderr.log` under the state directory.
|
|
783
515
|
|
|
784
516
|
### `daemon` — background rules-engine process
|
|
785
517
|
|
|
@@ -878,15 +610,20 @@ switchbot mcp serve
|
|
|
878
610
|
```
|
|
879
611
|
|
|
880
612
|
Exposes MCP tools (`list_devices`, `describe_device`, `get_device_status`,
|
|
613
|
+
`get_device_history`, `query_device_history`, `aggregate_device_history`,
|
|
881
614
|
`send_command`, `list_scenes`, `run_scene`, `search_catalog`,
|
|
882
615
|
`account_overview`, `plan_suggest`, `plan_run`, `audit_query`,
|
|
883
616
|
`audit_stats`, `policy_diff`, `policy_validate`, `policy_new`,
|
|
884
|
-
`policy_migrate`, `rules_suggest`, `rule_notifications
|
|
617
|
+
`policy_migrate`, `policy_add_rule`, `rules_suggest`, `rule_notifications`,
|
|
618
|
+
`rules_explain`, `rules_simulate`) plus a
|
|
885
619
|
`switchbot://events` resource for real-time shadow updates.
|
|
886
620
|
`rules_suggest` accepts an optional `llm` parameter (`openai | anthropic | auto`)
|
|
887
621
|
to generate YAML for complex intents via an LLM backend.
|
|
888
622
|
`rule_notifications` returns `rule-notify` audit entries, filterable by rule
|
|
889
623
|
name, time range, channel, and result.
|
|
624
|
+
`rules_explain` returns the decision trace for a specific evaluation (why a rule
|
|
625
|
+
fired or was blocked); `rules_simulate` replays historical events against a rule
|
|
626
|
+
and reports would-fire / blocked / throttled outcomes.
|
|
890
627
|
See [`docs/agent-guide.md`](./docs/agent-guide.md) for the full tool reference and safety rules (destructive-command guard).
|
|
891
628
|
|
|
892
629
|
### `doctor` — self-check
|
|
@@ -896,7 +633,7 @@ switchbot doctor
|
|
|
896
633
|
switchbot doctor --json
|
|
897
634
|
```
|
|
898
635
|
|
|
899
|
-
Runs local checks (Node version, credentials, profiles, catalog, cache, quota, clock, MQTT, policy, MCP, notify-connectivity) and exits 1 if any check fails. `warn` results exit 0. The MQTT check reports `ok` when REST credentials are configured (auto-provisioned on first use). The `notify-connectivity` check probes webhook URLs declared in `type: notify` actions. Use this to diagnose connectivity or config issues before running automation.
|
|
636
|
+
Runs local checks (Node version, credentials, profiles, catalog, catalog-schema, cache, quota, clock, MQTT, policy, MCP, keychain, path, inventory, audit, daemon, health, notify-connectivity, release-notes) and exits 1 if any check fails. `warn` results exit 0. The MQTT check reports `ok` when REST credentials are configured (auto-provisioned on first use). The `notify-connectivity` check probes webhook URLs declared in `type: notify` actions. Use this to diagnose connectivity or config issues before running automation.
|
|
900
637
|
|
|
901
638
|
`--json` output includes `maturityScore` (0–100) and `maturityLabel` (`production-ready` / `mostly-ready` / `needs-work` / `not-ready`) to give an at-a-glance readiness rating:
|
|
902
639
|
|
|
@@ -927,71 +664,44 @@ Port conflicts are reported immediately with a clear hint to choose a different
|
|
|
927
664
|
### `upgrade-check` — version check
|
|
928
665
|
|
|
929
666
|
```bash
|
|
930
|
-
switchbot upgrade-check
|
|
931
|
-
switchbot upgrade-check --json
|
|
932
|
-
switchbot upgrade-check --timeout 5000 # custom registry timeout (ms)
|
|
933
|
-
```
|
|
934
|
-
|
|
935
|
-
Queries the npm registry for the latest published version and compares it against the running version. When the registry's `dist-tags.latest` is itself a prerelease (e.g. `4.0.0-rc.1`), the check is skipped and the current version is treated as up-to-date — accidental prerelease tags don't trigger spurious upgrade prompts.
|
|
936
|
-
`--json` output:
|
|
937
|
-
|
|
938
|
-
```json
|
|
939
|
-
{
|
|
940
|
-
"current": "3.3.2",
|
|
941
|
-
"latest": "4.0.0",
|
|
942
|
-
"upToDate": false,
|
|
943
|
-
"updateAvailable": true,
|
|
944
|
-
"breakingChange": true,
|
|
945
|
-
"installCommand": "npm install -g @switchbot/openapi-cli@4.0.0"
|
|
946
|
-
}
|
|
667
|
+
switchbot upgrade-check # exits 1 when update available
|
|
668
|
+
switchbot upgrade-check --json # {current, latest, upToDate, updateAvailable, breakingChange, installCommand}
|
|
947
669
|
```
|
|
948
670
|
|
|
949
|
-
`breakingChange` is `true` when the latest major version is higher than the current — useful for agents or CI that need to distinguish breaking upgrades from patch releases.
|
|
950
|
-
|
|
951
671
|
### `quota` — API request counter
|
|
952
672
|
|
|
953
673
|
```bash
|
|
954
|
-
switchbot quota status # today's usage + last 7 days
|
|
955
|
-
switchbot quota reset
|
|
674
|
+
switchbot quota status # today's usage + last 7 days (10,000/day limit)
|
|
675
|
+
switchbot quota reset
|
|
956
676
|
```
|
|
957
677
|
|
|
958
|
-
Tracks daily API calls against the 10,000/day account limit. The counter is stored in `~/.switchbot/quota.json` and incremented on every mutating request. Pass `--no-quota` to skip tracking for a single run.
|
|
959
|
-
|
|
960
678
|
### `history` — audit log
|
|
961
679
|
|
|
962
680
|
```bash
|
|
963
|
-
switchbot history show
|
|
964
|
-
switchbot history
|
|
965
|
-
switchbot history replay 7 # re-run entry #7
|
|
681
|
+
switchbot history show --limit 20
|
|
682
|
+
switchbot history replay 7 # re-run entry #7
|
|
966
683
|
switchbot --json history show --limit 50 | jq '.entries[] | select(.result=="error")'
|
|
967
684
|
```
|
|
968
685
|
|
|
969
|
-
Reads the JSONL audit log (`~/.switchbot/audit.log` by default; override with `--audit-log --audit-log-path <path>`). Each entry records the timestamp, command, device ID, result, and dry-run flag. `replay` re-runs the original command with the original arguments.
|
|
970
|
-
|
|
971
686
|
### `catalog` — device type catalog
|
|
972
687
|
|
|
973
688
|
```bash
|
|
974
|
-
switchbot catalog show # all
|
|
975
|
-
switchbot catalog list # alias for `show`
|
|
689
|
+
switchbot catalog show # all built-in types
|
|
976
690
|
switchbot catalog show Bot # one type
|
|
977
|
-
switchbot catalog search Hub # fuzzy match
|
|
978
|
-
switchbot catalog diff #
|
|
979
|
-
switchbot catalog path # location of the local overlay file
|
|
980
|
-
switchbot catalog refresh # reload local overlay (clears in-process cache)
|
|
691
|
+
switchbot catalog search Hub # fuzzy match
|
|
692
|
+
switchbot catalog diff # local overlay vs built-in
|
|
981
693
|
```
|
|
982
694
|
|
|
983
|
-
|
|
695
|
+
Create `~/.switchbot/catalog-overlay.json` to extend or override type definitions without modifying the package.
|
|
984
696
|
|
|
985
697
|
### `schema` — export catalog as JSON
|
|
986
698
|
|
|
987
699
|
```bash
|
|
988
|
-
switchbot schema export # all types
|
|
989
|
-
switchbot schema export --type 'Strip Light'
|
|
990
|
-
switchbot schema export --role sensor
|
|
700
|
+
switchbot schema export # all types
|
|
701
|
+
switchbot schema export --type 'Strip Light'
|
|
702
|
+
switchbot schema export --role sensor
|
|
991
703
|
```
|
|
992
704
|
|
|
993
|
-
Exports the effective catalog in a machine-readable format. Pipe the output into an agent's system prompt or tool schema to give it a complete picture of controllable devices.
|
|
994
|
-
|
|
995
705
|
### `capabilities` — CLI manifest
|
|
996
706
|
|
|
997
707
|
```bash
|
|
@@ -999,127 +709,58 @@ switchbot capabilities --json
|
|
|
999
709
|
switchbot capabilities --used --json # only types seen in the local cache
|
|
1000
710
|
```
|
|
1001
711
|
|
|
1002
|
-
Prints a versioned
|
|
712
|
+
Prints a versioned manifest of surfaces, commands, and environment variables. Each command leaf includes `{mutating, consumesQuota, agentSafetyTier, typicalLatencyMs}`.
|
|
1003
713
|
|
|
1004
714
|
### `cache` — inspect and clear local cache
|
|
1005
715
|
|
|
1006
716
|
```bash
|
|
1007
|
-
|
|
1008
|
-
switchbot cache
|
|
1009
|
-
|
|
1010
|
-
#
|
|
1011
|
-
switchbot cache clear
|
|
1012
|
-
|
|
1013
|
-
# Clear only the device-list cache or only the status cache
|
|
1014
|
-
switchbot cache clear --key list
|
|
1015
|
-
switchbot cache clear --key status
|
|
717
|
+
switchbot cache show # paths, age, entry counts
|
|
718
|
+
switchbot cache clear # clear everything
|
|
719
|
+
switchbot cache clear --key list # list cache only
|
|
720
|
+
switchbot cache clear --key status # status cache only
|
|
1016
721
|
```
|
|
1017
722
|
|
|
1018
723
|
### `policy` — validate, scaffold, and migrate policy.yaml
|
|
1019
724
|
|
|
1020
|
-
Companion to the separate SwitchBot skill repository for third-party agent hosts. The skill reads behaviour (aliases, confirmations, quiet hours, audit path) from `policy.yaml`. This command group checks that file before the skill ever sees it, turning what used to be silent failures into line-accurate errors.
|
|
1021
|
-
|
|
1022
725
|
```bash
|
|
1023
|
-
#
|
|
1024
|
-
switchbot policy
|
|
1025
|
-
switchbot policy new ./custom/policy.yaml --force
|
|
1026
|
-
|
|
1027
|
-
# Validate (compiler-style errors with line:col + caret + hints)
|
|
1028
|
-
switchbot policy validate
|
|
1029
|
-
switchbot policy validate ./custom/policy.yaml
|
|
726
|
+
switchbot policy new # write a starter policy
|
|
727
|
+
switchbot policy validate # compiler-style errors (line:col + caret)
|
|
1030
728
|
switchbot policy validate --json | jq '.data.errors'
|
|
1031
|
-
switchbot policy
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
switchbot policy migrate
|
|
1035
|
-
|
|
1036
|
-
# Snapshot and restore the active policy
|
|
1037
|
-
switchbot policy backup # write timestamped backup alongside policy file
|
|
1038
|
-
switchbot policy backup --out ./backups/ # custom destination directory
|
|
1039
|
-
switchbot policy restore <backup-file> # overwrite active policy from backup (auto-backups first)
|
|
729
|
+
switchbot policy migrate # upgrade v0.1 → v0.2 in-place
|
|
730
|
+
switchbot policy backup # timestamped backup
|
|
731
|
+
switchbot policy restore <backup-file>
|
|
1040
732
|
```
|
|
1041
733
|
|
|
1042
|
-
Path resolution
|
|
1043
|
-
|
|
1044
|
-
**Exit codes:** `0` valid / `1` invalid / `2` file-not-found / `3` yaml-parse / `4` internal / `5` file already exists (on `new`, overridden with `--force`) / `6` unsupported schema version (on `migrate`).
|
|
1045
|
-
|
|
1046
|
-
Example — editing an alias without quoting the deviceId:
|
|
1047
|
-
|
|
1048
|
-
```console
|
|
1049
|
-
$ switchbot policy validate
|
|
1050
|
-
<policy-path>:14:11
|
|
1051
|
-
14 | bedroom light: 01-abc-12345
|
|
1052
|
-
^^^^^^^^^^^^^
|
|
1053
|
-
error: /aliases/bedroom light does not match pattern ^[A-Z0-9]{2,}-[A-Z0-9-]+$
|
|
1054
|
-
hint: paste the deviceId from `switchbot devices list --format=tsv`, e.g. 01-202407090924-26354212
|
|
1055
|
-
|
|
1056
|
-
✗ 1 error in <policy-path> (schema v0.1)
|
|
1057
|
-
```
|
|
1058
|
-
|
|
1059
|
-
The default policy schema shipped with the CLI (`src/policy/schema/v0.2.json`) is mirrored as `examples/policy.schema.json` in the companion skill repo; a CI job on every push diffs the two to prevent drift.
|
|
734
|
+
Path resolution: positional `[path]` > `SWITCHBOT_POLICY_PATH` > default. Exit codes: `0` valid / `1` invalid / `2` missing / `3` yaml-parse / `4` internal / `5` exists (use `--force`) / `6` unsupported version.
|
|
1060
735
|
|
|
1061
736
|
## Output modes
|
|
1062
737
|
|
|
1063
738
|
- **Default** — ANSI-colored tables for `list`/`status`, key-value tables for details.
|
|
1064
|
-
- **`--json`** — raw API payload passthrough.
|
|
1065
|
-
- **`--format=json`** — projected row view
|
|
1066
|
-
- **`--format=tsv|yaml|jsonl|id`** — tabular text formats
|
|
739
|
+
- **`--json`** — raw API payload passthrough. Errors are also JSON on **stdout**: `{ "schemaVersion": "1.2", "error": { "code", "kind", "message", "hint?" } }`.
|
|
740
|
+
- **`--format=json`** — projected row view; `--fields` applies.
|
|
741
|
+
- **`--format=tsv|yaml|jsonl|id`** — tabular text formats.
|
|
1067
742
|
|
|
1068
743
|
```bash
|
|
1069
|
-
# Raw API payload (--json)
|
|
1070
744
|
switchbot devices list --json | jq '.deviceList[] | {id: .deviceId, name: .deviceName}'
|
|
1071
|
-
|
|
1072
|
-
# Projected rows with field filter (--format)
|
|
1073
745
|
switchbot devices list --format tsv --fields deviceId,deviceName,type,cloud
|
|
1074
746
|
switchbot devices list --format id # one deviceId per line
|
|
1075
|
-
switchbot devices status <id> --format yaml
|
|
1076
747
|
```
|
|
1077
748
|
|
|
1078
749
|
## Cache
|
|
1079
750
|
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
- `devices.json`: Device metadata (id, name, type, category, hub, room…).
|
|
1083
|
-
Default TTL: 1 hour.
|
|
1084
|
-
- `status.json`: Per-device status bodies.
|
|
1085
|
-
Default TTL: off (0).
|
|
751
|
+
Two local disk caches under `~/.switchbot/`:
|
|
1086
752
|
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
753
|
+
| Cache | Default TTL | Purpose |
|
|
754
|
+
|---|---|---|
|
|
755
|
+
| `devices.json` | 1 hour | device metadata; powers offline validation |
|
|
756
|
+
| `status.json` | off | per-device status; GC'd after 24h |
|
|
1090
757
|
|
|
1091
758
|
```bash
|
|
1092
|
-
|
|
1093
|
-
switchbot devices
|
|
1094
|
-
|
|
1095
|
-
# Set both list and status TTL to 5 minutes
|
|
1096
|
-
switchbot devices status <id> --cache 5m
|
|
1097
|
-
|
|
1098
|
-
# Set TTLs independently
|
|
759
|
+
switchbot devices list --no-cache # bypass for one invocation
|
|
760
|
+
switchbot devices status <id> --cache 5m # set list + status TTL
|
|
1099
761
|
switchbot devices status <id> --cache-list 2h --cache-status 30s
|
|
1100
|
-
|
|
1101
|
-
# Disable only the list cache (keep status cache at its current TTL)
|
|
1102
|
-
switchbot devices list --cache-list 0
|
|
1103
762
|
```
|
|
1104
763
|
|
|
1105
|
-
### Cache management commands
|
|
1106
|
-
|
|
1107
|
-
```bash
|
|
1108
|
-
# Show paths, age, and entry counts
|
|
1109
|
-
switchbot cache show
|
|
1110
|
-
|
|
1111
|
-
# Clear all cached data
|
|
1112
|
-
switchbot cache clear
|
|
1113
|
-
|
|
1114
|
-
# Scope the clear to one store
|
|
1115
|
-
switchbot cache clear --key list
|
|
1116
|
-
switchbot cache clear --key status
|
|
1117
|
-
```
|
|
1118
|
-
|
|
1119
|
-
### Status-cache GC
|
|
1120
|
-
|
|
1121
|
-
`status.json` entries are automatically evicted after 24 hours (or 10× the configured status TTL, whichever is longer), so the file cannot grow without bound even when the status cache is left enabled long-term.
|
|
1122
|
-
|
|
1123
764
|
## Exit codes & error codes
|
|
1124
765
|
|
|
1125
766
|
- `0`: Success (including `--dry-run` intercept when validation passes).
|
|
@@ -1166,111 +807,22 @@ npm install
|
|
|
1166
807
|
|
|
1167
808
|
npm run dev -- <args> # Run from TypeScript sources via tsx
|
|
1168
809
|
npm run build # Compile to dist/
|
|
1169
|
-
npm test # Run the Vitest suite (
|
|
810
|
+
npm test # Run the Vitest suite (2225 tests)
|
|
1170
811
|
npm run test:watch # Watch mode
|
|
1171
812
|
npm run test:coverage # Coverage report (v8, HTML + text)
|
|
1172
813
|
```
|
|
1173
814
|
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
```text
|
|
1177
|
-
src/
|
|
1178
|
-
├── index.ts # Commander entry; mounts all subcommands; global flags
|
|
1179
|
-
├── auth.ts # HMAC-SHA256 signature (token + t + nonce → sign)
|
|
1180
|
-
├── config.ts # Credential load/save; env > keychain > file priority
|
|
1181
|
-
├── api/client.ts # axios instance + request/response interceptors;
|
|
1182
|
-
│ # --verbose / --dry-run / --timeout wiring
|
|
1183
|
-
├── credentials/
|
|
1184
|
-
│ ├── keychain.ts # Credential store interface + OS backend selection
|
|
1185
|
-
│ └── backends/ # macos.ts / linux.ts / windows.ts / file.ts
|
|
1186
|
-
├── devices/
|
|
1187
|
-
│ ├── catalog.ts # Static device catalog (commands, params, status fields)
|
|
1188
|
-
│ └── cache.ts # Disk + in-memory cache for device list and status
|
|
1189
|
-
├── install/
|
|
1190
|
-
│ ├── steps.ts # Generic step runner with rollback support
|
|
1191
|
-
│ ├── preflight.ts # Pre-flight checks (Node, npm, network, agent)
|
|
1192
|
-
│ └── default-steps.ts # Concrete steps: credentials, keychain, policy, skill, doctor
|
|
1193
|
-
├── policy/
|
|
1194
|
-
│ ├── validate.ts # Schema version dispatch + JSON Schema validation
|
|
1195
|
-
│ ├── migrate.ts # v0.1 → v0.2 migration
|
|
1196
|
-
│ ├── load.ts # YAML file loading + error handling
|
|
1197
|
-
│ ├── add-rule.ts # Rule injection into automation.rules[]
|
|
1198
|
-
│ ├── diff.ts # Structural + line diff
|
|
1199
|
-
│ └── schema/v0.2.json # Authoritative v0.2 JSON Schema
|
|
1200
|
-
├── rules/
|
|
1201
|
-
│ ├── engine.ts # Main orchestrator (MQTT + cron + webhook)
|
|
1202
|
-
│ ├── matcher.ts # Trigger + condition matchers
|
|
1203
|
-
│ ├── action.ts # Command renderer + executor
|
|
1204
|
-
│ ├── throttle.ts # Per-rule throttle gate
|
|
1205
|
-
│ ├── cron-scheduler.ts # 5-field cron + days filter
|
|
1206
|
-
│ ├── webhook-listener.ts # HTTP listener (bearer token, localhost-only)
|
|
1207
|
-
│ ├── pid-file.ts # Hot-reload via SIGHUP or sentinel file
|
|
1208
|
-
│ ├── audit-query.ts # Audit log filtering + aggregation
|
|
1209
|
-
│ ├── conflict-analyzer.ts # Static conflict detection (opposing actions,
|
|
1210
|
-
│ │ # high-freq MQTT, destructive cmds, quiet-hours gaps)
|
|
1211
|
-
│ ├── suggest.ts # Heuristic + LLM-backed rule YAML generation
|
|
1212
|
-
│ ├── notify.ts # notify action executor (webhook / file / openclaw)
|
|
1213
|
-
│ └── types.ts # Shared rule/trigger/condition/action types (CommandAction | NotifyAction)
|
|
1214
|
-
├── llm/
|
|
1215
|
-
│ ├── index.ts # createLLMProvider factory + LLM_AUTO_THRESHOLD
|
|
1216
|
-
│ ├── complexity.ts # Intent complexity scorer (0–10) for auto-routing
|
|
1217
|
-
│ ├── rule-prompt.ts # System prompt builder (embeds v0.2 schema snippet)
|
|
1218
|
-
│ └── providers/
|
|
1219
|
-
│ ├── openai.ts # OpenAI-compatible provider (uses Node.js https)
|
|
1220
|
-
│ └── anthropic.ts # Anthropic provider
|
|
1221
|
-
├── status-sync/
|
|
1222
|
-
│ └── manager.ts # Spawn/stop logic, state file, OpenClaw bridge
|
|
1223
|
-
├── lib/
|
|
1224
|
-
│ └── devices.ts # Shared logic: listDevices, describeDevice, isDestructiveCommand
|
|
1225
|
-
├── commands/
|
|
1226
|
-
│ ├── auth.ts # `auth keychain` subcommand group
|
|
1227
|
-
│ ├── config.ts
|
|
1228
|
-
│ ├── devices.ts
|
|
1229
|
-
│ ├── expand.ts # `devices expand` — semantic flag builder
|
|
1230
|
-
│ ├── explain.ts # `devices explain` — one-shot device summary
|
|
1231
|
-
│ ├── device-meta.ts # `devices meta` — local aliases / hide flags
|
|
1232
|
-
│ ├── install.ts # `switchbot install` / `uninstall`
|
|
1233
|
-
│ ├── policy.ts # `policy validate/new/migrate/diff/add-rule/backup/restore`
|
|
1234
|
-
│ ├── rules.ts # `rules suggest/lint/list/explain/run/reload/tail/replay/
|
|
1235
|
-
│ │ # conflicts/doctor/summary/last-fired/webhook-*`
|
|
1236
|
-
│ ├── scenes.ts
|
|
1237
|
-
│ ├── health.ts # `health check/serve` — report + HTTP endpoints
|
|
1238
|
-
│ ├── upgrade-check.ts # `upgrade-check` — npm registry version check
|
|
1239
|
-
│ ├── status-sync.ts # `status-sync run/start/stop/status`
|
|
1240
|
-
│ ├── webhook.ts
|
|
1241
|
-
│ ├── watch.ts # `devices watch <deviceId>`
|
|
1242
|
-
│ ├── events.ts # `events tail` / `events mqtt-tail`
|
|
1243
|
-
│ ├── mcp.ts # `mcp serve` (MCP stdio/HTTP server)
|
|
1244
|
-
│ ├── plan.ts # `plan run/validate/suggest`
|
|
1245
|
-
│ ├── cache.ts # `cache show/clear`
|
|
1246
|
-
│ ├── history.ts # `history show/replay`
|
|
1247
|
-
│ ├── quota.ts # `quota status/reset`
|
|
1248
|
-
│ ├── catalog.ts # `catalog show/diff/path`
|
|
1249
|
-
│ ├── schema.ts # `schema export`
|
|
1250
|
-
│ ├── doctor.ts # `doctor`
|
|
1251
|
-
│ ├── capabilities.ts # `capabilities`
|
|
1252
|
-
│ └── completion.ts # `completion bash|zsh|fish|powershell`
|
|
1253
|
-
└── utils/
|
|
1254
|
-
├── flags.ts # Global flag readers (isVerbose / isDryRun / getCacheMode / …)
|
|
1255
|
-
├── output.ts # printTable / printKeyValue / printJson / handleError
|
|
1256
|
-
├── format.ts # renderRows / filterFields / output-format dispatch
|
|
1257
|
-
├── audit.ts # JSONL audit log writer
|
|
1258
|
-
└── quota.ts # Local daily-quota counter
|
|
1259
|
-
tests/ # Vitest suite (1959 tests, mocked axios, no network)
|
|
1260
|
-
```
|
|
815
|
+
Source layout: `src/commands/` (one file per command group), `src/devices/` (catalog + cache), `src/rules/` (engine, matcher, throttle, audit), `src/policy/` (validate, migrate, schema), `src/llm/` (providers), `src/utils/` (output, format, flags). Tests are in `tests/` and mirror the `src/` structure.
|
|
1261
816
|
|
|
1262
817
|
### Release flow
|
|
1263
818
|
|
|
1264
|
-
Releases are cut on tag push and published to npm by GitHub Actions:
|
|
1265
|
-
|
|
1266
819
|
```bash
|
|
1267
|
-
npm version patch # bump
|
|
820
|
+
npm version patch # bump + create git tag
|
|
1268
821
|
git push --follow-tags
|
|
822
|
+
# then: GitHub → Releases → Draft → Publish
|
|
1269
823
|
```
|
|
1270
824
|
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
See [`docs/release-pipeline.md`](./docs/release-pipeline.md) for the full pre-publish and post-publish verification flow (local hooks → CI → `publish.yml` → `npm-published-smoke.yml`).
|
|
825
|
+
See [`docs/release-pipeline.md`](./docs/release-pipeline.md) for the full CI / publish verification flow.
|
|
1274
826
|
|
|
1275
827
|
## License
|
|
1276
828
|
|