Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ jobs:
cache: npm
- run: npm ci
- run: npm run build
- run: npm run typecheck
- name: CLI --version matches package.json
run: |
PKG=$(node -p "require('./package.json').version")
Expand Down
91 changes: 87 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ List devices, query live status, send control commands, run scenes, and manage w

- **npm package:** [`@switchbot/openapi-cli`](https://www.npmjs.com/package/@switchbot/openapi-cli)
- **Source code:** [github.com/OpenWonderLabs/switchbot-openapi-cli](https://github.com/OpenWonderLabs/switchbot-openapi-cli)
- **Release notes:** [GitHub Releases](https://github.com/OpenWonderLabs/switchbot-openapi-cli/releases)
- **Issues / feature requests:** [GitHub Issues](https://github.com/OpenWonderLabs/switchbot-openapi-cli/issues)

---
Expand Down Expand Up @@ -42,6 +43,7 @@ Under the hood every surface shares the same catalog, cache, and HMAC client —
- [`devices`](#devices--list-status-control)
- [`scenes`](#scenes--run-manual-scenes)
- [`webhook`](#webhook--receive-device-events-over-http)
- [`events`](#events--receive-mqtt-device-updates)
- [`batch`](#batch--run-multiple-commands)
- [`watch`](#watch--poll-device-status)
- [`mcp`](#mcp--model-context-protocol-server)
Expand Down Expand Up @@ -140,7 +142,8 @@ switchbot config show

| Option | Description |
| --------------------------- | ------------------------------------------------------------------------ |
| `--json` | Print the raw JSON response instead of a formatted table |
| `--json` | Print a structured JSON envelope instead of a formatted table |
| `--json-legacy` | Opt out of the v1.6.0 envelope — emit the bare v1.5.0 payload (removed in v1.7.0) |
| `--format <fmt>` | Output format: `tsv`, `yaml`, `jsonl`, `json`, `id` |
| `--fields <cols>` | Comma-separated column names to include (e.g. `deviceId,type`) |
| `-v`, `--verbose` | Log HTTP request/response details to stderr |
Expand Down Expand Up @@ -179,6 +182,41 @@ switchbot devices command ABC123 turnOn --dry-run
# [dry-run] body: {"command":"turnOn","parameter":"default","commandType":"command"}
```

### JSON envelope (v1.6.0+)

Every `--json` response is wrapped in a unified envelope so agents can parse one
shape across every command:

```json
{
"schemaVersion": "1",
"ok": true,
"data": { /* command-specific payload */ },
"meta": { "command": "devices.status", "durationMs": 123 }
}
```

Errors use the same envelope with `ok: false` and an `error` block:

```json
{
"schemaVersion": "1",
"ok": false,
"error": { "code": 190, "kind": "api", "subKind": "device-busy", "message": "...", "hint": "...", "retryable": false },
"meta": { "command": "devices.command", "durationMs": 12 }
}
```

Key changes vs v1.5.0:

- **Errors now go to `stdout`** in `--json` mode (previously `stderr`). Agents can pipe a single stream.
- A top-level `schemaVersion: "1"` lets consumers detect breaking shape changes.
- Streaming commands (`devices watch`, `events stream`, `events tail`) still emit **bare JSON per line** — the envelope applies to one-shot responses.

Migration: scripts that parsed `--json` against the v1.5.0 shape can either
unwrap `.data` or pass `--json-legacy` for the old bare payload (removed in
v1.7.0).

## Commands

### `config` — credential management
Expand Down Expand Up @@ -278,6 +316,36 @@ switchbot webhook delete https://your.host/hook

The CLI validates that `<url>` is an absolute `http://` or `https://` URL before calling the API. `--enable` and `--disable` are mutually exclusive.

### `events` — receive MQTT device updates

```bash
# Subscribe to all device shadow updates over MQTT
switchbot events stream

# Filter events by device type
switchbot events stream --filter type="Motion Sensor"

# Filter by device ID and stop after 10 events
switchbot events stream --filter deviceId=ABC123 --max 10

# Verify MQTT connectivity without streaming
switchbot events stream --probe

# Output as JSONL for scripting
switchbot events stream --filter type="Contact Sensor" --json | jq '.payload.moveDetected'
```

**Important:** `events stream` depends on the **SwitchBot IoT MQTT service**, which is not part of the official OpenAPI and is not documented in the SwitchBot API reference.
This feature provides real-time device state changes but relies on an undocumented service endpoint.
If SwitchBot's policy changes, this service may become unavailable; fall back to [`devices watch`](#watch--poll-device-status) for polling-based monitoring.

Credentials are cached in `~/.switchbot/mqtt-credential.json` with a 1-hour TTL; use `--no-cache` to fetch fresh credentials.

Output is JSONL (one event per line):
```json
{ "ts": "2026-04-19T10:23:45.123Z", "deviceId": "ABC123", "deviceType": "Motion Sensor", "payload": { "battery": 92, "moveDetected": true } }
```

### `completion` — shell tab-completion

```bash
Expand Down Expand Up @@ -324,10 +392,25 @@ Output is a stream of JSON status objects (with `--json`) or a refreshed table.
```bash
# Start the stdio MCP server (connect via Claude, Cursor, etc.)
switchbot mcp serve

# Or HTTP transport for multi-tenant MCP hosts
switchbot mcp serve --port 3030
# Pass x-switchbot-profile: <name> header (or ?profile=<name>) to route
# a request to a specific credential profile.
```

Exposes 7 MCP tools: `list_devices`, `describe_device`, `get_device_status`, `send_command`, `list_scenes`, `run_scene`, `search_catalog`.
See [`docs/agent-guide.md`](./docs/agent-guide.md) for the full tool reference and safety rules (destructive-command guard).
Exposes 15 MCP tools:

- Control/read: `list_devices`, `describe_device`, `get_device_status`, `send_command`, `devices_batch`, `list_scenes`, `run_scene`, `search_catalog`
- Plans & events: `plan_run`, `events_recent`
- Webhook management: `webhook_setup`, `webhook_query`, `webhook_update`, `webhook_delete`
- Diagnostics: `quota_status`

Plus one subscribable resource `switchbot://events` that pushes `notifications/resources/updated` on every MQTT shadow event (no polling required).

Destructive commands (Smart Lock unlock, Garage open, Keypad createKey/deleteKey) require `confirm: true` on `send_command` and `yes: true` on `devices_batch` / `plan_run`; refused attempts are recorded in the audit log when `--audit-log` is enabled.

See [`docs/agent-guide.md`](./docs/agent-guide.md) for the full tool reference and safety rules.

### `cache` — inspect and clear local cache

Expand Down Expand Up @@ -498,7 +581,7 @@ src/
├── format.ts # renderRows / filterFields / output-format dispatch
├── audit.ts # JSONL audit log writer
└── quota.ts # Local daily-quota counter
tests/ # Vitest suite (592 tests, mocked axios, no network)
tests/ # Vitest suite (725 tests, mocked axios, no network)
```

### Release flow
Expand Down
Loading
Loading