Releases: OpenWonderLabs/switchbot-openapi-cli
3.0.0
Major release — breaking changes, full feature parity across all branches.
Includes all features shipped in v2.10.0–v2.15.0: one-command install/uninstall, L3 autonomous rule authoring, plan suggest + --require-approval, MCP policy tools, and rules engine enhancements.
Breaking changes
- Remove
destructive: booleanfield from all CLI/MCP output (schema export,devices describe,agent-bootstrap,explain, MCPcatalog_search). UsesafetyTier === 'destructive'instead. - Drop policy schema v0.1 support. Files with
version: "0.1"are rejected with a migration hint. Runswitchbot policy migrateon CLI ≤2.15 before upgrading. buildStatusSyncChildArgsno longer acceptsopenclawToken; pass via envOPENCLAW_TOKEN.
What's new
switchbot install/uninstall— one-command setup with preflight, rollback, and 4-backend keychain (macOS / Linux / Windows / file)switchbot auth keychain— describe, get, set, delete, migrate credentials in native OS keychainswitchbot policy— validate, new, migrate, diff, add-rule; v0.2 schema with typedautomation.rules[]switchbot rules— suggest, lint, list, run, reload, tail, replay, webhook-rotate-token; MQTT + cron + webhook triggers;time_between/device_state/all/any/notconditions; per-rule throttle + dry-run; SIGHUP hot-reloadswitchbot status-sync— run, start, stop, status; background OpenClaw MQTT bridge- MCP:
plan_suggest,plan_run,audit_query,audit_stats,policy_diff,policy_validate,policy_new,policy_migrate doctor: policy check, MCP check, keychain backend detection, quota + catalog-schema parity checks- 1765 tests (up from 1624)
Migration
- if (spec.destructive) { ... }
+ if (spec.safetyTier === 'destructive') { ... }Policy v0.1 → v0.2 (run on CLI ≤2.15 before upgrading):
switchbot policy migrate
v2.7.2 — AI-first maturity release (bundles 2.7.0 + 2.7.1 + 2.7.2)
Consolidated v2.7 release. This single tag bundles three semver-logical cuts that were developed and merged together (PR #28). Neither v2.7.0 nor v2.7.1 were ever published as standalone tags — the CI size-budget fix (v2.7.2) was folded in before release.
v2.7.2 — CI size-budget fix
schema export --compact— dropped theresourcesblock from compact output. In v2.7.0 the resources catalog (~12 KB) was added to the schema payload unconditionally, which pushedschema export --compact --usedpast the 15 KB agent-prompt budget enforced by CI. Theresourcesblock is still emitted under the full (non---compact) output, and remains available viacapabilities --json, which is the canonical source for CLI resource metadata. No behaviour change forcapabilities --jsonconsumers.
v2.7.1 — help identity / AI discoverability
Top-level --help / --help --json and every subcommand description now lead with the SwitchBot product category (smart home: lights, locks, curtains, sensors, plugs, IR appliances) so AI agents reading help text can identify scope without parsing the catalog.
- New
src/commands/identity.ts— single source of truth (IDENTITY + PRODUCT_TAGLINE); replaces duplicated constants incapabilities.tsandagent-bootstrap.ts - Top-level
switchbot --help— rewritten from "Command-line tool for SwitchBot API v1.1" to "SwitchBot smart home CLI — control lights, locks, curtains, sensors, plugs, and IR appliances (TV/AC/fan) via Cloud API v1.1; run scenes, stream real-time events, and integrate AI agents via MCP." - Root
--help --json— carriesproduct / domain / vendor / apiVersion / apiDocs / productCategories[8]at top level; subcommand help-json unchanged (root-only to keep per-command payloads tight) - Subcommand descriptions —
catalog / schema / history / plan / doctor / capabilitiesnow self-identify with "SwitchBot" - README intro — leads with product category, not the API version
agent-bootstrap --json— additive identity fields:apiDocs,deviceCategories,productCategories,agentGuide
No "BLE" in tagline/README/CHANGELOG by design: the CLI only talks to the SwitchBot Cloud API over HTTPS. BLE-only devices are reached through a SwitchBot Hub, which the Cloud API handles transparently.
v2.7.0 — AI-first maturity (headline release)
Broader field-alias coverage, richer capability metadata, agent-discoverable resource surfaces.
Added
- Field aliases — registry expanded from ~10 to ~51 canonical keys (~98% coverage of catalog
statusFields+ webhook payload fields) safetyTierenum (5 tiers) —read | mutation | ir-fire-forget | destructive | maintenance; replaces legacydestructive: booleanflagDeviceCatalogEntry.statusQueries— read-tier catalog entries exposing queryable status fields; powerssafetyTier: 'read'andcapabilities.catalog.readOnlyQueryCountcapabilities.resources— new top-levelresourcesblock exposing scenes (list/execute/describe), webhooks (4 endpoints + 15 event specs + constraints), and keypad keys (4 types). Each endpoint/event declares its safety tier- Multi-format output —
--format=yamland--format=tsvfor all non-streaming commands - doctor upgrades —
--section / --list / --fix / --yes / --probeflags; new checkscatalog-schema,audit,mcp(dry-run); live MQTT probe (5 s timeout under--probe) - Streaming JSON contract — every streaming command emits a
{ schemaVersion, stream: true, eventKind, cadence }header as its first NDJSON line (docs/json-contract.md) - Events envelope — unified
{ schemaVersion, t, source, deviceId, topic, type, payload }acrossevents tailandevents mqtt-tail - MCP tool schema completeness — every tool input schema now carries
.describe()annotations - Help-JSON contract test — table-driven coverage for all 16 top-level commands
- batch
--emit-plan— canonical alias for deprecated--plan
Changed
- Error envelope — all error paths route through
exitWithError()/handleError() - Quota accounting — records on attempt (request interceptor) instead of on success, so timeouts / 4xx / 5xx count against daily quota
Deprecated (all removed in v3.0)
destructive: booleanon catalog entries — derived fromsafetyTier === 'destructive'DeviceCatalogEntry.statusFields— superseded bystatusQueriesbatch --plan— renamed to--emit-plan; old flag prints deprecation warningevents taillegacybody/remotefields — superseded by unified envelope
Fixed
- Quota counter no longer under-counts requests that fail at the transport or server layer
Install: npm install -g @switchbot/openapi-cli
Full changelog: CHANGELOG.md
Compare: v2.6.4...v2.7.2
2.6.4
Added
devices describenow shows a tip for device types that supportdevices expand(Air Conditioner, Curtain, Curtain 3, Blind Tilt, Relay Switch 2PM);--jsonoutput includes anexpandHintfield withcommand,flags, and a ready-to-runexamplestring
Fixed
--filter controlType=Xnow works correctly;controlTypewas documented as a filterable key but was missing from the canonical key registryfield-aliases.ts: removedcategoryfrom thecontrolTypealias list to prevent collision with the physical/IRcategoryfilter key- MCP stdio path now handles
SIGTERMandSIGINTwith the same graceful shutdown as the HTTP path (30 s force-exit timeout,isShuttingDownguard)
Changed
- Extracted
exitWithError()helper inoutput.ts; deduplicatedisJsonMode()acrossindex.tsandconfig.ts
v2.6.3
Fixed
- MCP
send_commanddry-run now strictly rejects unknown command names when catalog has a definitive match (#55) - MCP
send_commanddry-run rejects commands sent to read-only sensors (e.g. Meter) - Previous v2.6.2 fix used lenient
validateCommandwhich silently passed when catalog lookup was ambiguous
2.6.2
Fixed
scenes execute --dry-runnow outputs structured result on stdout (#54)- MCP
send_commanddry-run validates command name against catalog (#55) - MCP
run_scenedry-run validates sceneId against scene list (#56)
v2.6.1
v2.6.0
See CHANGELOG.md for the full list of changes.
v2.5.1
Round-2 + Round-3 smoke-test response — 24 bugs closed in one patch.
Breaking: --filter grammar unified across devices list, devices batch, events tail / mqtt-tail. devices batch and events tail = is now substring (was exact); ~= spelling removed (use ~). See CHANGELOG §Changed (BREAKING) for migration.
Full notes: CHANGELOG.md
v2.5.0 — history aggregate + v2.4.0 report bug fixes
[2.5.0] - 2026-04-20
Added
history aggregate <deviceId>— on-demand bucketed statistics
(count / min / max / avg / sum / p50 / p95) over the append-only JSONL
device history. Flags:--since/--from/--to, repeatable
--metric,--agg <csv>,--bucket <dur>,
--max-bucket-samples <n>. Non-numeric samples are skipped; empty
metrics are omitted from their bucket.- MCP
aggregate_device_history— same contract as the CLI, exposed
as a read-tier tool (_meta.agentSafetyTier: "read") with a strict
Zod input schema (unknown keys reject with JSON-RPC-32602). - Capabilities manifest — new
history aggregateentry in
COMMAND_META; newaggregate_device_historyentry in
surfaces.mcp.tools. scenes describe <sceneId>— returns{sceneId, sceneName, stepCount:null, note}; SwitchBot API v1.1 does not expose scene
steps. Unknown sceneId returns structuredscene_not_foundwith a
candidate list. (bug #17)--no-colorflag +NO_COLORenv var — honors the standard
https://no-color.org/ contract; disables chalk colors globally before
any subcommand runs. (bug #12)--format markdown— accepted as an alias for--format table
with--table-style markdownforced at render time, independent of
the user's--table-styleflag. (bug #8)cache status— alias forcache show, matching thequota
subcommand's status/show parity. (bug #9)
Fixed (security & correctness — v2.4.0 report)
- MCP strict input schemas on all 11 tools — unknown keys now
reject with JSON-RPC-32602. Fixes the v2.4.0 hole where
send_command {dryRun:true}silently fired the command anyway —
particularly dangerous for Smart Lock / Garage. (bug #4) - MCP
dryRunon mutating tools —send_commandandrun_scene
acceptdryRun:true; when set, no API call is made and the response
is{ok:true, dryRun:true, wouldSend:{...}}. (bug #4) - MCP
serverInfo.version— wired topackage.json#version; was
hardcoded"2.0.0"despite the CLI reporting the real version
everywhere else. (bug #5) - MCP
_meta.agentSafetyTier— every tool now emits its tier
(read/action/destructive). Release notes already claimed
this but no tool was actually emitting it. (bug #6) --namerequire-unique + exact-match — exact-name short-circuit
inname-resolverwas returning the exact hit even when substring
matches existed, defeating the write-pathrequire-uniquedefault.
Exact hits now enter the candidate list underrequire-uniqueand
go through the ambiguity check like any other match. (bug #1)history verifyon missing audit.log — exits 0 withstatus:"warn"
andfileMissing:truerather than exit 1. Malformed/unversioned
content still exits 1 as before. (bug #11)events mqtt-tailcontrol events —__connect/__reconnect/
__disconnect/__heartbeatnow append to
~/.switchbot/device-history/__control.jsonlalongside per-device
files, honoring the v2.4.0 "every event is persisted" claim. (bug #10)
Changed (docs)
--idempotency-keyhelp text ondevices command,devices batch,
plan run,history replaynow explicitly mentions the process-local
60s scope — independent CLI invocations do NOT share the cache. (bug #14)mcp --helpnow says "eleven tools" and lists all 11 names. (bug #15)- New
docs/verbose-redaction.md— documents the nine masked headers
(authorization,token,sign,nonce,x-api-key,cookie,
set-cookie,x-auth-token,t) and the--trace-unsafeopt-out. (bug #16) plan schemanow includesagentNotes.deviceNameStrategydeclaring
that plan steps usingdeviceNameresolve withrequire-unique. (bug #18)agent-bootstraphintsfield carries JSDoc +schema export
declares it incliAddedFields— empty array means "no hints",
never null. (bug #13)
Notes
- Storage format unchanged. Aggregation streams the existing JSONL
rotation files viareadline— zero memory blow-up for large
windows, with a hard ceiling of--max-bucket-samples× 8 bytes per
(bucket × metric)for quantile computation. - Quantiles use nearest-rank on sorted per-bucket samples; if the cap
is reached the result carriespartial: trueand a per-bucket
notes[]entry.count / min / max / avg / sumremain exact. - All bug-fix items bundled into 2.5.0 rather than shipping a separate
2.4.1. Source of bug numbers: the v2.4.0 smoke-test report at
D:/servicdata/openclaw/workspace/switchbot-cli-v2.4.0-report.md.
Not included (deferred)
- Cross-device aggregation (agents merge locally).
- Trend / rate-of-change helpers (derivable from bucket series).
--fill-emptyfor missing buckets.- Disk-persisted idempotency cache for cross-invocation replay
(report bug #2). Process-local is the documented 2.4.0 contract;
the--helptext now states this plainly — no code change. Revisit
only if a concrete use case forces it. capabilities --types/--fields/--used(report bug #7).
schema exportalready offers these for the agent bootstrap path;
capabilities --compact --surface <s>covers the payload-size story.
v2.4.0 — Agent-experience overhaul + device history
Highlights
2.4.0 bundles 19 OpenClaw/Claude integration feedback items (P0–P3) plus a new device-history query subsystem into a single minor release. All schema changes are additive-only — existing MCP and CLI integrations keep working unchanged and pick up the new fields on upgrade.
P0 — Correctness & security
- IR command verifiability —
devices commandresponses for IR devices now carryverification: { verifiable: false, reason, suggestedFollowup }; MCPsend_commandmirrors the field. Agents no longer treat one-way IR transmissions as verified success. config set-tokensecret scrubbing — positional invocations have their token/secret replaced inprocess.argvbefore any hook, audit log, or verbose trace can observe them. Interactive hidden-echoreadlineis now the primary path.
P1 — Agent hardening
devices status/devices commandaccept--namedirectly on the subcommand (previously root-only).- New
src/devices/resolve-name.tswith six strategies (exact | prefix | substring | fuzzy | first | require-unique). Reads default tofuzzy; writes default torequire-uniqueand fail with exit code 2 +error: "ambiguous_name_match"and a candidate list when multiple devices match. Global filters--type,--room,--category,--name-strategycompose with--name. schema export/capabilitiesgained--compact,--types <csv>,--used,--fields <csv>,--surface cli|mcp|plan. Banners/tips/progress messages go to stderr; stdout is exactly one JSON document; non-TTY no longer emits ANSI.- Every leaf command in
capabilitiescarries{ mutating, consumesQuota, idempotencySupported, agentSafetyTier: "read"|"action"|"destructive", verifiability, typicalLatencyMs }. MCP tools mirror the tier inmeta.agentSafetyTier.
Device history — new subsystem
- JSONL storage — every
events mqtt-tailevent / MCP status refresh is appended to~/.switchbot/device-history/<deviceId>.jsonl. Files rotate at 50 MB (.jsonl.1 → .2 → .3, oldest discarded). Writes are best-effort with0o600perms. history range <deviceId>— time-windowed query with--since 7d/--from <iso>/--to <iso>, payload-field projection via repeatable--field <name>,--limit <n>(default 1000). Uses streamingreadline— even 50 MB files never load into memory.history stats <deviceId>— reports file count, total bytes, record count, earliest/latest timestamp.- MCP
query_device_history— same contract as the CLI, exposed as an agent tool with a 1000-record default safety cap.
P2 — DX & stability
doctor --jsonstable contract — locked shape{ ok, generatedAt, checks[], summary }; each check is{ name, status: ok|warn|fail, detail }. Theclockcheck now probes the real API once and reportsskewMs.events mqtt-tailcontrol events — synthesized JSONL records of__connect/__reconnect/__disconnect/__heartbeat. Every real event gets a UUIDv4eventIdwhen the broker doesn't supply one.devices batch --stagger <ms>/--max-concurrent <n>/--plan— throttled concurrent execution with per-stepstartedAt/finishedAt/durationMs/replayedtelemetry and a planner that prints the plan JSON without executing.- Idempotency contract +
replayedflag — cache hits returnreplayed: true. A reused key with a different(command, parameter)shape within the 60 s window exits 2 witherror: "idempotency_conflict"and the old/new shape in the payload. The cache is process-local in-memory (SHA-256 fingerprints on the heap; raw key never stored, no disk persistence) — replay + conflict apply within a single long-lived process (MCP session,devices batch,plan run,history replay) and do not carry across independent CLI invocations. - Profile label / description / daily cap / default flags —
config set-tokengained--label,--description,--daily-cap <N>,--default-flags "<csv>". The daily cap is enforced before any request leaves the CLI (pre-flight refusal, exit 2). --verboseheader redaction —Authorization,token,sign,t,nonce, cookies are mid-masked in verbose output.--trace-unsafeopts into raw output with a prominent one-time warning.
P3 — Polish
quota showalias forquota status.showSuggestionAfterErroracross the full subcommand tree — typos likedevices lstnow suggestdevices list.schema exportdeclarescliAddedFields—_fetchedAt,replayed,verification— so agents can distinguish CLI-synthesized data from upstream API fields.switchbot agent-bootstrap [--compact]— single-command aggregate (identity, cached devices, catalog, quota, profile, safety tiers, quick reference) that stays under 20 KB in--compactmode. Offline-safe; no API calls.--table-style <unicode|ascii|simple|markdown>and--format markdown— non-TTY defaults toascii;markdownemits fenced|col|col|tables for agent UI embedding.- Audit log versioning — every line carries
"auditVersion": 1. Newdocs/audit-log.mddocuments the format, crash-safety, and rotation guidance. Newswitchbot history verifyreports parsed / malformed / version counts and exits non-zero on malformed content.
Continuous integration
offline-smokeCI job — seeds a fixture device cache and assertsagent-bootstrap --compact< 20 KB andschema export --compact --used< 15 KB on every push / PR.Smoke (real device)workflow —workflow_dispatch-only job that validates IR verification,--idempotency-keyacceptance, andhistory rangeend-to-end against a live account.
Migration notes
Fully backwards compatible. No fields changed or were removed; only added. Existing MCP and CLI integrations continue to work. Agents that want the richer context can refresh their prompts by running switchbot agent-bootstrap --compact once per session instead of combining doctor + capabilities + schema + devices list.
See the full CHANGELOG for the complete list.