@switchbot/openapi-cli 1.0.0 → 1.0.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 CHANGED
@@ -1,278 +1,376 @@
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
1
+ # @switchbot/openapi-cli
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@switchbot/openapi-cli.svg)](https://www.npmjs.com/package/@switchbot/openapi-cli)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@switchbot/openapi-cli.svg)](https://www.npmjs.com/package/@switchbot/openapi-cli)
5
+ [![license](https://img.shields.io/npm/l/@switchbot/openapi-cli.svg)](./LICENSE)
6
+ [![node](https://img.shields.io/node/v/@switchbot/openapi-cli.svg)](https://nodejs.org)
7
+ [![CI](https://github.com/OpenWonderLabs/switchbot-openapi-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/OpenWonderLabs/switchbot-openapi-cli/actions/workflows/ci.yml)
8
+
9
+ Command-line interface for the [SwitchBot API v1.1](https://github.com/OpenWonderLabs/SwitchBotAPI).
10
+ List devices, query live status, send control commands, run scenes, and manage webhooks — all from your terminal or shell scripts.
11
+
12
+ - **npm package:** [`@switchbot/openapi-cli`](https://www.npmjs.com/package/@switchbot/openapi-cli)
13
+ - **Source code:** [github.com/OpenWonderLabs/switchbot-openapi-cli](https://github.com/OpenWonderLabs/switchbot-openapi-cli)
14
+ - **Issues / feature requests:** [GitHub Issues](https://github.com/OpenWonderLabs/switchbot-openapi-cli/issues)
15
+
16
+ ---
17
+
18
+ ## Table of contents
19
+
20
+ - [Features](#features)
21
+ - [Requirements](#requirements)
22
+ - [Installation](#installation)
23
+ - [Quick start](#quick-start)
24
+ - [Credentials](#credentials)
25
+ - [Global options](#global-options)
26
+ - [Commands](#commands)
27
+ - [`config`](#config--credential-management)
28
+ - [`devices`](#devices--list-status-control)
29
+ - [`scenes`](#scenes--run-manual-scenes)
30
+ - [`webhook`](#webhook--receive-device-events-over-http)
31
+ - [`completion`](#completion--shell-tab-completion)
32
+ - [Output modes](#output-modes)
33
+ - [Exit codes & error codes](#exit-codes--error-codes)
34
+ - [Environment variables](#environment-variables)
35
+ - [Scripting examples](#scripting-examples)
36
+ - [Development](#development)
37
+ - [Contributing](#contributing)
38
+ - [License](#license)
39
+ - [References](#references)
40
+
41
+ ---
42
+
43
+ ## Features
44
+
45
+ - 🔌 **Complete API coverage** — every `/v1.1` endpoint (devices, scenes, webhooks)
46
+ - 📚 **Built-in catalog** offline reference for every device type's supported commands, parameter formats, and status fields (no API call needed)
47
+ - 🎨 **Dual output modes** — colorized tables by default; `--json` passthrough for `jq` and scripting
48
+ - 🔐 **Secure credentials** — HMAC-SHA256 signed requests; config file written with `0600`; env-var override for CI
49
+ - 🔍 **Dry-run mode** — preview every mutating request before it hits the API
50
+ - 🧪 **Fully tested** — 282 Vitest tests, mocked axios, zero network in CI
51
+ - ⚡ **Shell completion** — Bash / Zsh / Fish / PowerShell
52
+
53
+ ## Requirements
54
+
55
+ - **Node.js 18**
56
+ - A SwitchBot account with **Developer Options** enabled (see [Credentials](#credentials))
57
+
58
+ ## Installation
59
+
60
+ ### From npm (recommended)
61
+
62
+ ```bash
63
+ npm install -g @switchbot/openapi-cli
64
+ ```
65
+
66
+ This adds the `switchbot` binary to your `$PATH`.
67
+
68
+ ### From source
69
+
70
+ ```bash
71
+ git clone https://github.com/OpenWonderLabs/switchbot-openapi-cli.git
72
+ cd switchbot-openapi-cli
73
+ npm install
74
+ npm run build
75
+ npm link # optional — expose `switchbot` globally
76
+ ```
77
+
78
+ Verify:
79
+
80
+ ```bash
81
+ switchbot --version
82
+ switchbot --help
83
+ ```
84
+
85
+ ## Quick start
86
+
87
+ ```bash
88
+ # 1. Save your credentials (one-time)
89
+ switchbot config set-token <token> <secret>
90
+
91
+ # 2. List every device on your account
92
+ switchbot devices list
93
+
94
+ # 3. Control a device
95
+ switchbot devices command <deviceId> turnOn
96
+ ```
97
+
98
+ ## Credentials
99
+
100
+ The CLI reads credentials in this order (first match wins):
101
+
102
+ 1. **Environment variables** — `SWITCHBOT_TOKEN` and `SWITCHBOT_SECRET`
103
+ 2. **Config file** `~/.switchbot/config.json` (written by `config set-token`, mode `0600`)
104
+
105
+ Obtain the token and secret from the SwitchBot mobile app:
106
+ **Profile → Preferences → Developer Options → Get Token**.
107
+
108
+ ```bash
109
+ # One-time setup (writes ~/.switchbot/config.json)
110
+ switchbot config set-token <token> <secret>
111
+
112
+ # Or export environment variables (e.g. in CI)
113
+ export SWITCHBOT_TOKEN=...
114
+ export SWITCHBOT_SECRET=...
115
+
116
+ # Confirm which source is active and see the masked secret
117
+ switchbot config show
118
+ ```
119
+
120
+ ## Global options
121
+
122
+ | Option | Description |
123
+ | ------------------- | ------------------------------------------------------------------------ |
124
+ | `--json` | Print the raw JSON response instead of a formatted table |
125
+ | `-v`, `--verbose` | Log HTTP request/response details to stderr |
126
+ | `--dry-run` | Print mutating requests (POST/PUT/DELETE) without sending them |
127
+ | `--timeout <ms>` | HTTP request timeout in milliseconds (default: `30000`) |
128
+ | `--config <path>` | Override credential file location (default: `~/.switchbot/config.json`) |
129
+ | `-V`, `--version` | Print the CLI version |
130
+ | `-h`, `--help` | Show help for any command or subcommand |
131
+
132
+ Every subcommand supports `--help`, and most include a parameter-format reference and examples.
133
+
134
+ ```bash
135
+ switchbot --help
136
+ switchbot devices command --help
137
+ ```
138
+
139
+ ### `--dry-run`
140
+
141
+ Intercepts every non-GET request: the CLI prints the URL/body it would have
142
+ sent, then exits `0` without contacting the API. `GET` requests (list, status,
143
+ query) are still executed so you can preview the state involved.
144
+
145
+ ```bash
146
+ switchbot devices command ABC123 turnOn --dry-run
147
+ # [dry-run] Would POST https://api.switch-bot.com/v1.1/devices/ABC123/commands
148
+ # [dry-run] body: {"command":"turnOn","parameter":"default","commandType":"command"}
149
+ ```
150
+
151
+ ## Commands
152
+
153
+ ### `config` — credential management
154
+
155
+ ```bash
156
+ switchbot config set-token <token> <secret> # Save to ~/.switchbot/config.json
157
+ switchbot config show # Print current source + masked secret
158
+ ```
159
+
160
+ ### `devices` list, status, control
161
+
162
+ ```bash
163
+ # List all physical devices and IR remote devices
164
+ # Columns: deviceId, deviceName, type, controlType, family, roomID, room, hub, cloud
165
+ switchbot devices list
166
+ switchbot devices list --json | jq '.deviceList[].deviceId'
167
+
168
+ # Filter by family / room (family & room info requires the 'src: OpenClaw'
169
+ # header, which this CLI sends on every request)
170
+ switchbot devices list --json | jq '.deviceList[] | select(.familyName == "Home")'
171
+ switchbot devices list --json | jq '[.deviceList[], .infraredRemoteList[]] | group_by(.familyName)'
172
+
173
+ # Query real-time status of a physical device
174
+ switchbot devices status <deviceId>
175
+ switchbot devices status <deviceId> --json
176
+
177
+ # Send a control command
178
+ switchbot devices command <deviceId> <cmd> [parameter] [--type command|customize]
179
+
180
+ # Describe a specific device (1 API call): metadata + supported commands + status fields
181
+ switchbot devices describe <deviceId>
182
+ switchbot devices describe <deviceId> --json
183
+
184
+ # Discover what's supported (offline reference, no API call)
185
+ switchbot devices types # List all device types + IR remote types
186
+ switchbot devices commands <type> # Show commands, parameter formats, and status fields
187
+ switchbot devices commands Bot
188
+ switchbot devices commands "Smart Lock"
189
+ switchbot devices commands curtain # Case-insensitive, substring match
190
+ ```
191
+
192
+ #### Parameter formats
193
+
194
+ `parameter` is optional omit it for commands like `turnOn`/`turnOff` (auto-defaults to `"default"`).
195
+ Numeric-only and JSON-object parameters are auto-parsed; strings with colons / commas / semicolons pass through as-is.
196
+
197
+ For the exact commands and parameter formats a specific device supports, query the built-in catalog:
198
+
199
+ ```bash
200
+ switchbot devices commands <type> # e.g. Bot, Curtain, "Smart Lock", "Robot Vacuum Cleaner S10"
201
+ ```
202
+
203
+ Generic parameter shapes (which one applies is decided by the device — see the catalog):
204
+
205
+ | Shape | Example |
206
+ | ------------------- | -------------------------------------------------------- |
207
+ | _(none)_ | `devices command <id> turnOn` |
208
+ | `<integer>` | `devices command <id> setBrightness 75` |
209
+ | `<R:G:B>` | `devices command <id> setColor "255:0:0"` |
210
+ | `<direction;angle>` | `devices command <id> setPosition "up;60"` |
211
+ | `<a,b,c,…>` | `devices command <id> setAll "26,1,3,on"` |
212
+ | `<json object>` | `'{"action":"sweep","param":{"fanLevel":2,"times":1}}'` |
213
+ | Custom IR button | `devices command <id> MyButton --type customize` |
214
+
215
+ For the complete per-device command reference, see the [SwitchBot API docs](https://github.com/OpenWonderLabs/SwitchBotAPI#send-device-control-commands).
216
+
217
+ ### `scenes` run manual scenes
218
+
219
+ ```bash
220
+ switchbot scenes list # Columns: sceneId, sceneName
221
+ switchbot scenes execute <sceneId>
222
+ ```
223
+
224
+ ### `webhook` — receive device events over HTTP
225
+
226
+ ```bash
227
+ # Register a receiver URL for events from ALL devices
228
+ switchbot webhook setup https://your.host/hook
229
+
230
+ # Query what is currently configured
231
+ switchbot webhook query
232
+ switchbot webhook query --details https://your.host/hook
233
+
234
+ # Enable / disable / re-submit the registered URL
235
+ switchbot webhook update https://your.host/hook --enable
236
+ switchbot webhook update https://your.host/hook --disable
237
+
238
+ # Remove the configuration
239
+ switchbot webhook delete https://your.host/hook
240
+ ```
241
+
242
+ The CLI validates that `<url>` is an absolute `http://` or `https://` URL before calling the API. `--enable` and `--disable` are mutually exclusive.
243
+
244
+ ### `completion` — shell tab-completion
245
+
246
+ ```bash
247
+ # Bash: load on every new shell
248
+ echo 'source <(switchbot completion bash)' >> ~/.bashrc
249
+
250
+ # Zsh
251
+ echo 'source <(switchbot completion zsh)' >> ~/.zshrc
252
+
253
+ # Fish
254
+ switchbot completion fish > ~/.config/fish/completions/switchbot.fish
255
+
256
+ # PowerShell (profile)
257
+ switchbot completion powershell >> $PROFILE
258
+ ```
259
+
260
+ Supported shells: `bash`, `zsh`, `fish`, `powershell` (`pwsh` is accepted as an alias).
261
+
262
+ ## Output modes
263
+
264
+ - **Default** — ANSI-colored tables for `list`/`status`, key-value tables for details.
265
+ - **`--json`** — raw JSON passthrough, ideal for `jq` and scripting.
266
+
267
+ ```bash
268
+ switchbot devices list --json | jq '.deviceList[] | {id: .deviceId, name: .deviceName}'
269
+ ```
270
+
271
+ ## Exit codes & error codes
272
+
273
+ | Code | Meaning |
274
+ | ---- | ------------------------------------------------------------------------------------------------------------------------- |
275
+ | `0` | Success (including `--dry-run` intercept) |
276
+ | `1` | Runtime error — API error, network failure, missing credentials |
277
+ | `2` | Usage error — bad flag, missing/invalid argument, unknown subcommand, unknown device type, invalid URL, conflicting flags |
278
+
279
+ Typical errors bubble up in the form `Error: <message>` on stderr. The SwitchBot-specific error codes that get mapped to readable English messages:
280
+
281
+ | Code | Meaning |
282
+ | ---- | ------------------------------------------- |
283
+ | 151 | Device type error |
284
+ | 152 | Device not found |
285
+ | 160 | Command not supported by this device |
286
+ | 161 | Device offline (BLE devices need a Hub) |
287
+ | 171 | Hub offline |
288
+ | 190 | Device internal error / server busy |
289
+ | 401 | Authentication failed (check token/secret) |
290
+ | 429 | Request rate too high (10,000 req/day cap) |
291
+
292
+ ## Environment variables
293
+
294
+ | Variable | Description |
295
+ | ------------------- | ------------------------------------------------------------------ |
296
+ | `SWITCHBOT_TOKEN` | API token — takes priority over the config file |
297
+ | `SWITCHBOT_SECRET` | API secret — takes priority over the config file |
298
+ | `NO_COLOR` | Disable ANSI colors in all output (automatically respected) |
299
+
300
+ ## Scripting examples
301
+
302
+ ```bash
303
+ # Turn off every Bot device
304
+ switchbot devices list --json \
305
+ | jq -r '.deviceList[] | select(.deviceType == "Bot") | .deviceId' \
306
+ | while read id; do switchbot devices command "$id" turnOff; done
307
+
308
+ # Dump each scene as `<id> <name>`
309
+ switchbot scenes list --json | jq -r '.[] | "\(.sceneId) \(.sceneName)"'
310
+ ```
311
+
312
+ ## Development
313
+
314
+ ```bash
315
+ git clone https://github.com/OpenWonderLabs/switchbot-openapi-cli.git
316
+ cd switchbot-openapi-cli
317
+ npm install
318
+
319
+ npm run dev -- <args> # Run from TypeScript sources via tsx
320
+ npm run build # Compile to dist/
321
+ npm test # Run the Vitest suite (282 tests)
322
+ npm run test:watch # Watch mode
323
+ npm run test:coverage # Coverage report (v8, HTML + text)
324
+ ```
325
+
326
+ ### Project layout
327
+
328
+ ```
329
+ src/
330
+ ├── index.ts # Commander entry; mounts all subcommands; global flags
331
+ ├── auth.ts # HMAC-SHA256 signature (token + t + nonce → sign)
332
+ ├── config.ts # Credential load/save; env > file priority; --config override
333
+ ├── api/client.ts # axios instance + request/response interceptors;
334
+ │ # --verbose / --dry-run / --timeout wiring
335
+ ├── devices/catalog.ts # Static catalog powering `devices types`/`devices commands`
336
+ ├── commands/
337
+ │ ├── config.ts
338
+ │ ├── devices.ts
339
+ │ ├── scenes.ts
340
+ │ ├── webhook.ts
341
+ │ └── completion.ts # `switchbot completion bash|zsh|fish|powershell`
342
+ └── utils/
343
+ ├── flags.ts # Global flag readers (isVerbose / isDryRun / getTimeout / getConfigPath)
344
+ └── output.ts # printTable / printKeyValue / printJson / handleError
345
+ tests/ # Vitest suite (282 tests, mocked axios, no network)
346
+ ```
347
+
348
+ ### Release flow
349
+
350
+ Releases are cut on tag push and published to npm by GitHub Actions:
351
+
352
+ ```bash
353
+ npm version patch # bump version + create git tag
354
+ git push --follow-tags
355
+ ```
356
+
357
+ Then on GitHub → **Releases → Draft a new release → select tag → Publish**. The `publish.yml` workflow runs tests, verifies the tag matches `package.json`, and publishes `@switchbot/openapi-cli` to npm with [provenance](https://docs.npmjs.com/generating-provenance-statements).
358
+
359
+ ## Contributing
360
+
361
+ Bug reports, feature requests, and PRs are welcome.
362
+
363
+ 1. Fork the repo and create a topic branch.
364
+ 2. Keep changes small and focused; add or update Vitest cases for any behavior change.
365
+ 3. Run `npm test` and `npm run build` locally — both must pass.
366
+ 4. Open a pull request against `main`. CI runs on Node 18/20/22; all three must stay green.
367
+
368
+ ## License
369
+
370
+ [MIT](./LICENSE) © chenliuyun
371
+
372
+ ## References
373
+
374
+ - [SwitchBot API v1.1 documentation](https://github.com/OpenWonderLabs/SwitchBotAPI)
375
+ - Base URL: `https://api.switch-bot.com`
376
+ - Rate limit: 10,000 requests/day per account