@switchbot/openapi-cli 1.1.0 → 1.2.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/README.md +174 -18
- package/dist/api/client.d.ts +7 -1
- package/dist/api/client.js +44 -8
- package/dist/api/client.js.map +1 -1
- package/dist/commands/batch.d.ts +2 -0
- package/dist/commands/batch.js +252 -0
- package/dist/commands/batch.js.map +1 -0
- package/dist/commands/cache.d.ts +2 -0
- package/dist/commands/cache.js +108 -0
- package/dist/commands/cache.js.map +1 -0
- package/dist/commands/capabilities.d.ts +2 -0
- package/dist/commands/capabilities.js +91 -0
- package/dist/commands/capabilities.js.map +1 -0
- package/dist/commands/catalog.d.ts +2 -0
- package/dist/commands/catalog.js +291 -0
- package/dist/commands/catalog.js.map +1 -0
- package/dist/commands/config.js +123 -10
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/devices.js +234 -147
- package/dist/commands/devices.js.map +1 -1
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +147 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/events.d.ts +15 -0
- package/dist/commands/events.js +188 -0
- package/dist/commands/events.js.map +1 -0
- package/dist/commands/explain.d.ts +2 -0
- package/dist/commands/explain.js +137 -0
- package/dist/commands/explain.js.map +1 -0
- package/dist/commands/history.d.ts +2 -0
- package/dist/commands/history.js +104 -0
- package/dist/commands/history.js.map +1 -0
- package/dist/commands/mcp.d.ts +4 -0
- package/dist/commands/mcp.js +386 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/plan.d.ts +37 -0
- package/dist/commands/plan.js +344 -0
- package/dist/commands/plan.js.map +1 -0
- package/dist/commands/quota.d.ts +2 -0
- package/dist/commands/quota.js +77 -0
- package/dist/commands/quota.js.map +1 -0
- package/dist/commands/scenes.js +19 -13
- package/dist/commands/scenes.js.map +1 -1
- package/dist/commands/schema.d.ts +2 -0
- package/dist/commands/schema.js +77 -0
- package/dist/commands/schema.js.map +1 -0
- package/dist/commands/watch.d.ts +2 -0
- package/dist/commands/watch.js +161 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/commands/webhook.js +37 -22
- package/dist/commands/webhook.js.map +1 -1
- package/dist/config.d.ts +11 -0
- package/dist/config.js +32 -6
- package/dist/config.js.map +1 -1
- package/dist/devices/cache.d.ts +50 -0
- package/dist/devices/cache.js +152 -1
- package/dist/devices/cache.js.map +1 -1
- package/dist/devices/catalog.d.ts +49 -0
- package/dist/devices/catalog.js +362 -92
- package/dist/devices/catalog.js.map +1 -1
- package/dist/index.js +31 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/devices.d.ts +144 -0
- package/dist/lib/devices.js +329 -0
- package/dist/lib/devices.js.map +1 -0
- package/dist/lib/scenes.d.ts +7 -0
- package/dist/lib/scenes.js +11 -0
- package/dist/lib/scenes.js.map +1 -0
- package/dist/utils/audit.d.ts +13 -0
- package/dist/utils/audit.js +43 -0
- package/dist/utils/audit.js.map +1 -0
- package/dist/utils/filter.d.ts +45 -0
- package/dist/utils/filter.js +96 -0
- package/dist/utils/filter.js.map +1 -0
- package/dist/utils/flags.d.ts +42 -0
- package/dist/utils/flags.js +108 -0
- package/dist/utils/flags.js.map +1 -1
- package/dist/utils/format.d.ts +9 -0
- package/dist/utils/format.js +109 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/output.d.ts +11 -0
- package/dist/utils/output.js +37 -6
- package/dist/utils/output.js.map +1 -1
- package/dist/utils/quota.d.ts +48 -0
- package/dist/utils/quota.js +144 -0
- package/dist/utils/quota.js.map +1 -0
- package/dist/utils/retry.d.ts +23 -0
- package/dist/utils/retry.js +60 -0
- package/dist/utils/retry.js.map +1 -0
- package/package.json +4 -1
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { printJson, isJsonMode, handleError, UsageError } from '../utils/output.js';
|
|
2
|
+
import { fetchDeviceStatus } from '../lib/devices.js';
|
|
3
|
+
import { getCachedDevice } from '../devices/cache.js';
|
|
4
|
+
import { parseDurationToMs, getFields } from '../utils/flags.js';
|
|
5
|
+
const DEFAULT_INTERVAL_MS = 30_000;
|
|
6
|
+
const MIN_INTERVAL_MS = 1_000;
|
|
7
|
+
function diff(prev, next, fields) {
|
|
8
|
+
const out = {};
|
|
9
|
+
const keys = fields ?? Object.keys(next);
|
|
10
|
+
for (const k of keys) {
|
|
11
|
+
const a = prev ? prev[k] : undefined;
|
|
12
|
+
const b = next[k];
|
|
13
|
+
if (JSON.stringify(a) !== JSON.stringify(b)) {
|
|
14
|
+
out[k] = { from: prev ? a : null, to: b };
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return out;
|
|
18
|
+
}
|
|
19
|
+
function formatHumanLine(ev) {
|
|
20
|
+
const when = new Date(ev.t).toLocaleTimeString();
|
|
21
|
+
const head = `[${when}] ${ev.deviceId}${ev.type ? ` (${ev.type})` : ''}`;
|
|
22
|
+
if (ev.error)
|
|
23
|
+
return `${head}: error — ${ev.error}`;
|
|
24
|
+
const keys = Object.keys(ev.changed);
|
|
25
|
+
if (keys.length === 0)
|
|
26
|
+
return `${head}: no changes`;
|
|
27
|
+
const pairs = keys
|
|
28
|
+
.map((k) => {
|
|
29
|
+
const { from, to } = ev.changed[k];
|
|
30
|
+
if (from === null || from === undefined)
|
|
31
|
+
return `${k}=${JSON.stringify(to)}`;
|
|
32
|
+
return `${k}: ${JSON.stringify(from)} → ${JSON.stringify(to)}`;
|
|
33
|
+
})
|
|
34
|
+
.join(', ');
|
|
35
|
+
return `${head} ${pairs}`;
|
|
36
|
+
}
|
|
37
|
+
function sleep(ms, signal) {
|
|
38
|
+
return new Promise((resolve) => {
|
|
39
|
+
const t = setTimeout(() => resolve(), ms);
|
|
40
|
+
const onAbort = () => {
|
|
41
|
+
clearTimeout(t);
|
|
42
|
+
resolve();
|
|
43
|
+
};
|
|
44
|
+
if (signal.aborted) {
|
|
45
|
+
clearTimeout(t);
|
|
46
|
+
resolve();
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
export function registerWatchCommand(devices) {
|
|
54
|
+
devices
|
|
55
|
+
.command('watch')
|
|
56
|
+
.description('Poll device status on an interval and emit field-level changes (JSONL)')
|
|
57
|
+
.argument('<deviceId...>', 'One or more deviceIds to watch')
|
|
58
|
+
.option('--interval <dur>', `Polling interval: "30s", "1m", "500ms", ... (default 30s, min ${MIN_INTERVAL_MS / 1000}s)`, '30s')
|
|
59
|
+
.option('--max <n>', 'Stop after N ticks (default: run until Ctrl-C)')
|
|
60
|
+
.option('--include-unchanged', 'Emit a tick even when no field changed')
|
|
61
|
+
.addHelpText('after', `
|
|
62
|
+
Each poll emits one JSON line per deviceId with the shape:
|
|
63
|
+
{ "t": "<ISO>", "tick": <n>, "deviceId": "ID", "type": "Bot",
|
|
64
|
+
"changed": { "power": { "from": "off", "to": "on" } } }
|
|
65
|
+
|
|
66
|
+
The very first poll has "from": null for every field (seed).
|
|
67
|
+
|
|
68
|
+
Examples:
|
|
69
|
+
$ switchbot devices watch ABC123 --interval 10s
|
|
70
|
+
$ switchbot devices watch ABC123 --fields battery,power --interval 1m
|
|
71
|
+
$ switchbot devices watch ABC123 DEF456 --interval 30s --max 10
|
|
72
|
+
$ switchbot devices watch ABC123 --json | jq 'select(.changed.power)'
|
|
73
|
+
`)
|
|
74
|
+
.action(async (deviceIds, options) => {
|
|
75
|
+
try {
|
|
76
|
+
const parsed = parseDurationToMs(options.interval);
|
|
77
|
+
if (parsed === null || parsed < MIN_INTERVAL_MS) {
|
|
78
|
+
throw new UsageError(`Invalid --interval "${options.interval}". Minimum is ${MIN_INTERVAL_MS / 1000}s.`);
|
|
79
|
+
}
|
|
80
|
+
const intervalMs = parsed;
|
|
81
|
+
let maxTicks = null;
|
|
82
|
+
if (options.max !== undefined) {
|
|
83
|
+
const n = Number(options.max);
|
|
84
|
+
if (!Number.isFinite(n) || n < 1) {
|
|
85
|
+
throw new UsageError(`Invalid --max "${options.max}". Must be a positive integer.`);
|
|
86
|
+
}
|
|
87
|
+
maxTicks = Math.floor(n);
|
|
88
|
+
}
|
|
89
|
+
const fields = getFields() ?? null;
|
|
90
|
+
const ac = new AbortController();
|
|
91
|
+
const onSig = () => ac.abort();
|
|
92
|
+
process.on('SIGINT', onSig);
|
|
93
|
+
process.on('SIGTERM', onSig);
|
|
94
|
+
try {
|
|
95
|
+
const prev = new Map();
|
|
96
|
+
let tick = 0;
|
|
97
|
+
while (!ac.signal.aborted) {
|
|
98
|
+
tick++;
|
|
99
|
+
const t = new Date().toISOString();
|
|
100
|
+
// Poll all devices in parallel; one failure per device doesn't stop
|
|
101
|
+
// the others.
|
|
102
|
+
await Promise.all(deviceIds.map(async (id) => {
|
|
103
|
+
const cached = getCachedDevice(id);
|
|
104
|
+
try {
|
|
105
|
+
const body = await fetchDeviceStatus(id);
|
|
106
|
+
const changed = diff(prev.get(id), body, fields);
|
|
107
|
+
prev.set(id, body);
|
|
108
|
+
if (Object.keys(changed).length === 0 && !options.includeUnchanged) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const ev = {
|
|
112
|
+
t,
|
|
113
|
+
tick,
|
|
114
|
+
deviceId: id,
|
|
115
|
+
type: cached?.type,
|
|
116
|
+
changed,
|
|
117
|
+
};
|
|
118
|
+
if (isJsonMode()) {
|
|
119
|
+
// JSONL: one event per line (printJson with newline).
|
|
120
|
+
printJson(ev);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
console.log(formatHumanLine(ev));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
const ev = {
|
|
128
|
+
t,
|
|
129
|
+
tick,
|
|
130
|
+
deviceId: id,
|
|
131
|
+
type: cached?.type,
|
|
132
|
+
changed: {},
|
|
133
|
+
error: err instanceof Error ? err.message : String(err),
|
|
134
|
+
};
|
|
135
|
+
if (isJsonMode()) {
|
|
136
|
+
printJson(ev);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
console.error(formatHumanLine(ev));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}));
|
|
143
|
+
if (maxTicks !== null && tick >= maxTicks)
|
|
144
|
+
break;
|
|
145
|
+
await sleep(intervalMs, ac.signal);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
handleError(err);
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
process.off('SIGINT', onSig);
|
|
153
|
+
process.off('SIGTERM', onSig);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
handleError(error);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=watch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch.js","sourceRoot":"","sources":["../../src/commands/watch.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/webhook.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { createClient } from '../api/client.js';
|
|
2
|
-
import { printKeyValue, printJson, isJsonMode, handleError } from '../utils/output.js';
|
|
2
|
+
import { printKeyValue, printJson, isJsonMode, handleError, UsageError } from '../utils/output.js';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
-
function assertValidUrl(
|
|
4
|
+
function assertValidUrl(url) {
|
|
5
5
|
let parsed;
|
|
6
6
|
try {
|
|
7
7
|
parsed = new URL(url);
|
|
8
8
|
}
|
|
9
9
|
catch {
|
|
10
|
-
|
|
10
|
+
throw new UsageError(`Invalid URL "${url}" (expected absolute URL, e.g. https://example.com/hook)`);
|
|
11
11
|
}
|
|
12
12
|
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
13
|
-
|
|
13
|
+
throw new UsageError(`URL must use http:// or https:// (got "${parsed.protocol}")`);
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
export function registerWebhookCommand(program) {
|
|
@@ -22,24 +22,29 @@ A webhook lets SwitchBot POST device state-change events to a URL you host.
|
|
|
22
22
|
Only one webhook URL can be active per account; "setup" registers it for ALL devices.
|
|
23
23
|
`);
|
|
24
24
|
// switchbot webhook setup <url>
|
|
25
|
-
|
|
25
|
+
webhook
|
|
26
26
|
.command('setup')
|
|
27
27
|
.description('Configure the webhook receiver URL (receives events from all devices)')
|
|
28
28
|
.argument('<url>', 'Absolute http(s):// URL where SwitchBot will POST events')
|
|
29
29
|
.addHelpText('after', `
|
|
30
30
|
Example:
|
|
31
31
|
$ switchbot webhook setup https://example.com/switchbot/events
|
|
32
|
-
`)
|
|
33
|
-
|
|
34
|
-
assertValidUrl(setup, url);
|
|
32
|
+
`)
|
|
33
|
+
.action(async (url) => {
|
|
35
34
|
try {
|
|
35
|
+
assertValidUrl(url);
|
|
36
36
|
const client = createClient();
|
|
37
37
|
await client.post('/v1.1/webhook/setupWebhook', {
|
|
38
38
|
action: 'setupWebhook',
|
|
39
39
|
url,
|
|
40
40
|
deviceList: 'ALL',
|
|
41
41
|
});
|
|
42
|
-
|
|
42
|
+
if (isJsonMode()) {
|
|
43
|
+
printJson({ ok: true, url });
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
console.log(chalk.green(`✓ Webhook configured: ${url}`));
|
|
47
|
+
}
|
|
43
48
|
}
|
|
44
49
|
catch (error) {
|
|
45
50
|
handleError(error);
|
|
@@ -103,7 +108,7 @@ Examples:
|
|
|
103
108
|
}
|
|
104
109
|
});
|
|
105
110
|
// switchbot webhook update <url> [--enable | --disable]
|
|
106
|
-
|
|
111
|
+
webhook
|
|
107
112
|
.command('update')
|
|
108
113
|
.description('Update webhook configuration (enable / disable a registered URL)')
|
|
109
114
|
.argument('<url>', 'URL of the webhook to update (must already be configured)')
|
|
@@ -116,13 +121,13 @@ webhook is re-submitted with no change to its enabled state.
|
|
|
116
121
|
Examples:
|
|
117
122
|
$ switchbot webhook update https://example.com/hook --enable
|
|
118
123
|
$ switchbot webhook update https://example.com/hook --disable
|
|
119
|
-
`)
|
|
120
|
-
|
|
121
|
-
if (options.enable && options.disable) {
|
|
122
|
-
update.error('error: --enable and --disable are mutually exclusive', { exitCode: 2, code: 'switchbot.conflictingOptions' });
|
|
123
|
-
}
|
|
124
|
-
assertValidUrl(update, url);
|
|
124
|
+
`)
|
|
125
|
+
.action(async (url, options) => {
|
|
125
126
|
try {
|
|
127
|
+
if (options.enable && options.disable) {
|
|
128
|
+
throw new UsageError('--enable and --disable are mutually exclusive');
|
|
129
|
+
}
|
|
130
|
+
assertValidUrl(url);
|
|
126
131
|
const client = createClient();
|
|
127
132
|
const config = { url };
|
|
128
133
|
if (options.enable)
|
|
@@ -134,30 +139,40 @@ Examples:
|
|
|
134
139
|
config,
|
|
135
140
|
});
|
|
136
141
|
const statusText = options.enable ? 'enabled' : options.disable ? 'disabled' : 'updated';
|
|
137
|
-
|
|
142
|
+
if (isJsonMode()) {
|
|
143
|
+
printJson({ ok: true, url, status: statusText });
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
console.log(chalk.green(`✓ Webhook ${statusText}: ${url}`));
|
|
147
|
+
}
|
|
138
148
|
}
|
|
139
149
|
catch (error) {
|
|
140
150
|
handleError(error);
|
|
141
151
|
}
|
|
142
152
|
});
|
|
143
153
|
// switchbot webhook delete <url>
|
|
144
|
-
|
|
154
|
+
webhook
|
|
145
155
|
.command('delete')
|
|
146
156
|
.description('Delete webhook configuration')
|
|
147
157
|
.argument('<url>', 'URL of the webhook to remove')
|
|
148
158
|
.addHelpText('after', `
|
|
149
159
|
Example:
|
|
150
160
|
$ switchbot webhook delete https://example.com/hook
|
|
151
|
-
`)
|
|
152
|
-
|
|
153
|
-
assertValidUrl(del, url);
|
|
161
|
+
`)
|
|
162
|
+
.action(async (url) => {
|
|
154
163
|
try {
|
|
164
|
+
assertValidUrl(url);
|
|
155
165
|
const client = createClient();
|
|
156
166
|
await client.post('/v1.1/webhook/deleteWebhook', {
|
|
157
167
|
action: 'deleteWebhook',
|
|
158
168
|
url,
|
|
159
169
|
});
|
|
160
|
-
|
|
170
|
+
if (isJsonMode()) {
|
|
171
|
+
printJson({ ok: true, url });
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
console.log(chalk.green(`✓ Webhook deleted: ${url}`));
|
|
175
|
+
}
|
|
161
176
|
}
|
|
162
177
|
catch (error) {
|
|
163
178
|
handleError(error);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.js","sourceRoot":"","sources":["../../src/commands/webhook.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"webhook.js","sourceRoot":"","sources":["../../src/commands/webhook.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -2,6 +2,17 @@ export interface SwitchBotConfig {
|
|
|
2
2
|
token: string;
|
|
3
3
|
secret: string;
|
|
4
4
|
}
|
|
5
|
+
/**
|
|
6
|
+
* Credential file resolution priority:
|
|
7
|
+
* 1. --config <path> (absolute override — wins over everything)
|
|
8
|
+
* 2. --profile <name> → ~/.switchbot/profiles/<name>.json
|
|
9
|
+
* 3. default → ~/.switchbot/config.json
|
|
10
|
+
*
|
|
11
|
+
* Env SWITCHBOT_TOKEN+SWITCHBOT_SECRET still take priority inside loadConfig.
|
|
12
|
+
*/
|
|
13
|
+
export declare function configFilePath(): string;
|
|
14
|
+
export declare function profileFilePath(profile: string): string;
|
|
15
|
+
export declare function listProfiles(): string[];
|
|
5
16
|
export declare function loadConfig(): SwitchBotConfig;
|
|
6
17
|
export declare function saveConfig(token: string, secret: string): void;
|
|
7
18
|
export declare function showConfig(): void;
|
package/dist/config.js
CHANGED
|
@@ -1,15 +1,38 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import os from 'node:os';
|
|
4
|
-
import { getConfigPath } from './utils/flags.js';
|
|
5
|
-
|
|
4
|
+
import { getConfigPath, getProfile } from './utils/flags.js';
|
|
5
|
+
/**
|
|
6
|
+
* Credential file resolution priority:
|
|
7
|
+
* 1. --config <path> (absolute override — wins over everything)
|
|
8
|
+
* 2. --profile <name> → ~/.switchbot/profiles/<name>.json
|
|
9
|
+
* 3. default → ~/.switchbot/config.json
|
|
10
|
+
*
|
|
11
|
+
* Env SWITCHBOT_TOKEN+SWITCHBOT_SECRET still take priority inside loadConfig.
|
|
12
|
+
*/
|
|
13
|
+
export function configFilePath() {
|
|
6
14
|
const override = getConfigPath();
|
|
7
15
|
if (override)
|
|
8
16
|
return path.resolve(override);
|
|
17
|
+
const profile = getProfile();
|
|
18
|
+
if (profile) {
|
|
19
|
+
return path.join(os.homedir(), '.switchbot', 'profiles', `${profile}.json`);
|
|
20
|
+
}
|
|
9
21
|
return path.join(os.homedir(), '.switchbot', 'config.json');
|
|
10
22
|
}
|
|
23
|
+
export function profileFilePath(profile) {
|
|
24
|
+
return path.join(os.homedir(), '.switchbot', 'profiles', `${profile}.json`);
|
|
25
|
+
}
|
|
26
|
+
export function listProfiles() {
|
|
27
|
+
const dir = path.join(os.homedir(), '.switchbot', 'profiles');
|
|
28
|
+
if (!fs.existsSync(dir))
|
|
29
|
+
return [];
|
|
30
|
+
return fs.readdirSync(dir)
|
|
31
|
+
.filter((f) => f.endsWith('.json'))
|
|
32
|
+
.map((f) => f.slice(0, -5))
|
|
33
|
+
.sort();
|
|
34
|
+
}
|
|
11
35
|
export function loadConfig() {
|
|
12
|
-
// Environment variables take priority (useful for CI)
|
|
13
36
|
const envToken = process.env.SWITCHBOT_TOKEN;
|
|
14
37
|
const envSecret = process.env.SWITCHBOT_SECRET;
|
|
15
38
|
if (envToken && envSecret) {
|
|
@@ -17,15 +40,18 @@ export function loadConfig() {
|
|
|
17
40
|
}
|
|
18
41
|
const file = configFilePath();
|
|
19
42
|
if (!fs.existsSync(file)) {
|
|
20
|
-
|
|
21
|
-
|
|
43
|
+
const profile = getProfile();
|
|
44
|
+
const hint = profile
|
|
45
|
+
? `No credentials configured for profile "${profile}". Run: switchbot --profile ${profile} config set-token <token> <secret>`
|
|
46
|
+
: 'No credentials configured. Run: switchbot config set-token <token> <secret>';
|
|
47
|
+
console.error(`${hint}\nOr set SWITCHBOT_TOKEN and SWITCHBOT_SECRET environment variables.`);
|
|
22
48
|
process.exit(1);
|
|
23
49
|
}
|
|
24
50
|
try {
|
|
25
51
|
const raw = fs.readFileSync(file, 'utf-8');
|
|
26
52
|
const cfg = JSON.parse(raw);
|
|
27
53
|
if (!cfg.token || !cfg.secret) {
|
|
28
|
-
console.error('Invalid config
|
|
54
|
+
console.error('Invalid config format. Please re-run: switchbot config set-token');
|
|
29
55
|
process.exit(1);
|
|
30
56
|
}
|
|
31
57
|
return cfg;
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/devices/cache.d.ts
CHANGED
|
@@ -2,6 +2,12 @@ export interface CachedDevice {
|
|
|
2
2
|
type: string;
|
|
3
3
|
name: string;
|
|
4
4
|
category: 'physical' | 'ir';
|
|
5
|
+
hubDeviceId?: string;
|
|
6
|
+
enableCloudService?: boolean;
|
|
7
|
+
roomID?: string;
|
|
8
|
+
roomName?: string | null;
|
|
9
|
+
familyName?: string;
|
|
10
|
+
controlType?: string;
|
|
5
11
|
}
|
|
6
12
|
export interface DeviceCache {
|
|
7
13
|
lastUpdated: string;
|
|
@@ -12,14 +18,58 @@ export interface DeviceListBodyShape {
|
|
|
12
18
|
deviceId: string;
|
|
13
19
|
deviceName: string;
|
|
14
20
|
deviceType?: string;
|
|
21
|
+
hubDeviceId?: string;
|
|
22
|
+
enableCloudService?: boolean;
|
|
23
|
+
roomID?: string;
|
|
24
|
+
roomName?: string | null;
|
|
25
|
+
familyName?: string;
|
|
26
|
+
controlType?: string;
|
|
15
27
|
}>;
|
|
16
28
|
infraredRemoteList: Array<{
|
|
17
29
|
deviceId: string;
|
|
18
30
|
deviceName: string;
|
|
19
31
|
remoteType: string;
|
|
32
|
+
hubDeviceId?: string;
|
|
33
|
+
controlType?: string;
|
|
20
34
|
}>;
|
|
21
35
|
}
|
|
36
|
+
/** Force the next loadCache() call to re-read from disk. Used in tests. */
|
|
37
|
+
export declare function resetListCache(): void;
|
|
22
38
|
export declare function loadCache(): DeviceCache | null;
|
|
23
39
|
export declare function getCachedDevice(deviceId: string): CachedDevice | null;
|
|
24
40
|
export declare function updateCacheFromDeviceList(body: DeviceListBodyShape): void;
|
|
25
41
|
export declare function clearCache(): void;
|
|
42
|
+
/** Age of the on-disk list cache in ms, or null if there is no cache. */
|
|
43
|
+
export declare function listCacheAgeMs(now?: number): number | null;
|
|
44
|
+
/** True when the on-disk list cache is present and younger than `ttlMs`. */
|
|
45
|
+
export declare function isListCacheFresh(ttlMs: number, now?: number): boolean;
|
|
46
|
+
export interface CachedStatus {
|
|
47
|
+
fetchedAt: string;
|
|
48
|
+
body: Record<string, unknown>;
|
|
49
|
+
}
|
|
50
|
+
export interface StatusCache {
|
|
51
|
+
entries: Record<string, CachedStatus>;
|
|
52
|
+
}
|
|
53
|
+
export declare function loadStatusCache(): StatusCache;
|
|
54
|
+
/** Read a status entry; returns null when missing or older than `ttlMs`. */
|
|
55
|
+
export declare function getCachedStatus(deviceId: string, ttlMs: number, now?: number): Record<string, unknown> | null;
|
|
56
|
+
export declare function setCachedStatus(deviceId: string, body: Record<string, unknown>, now?: Date): void;
|
|
57
|
+
export declare function clearStatusCache(): void;
|
|
58
|
+
/** Summary for `switchbot cache show`. */
|
|
59
|
+
export interface CacheSummary {
|
|
60
|
+
list: {
|
|
61
|
+
path: string;
|
|
62
|
+
exists: boolean;
|
|
63
|
+
lastUpdated?: string;
|
|
64
|
+
ageMs?: number;
|
|
65
|
+
deviceCount?: number;
|
|
66
|
+
};
|
|
67
|
+
status: {
|
|
68
|
+
path: string;
|
|
69
|
+
exists: boolean;
|
|
70
|
+
entryCount: number;
|
|
71
|
+
oldestFetchedAt?: string;
|
|
72
|
+
newestFetchedAt?: string;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export declare function describeCache(now?: number): CacheSummary;
|