@switchbot/openapi-cli 3.4.1 โ 3.5.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 +1 -1
- package/dist/index.js +259 -37
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ Under the hood every surface shares the same catalog, cache, and HMAC client โ
|
|
|
63
63
|
- ๐จ **Dual output modes** โ colorized tables by default; `--json` passthrough for `jq` and scripting
|
|
64
64
|
- ๐ **Secure credentials** โ HMAC-SHA256 signed requests; config file written with `0600`; env-var override for CI
|
|
65
65
|
- ๐ **Dry-run mode** โ preview every mutating request before it hits the API
|
|
66
|
-
- ๐งช **Fully tested** โ
|
|
66
|
+
- ๐งช **Fully tested** โ 2263 Vitest tests, mocked axios, zero network in CI
|
|
67
67
|
- โก **Shell completion** โ Bash / Zsh / Fish / PowerShell
|
|
68
68
|
|
|
69
69
|
## Requirements
|
package/dist/index.js
CHANGED
|
@@ -8380,9 +8380,8 @@ function updateCacheFromDeviceList(body) {
|
|
|
8380
8380
|
for (const d of body.deviceList) {
|
|
8381
8381
|
if (!d.deviceId) continue;
|
|
8382
8382
|
devices[d.deviceId] = {
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
type: d.deviceType ?? "",
|
|
8383
|
+
type: d.deviceType || d.controlType || "Unknown Device",
|
|
8384
|
+
typeSource: d.deviceType ? "deviceType" : d.controlType ? "controlType" : "deviceType",
|
|
8386
8385
|
name: d.deviceName,
|
|
8387
8386
|
category: "physical",
|
|
8388
8387
|
hubDeviceId: d.hubDeviceId,
|
|
@@ -8397,6 +8396,7 @@ function updateCacheFromDeviceList(body) {
|
|
|
8397
8396
|
if (!d.deviceId) continue;
|
|
8398
8397
|
devices[d.deviceId] = {
|
|
8399
8398
|
type: d.remoteType,
|
|
8399
|
+
typeSource: "remoteType",
|
|
8400
8400
|
name: d.deviceName,
|
|
8401
8401
|
category: "ir",
|
|
8402
8402
|
hubDeviceId: d.hubDeviceId,
|
|
@@ -8793,10 +8793,11 @@ async function fetchDeviceList(client, options = {}) {
|
|
|
8793
8793
|
const infraredRemoteList = [];
|
|
8794
8794
|
for (const [deviceId, entry] of Object.entries(cached2.devices)) {
|
|
8795
8795
|
if (entry.category === "physical") {
|
|
8796
|
+
const cachedDeviceType = entry.typeSource === "deviceType" ? entry.type : entry.typeSource === void 0 && entry.type !== entry.controlType ? entry.type : void 0;
|
|
8796
8797
|
deviceList.push({
|
|
8797
8798
|
deviceId,
|
|
8798
8799
|
deviceName: entry.name,
|
|
8799
|
-
...
|
|
8800
|
+
...cachedDeviceType && cachedDeviceType !== "Unknown Device" ? { deviceType: cachedDeviceType } : {},
|
|
8800
8801
|
enableCloudService: entry.enableCloudService ?? true,
|
|
8801
8802
|
hubDeviceId: entry.hubDeviceId ?? "",
|
|
8802
8803
|
roomID: entry.roomID,
|
|
@@ -8867,10 +8868,12 @@ async function executeCommand(deviceId, cmd, parameter, commandType, client, opt
|
|
|
8867
8868
|
if (err instanceof Error && err.name === "DryRunSignal") {
|
|
8868
8869
|
writeAudit({ ...baseAudit, result: "dry-run" });
|
|
8869
8870
|
} else {
|
|
8871
|
+
const statusCode = err instanceof ApiError ? err.code : void 0;
|
|
8870
8872
|
writeAudit({
|
|
8871
8873
|
...baseAudit,
|
|
8872
8874
|
result: "error",
|
|
8873
|
-
error: err instanceof Error ? err.message : String(err)
|
|
8875
|
+
error: err instanceof Error ? err.message : String(err),
|
|
8876
|
+
...statusCode !== void 0 ? { statusCode } : {}
|
|
8874
8877
|
});
|
|
8875
8878
|
}
|
|
8876
8879
|
throw err;
|
|
@@ -8991,7 +8994,23 @@ async function describeDevice(deviceId, options = {}, client) {
|
|
|
8991
8994
|
ir = infraredRemoteList.find((d) => d.deviceId === deviceId);
|
|
8992
8995
|
}
|
|
8993
8996
|
if (!physical && !ir) throw new DeviceNotFoundError(deviceId);
|
|
8994
|
-
|
|
8997
|
+
let typeName;
|
|
8998
|
+
let typeSource;
|
|
8999
|
+
if (physical) {
|
|
9000
|
+
if (physical.deviceType) {
|
|
9001
|
+
typeName = physical.deviceType;
|
|
9002
|
+
typeSource = "deviceType";
|
|
9003
|
+
} else if (physical.controlType) {
|
|
9004
|
+
typeName = physical.controlType;
|
|
9005
|
+
typeSource = "controlType";
|
|
9006
|
+
} else {
|
|
9007
|
+
typeName = "Unknown Device";
|
|
9008
|
+
typeSource = "deviceType";
|
|
9009
|
+
}
|
|
9010
|
+
} else {
|
|
9011
|
+
typeName = ir.remoteType;
|
|
9012
|
+
typeSource = "remoteType";
|
|
9013
|
+
}
|
|
8995
9014
|
const match = typeName ? findCatalogEntry(typeName) : null;
|
|
8996
9015
|
const catalogEntry = !match || Array.isArray(match) ? null : match;
|
|
8997
9016
|
let liveStatus;
|
|
@@ -9027,6 +9046,7 @@ async function describeDevice(deviceId, options = {}, client) {
|
|
|
9027
9046
|
device: selectedDevice,
|
|
9028
9047
|
isPhysical: Boolean(physical),
|
|
9029
9048
|
typeName,
|
|
9049
|
+
typeSource,
|
|
9030
9050
|
controlType: physical?.controlType ?? ir?.controlType ?? null,
|
|
9031
9051
|
catalog: catalogEntry,
|
|
9032
9052
|
capabilities,
|
|
@@ -31710,6 +31730,161 @@ function applyFilter(clauses, deviceList, infraredRemoteList, hubLocation) {
|
|
|
31710
31730
|
// src/devices/param-validator.ts
|
|
31711
31731
|
init_cjs_shim();
|
|
31712
31732
|
init_output();
|
|
31733
|
+
|
|
31734
|
+
// src/devices/css-colors.ts
|
|
31735
|
+
init_cjs_shim();
|
|
31736
|
+
var CSS_COLORS = {
|
|
31737
|
+
aliceblue: [240, 248, 255],
|
|
31738
|
+
antiquewhite: [250, 235, 215],
|
|
31739
|
+
aqua: [0, 255, 255],
|
|
31740
|
+
aquamarine: [127, 255, 212],
|
|
31741
|
+
azure: [240, 255, 255],
|
|
31742
|
+
beige: [245, 245, 220],
|
|
31743
|
+
bisque: [255, 228, 196],
|
|
31744
|
+
black: [0, 0, 0],
|
|
31745
|
+
blanchedalmond: [255, 235, 205],
|
|
31746
|
+
blue: [0, 0, 255],
|
|
31747
|
+
blueviolet: [138, 43, 226],
|
|
31748
|
+
brown: [165, 42, 42],
|
|
31749
|
+
burlywood: [222, 184, 135],
|
|
31750
|
+
cadetblue: [95, 158, 160],
|
|
31751
|
+
chartreuse: [127, 255, 0],
|
|
31752
|
+
chocolate: [210, 105, 30],
|
|
31753
|
+
coral: [255, 127, 80],
|
|
31754
|
+
cornflowerblue: [100, 149, 237],
|
|
31755
|
+
cornsilk: [255, 248, 220],
|
|
31756
|
+
crimson: [220, 20, 60],
|
|
31757
|
+
cyan: [0, 255, 255],
|
|
31758
|
+
darkblue: [0, 0, 139],
|
|
31759
|
+
darkcyan: [0, 139, 139],
|
|
31760
|
+
darkgoldenrod: [184, 134, 11],
|
|
31761
|
+
darkgray: [169, 169, 169],
|
|
31762
|
+
darkgreen: [0, 100, 0],
|
|
31763
|
+
darkgrey: [169, 169, 169],
|
|
31764
|
+
darkkhaki: [189, 183, 107],
|
|
31765
|
+
darkmagenta: [139, 0, 139],
|
|
31766
|
+
darkolivegreen: [85, 107, 47],
|
|
31767
|
+
darkorange: [255, 140, 0],
|
|
31768
|
+
darkorchid: [153, 50, 204],
|
|
31769
|
+
darkred: [139, 0, 0],
|
|
31770
|
+
darksalmon: [233, 150, 122],
|
|
31771
|
+
darkseagreen: [143, 188, 143],
|
|
31772
|
+
darkslateblue: [72, 61, 139],
|
|
31773
|
+
darkslategray: [47, 79, 79],
|
|
31774
|
+
darkslategrey: [47, 79, 79],
|
|
31775
|
+
darkturquoise: [0, 206, 209],
|
|
31776
|
+
darkviolet: [148, 0, 211],
|
|
31777
|
+
deeppink: [255, 20, 147],
|
|
31778
|
+
deepskyblue: [0, 191, 255],
|
|
31779
|
+
dimgray: [105, 105, 105],
|
|
31780
|
+
dimgrey: [105, 105, 105],
|
|
31781
|
+
dodgerblue: [30, 144, 255],
|
|
31782
|
+
firebrick: [178, 34, 34],
|
|
31783
|
+
floralwhite: [255, 250, 240],
|
|
31784
|
+
forestgreen: [34, 139, 34],
|
|
31785
|
+
fuchsia: [255, 0, 255],
|
|
31786
|
+
gainsboro: [220, 220, 220],
|
|
31787
|
+
ghostwhite: [248, 248, 255],
|
|
31788
|
+
gold: [255, 215, 0],
|
|
31789
|
+
goldenrod: [218, 165, 32],
|
|
31790
|
+
gray: [128, 128, 128],
|
|
31791
|
+
green: [0, 128, 0],
|
|
31792
|
+
greenyellow: [173, 255, 47],
|
|
31793
|
+
grey: [128, 128, 128],
|
|
31794
|
+
honeydew: [240, 255, 240],
|
|
31795
|
+
hotpink: [255, 105, 180],
|
|
31796
|
+
indianred: [205, 92, 92],
|
|
31797
|
+
indigo: [75, 0, 130],
|
|
31798
|
+
ivory: [255, 255, 240],
|
|
31799
|
+
khaki: [240, 230, 140],
|
|
31800
|
+
lavender: [230, 230, 250],
|
|
31801
|
+
lavenderblush: [255, 240, 245],
|
|
31802
|
+
lawngreen: [124, 252, 0],
|
|
31803
|
+
lemonchiffon: [255, 250, 205],
|
|
31804
|
+
lightblue: [173, 216, 230],
|
|
31805
|
+
lightcoral: [240, 128, 128],
|
|
31806
|
+
lightcyan: [224, 255, 255],
|
|
31807
|
+
lightgoldenrodyellow: [250, 250, 210],
|
|
31808
|
+
lightgray: [211, 211, 211],
|
|
31809
|
+
lightgreen: [144, 238, 144],
|
|
31810
|
+
lightgrey: [211, 211, 211],
|
|
31811
|
+
lightpink: [255, 182, 193],
|
|
31812
|
+
lightsalmon: [255, 160, 122],
|
|
31813
|
+
lightseagreen: [32, 178, 170],
|
|
31814
|
+
lightskyblue: [135, 206, 250],
|
|
31815
|
+
lightslategray: [119, 136, 153],
|
|
31816
|
+
lightslategrey: [119, 136, 153],
|
|
31817
|
+
lightsteelblue: [176, 196, 222],
|
|
31818
|
+
lightyellow: [255, 255, 224],
|
|
31819
|
+
lime: [0, 255, 0],
|
|
31820
|
+
limegreen: [50, 205, 50],
|
|
31821
|
+
linen: [250, 240, 230],
|
|
31822
|
+
magenta: [255, 0, 255],
|
|
31823
|
+
maroon: [128, 0, 0],
|
|
31824
|
+
mediumaquamarine: [102, 205, 170],
|
|
31825
|
+
mediumblue: [0, 0, 205],
|
|
31826
|
+
mediumorchid: [186, 85, 211],
|
|
31827
|
+
mediumpurple: [147, 111, 219],
|
|
31828
|
+
mediumseagreen: [60, 179, 113],
|
|
31829
|
+
mediumslateblue: [123, 104, 238],
|
|
31830
|
+
mediumspringgreen: [0, 250, 154],
|
|
31831
|
+
mediumturquoise: [72, 209, 204],
|
|
31832
|
+
mediumvioletred: [199, 21, 133],
|
|
31833
|
+
midnightblue: [25, 25, 112],
|
|
31834
|
+
mintcream: [245, 255, 250],
|
|
31835
|
+
mistyrose: [255, 228, 225],
|
|
31836
|
+
moccasin: [255, 228, 181],
|
|
31837
|
+
navajowhite: [255, 222, 173],
|
|
31838
|
+
navy: [0, 0, 128],
|
|
31839
|
+
oldlace: [253, 245, 230],
|
|
31840
|
+
olive: [128, 128, 0],
|
|
31841
|
+
olivedrab: [107, 142, 35],
|
|
31842
|
+
orange: [255, 165, 0],
|
|
31843
|
+
orangered: [255, 69, 0],
|
|
31844
|
+
orchid: [218, 112, 214],
|
|
31845
|
+
palegoldenrod: [238, 232, 170],
|
|
31846
|
+
palegreen: [152, 251, 152],
|
|
31847
|
+
paleturquoise: [175, 238, 238],
|
|
31848
|
+
palevioletred: [219, 112, 147],
|
|
31849
|
+
papayawhip: [255, 239, 213],
|
|
31850
|
+
peachpuff: [255, 218, 185],
|
|
31851
|
+
peru: [205, 133, 63],
|
|
31852
|
+
pink: [255, 192, 203],
|
|
31853
|
+
plum: [221, 160, 221],
|
|
31854
|
+
powderblue: [176, 224, 230],
|
|
31855
|
+
purple: [128, 0, 128],
|
|
31856
|
+
rebeccapurple: [102, 51, 153],
|
|
31857
|
+
red: [255, 0, 0],
|
|
31858
|
+
rosybrown: [188, 143, 143],
|
|
31859
|
+
royalblue: [65, 105, 225],
|
|
31860
|
+
saddlebrown: [139, 69, 19],
|
|
31861
|
+
salmon: [250, 128, 114],
|
|
31862
|
+
sandybrown: [244, 164, 96],
|
|
31863
|
+
seagreen: [46, 139, 87],
|
|
31864
|
+
seashell: [255, 245, 238],
|
|
31865
|
+
sienna: [160, 82, 45],
|
|
31866
|
+
silver: [192, 192, 192],
|
|
31867
|
+
skyblue: [135, 206, 235],
|
|
31868
|
+
slateblue: [106, 90, 205],
|
|
31869
|
+
slategray: [112, 128, 144],
|
|
31870
|
+
slategrey: [112, 128, 144],
|
|
31871
|
+
snow: [255, 250, 250],
|
|
31872
|
+
springgreen: [0, 255, 127],
|
|
31873
|
+
steelblue: [70, 130, 180],
|
|
31874
|
+
tan: [210, 180, 140],
|
|
31875
|
+
teal: [0, 128, 128],
|
|
31876
|
+
thistle: [216, 191, 216],
|
|
31877
|
+
tomato: [255, 99, 71],
|
|
31878
|
+
turquoise: [64, 224, 208],
|
|
31879
|
+
violet: [238, 130, 238],
|
|
31880
|
+
wheat: [245, 222, 179],
|
|
31881
|
+
white: [255, 255, 255],
|
|
31882
|
+
whitesmoke: [245, 245, 245],
|
|
31883
|
+
yellow: [255, 255, 0],
|
|
31884
|
+
yellowgreen: [154, 205, 50]
|
|
31885
|
+
};
|
|
31886
|
+
|
|
31887
|
+
// src/devices/param-validator.ts
|
|
31713
31888
|
var AC_MODE_MAP = { auto: 1, cool: 2, dry: 3, fan: 4, heat: 5 };
|
|
31714
31889
|
var AC_FAN_MAP = { auto: 1, low: 2, mid: 3, high: 4 };
|
|
31715
31890
|
var CURTAIN_MODE_MAP = { default: "ff", performance: "0", silent: "1" };
|
|
@@ -31858,29 +32033,18 @@ function validateSetBrightness(raw) {
|
|
|
31858
32033
|
function hintBrightnessRetry() {
|
|
31859
32034
|
return `Ask the user whether they meant a percentage (1-100). Example: "50".`;
|
|
31860
32035
|
}
|
|
31861
|
-
var
|
|
31862
|
-
red: [255, 0, 0],
|
|
31863
|
-
green: [0, 128, 0],
|
|
31864
|
-
lime: [0, 255, 0],
|
|
31865
|
-
blue: [0, 0, 255],
|
|
31866
|
-
yellow: [255, 255, 0],
|
|
31867
|
-
cyan: [0, 255, 255],
|
|
31868
|
-
magenta: [255, 0, 255],
|
|
31869
|
-
white: [255, 255, 255],
|
|
31870
|
-
black: [0, 0, 0],
|
|
31871
|
-
orange: [255, 165, 0],
|
|
31872
|
-
purple: [128, 0, 128],
|
|
31873
|
-
pink: [255, 192, 203],
|
|
31874
|
-
brown: [165, 42, 42],
|
|
31875
|
-
grey: [128, 128, 128],
|
|
31876
|
-
gray: [128, 128, 128],
|
|
32036
|
+
var CUSTOM_COLORS = {
|
|
31877
32037
|
warm: [255, 180, 100]
|
|
31878
32038
|
};
|
|
32039
|
+
var NAMED_COLORS = {
|
|
32040
|
+
...CSS_COLORS,
|
|
32041
|
+
...CUSTOM_COLORS
|
|
32042
|
+
};
|
|
31879
32043
|
function validateSetColor(raw) {
|
|
31880
32044
|
if (raw === void 0 || raw === "" || raw === "default") {
|
|
31881
32045
|
return {
|
|
31882
32046
|
ok: false,
|
|
31883
|
-
error: `setColor requires a color.
|
|
32047
|
+
error: `setColor requires a color. Use a CSS color name (e.g. coral, teal, salmon), hex (#RRGGBB / #RGB), or R:G:B format.`
|
|
31884
32048
|
};
|
|
31885
32049
|
}
|
|
31886
32050
|
const trimmed = raw.trim();
|
|
@@ -32395,8 +32559,11 @@ Examples:
|
|
|
32395
32559
|
}
|
|
32396
32560
|
return;
|
|
32397
32561
|
}
|
|
32562
|
+
const totalDevices = resolved.ids.length;
|
|
32563
|
+
const deviceIndices = new Map(resolved.ids.map((id, i) => [id, i + 1]));
|
|
32398
32564
|
const startedAt = Date.now();
|
|
32399
32565
|
const outcomes = await runPool(resolved.ids, concurrency, staggerMs, async (id) => {
|
|
32566
|
+
const stepIdx = deviceIndices.get(id);
|
|
32400
32567
|
const stepStart = Date.now();
|
|
32401
32568
|
const startedIso = new Date(stepStart).toISOString();
|
|
32402
32569
|
try {
|
|
@@ -32408,7 +32575,7 @@ Examples:
|
|
|
32408
32575
|
const durationMs = Date.now() - stepStart;
|
|
32409
32576
|
const replayed = typeof result2 === "object" && result2 !== null && result2.replayed === true;
|
|
32410
32577
|
if (!isJsonMode()) {
|
|
32411
|
-
console.log(
|
|
32578
|
+
console.log(`[${stepIdx}/${totalDevices}] \u2713 ${id}: ${cmd}${replayed ? " (replayed)" : ""}`);
|
|
32412
32579
|
}
|
|
32413
32580
|
return {
|
|
32414
32581
|
ok: true,
|
|
@@ -32431,7 +32598,7 @@ Examples:
|
|
|
32431
32598
|
}
|
|
32432
32599
|
const errorPayload = buildErrorPayload(err);
|
|
32433
32600
|
if (!isJsonMode()) {
|
|
32434
|
-
console.error(
|
|
32601
|
+
console.error(`[${stepIdx}/${totalDevices}] \u2717 ${id}: ${errorPayload.message}`);
|
|
32435
32602
|
}
|
|
32436
32603
|
return {
|
|
32437
32604
|
ok: false,
|
|
@@ -32681,7 +32848,7 @@ function registerWatchCommand(devices) {
|
|
|
32681
32848
|
`Polling interval: "30s", "1m", "500ms", ... (default 30s, min ${MIN_INTERVAL_MS / 1e3}s)`,
|
|
32682
32849
|
durationArg("--interval"),
|
|
32683
32850
|
"30s"
|
|
32684
|
-
).option("--max <n>", "Stop after N ticks (default: run until Ctrl-C)", intArg("--max", { min: 1 })).option("--for <dur>", 'Stop after elapsed time (e.g. "5m", "30s"). Combines with --max: first limit wins.', durationArg("--for")).option("--include-unchanged", "Emit a tick even when no field changed").option("--initial <mode>", "How to handle the first poll: snapshot | emit | skip (default: snapshot)", enumArg("--initial", INITIAL_MODES), "snapshot").addHelpText(
|
|
32851
|
+
).option("--max <n>", "Stop after N ticks (default: run until Ctrl-C)", intArg("--max", { min: 1 })).option("--once", "Stop after one tick (shorthand for --max 1)").option("--for <dur>", 'Stop after elapsed time (e.g. "5m", "30s"). Combines with --max: first limit wins.', durationArg("--for")).option("--include-unchanged", "Emit a tick even when no field changed").option("--initial <mode>", "How to handle the first poll: snapshot | emit | skip (default: snapshot)", enumArg("--initial", INITIAL_MODES), "snapshot").addHelpText(
|
|
32685
32852
|
"after",
|
|
32686
32853
|
`
|
|
32687
32854
|
Default output is a human-readable table of field changes per tick; add --json
|
|
@@ -32710,6 +32877,12 @@ Examples:
|
|
|
32710
32877
|
).action(
|
|
32711
32878
|
async (deviceIds, options) => {
|
|
32712
32879
|
try {
|
|
32880
|
+
if (options.once && options.max !== void 0) {
|
|
32881
|
+
throw new UsageError("--once and --max are mutually exclusive.");
|
|
32882
|
+
}
|
|
32883
|
+
if (options.once) {
|
|
32884
|
+
options.max = "1";
|
|
32885
|
+
}
|
|
32713
32886
|
const allIds = [...deviceIds];
|
|
32714
32887
|
if (options.name) {
|
|
32715
32888
|
const resolved = resolveDeviceId(void 0, options.name);
|
|
@@ -33258,13 +33431,16 @@ var EXPAND_HINTS = {
|
|
|
33258
33431
|
"Relay Switch 2PM": { command: "setMode", flags: "--channel 1 --mode edge" }
|
|
33259
33432
|
};
|
|
33260
33433
|
function annotateStatusPayload(deviceId, body) {
|
|
33261
|
-
const
|
|
33434
|
+
const cached2 = getCachedDevice(deviceId);
|
|
33435
|
+
const deviceType = cached2?.type ?? "";
|
|
33436
|
+
const annotated = { deviceId, deviceType, ...body };
|
|
33437
|
+
annotated.deviceId = deviceId;
|
|
33438
|
+
annotated.deviceType = deviceType;
|
|
33262
33439
|
if (Object.keys(body).length === 0) {
|
|
33263
33440
|
annotated.supported = false;
|
|
33264
33441
|
annotated.note = "this device does not expose cloud status";
|
|
33265
33442
|
return annotated;
|
|
33266
33443
|
}
|
|
33267
|
-
const cached2 = getCachedDevice(deviceId);
|
|
33268
33444
|
const looksLikeMeter = cached2?.type?.toLowerCase().includes("meter") ?? false;
|
|
33269
33445
|
const staleZeroReading = looksLikeMeter && !Object.prototype.hasOwnProperty.call(body, "onlineStatus") && body.battery === 0 && body.temperature === 0 && body.humidity === 0;
|
|
33270
33446
|
if (staleZeroReading) {
|
|
@@ -33421,7 +33597,7 @@ Examples:
|
|
|
33421
33597
|
rows.push([
|
|
33422
33598
|
d.deviceId,
|
|
33423
33599
|
d.deviceName,
|
|
33424
|
-
d.deviceType || "
|
|
33600
|
+
d.deviceType || d.controlType || "Unknown Device",
|
|
33425
33601
|
"physical",
|
|
33426
33602
|
d.controlType || "\u2014",
|
|
33427
33603
|
d.familyName || "\u2014",
|
|
@@ -33970,12 +34146,19 @@ Examples:
|
|
|
33970
34146
|
});
|
|
33971
34147
|
return;
|
|
33972
34148
|
}
|
|
34149
|
+
if (result.typeSource === "controlType") {
|
|
34150
|
+
const deviceName2 = device.deviceName ?? deviceId;
|
|
34151
|
+
console.error(`warning: ${deviceName2} (${deviceId}): deviceType not reported by API, using controlType "${result.controlType}". Capabilities may be limited.`);
|
|
34152
|
+
} else if (typeName === "Unknown Device") {
|
|
34153
|
+
const deviceName2 = device.deviceName ?? deviceId;
|
|
34154
|
+
console.error(`warning: ${deviceName2} (${deviceId}): neither deviceType nor controlType reported by API. Capabilities may be limited.`);
|
|
34155
|
+
}
|
|
33973
34156
|
if (isPhysical) {
|
|
33974
34157
|
const physical = device;
|
|
33975
34158
|
printKeyValue({
|
|
33976
34159
|
deviceId: physical.deviceId,
|
|
33977
34160
|
deviceName: physical.deviceName,
|
|
33978
|
-
deviceType: physical.deviceType || "
|
|
34161
|
+
deviceType: physical.deviceType || physical.controlType || "Unknown Device",
|
|
33979
34162
|
controlType: physical.controlType || "\u2014",
|
|
33980
34163
|
family: physical.familyName || "\u2014",
|
|
33981
34164
|
roomID: physical.roomID || "\u2014",
|
|
@@ -34154,7 +34337,7 @@ Examples:
|
|
|
34154
34337
|
handleError(error48);
|
|
34155
34338
|
}
|
|
34156
34339
|
});
|
|
34157
|
-
scenes.command("execute").description("Execute a manual scene by its ID").argument("<sceneId>", 'Scene ID from "scenes list"').addHelpText("after", `
|
|
34340
|
+
scenes.command("execute").alias("run").description("Execute a manual scene by its ID").argument("<sceneId>", 'Scene ID from "scenes list"').addHelpText("after", `
|
|
34158
34341
|
Example:
|
|
34159
34342
|
$ switchbot scenes execute T12345678
|
|
34160
34343
|
`).action(async (sceneId) => {
|
|
@@ -49887,15 +50070,27 @@ against the live API without executing any mutations.
|
|
|
49887
50070
|
handleError(err);
|
|
49888
50071
|
}
|
|
49889
50072
|
});
|
|
49890
|
-
plan.command("run").description("Validate + preview/execute a plan. Respects --dry-run; destructive steps require the reviewed plan flow by default").argument("[file]", 'Path to plan.json, or "-" / omit to read stdin').option("--yes", "Authorize destructive commands (e.g. Smart Lock unlock, Garage open)").option("--require-approval", "Prompt for confirmation before each destructive step (TTY only; mutually exclusive with --json)").option("--continue-on-error", "Keep running after a failed step (default: stop at first error)").action(
|
|
50073
|
+
plan.command("run").description("Validate + preview/execute a plan. Respects --dry-run; destructive steps require the reviewed plan flow by default").argument("[file]", 'Path to plan.json, or "-" / omit to read stdin').option("--yes", "Authorize destructive commands (e.g. Smart Lock unlock, Garage open)").option("--require-approval", "Prompt for confirmation before each destructive step (TTY only; mutually exclusive with --json)").option("--continue-on-error", "Keep running after a failed step (default: stop at first error)").option("--plan <json>", "Inline plan JSON (alternative to file argument or stdin)").action(
|
|
49891
50074
|
async (file2, options) => {
|
|
49892
50075
|
if (options.requireApproval && isJsonMode()) {
|
|
49893
50076
|
console.error("error: --require-approval cannot be used with --json (no TTY available for prompts)");
|
|
49894
50077
|
process.exit(1);
|
|
49895
50078
|
}
|
|
50079
|
+
if (options.plan !== void 0 && file2 !== void 0) {
|
|
50080
|
+
console.error("error: --plan and a file argument are mutually exclusive.");
|
|
50081
|
+
process.exit(2);
|
|
50082
|
+
}
|
|
49896
50083
|
let raw;
|
|
49897
50084
|
try {
|
|
49898
|
-
|
|
50085
|
+
if (options.plan !== void 0) {
|
|
50086
|
+
try {
|
|
50087
|
+
raw = JSON.parse(options.plan);
|
|
50088
|
+
} catch (err) {
|
|
50089
|
+
throw new UsageError(`--plan is not valid JSON: ${err.message}`);
|
|
50090
|
+
}
|
|
50091
|
+
} else {
|
|
50092
|
+
raw = await readPlanSource(file2);
|
|
50093
|
+
}
|
|
49899
50094
|
} catch (err) {
|
|
49900
50095
|
handleError(err);
|
|
49901
50096
|
}
|
|
@@ -55689,7 +55884,7 @@ Examples:
|
|
|
55689
55884
|
$ switchbot history show --limit 10
|
|
55690
55885
|
$ switchbot history replay 3
|
|
55691
55886
|
`);
|
|
55692
|
-
history.command("show").description("Print recent audit entries").option("--file <path>", `Path to the audit log (default ${DEFAULT_AUDIT})`, stringArg("--file")).option("--limit <n>", "Show only the last N entries", intArg("--limit", { min: 1 })).action((options) => {
|
|
55887
|
+
history.command("show").alias("list").description("Print recent audit entries").option("--file <path>", `Path to the audit log (default ${DEFAULT_AUDIT})`, stringArg("--file")).option("--limit <n>", "Show only the last N entries", intArg("--limit", { min: 1 })).action((options) => {
|
|
55693
55888
|
const file2 = options.file ?? DEFAULT_AUDIT;
|
|
55694
55889
|
const entries = readAudit(file2);
|
|
55695
55890
|
const limited = options.limit !== void 0 ? entries.slice(-Math.max(1, Number(options.limit) || 1)) : entries;
|
|
@@ -59338,6 +59533,7 @@ import os25 from "node:os";
|
|
|
59338
59533
|
import path27 from "node:path";
|
|
59339
59534
|
var DEFAULT_AUDIT_PATH3 = path27.join(os25.homedir(), ".switchbot", "audit.log");
|
|
59340
59535
|
var AUDIT_ERROR_WINDOW_MS = 24 * 60 * 60 * 1e3;
|
|
59536
|
+
var EXPECTED_ERROR_CODES = /* @__PURE__ */ new Set([161, 171, 190]);
|
|
59341
59537
|
function getHealthReport(auditPath = DEFAULT_AUDIT_PATH3) {
|
|
59342
59538
|
const now = /* @__PURE__ */ new Date();
|
|
59343
59539
|
const procHealth = {
|
|
@@ -59358,20 +59554,46 @@ function getHealthReport(auditPath = DEFAULT_AUDIT_PATH3) {
|
|
|
59358
59554
|
};
|
|
59359
59555
|
let auditHealth;
|
|
59360
59556
|
if (!fs31.existsSync(auditPath)) {
|
|
59361
|
-
auditHealth = {
|
|
59557
|
+
auditHealth = {
|
|
59558
|
+
present: false,
|
|
59559
|
+
recentErrors: 0,
|
|
59560
|
+
recentTotal: 0,
|
|
59561
|
+
errorRatePercent: 0,
|
|
59562
|
+
expectedErrors: 0,
|
|
59563
|
+
unexpectedErrors: 0,
|
|
59564
|
+
unexpectedRatePercent: 0,
|
|
59565
|
+
breakdown: {},
|
|
59566
|
+
status: "ok"
|
|
59567
|
+
};
|
|
59362
59568
|
} else {
|
|
59363
59569
|
const entries = readAudit(auditPath);
|
|
59364
59570
|
const windowStart = now.getTime() - AUDIT_ERROR_WINDOW_MS;
|
|
59365
59571
|
const recent = entries.filter((e) => new Date(e.t).getTime() >= windowStart);
|
|
59366
|
-
const
|
|
59572
|
+
const errorEntries = recent.filter((e) => e.result === "error");
|
|
59367
59573
|
const total = recent.length;
|
|
59574
|
+
const errors = errorEntries.length;
|
|
59368
59575
|
const errorRate = total > 0 ? Math.round(errors / total * 100) : 0;
|
|
59576
|
+
const breakdown = {};
|
|
59577
|
+
let expectedErrors = 0;
|
|
59578
|
+
for (const e of errorEntries) {
|
|
59579
|
+
const code = e.statusCode !== void 0 ? String(e.statusCode) : "unknown";
|
|
59580
|
+
breakdown[code] = (breakdown[code] ?? 0) + 1;
|
|
59581
|
+
if (e.statusCode !== void 0 && EXPECTED_ERROR_CODES.has(e.statusCode)) {
|
|
59582
|
+
expectedErrors++;
|
|
59583
|
+
}
|
|
59584
|
+
}
|
|
59585
|
+
const unexpectedErrors = errors - expectedErrors;
|
|
59586
|
+
const unexpectedRatePercent = total > 0 ? Math.round(unexpectedErrors / total * 100 * 10) / 10 : 0;
|
|
59369
59587
|
auditHealth = {
|
|
59370
59588
|
present: true,
|
|
59371
59589
|
recentErrors: errors,
|
|
59372
59590
|
recentTotal: total,
|
|
59373
59591
|
errorRatePercent: errorRate,
|
|
59374
|
-
|
|
59592
|
+
expectedErrors,
|
|
59593
|
+
unexpectedErrors,
|
|
59594
|
+
unexpectedRatePercent,
|
|
59595
|
+
breakdown,
|
|
59596
|
+
status: unexpectedRatePercent >= 30 ? "warn" : "ok"
|
|
59375
59597
|
};
|
|
59376
59598
|
}
|
|
59377
59599
|
const cbStats = apiCircuitBreaker.getStats();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@switchbot/openapi-cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "SwitchBot smart home CLI โ control devices, run scenes, stream real-time events, and integrate AI agents via MCP. Full API v1.1 coverage.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"switchbot",
|