@switchbot/openapi-cli 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 chenliuyun
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,278 @@
1
+ # switchbot-openapi-cli
2
+
3
+ Command-line interface for the [SwitchBot API v1.1](https://github.com/OpenWonderLabs/SwitchBotAPI).
4
+ List devices, query status, send control commands, run scenes, and manage webhooks from your terminal or shell scripts.
5
+
6
+ ## Requirements
7
+
8
+ - Node.js ≥ 18
9
+ - A SwitchBot account with Developer Options enabled (see [Credentials](#credentials))
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ git clone <repo-url>
15
+ cd switchbot-openapi-cli
16
+ npm install
17
+ npm run build
18
+ # Optional: make it globally available as `switchbot`
19
+ npm link
20
+ ```
21
+
22
+ During development you can run directly from sources without building:
23
+
24
+ ```bash
25
+ npm run dev -- devices list
26
+ ```
27
+
28
+ ## Credentials
29
+
30
+ The CLI reads credentials in this order (first match wins):
31
+
32
+ 1. **Environment variables** — `SWITCHBOT_TOKEN` and `SWITCHBOT_SECRET`
33
+ 2. **Config file** — `~/.switchbot/config.json` (written by `config set-token`, mode `0600`)
34
+
35
+ Obtain the token and secret from the SwitchBot mobile app:
36
+ **Profile → Preferences → Developer Options → Get Token**.
37
+
38
+ ```bash
39
+ # One-time setup (writes ~/.switchbot/config.json)
40
+ switchbot config set-token <token> <secret>
41
+
42
+ # Or export environment variables (e.g. in CI)
43
+ export SWITCHBOT_TOKEN=...
44
+ export SWITCHBOT_SECRET=...
45
+
46
+ # Confirm which source is active and see the masked secret
47
+ switchbot config show
48
+ ```
49
+
50
+ ## Global options
51
+
52
+ | Option | Description |
53
+ | ------------------- | ------------------------------------------------------------------ |
54
+ | `--json` | Print the raw JSON response instead of a formatted table |
55
+ | `-v`, `--verbose` | Log HTTP request/response details to stderr |
56
+ | `--dry-run` | Print mutating requests (POST/PUT/DELETE) without sending them |
57
+ | `--timeout <ms>` | HTTP request timeout in milliseconds (default: `30000`) |
58
+ | `--config <path>` | Override credential file location (default: `~/.switchbot/config.json`) |
59
+ | `-V`, `--version` | Print the CLI version |
60
+ | `-h`, `--help` | Show help for any command or subcommand |
61
+
62
+ Every subcommand supports `--help`, and most include a parameter-format reference and examples.
63
+
64
+ ```bash
65
+ switchbot --help
66
+ switchbot devices command --help
67
+ ```
68
+
69
+ ### `--dry-run`
70
+
71
+ Intercepts every non-GET request: the CLI prints the URL/body it would have
72
+ sent, then exits `0` without contacting the API. `GET` requests (list, status,
73
+ query) are still executed so you can preview the state involved.
74
+
75
+ ```bash
76
+ switchbot devices command ABC123 turnOn --dry-run
77
+ # [dry-run] Would POST https://api.switch-bot.com/v1.1/devices/ABC123/commands
78
+ # [dry-run] body: {"command":"turnOn","parameter":"default","commandType":"command"}
79
+ ```
80
+
81
+ ## Commands
82
+
83
+ ### `config` — credential management
84
+
85
+ ```bash
86
+ switchbot config set-token <token> <secret> # Save to ~/.switchbot/config.json
87
+ switchbot config show # Print current source + masked secret
88
+ ```
89
+
90
+ ### `devices` — list, status, control
91
+
92
+ ```bash
93
+ # List all physical devices and IR remote devices
94
+ # Columns: deviceId, deviceName, type, controlType, family, roomID, room, hub, cloud
95
+ switchbot devices list
96
+ switchbot devices list --json | jq '.deviceList[].deviceId'
97
+
98
+ # Filter by family / room (family & room info requires the 'src: OpenClaw'
99
+ # header, which this CLI sends on every request)
100
+ switchbot devices list --json | jq '.deviceList[] | select(.familyName == "Home")'
101
+ switchbot devices list --json | jq '[.deviceList[], .infraredRemoteList[]] | group_by(.familyName)'
102
+
103
+ # Query real-time status of a physical device
104
+ switchbot devices status <deviceId>
105
+ switchbot devices status <deviceId> --json
106
+
107
+ # Send a control command
108
+ switchbot devices command <deviceId> <cmd> [parameter] [--type command|customize]
109
+
110
+ # Describe a specific device (1 API call): metadata + supported commands + status fields
111
+ switchbot devices describe <deviceId>
112
+ switchbot devices describe <deviceId> --json
113
+
114
+ # Discover what's supported (offline reference, no API call)
115
+ switchbot devices types # List all device types + IR remote types
116
+ switchbot devices commands <type> # Show commands, parameter formats, and status fields
117
+ switchbot devices commands Bot
118
+ switchbot devices commands "Smart Lock"
119
+ switchbot devices commands curtain # Case-insensitive, substring match
120
+ ```
121
+
122
+ #### Parameter formats
123
+
124
+ `parameter` is optional — omit it for commands like `turnOn`/`turnOff` (auto-defaults to `"default"`).
125
+ Numeric-only and JSON-object parameters are auto-parsed; strings with colons / commas / semicolons pass through as-is.
126
+
127
+ For the exact commands and parameter formats a specific device supports, query the built-in catalog:
128
+
129
+ ```bash
130
+ switchbot devices commands <type> # e.g. Bot, Curtain, "Smart Lock", "Robot Vacuum Cleaner S10"
131
+ ```
132
+
133
+ Generic parameter shapes (which one applies is decided by the device — see the catalog):
134
+
135
+ | Shape | Example |
136
+ | ------------------- | -------------------------------------------------------- |
137
+ | _(none)_ | `devices command <id> turnOn` |
138
+ | `<integer>` | `devices command <id> setBrightness 75` |
139
+ | `<R:G:B>` | `devices command <id> setColor "255:0:0"` |
140
+ | `<direction;angle>` | `devices command <id> setPosition "up;60"` |
141
+ | `<a,b,c,…>` | `devices command <id> setAll "26,1,3,on"` |
142
+ | `<json object>` | `'{"action":"sweep","param":{"fanLevel":2,"times":1}}'` |
143
+ | Custom IR button | `devices command <id> MyButton --type customize` |
144
+
145
+ For the complete per-device command reference, see the [SwitchBot API docs](https://github.com/OpenWonderLabs/SwitchBotAPI#send-device-control-commands).
146
+
147
+ ### `scenes` — run manual scenes
148
+
149
+ ```bash
150
+ switchbot scenes list # Columns: sceneId, sceneName
151
+ switchbot scenes execute <sceneId>
152
+ ```
153
+
154
+ ### `webhook` — receive device events over HTTP
155
+
156
+ ```bash
157
+ # Register a receiver URL for events from ALL devices
158
+ switchbot webhook setup https://your.host/hook
159
+
160
+ # Query what is currently configured
161
+ switchbot webhook query
162
+ switchbot webhook query --details https://your.host/hook
163
+
164
+ # Enable / disable / re-submit the registered URL
165
+ switchbot webhook update https://your.host/hook --enable
166
+ switchbot webhook update https://your.host/hook --disable
167
+
168
+ # Remove the configuration
169
+ switchbot webhook delete https://your.host/hook
170
+ ```
171
+
172
+ The CLI validates that `<url>` is an absolute `http://` or `https://` URL before calling the API. `--enable` and `--disable` are mutually exclusive.
173
+
174
+ ### `completion` — shell tab-completion
175
+
176
+ ```bash
177
+ # Bash: load on every new shell
178
+ echo 'source <(switchbot completion bash)' >> ~/.bashrc
179
+
180
+ # Zsh
181
+ echo 'source <(switchbot completion zsh)' >> ~/.zshrc
182
+
183
+ # Fish
184
+ switchbot completion fish > ~/.config/fish/completions/switchbot.fish
185
+
186
+ # PowerShell (profile)
187
+ switchbot completion powershell >> $PROFILE
188
+ ```
189
+
190
+ Supported shells: `bash`, `zsh`, `fish`, `powershell` (`pwsh` is accepted as an alias).
191
+
192
+ ## Output modes
193
+
194
+ - **Default** — ANSI-colored tables for `list`/`status`, key-value tables for details.
195
+ - **`--json`** — raw JSON passthrough, ideal for `jq` and scripting.
196
+
197
+ ```bash
198
+ switchbot devices list --json | jq '.deviceList[] | {id: .deviceId, name: .deviceName}'
199
+ ```
200
+
201
+ ## Exit codes
202
+
203
+ | Code | Meaning |
204
+ | ---- | ----------------------------------------------------------------------------------- |
205
+ | `0` | Success (including `--dry-run` intercept) |
206
+ | `1` | Runtime error — API error, network failure, missing credentials |
207
+ | `2` | Usage error — bad flag, missing/invalid argument, unknown subcommand, unknown device type, invalid URL, conflicting flags |
208
+
209
+ Typical errors bubble up in the form `Error: <message>` on stderr. The SwitchBot-specific error codes that get mapped to readable English messages:
210
+
211
+ | Code | Meaning |
212
+ | ---- | ------------------------------------------- |
213
+ | 151 | Device type error |
214
+ | 152 | Device not found |
215
+ | 160 | Command not supported by this device |
216
+ | 161 | Device offline (BLE devices need a Hub) |
217
+ | 171 | Hub offline |
218
+ | 190 | Device internal error / server busy |
219
+ | 401 | Authentication failed (check token/secret) |
220
+ | 429 | Request rate too high (10,000 req/day cap) |
221
+
222
+ ## Environment variables
223
+
224
+ | Variable | Description |
225
+ | ------------------- | ------------------------------------------------------------------ |
226
+ | `SWITCHBOT_TOKEN` | API token — takes priority over the config file |
227
+ | `SWITCHBOT_SECRET` | API secret — takes priority over the config file |
228
+ | `NO_COLOR` | Disable ANSI colors in all output (automatically respected) |
229
+
230
+ ## Scripting examples
231
+
232
+ ```bash
233
+ # Turn off every Bot device
234
+ switchbot devices list --json \
235
+ | jq -r '.deviceList[] | select(.deviceType == "Bot") | .deviceId' \
236
+ | while read id; do switchbot devices command "$id" turnOff; done
237
+
238
+ # Dump each scene as `<id> <name>`
239
+ switchbot scenes list --json | jq -r '.[] | "\(.sceneId) \(.sceneName)"'
240
+ ```
241
+
242
+ ## Development
243
+
244
+ ```bash
245
+ npm run dev -- <args> # Run from TypeScript sources via tsx
246
+ npm run build # Compile to dist/
247
+ npm test # Run the Vitest suite (282 tests)
248
+ npm run test:watch # Watch mode
249
+ npm run test:coverage # Coverage report (v8, HTML + text)
250
+ ```
251
+
252
+ ### Project layout
253
+
254
+ ```
255
+ src/
256
+ ├── index.ts # Commander entry; mounts all subcommands; global flags
257
+ ├── auth.ts # HMAC-SHA256 signature (token + t + nonce → sign)
258
+ ├── config.ts # Credential load/save; env > file priority; --config override
259
+ ├── api/client.ts # axios instance + request/response interceptors;
260
+ │ # --verbose / --dry-run / --timeout wiring
261
+ ├── devices/catalog.ts # Static catalog powering `devices types`/`devices commands`
262
+ ├── commands/
263
+ │ ├── config.ts
264
+ │ ├── devices.ts
265
+ │ ├── scenes.ts
266
+ │ ├── webhook.ts
267
+ │ └── completion.ts # `switchbot completion bash|zsh|fish|powershell`
268
+ └── utils/
269
+ ├── flags.ts # Global flag readers (isVerbose / isDryRun / getTimeout / getConfigPath)
270
+ └── output.ts # printTable / printKeyValue / printJson / handleError
271
+ tests/ # Vitest suite (282 tests, mocked axios, no network)
272
+ ```
273
+
274
+ ## References
275
+
276
+ - [SwitchBot API v1.1 documentation](https://github.com/OpenWonderLabs/SwitchBotAPI)
277
+ - Base URL: `https://api.switch-bot.com`
278
+ - Rate limit: 10,000 requests/day per account
@@ -0,0 +1,12 @@
1
+ import { type AxiosInstance } from 'axios';
2
+ /** Thrown by the request interceptor when --dry-run intercepts a mutating call. */
3
+ export declare class DryRunSignal extends Error {
4
+ readonly method: string;
5
+ readonly url: string;
6
+ constructor(method: string, url: string);
7
+ }
8
+ export declare function createClient(): AxiosInstance;
9
+ export declare class ApiError extends Error {
10
+ readonly code: number;
11
+ constructor(message: string, code: number);
12
+ }
@@ -0,0 +1,95 @@
1
+ import axios from 'axios';
2
+ import chalk from 'chalk';
3
+ import { buildAuthHeaders } from '../auth.js';
4
+ import { loadConfig } from '../config.js';
5
+ import { isVerbose, isDryRun, getTimeout } from '../utils/flags.js';
6
+ const API_ERROR_MESSAGES = {
7
+ 151: 'Device type does not support this command',
8
+ 152: 'Device ID does not exist',
9
+ 160: 'This device does not support this command',
10
+ 161: 'Device offline (check Wi-Fi / Bluetooth connection)',
11
+ 171: 'Hub device offline (BLE devices require a Hub to communicate)',
12
+ 190: 'Device internal error — often an invalid deviceId, unsupported parameter, or device busy',
13
+ };
14
+ /** Thrown by the request interceptor when --dry-run intercepts a mutating call. */
15
+ export class DryRunSignal extends Error {
16
+ method;
17
+ url;
18
+ constructor(method, url) {
19
+ super('dry-run');
20
+ this.method = method;
21
+ this.url = url;
22
+ this.name = 'DryRunSignal';
23
+ }
24
+ }
25
+ export function createClient() {
26
+ const { token, secret } = loadConfig();
27
+ const verbose = isVerbose();
28
+ const dryRun = isDryRun();
29
+ const client = axios.create({
30
+ baseURL: 'https://api.switch-bot.com',
31
+ timeout: getTimeout(),
32
+ });
33
+ // Inject auth headers; optionally log the request; short-circuit on --dry-run.
34
+ client.interceptors.request.use((config) => {
35
+ const authHeaders = buildAuthHeaders(token, secret);
36
+ Object.assign(config.headers, authHeaders);
37
+ const method = (config.method ?? 'get').toUpperCase();
38
+ const url = `${config.baseURL ?? ''}${config.url ?? ''}`;
39
+ if (verbose) {
40
+ process.stderr.write(chalk.grey(`[verbose] ${method} ${url}\n`));
41
+ if (config.data !== undefined) {
42
+ process.stderr.write(chalk.grey(`[verbose] body: ${JSON.stringify(config.data)}\n`));
43
+ }
44
+ }
45
+ if (dryRun && method !== 'GET') {
46
+ console.log(chalk.yellow(`[dry-run] Would ${method} ${url}`));
47
+ if (config.data !== undefined) {
48
+ console.log(chalk.yellow(`[dry-run] body: ${JSON.stringify(config.data)}`));
49
+ }
50
+ throw new DryRunSignal(method, url);
51
+ }
52
+ return config;
53
+ });
54
+ // Handle API-level errors (HTTP 200 but statusCode !== 100)
55
+ client.interceptors.response.use((response) => {
56
+ if (verbose) {
57
+ process.stderr.write(chalk.grey(`[verbose] ${response.status} ${response.statusText}\n`));
58
+ }
59
+ const data = response.data;
60
+ if (data.statusCode !== undefined && data.statusCode !== 100) {
61
+ const msg = API_ERROR_MESSAGES[data.statusCode] ??
62
+ data.message ??
63
+ `API error code: ${data.statusCode}`;
64
+ throw new ApiError(msg, data.statusCode);
65
+ }
66
+ return response;
67
+ }, (error) => {
68
+ if (error instanceof DryRunSignal)
69
+ throw error;
70
+ if (axios.isAxiosError(error)) {
71
+ if (error.code === 'ECONNABORTED' || error.code === 'ETIMEDOUT') {
72
+ throw new ApiError(`Request timed out after ${getTimeout()}ms (override with --timeout <ms>)`, 0);
73
+ }
74
+ const status = error.response?.status;
75
+ if (status === 401) {
76
+ throw new ApiError('Authentication failed: invalid token or daily 10,000-request quota exceeded', 401);
77
+ }
78
+ if (status === 429) {
79
+ throw new ApiError('Request rate too high: daily 10,000-request quota exceeded', 429);
80
+ }
81
+ throw new ApiError(`HTTP ${status ?? '?'}: ${error.message}`, status ?? 0);
82
+ }
83
+ throw error;
84
+ });
85
+ return client;
86
+ }
87
+ export class ApiError extends Error {
88
+ code;
89
+ constructor(message, code) {
90
+ super(message);
91
+ this.code = code;
92
+ this.name = 'ApiError';
93
+ }
94
+ }
95
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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"}
package/dist/auth.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function buildAuthHeaders(token: string, secret: string): Record<string, string>;
package/dist/auth.js ADDED
@@ -0,0 +1,21 @@
1
+ import crypto from 'node:crypto';
2
+ import { v4 as uuidv4 } from 'uuid';
3
+ export function buildAuthHeaders(token, secret) {
4
+ const t = String(Date.now()); // 13-digit millisecond timestamp
5
+ const nonce = uuidv4(); // unique per request
6
+ const data = token + t + nonce;
7
+ const sign = crypto
8
+ .createHmac('sha256', secret)
9
+ .update(data)
10
+ .digest('base64')
11
+ .toUpperCase(); // API requires uppercase
12
+ return {
13
+ Authorization: token,
14
+ t,
15
+ sign,
16
+ nonce,
17
+ src: 'OpenClaw',
18
+ 'Content-Type': 'application/json',
19
+ };
20
+ }
21
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,MAAM,CAAC;AAEpC,MAAM,UAAU,gBAAgB,CAAC,KAAa,EAAE,MAAc;IAC5D,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAE,iCAAiC;IAChE,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,CAAQ,qBAAqB;IACpD,MAAM,IAAI,GAAG,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC;IAC/B,MAAM,IAAI,GAAG,MAAM;SAChB,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC;SAC5B,MAAM,CAAC,IAAI,CAAC;SACZ,MAAM,CAAC,QAAQ,CAAC;SAChB,WAAW,EAAE,CAAC,CAAc,yBAAyB;IAExD,OAAO;QACL,aAAa,EAAE,KAAK;QACpB,CAAC;QACD,IAAI;QACJ,KAAK;QACL,GAAG,EAAE,UAAU;QACf,cAAc,EAAE,kBAAkB;KACnC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerCompletionCommand(program: Command): void;