@switchbot/homebridge-switchbot 5.0.0-beta.74 → 5.0.0-beta.76

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.
@@ -59,11 +59,11 @@
59
59
  async function loadCredentialStatus() {
60
60
  try {
61
61
  const resp = await homebridge.request('/credentials', {})
62
- if (!resp || !resp.success === false) {
63
- if (!resp?.data) {
64
- console.error('Failed to load credentials:', resp?.data?.message)
65
- return
66
- }
62
+ console.log('Load credentials response:', resp)
63
+
64
+ if (!resp || resp.success === false) {
65
+ console.error('Failed to load credentials:', resp)
66
+ return
67
67
  }
68
68
 
69
69
  const creds = resp.data || {}
@@ -75,6 +75,7 @@
75
75
  tokenStatus.classList.add('ok')
76
76
  } else {
77
77
  tokenStatus.textContent = 'Not configured'
78
+ tokenStatus.classList.remove('ok')
78
79
  }
79
80
 
80
81
  if (creds.hasSecret) {
@@ -82,6 +83,7 @@
82
83
  secretStatus.classList.add('ok')
83
84
  } else {
84
85
  secretStatus.textContent = 'Not configured'
86
+ secretStatus.classList.remove('ok')
85
87
  }
86
88
  } catch (e) {
87
89
  console.error('Error loading credentials:', e)
@@ -104,12 +106,16 @@
104
106
  saveBtn.disabled = true
105
107
  saveBtn.textContent = 'Saving...'
106
108
 
109
+ console.log('Saving credentials...')
107
110
  const resp = await homebridge.request('/credentials', { token, secret })
111
+ console.log('Save response:', resp)
112
+
108
113
  if (!resp || resp.success === false) {
109
- throw new Error(resp?.data?.message || 'Save failed')
114
+ throw new Error(resp?.message || 'Save failed')
110
115
  }
111
116
 
112
- saveStatus.textContent = '✓ ' + (resp.data?.message || 'Credentials saved')
117
+ const result = resp.data || resp
118
+ saveStatus.textContent = '✓ ' + (result.message || 'Credentials saved successfully')
113
119
  saveStatus.classList.remove('error')
114
120
  saveStatus.classList.add('success-msg')
115
121
 
@@ -117,8 +123,8 @@
117
123
  document.getElementById('token').value = ''
118
124
  document.getElementById('secret').value = ''
119
125
 
120
- // Reload status
121
- setTimeout(() => loadCredentialStatus(), 1000)
126
+ // Reload status to verify save
127
+ setTimeout(() => loadCredentialStatus(), 500)
122
128
 
123
129
  // Clear status message
124
130
  setTimeout(() => {
@@ -126,6 +132,7 @@
126
132
  saveStatus.classList.remove('success-msg')
127
133
  }, 3000)
128
134
  } catch (e) {
135
+ console.error('Save error:', e)
129
136
  saveStatus.textContent = 'Error: ' + (e?.message || 'Failed to save')
130
137
  saveStatus.classList.add('error')
131
138
  } finally {
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/homebridge-ui/server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,wBAAwB,EAAgB,MAAM,6BAA6B,CAAA;AAEpF,QAAA,MAAM,MAAM,0BAAiC,CAAA;AAiH7C,eAAe,MAAM,CAAA"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/homebridge-ui/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAgB,MAAM,6BAA6B,CAAA;AAGpF,QAAA,MAAM,MAAM,0BAAiC,CAAA;AA2H7C,eAAe,MAAM,CAAA"}
@@ -1,5 +1,6 @@
1
- import fs from 'node:fs/promises';
1
+ /* eslint-disable no-console */
2
2
  import { HomebridgePluginUiServer, RequestError } from '@homebridge/plugin-ui-utils';
3
+ import fs from 'node:fs/promises';
3
4
  const server = new HomebridgePluginUiServer();
4
5
  // Helper function to find the SwitchBot platform config
5
6
  async function getSwitchBotPlatformConfig() {
@@ -68,12 +69,14 @@ server.onRequest('/credentials', async (body) => {
68
69
  if (!body || Object.keys(body).length === 0) {
69
70
  // GET request - return current status
70
71
  const { platform } = await getSwitchBotPlatformConfig();
71
- return {
72
+ const status = {
72
73
  hasToken: !!platform.openApiToken,
73
74
  hasSecret: !!platform.openApiSecret,
74
75
  tokenLength: platform.openApiToken ? String(platform.openApiToken).length : 0,
75
76
  secretLength: platform.openApiSecret ? String(platform.openApiSecret).length : 0,
76
77
  };
78
+ console.log('[SwitchBot UI] GET /credentials - Status:', status);
79
+ return status;
77
80
  }
78
81
  else {
79
82
  // POST request - save credentials
@@ -82,11 +85,15 @@ server.onRequest('/credentials', async (body) => {
82
85
  throw new Error('Token and secret are required');
83
86
  }
84
87
  const { config, platform, cfgPath } = await getSwitchBotPlatformConfig();
88
+ console.log('[SwitchBot UI] POST /credentials - Saving to platform:', platform.platform || platform.name);
89
+ console.log('[SwitchBot UI] Config path:', cfgPath);
90
+ console.log('[SwitchBot UI] Token length:', token.length, 'Secret length:', secret.length);
85
91
  // Save token and secret directly on the platform config
86
92
  platform.openApiToken = token;
87
93
  platform.openApiSecret = secret;
88
94
  // Write back to config file
89
95
  await fs.writeFile(cfgPath, JSON.stringify(config, null, 2), 'utf8');
96
+ console.log('[SwitchBot UI] Credentials saved successfully');
90
97
  return {
91
98
  success: true,
92
99
  message: 'Credentials saved successfully',
@@ -94,6 +101,7 @@ server.onRequest('/credentials', async (body) => {
94
101
  }
95
102
  }
96
103
  catch (e) {
104
+ console.error('[SwitchBot UI] Error in /credentials:', e);
97
105
  throw new RequestError('Failed to handle credentials request', e);
98
106
  }
99
107
  });
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/homebridge-ui/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/homebridge-ui/server.ts"],"names":[],"mappings":"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"}
@@ -2,29 +2,29 @@ import type { SwitchBotPluginConfig } from './settings.js';
2
2
  export interface ISwitchBotClient {
3
3
  init(): Promise<void>;
4
4
  getDevice(id: string): Promise<any>;
5
+ getDevices(): Promise<any[]>;
6
+ setDeviceState(id: string, body: any): Promise<any>;
5
7
  destroy(): Promise<void>;
6
8
  }
9
+ /**
10
+ * Thin wrapper around node-switchbot v4.0.0-beta.2+
11
+ * Leverages upstream resilience features (retry, circuit breaker, connection intelligence)
12
+ * while maintaining plugin-specific features like write debouncing and OpenAPI fallback.
13
+ */
7
14
  export declare class SwitchBotClient implements ISwitchBotClient {
8
15
  private cfg;
9
16
  private client;
10
17
  private baseUrl;
11
18
  private requestTimeout;
12
19
  private maxRetries;
13
- private baseBackoffMs;
14
- private maxBackoffMs;
15
- private backoffFactor;
16
- private jitter;
17
20
  private writeDebounceMs;
18
21
  private logger;
19
- private perDeviceRetries;
20
- private perDeviceMaxRetries;
21
- private perDeviceCooldownMs;
22
- private perDeviceCooldownUntil;
23
- constructor(cfg: SwitchBotPluginConfig);
24
22
  private pendingWrites;
23
+ constructor(cfg: SwitchBotPluginConfig);
25
24
  init(): Promise<void>;
26
25
  private fetchWithTimeoutAndRetry;
27
26
  getDevice(id: string): Promise<any>;
27
+ getDevices(): Promise<any[]>;
28
28
  setDeviceState(id: string, body: any): Promise<any>;
29
29
  private _doSetDeviceState;
30
30
  destroy(): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"switchbotClient.d.ts","sourceRoot":"","sources":["../src/switchbotClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAE1D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;IACnC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACzB;AAGD,qBAAa,eAAgB,YAAW,gBAAgB;IACtD,OAAO,CAAC,GAAG,CAAuB;IAClC,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,OAAO,CAAoC;IAEnD,OAAO,CAAC,cAAc,CAAO;IAC7B,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,aAAa,CAAM;IAC3B,OAAO,CAAC,YAAY,CAAO;IAC3B,OAAO,CAAC,aAAa,CAAI;IACzB,OAAO,CAAC,MAAM,CAAO;IAGrB,OAAO,CAAC,eAAe,CAAM;IAE7B,OAAO,CAAC,MAAM,CAAK;IAEnB,OAAO,CAAC,gBAAgB,CAAiC;IACzD,OAAO,CAAC,mBAAmB,CAAI;IAE/B,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,sBAAsB,CAAiC;gBAEnD,GAAG,EAAE,qBAAqB;IAetC,OAAO,CAAC,aAAa,CAA0H;IAEzI,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAoBb,wBAAwB;IAkHhC,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAcnC,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;YAiC3C,iBAAiB;IA6BzB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAM/B"}
1
+ {"version":3,"file":"switchbotClient.d.ts","sourceRoot":"","sources":["../src/switchbotClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAE1D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;IACnC,UAAU,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;IAC5B,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;IACnD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACzB;AAED;;;;GAIG;AACH,qBAAa,eAAgB,YAAW,gBAAgB;IACtD,OAAO,CAAC,GAAG,CAAuB;IAClC,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,OAAO,CAAoC;IACnD,OAAO,CAAC,cAAc,CAAO;IAC7B,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,eAAe,CAAM;IAC7B,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,aAAa,CAA0H;gBAEnI,GAAG,EAAE,qBAAqB;IAQhC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAqBb,wBAAwB;IAkChC,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAmBnC,UAAU,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAoB5B,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;YA+B3C,iBAAiB;IAqCzB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAM/B"}
@@ -1,26 +1,17 @@
1
- // Adapter that tries to use the node-switchbot v4 hybrid API when available.
1
+ /**
2
+ * Thin wrapper around node-switchbot v4.0.0-beta.2+
3
+ * Leverages upstream resilience features (retry, circuit breaker, connection intelligence)
4
+ * while maintaining plugin-specific features like write debouncing and OpenAPI fallback.
5
+ */
2
6
  export class SwitchBotClient {
3
7
  cfg;
4
8
  client = null;
5
9
  baseUrl = 'https://api.switch-bot.com/v1.0';
6
- // configurable network options
7
10
  requestTimeout = 5000;
8
11
  maxRetries = 2;
9
- baseBackoffMs = 100;
10
- maxBackoffMs = 2000;
11
- backoffFactor = 2;
12
- jitter = true;
13
- // per-device write debounce (ms). 0 = disabled. Configurable via plugin options.
14
- // Default enabled to coalesce rapid writes and prevent command floods.
15
12
  writeDebounceMs = 100;
16
- // structured logger (expects info/warn/error/debug); defaults to console
17
13
  logger;
18
- // per-device retry tracking
19
- perDeviceRetries = new Map();
20
- perDeviceMaxRetries = 3;
21
- // per-device cooldown after exceeding retries (ms)
22
- perDeviceCooldownMs = 60_000;
23
- perDeviceCooldownUntil = new Map();
14
+ pendingWrites = new Map();
24
15
  constructor(cfg) {
25
16
  this.cfg = cfg;
26
17
  this.logger = cfg?.logger ?? console;
@@ -28,181 +19,115 @@ export class SwitchBotClient {
28
19
  this.requestTimeout = cfg.requestTimeout;
29
20
  if (typeof cfg?.maxRetries === 'number')
30
21
  this.maxRetries = cfg.maxRetries;
31
- if (typeof cfg?.perDeviceMaxRetries === 'number')
32
- this.perDeviceMaxRetries = cfg.perDeviceMaxRetries;
33
- if (typeof cfg?.baseBackoffMs === 'number')
34
- this.baseBackoffMs = cfg.baseBackoffMs;
35
- if (typeof cfg?.maxBackoffMs === 'number')
36
- this.maxBackoffMs = cfg.maxBackoffMs;
37
- if (typeof cfg?.backoffFactor === 'number')
38
- this.backoffFactor = cfg.backoffFactor;
39
- if (typeof cfg?.jitter === 'boolean')
40
- this.jitter = cfg.jitter;
41
- if (typeof cfg?.perDeviceCooldownMs === 'number')
42
- this.perDeviceCooldownMs = cfg.perDeviceCooldownMs;
43
22
  if (typeof cfg?.writeDebounceMs === 'number')
44
23
  this.writeDebounceMs = cfg.writeDebounceMs;
45
24
  }
46
- // pending writes coalescing: deviceId -> { timer, body, resolvers }
47
- pendingWrites = new Map();
48
25
  async init() {
49
- // Try to dynamically import the new node-switchbot package. If it exposes
50
- // a hybrid client we will use it. Otherwise the consumer can implement
51
- // OpenAPI fallback logic here.
52
26
  try {
53
- const mod = await import('node-switchbot');
54
- // The new library may export a default or named client. Try common names.
55
- const m = mod;
56
- this.client = m?.default ?? m?.SwitchBot ?? m;
57
- if (typeof this.client === 'function') {
58
- // some builds export a constructor
59
- this.client = new this.client({ token: this.cfg.openApiToken });
60
- }
27
+ // Dynamic import of node-switchbot v4 with native resilience features
28
+ const { SwitchBot } = await import('node-switchbot');
29
+ this.client = new SwitchBot({
30
+ token: this.cfg.openApiToken,
31
+ secret: this.cfg.openApiSecret,
32
+ // Enable all built-in resilience features from node-switchbot v4
33
+ enableFallback: true, // Auto-fallback from BLE to API
34
+ enableRetry: true, // Retry with exponential backoff
35
+ enableCircuitBreaker: true, // Circuit breaker per connection type
36
+ enableMetrics: true, // Connection tracking and statistics
37
+ ...(typeof this.cfg?.nodeClientConfig === 'object' && this.cfg.nodeClientConfig),
38
+ });
39
+ this.logger?.info?.('SwitchBot client initialized with native resilience features');
61
40
  }
62
41
  catch (e) {
63
- // If import fails, leave client null and rely on OpenAPI code paths later.
64
- this.logger?.warn?.('node-switchbot import failed; falling back to OpenAPI when token available');
42
+ this.logger?.warn?.('Failed to load node-switchbot; will use OpenAPI fallback:', e);
65
43
  this.client = null;
66
44
  }
67
45
  }
68
- async fetchWithTimeoutAndRetry(url, opts = {}, timeoutMs, retries, deviceId) {
46
+ async fetchWithTimeoutAndRetry(url, opts = {}, timeoutMs, retries) {
69
47
  const to = typeof timeoutMs === 'number' ? timeoutMs : this.requestTimeout;
70
48
  const max = typeof retries === 'number' ? retries : this.maxRetries;
71
- // Check per-device cooldown
72
- if (deviceId) {
73
- const until = this.perDeviceCooldownUntil.get(deviceId) ?? 0;
74
- if (Date.now() < until) {
75
- const ms = until - Date.now();
76
- this.logger?.warn?.('device in cooldown, aborting fetch', { deviceId, cooldownMs: ms });
77
- throw new Error(`device ${deviceId} in cooldown for ${ms}ms`);
78
- }
79
- }
80
49
  let attempt = 0;
81
50
  while (true) {
82
51
  attempt += 1;
83
- // AbortController-based timeout
84
52
  const controller = new AbortController();
85
53
  const timer = setTimeout(() => controller.abort(), to);
86
54
  try {
87
- this.logger?.debug?.('fetch', { url, attempt, deviceId });
88
55
  const fetchOpts = Object.assign({}, opts);
89
56
  try {
90
57
  Object.defineProperty(fetchOpts, 'signal', { value: controller.signal, enumerable: false, configurable: true });
91
58
  }
92
59
  catch (_e) {
93
- // ignore environments where defineProperty may fail
60
+ // ignore
94
61
  }
95
62
  const resp = await fetch(url, fetchOpts);
96
63
  clearTimeout(timer);
97
- // helper to safely read response body when available
98
- const safeReadBody = async (r) => {
99
- try {
100
- if (!r)
101
- return '';
102
- if (typeof r.text === 'function')
103
- return await r.text();
104
- if (typeof r.json === 'function') {
105
- const j = await r.json().catch(() => null);
106
- return j ? JSON.stringify(j) : '';
107
- }
108
- return String(r);
109
- }
110
- catch (_e) {
111
- return '';
112
- }
113
- };
114
- // Some tests/mocks provide response-like objects without numeric `status`.
115
- // In those cases attempt to parse the body and return a Response-like
116
- // object so callers (resp.json()) continue to work instead of forcing
117
- // a retry path.
118
- if (typeof resp.status !== 'number') {
119
- const bodyStr = await safeReadBody(resp);
120
- try {
121
- const parsed = bodyStr ? JSON.parse(bodyStr) : {};
122
- if (deviceId)
123
- this.perDeviceRetries.set(deviceId, 0);
124
- return { ok: true, status: 200, json: async () => parsed };
125
- }
126
- catch (_e) {
127
- if (deviceId)
128
- this.perDeviceRetries.set(deviceId, 0);
129
- return { ok: true, status: 200, json: async () => ({ body: bodyStr }) };
130
- }
131
- }
132
- // classify response
133
- if (resp.ok) {
134
- if (deviceId)
135
- this.perDeviceRetries.set(deviceId, 0);
64
+ if (resp.ok)
136
65
  return resp;
66
+ // server error / rate limit: retry
67
+ if (resp.status >= 500 || resp.status === 429) {
68
+ throw new Error(`retriable response: ${resp.status}`);
137
69
  }
138
- // Do not retry on client errors (4xx), except 429 (rate limit)
139
- if (resp.status >= 400 && resp.status < 500 && resp.status !== 429) {
140
- const body = await safeReadBody(resp);
141
- this.logger?.error?.('fetch non-retriable response', { url, status: resp.status, body });
142
- const err = new Error(`HTTP ${resp.status}`);
143
- err.status = resp.status;
144
- throw err;
145
- }
146
- // server error / rate limit: treat as retriable
147
- const body = await safeReadBody(resp);
148
- this.logger?.warn?.('fetch retriable response', { url, status: resp.status, body });
149
- // throw to enter retry/catch logic below and apply backoff
150
- throw new Error(`retriable response: ${resp.status || 'unknown'}`);
70
+ return resp;
151
71
  }
152
72
  catch (err) {
153
73
  clearTimeout(timer);
154
- const isAbort = err?.name === 'AbortError' || err?.message === 'The user aborted a request.' || err?.message === 'timeout';
155
- const reason = isAbort ? 'timeout' : err?.message ?? String(err);
156
- this.logger?.warn?.('fetch failed', { url, attempt, deviceId, reason });
157
- // per-device retry guard
158
- if (deviceId) {
159
- const prev = this.perDeviceRetries.get(deviceId) ?? 0;
160
- const next = prev + 1;
161
- this.perDeviceRetries.set(deviceId, next);
162
- if (next > this.perDeviceMaxRetries) {
163
- this.logger?.error?.('per-device retry limit exceeded, entering cooldown', { deviceId, attempts: next });
164
- this.perDeviceCooldownUntil.set(deviceId, Date.now() + this.perDeviceCooldownMs);
165
- throw new Error(`per-device retry limit exceeded for ${deviceId}`);
166
- }
167
- }
168
74
  if (attempt > max)
169
75
  throw err;
170
- // exponential backoff with optional jitter
171
- let backoff = Math.min(this.baseBackoffMs * Math.pow(this.backoffFactor, attempt - 1), this.maxBackoffMs);
172
- if (this.jitter) {
173
- const jitterVal = Math.floor(Math.random() * backoff);
174
- backoff = Math.floor(backoff / 2) + jitterVal;
175
- }
176
- this.logger?.debug?.('backoff before retry', { url, attempt, backoff });
76
+ // exponential backoff
77
+ const backoff = Math.min(100 * Math.pow(2, attempt - 1), 2000);
177
78
  await new Promise((r) => setTimeout(r, backoff));
178
79
  }
179
80
  }
180
81
  }
181
82
  async getDevice(id) {
182
- if (this.client && typeof this.client.getDevice === 'function') {
183
- return this.client.getDevice(id);
83
+ // Try client API first (with node-switchbot's smart fallback and retry)
84
+ if (this.client?.getDevice) {
85
+ try {
86
+ return await this.client.getDevice(id);
87
+ }
88
+ catch (e) {
89
+ this.logger?.warn?.(`Client getDevice failed for ${id}, trying OpenAPI fallback:`, e);
90
+ }
184
91
  }
185
- // Fallback: call OpenAPI via HTTP using token if available.
92
+ // Fallback: call OpenAPI via HTTP
186
93
  if (this.cfg.openApiToken) {
187
94
  const url = `${this.baseUrl}/devices/${id}`;
188
95
  const opts = { headers: { Authorization: this.cfg.openApiToken } };
189
- const resp = await this.fetchWithTimeoutAndRetry(url, opts, undefined, undefined, id);
96
+ const resp = await this.fetchWithTimeoutAndRetry(url, opts);
190
97
  return resp.json();
191
98
  }
192
99
  throw new Error('No SwitchBot client available');
193
100
  }
101
+ async getDevices() {
102
+ // Try client API first
103
+ if (this.client?.getDevices) {
104
+ try {
105
+ return await this.client.getDevices();
106
+ }
107
+ catch (e) {
108
+ this.logger?.warn?.('Client getDevices failed, trying OpenAPI fallback:', e);
109
+ }
110
+ }
111
+ // Fallback: call OpenAPI
112
+ if (this.cfg.openApiToken) {
113
+ const url = `${this.baseUrl}/devices`;
114
+ const opts = { headers: { Authorization: this.cfg.openApiToken } };
115
+ const resp = await this.fetchWithTimeoutAndRetry(url, opts);
116
+ const data = await resp.json();
117
+ return (data?.body || data);
118
+ }
119
+ return [];
120
+ }
194
121
  async setDeviceState(id, body) {
195
- // If debounce is disabled do a direct write path
122
+ // Plugin-level debounce: coalesce rapid writes per device
196
123
  if (!this.writeDebounceMs || this.writeDebounceMs <= 0) {
197
124
  return this._doSetDeviceState(id, body);
198
125
  }
199
- // Coalesce writes per-device within debounce window. Last write wins.
200
126
  return new Promise((resolve, reject) => {
201
127
  const existing = this.pendingWrites.get(id);
202
128
  if (existing) {
203
129
  existing.body = body;
204
130
  existing.resolvers.push({ resolve, reject });
205
- // timer already scheduled
206
131
  return;
207
132
  }
208
133
  const resolvers = [{ resolve, reject }];
@@ -225,13 +150,23 @@ export class SwitchBotClient {
225
150
  });
226
151
  }
227
152
  async _doSetDeviceState(id, body) {
228
- // Prefer client API
229
- if (this.client && typeof this.client.setDeviceState === 'function') {
230
- return this.client.setDeviceState(id, body);
153
+ // Prefer client API (which has node-switchbot's native resilience)
154
+ if (this.client?.setDeviceState) {
155
+ try {
156
+ return await this.client.setDeviceState(id, body);
157
+ }
158
+ catch (e) {
159
+ this.logger?.warn?.(`Client setDeviceState failed for ${id}, trying fallback:`, e);
160
+ }
231
161
  }
232
- // Try generic command methods
233
- if (this.client && typeof this.client.sendCommand === 'function') {
234
- return this.client.sendCommand(id, body);
162
+ // Try generic sendCommand if available
163
+ if (this.client?.sendCommand) {
164
+ try {
165
+ return await this.client.sendCommand(id, body);
166
+ }
167
+ catch (e) {
168
+ this.logger?.warn?.(`Client sendCommand failed for ${id}, trying OpenAPI:`, e);
169
+ }
235
170
  }
236
171
  // OpenAPI fallback
237
172
  if (this.cfg.openApiToken) {
@@ -244,13 +179,13 @@ export class SwitchBotClient {
244
179
  },
245
180
  body: JSON.stringify(body),
246
181
  };
247
- const resp = await this.fetchWithTimeoutAndRetry(url, opts, undefined, undefined, id);
182
+ const resp = await this.fetchWithTimeoutAndRetry(url, opts);
248
183
  return resp.json();
249
184
  }
250
185
  throw new Error('No SwitchBot client available for setDeviceState');
251
186
  }
252
187
  async destroy() {
253
- if (this.client && typeof this.client.destroy === 'function') {
188
+ if (this.client?.destroy) {
254
189
  await this.client.destroy();
255
190
  }
256
191
  this.client = null;
@@ -1 +1 @@
1
- {"version":3,"file":"switchbotClient.js","sourceRoot":"","sources":["../src/switchbotClient.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"switchbotClient.js","sourceRoot":"","sources":["../src/switchbotClient.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- <!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>default | @switchbot/homebridge-switchbot</title><meta name="description" content="Documentation for @switchbot/homebridge-switchbot"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar"><div class="tsd-toolbar-contents container"><a href="../index.html" class="title">@switchbot/homebridge-switchbot</a><div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/><ul role="listbox" id="tsd-search-results"></ul><div id="tsd-search-status" aria-live="polite" aria-atomic="true"><div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header><div class="container container-main"><div class="col-content"><div class="tsd-page-title"><ul class="tsd-breadcrumb" aria-label="Breadcrumb"><li><a href="" aria-current="page">default</a></li></ul><h1>Variable default</h1></div><div class="tsd-signature"><span class="tsd-kind-variable">default</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">api</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">API</span><span class="tsd-signature-symbol">)</span> <span class="tsd-signature-symbol">=&gt;</span> <span class="tsd-signature-type">void</span></div><div class="tsd-type-declaration"><h4>Type Declaration</h4><ul class="tsd-parameters"><li class="tsd-parameter-signature"><ul class="tsd-signatures"><li class="tsd-signature" id="__type"><span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">api</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">API</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">void</span></li><li class="tsd-description"><div class="tsd-parameters"><h4 class="tsd-parameters-title">Parameters</h4><ul class="tsd-parameter-list"><li><span><span class="tsd-kind-parameter">api</span>: <span class="tsd-signature-type">API</span></span></li></ul></div><h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">void</span></h4></li></ul></li></ul></div><aside class="tsd-sources"><ul><li>Defined in <a href="https://github.com/OpenWonderLabs/homebridge-switchbot/blob/af2eb0c3a4dfe2830519b32acc45b53dbb204968/src/index.ts#L12">index.ts:12</a></li></ul></aside></div><div class="col-sidebar"><div class="page-menu"><div class="tsd-navigation settings"><details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg><h3>Settings</h3></summary><div class="tsd-accordion-details"><div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span><ul id="tsd-filter-options"><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div><div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div></div><div class="site-menu"><nav class="tsd-navigation"><a href="../modules.html">@switchbot/homebridge-switchbot</a><ul class="tsd-small-nested-navigation" id="tsd-nav-container"><li>Loading...</li></ul></nav></div></div></div><footer></footer><div class="overlay"></div></body></html>
1
+ <!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>default | @switchbot/homebridge-switchbot</title><meta name="description" content="Documentation for @switchbot/homebridge-switchbot"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar"><div class="tsd-toolbar-contents container"><a href="../index.html" class="title">@switchbot/homebridge-switchbot</a><div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/><ul role="listbox" id="tsd-search-results"></ul><div id="tsd-search-status" aria-live="polite" aria-atomic="true"><div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header><div class="container container-main"><div class="col-content"><div class="tsd-page-title"><ul class="tsd-breadcrumb" aria-label="Breadcrumb"><li><a href="" aria-current="page">default</a></li></ul><h1>Variable default</h1></div><div class="tsd-signature"><span class="tsd-kind-variable">default</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">api</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">API</span><span class="tsd-signature-symbol">)</span> <span class="tsd-signature-symbol">=&gt;</span> <span class="tsd-signature-type">void</span></div><div class="tsd-type-declaration"><h4>Type Declaration</h4><ul class="tsd-parameters"><li class="tsd-parameter-signature"><ul class="tsd-signatures"><li class="tsd-signature" id="__type"><span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">api</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">API</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">void</span></li><li class="tsd-description"><div class="tsd-parameters"><h4 class="tsd-parameters-title">Parameters</h4><ul class="tsd-parameter-list"><li><span><span class="tsd-kind-parameter">api</span>: <span class="tsd-signature-type">API</span></span></li></ul></div><h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">void</span></h4></li></ul></li></ul></div><aside class="tsd-sources"><ul><li>Defined in <a href="https://github.com/OpenWonderLabs/homebridge-switchbot/blob/0c3bba22e06039260e02162e71169960ccffbc82/src/index.ts#L12">index.ts:12</a></li></ul></aside></div><div class="col-sidebar"><div class="page-menu"><div class="tsd-navigation settings"><details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg><h3>Settings</h3></summary><div class="tsd-accordion-details"><div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span><ul id="tsd-filter-options"><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div><div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div></div><div class="site-menu"><nav class="tsd-navigation"><a href="../modules.html">@switchbot/homebridge-switchbot</a><ul class="tsd-small-nested-navigation" id="tsd-nav-container"><li>Loading...</li></ul></nav></div></div></div><footer></footer><div class="overlay"></div></body></html>
package/eslint.config.js CHANGED
@@ -10,7 +10,6 @@ export default antfu(
10
10
  },
11
11
  rules: {
12
12
  'curly': ['error', 'multi-line'],
13
- 'import/order': 0,
14
13
  'jsdoc/check-alignment': 'error',
15
14
  'jsdoc/check-line-alignment': 'error',
16
15
  'perfectionist/sort-exports': 'error',
@@ -18,15 +17,10 @@ export default antfu(
18
17
  'error',
19
18
  {
20
19
  groups: [
21
- 'builtin-type',
22
- 'external-type',
23
- 'internal-type',
24
- ['parent-type', 'sibling-type', 'index-type'],
25
- 'builtin',
26
- 'external',
20
+ 'type',
21
+ ['builtin', 'external'],
27
22
  'internal',
28
23
  ['parent', 'sibling', 'index'],
29
- 'object',
30
24
  'unknown',
31
25
  ],
32
26
  order: 'asc',
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@switchbot/homebridge-switchbot",
3
3
  "displayName": "SwitchBot",
4
4
  "type": "module",
5
- "version": "5.0.0-beta.74",
5
+ "version": "5.0.0-beta.76",
6
6
  "description": "The SwitchBot plugin allows you to access your SwitchBot device(s) from HomeKit.",
7
7
  "author": "SwitchBot <support@wondertechlabs.com> (https://github.com/SwitchBot)",
8
8
  "contributors": [
@@ -52,6 +52,7 @@
52
52
  "ir"
53
53
  ],
54
54
  "main": "dist/index.js",
55
+ "bin": {},
55
56
  "icon": "https://raw.githubusercontent.com/OpenWonderLabs/homebridge-switchbot/latest/branding/icon.png",
56
57
  "engineStrict": true,
57
58
  "engines": {
@@ -77,36 +78,27 @@
77
78
  "docs": "typedoc",
78
79
  "docs:lint": "typedoc --emit none --treatWarningsAsErrors"
79
80
  },
80
- "bin": {},
81
81
  "publishConfig": {
82
82
  "access": "public"
83
83
  },
84
84
  "dependencies": {
85
- "@homebridge/plugin-ui-utils": "^2.2.0",
86
- "async-mqtt": "^2.6.3",
87
- "fakegato-history": "^0.6.7",
88
- "homebridge-lib": "^7.3.1",
89
- "node-switchbot": "^4.0.0-beta.1",
90
- "rxjs": "^7.8.2"
85
+ "@homebridge/plugin-ui-utils": "^2.2.1",
86
+ "node-switchbot": "^4.0.0-beta.2"
91
87
  },
92
88
  "devDependencies": {
93
- "@antfu/eslint-config": "^6.7.3",
94
- "@types/aes-js": "^3.1.4",
89
+ "@antfu/eslint-config": "^7.6.1",
95
90
  "@types/debug": "^4.1.12",
96
91
  "@types/fs-extra": "^11.0.4",
97
- "@types/mdast": "^4.0.4",
98
- "@types/node": "^24.11.0",
92
+ "@types/node": "^25.3.3",
99
93
  "@types/semver": "^7.7.1",
100
94
  "@types/source-map-support": "^0.5.10",
101
95
  "@vitest/coverage-v8": "^4.0.18",
102
- "eslint": "^9.39.3",
103
- "eslint-plugin-format": "^1.5.0",
104
- "eslint-plugin-import": "^2.32.0",
96
+ "eslint": "^10.0.2",
97
+ "eslint-plugin-format": "^2.0.1",
105
98
  "homebridge": "^2.0.0-beta.76",
106
- "homebridge-config-ui-x": "^5.18.0",
99
+ "homebridge-config-ui-x": "^5.19.0",
107
100
  "nodemon": "^3.1.14",
108
101
  "shx": "^0.4.0",
109
- "ts-node": "^10.9.2",
110
102
  "typedoc": "^0.28.17",
111
103
  "typescript": "^5.9.3",
112
104
  "vitest": "^4.0.18"
@@ -59,11 +59,11 @@
59
59
  async function loadCredentialStatus() {
60
60
  try {
61
61
  const resp = await homebridge.request('/credentials', {})
62
- if (!resp || !resp.success === false) {
63
- if (!resp?.data) {
64
- console.error('Failed to load credentials:', resp?.data?.message)
65
- return
66
- }
62
+ console.log('Load credentials response:', resp)
63
+
64
+ if (!resp || resp.success === false) {
65
+ console.error('Failed to load credentials:', resp)
66
+ return
67
67
  }
68
68
 
69
69
  const creds = resp.data || {}
@@ -75,6 +75,7 @@
75
75
  tokenStatus.classList.add('ok')
76
76
  } else {
77
77
  tokenStatus.textContent = 'Not configured'
78
+ tokenStatus.classList.remove('ok')
78
79
  }
79
80
 
80
81
  if (creds.hasSecret) {
@@ -82,6 +83,7 @@
82
83
  secretStatus.classList.add('ok')
83
84
  } else {
84
85
  secretStatus.textContent = 'Not configured'
86
+ secretStatus.classList.remove('ok')
85
87
  }
86
88
  } catch (e) {
87
89
  console.error('Error loading credentials:', e)
@@ -104,12 +106,16 @@
104
106
  saveBtn.disabled = true
105
107
  saveBtn.textContent = 'Saving...'
106
108
 
109
+ console.log('Saving credentials...')
107
110
  const resp = await homebridge.request('/credentials', { token, secret })
111
+ console.log('Save response:', resp)
112
+
108
113
  if (!resp || resp.success === false) {
109
- throw new Error(resp?.data?.message || 'Save failed')
114
+ throw new Error(resp?.message || 'Save failed')
110
115
  }
111
116
 
112
- saveStatus.textContent = '✓ ' + (resp.data?.message || 'Credentials saved')
117
+ const result = resp.data || resp
118
+ saveStatus.textContent = '✓ ' + (result.message || 'Credentials saved successfully')
113
119
  saveStatus.classList.remove('error')
114
120
  saveStatus.classList.add('success-msg')
115
121
 
@@ -117,8 +123,8 @@
117
123
  document.getElementById('token').value = ''
118
124
  document.getElementById('secret').value = ''
119
125
 
120
- // Reload status
121
- setTimeout(() => loadCredentialStatus(), 1000)
126
+ // Reload status to verify save
127
+ setTimeout(() => loadCredentialStatus(), 500)
122
128
 
123
129
  // Clear status message
124
130
  setTimeout(() => {
@@ -126,6 +132,7 @@
126
132
  saveStatus.classList.remove('success-msg')
127
133
  }, 3000)
128
134
  } catch (e) {
135
+ console.error('Save error:', e)
129
136
  saveStatus.textContent = 'Error: ' + (e?.message || 'Failed to save')
130
137
  saveStatus.classList.add('error')
131
138
  } finally {
@@ -1,6 +1,6 @@
1
- import fs from 'node:fs/promises'
2
-
1
+ /* eslint-disable no-console */
3
2
  import { HomebridgePluginUiServer, RequestError } from '@homebridge/plugin-ui-utils'
3
+ import fs from 'node:fs/promises'
4
4
 
5
5
  const server = new HomebridgePluginUiServer()
6
6
 
@@ -80,12 +80,15 @@ server.onRequest('/credentials', async (body: any) => {
80
80
  // GET request - return current status
81
81
  const { platform } = await getSwitchBotPlatformConfig()
82
82
 
83
- return {
83
+ const status = {
84
84
  hasToken: !!platform.openApiToken,
85
85
  hasSecret: !!platform.openApiSecret,
86
86
  tokenLength: platform.openApiToken ? String(platform.openApiToken).length : 0,
87
87
  secretLength: platform.openApiSecret ? String(platform.openApiSecret).length : 0,
88
88
  }
89
+
90
+ console.log('[SwitchBot UI] GET /credentials - Status:', status)
91
+ return status
89
92
  } else {
90
93
  // POST request - save credentials
91
94
  const { token, secret } = body
@@ -96,6 +99,10 @@ server.onRequest('/credentials', async (body: any) => {
96
99
 
97
100
  const { config, platform, cfgPath } = await getSwitchBotPlatformConfig()
98
101
 
102
+ console.log('[SwitchBot UI] POST /credentials - Saving to platform:', platform.platform || platform.name)
103
+ console.log('[SwitchBot UI] Config path:', cfgPath)
104
+ console.log('[SwitchBot UI] Token length:', token.length, 'Secret length:', secret.length)
105
+
99
106
  // Save token and secret directly on the platform config
100
107
  platform.openApiToken = token
101
108
  platform.openApiSecret = secret
@@ -103,12 +110,15 @@ server.onRequest('/credentials', async (body: any) => {
103
110
  // Write back to config file
104
111
  await fs.writeFile(cfgPath, JSON.stringify(config, null, 2), 'utf8')
105
112
 
113
+ console.log('[SwitchBot UI] Credentials saved successfully')
114
+
106
115
  return {
107
116
  success: true,
108
117
  message: 'Credentials saved successfully',
109
118
  }
110
119
  }
111
120
  } catch (e) {
121
+ console.error('[SwitchBot UI] Error in /credentials:', e)
112
122
  throw new RequestError('Failed to handle credentials request', e)
113
123
  }
114
124
  })
@@ -3,211 +3,139 @@ import type { SwitchBotPluginConfig } from './settings.js'
3
3
  export interface ISwitchBotClient {
4
4
  init(): Promise<void>
5
5
  getDevice(id: string): Promise<any>
6
+ getDevices(): Promise<any[]>
7
+ setDeviceState(id: string, body: any): Promise<any>
6
8
  destroy(): Promise<void>
7
9
  }
8
10
 
9
- // Adapter that tries to use the node-switchbot v4 hybrid API when available.
11
+ /**
12
+ * Thin wrapper around node-switchbot v4.0.0-beta.2+
13
+ * Leverages upstream resilience features (retry, circuit breaker, connection intelligence)
14
+ * while maintaining plugin-specific features like write debouncing and OpenAPI fallback.
15
+ */
10
16
  export class SwitchBotClient implements ISwitchBotClient {
11
17
  private cfg: SwitchBotPluginConfig
12
18
  private client: any | null = null
13
19
  private baseUrl = 'https://api.switch-bot.com/v1.0'
14
- // configurable network options
15
20
  private requestTimeout = 5000
16
21
  private maxRetries = 2
17
- private baseBackoffMs = 100
18
- private maxBackoffMs = 2000
19
- private backoffFactor = 2
20
- private jitter = true
21
- // per-device write debounce (ms). 0 = disabled. Configurable via plugin options.
22
- // Default enabled to coalesce rapid writes and prevent command floods.
23
22
  private writeDebounceMs = 100
24
- // structured logger (expects info/warn/error/debug); defaults to console
25
23
  private logger: any
26
- // per-device retry tracking
27
- private perDeviceRetries: Map<string, number> = new Map()
28
- private perDeviceMaxRetries = 3
29
- // per-device cooldown after exceeding retries (ms)
30
- private perDeviceCooldownMs = 60_000
31
- private perDeviceCooldownUntil: Map<string, number> = new Map()
24
+ private pendingWrites: Map<string, { timer: any; body: any; resolvers: Array<{ resolve: (v:any)=>void; reject: (e:any)=>void }>; }> = new Map()
32
25
 
33
26
  constructor(cfg: SwitchBotPluginConfig) {
34
27
  this.cfg = cfg
35
28
  this.logger = (cfg as any)?.logger ?? console
36
29
  if (typeof (cfg as any)?.requestTimeout === 'number') this.requestTimeout = (cfg as any).requestTimeout
37
30
  if (typeof (cfg as any)?.maxRetries === 'number') this.maxRetries = (cfg as any).maxRetries
38
- if (typeof (cfg as any)?.perDeviceMaxRetries === 'number') this.perDeviceMaxRetries = (cfg as any).perDeviceMaxRetries
39
- if (typeof (cfg as any)?.baseBackoffMs === 'number') this.baseBackoffMs = (cfg as any).baseBackoffMs
40
- if (typeof (cfg as any)?.maxBackoffMs === 'number') this.maxBackoffMs = (cfg as any).maxBackoffMs
41
- if (typeof (cfg as any)?.backoffFactor === 'number') this.backoffFactor = (cfg as any).backoffFactor
42
- if (typeof (cfg as any)?.jitter === 'boolean') this.jitter = (cfg as any).jitter
43
- if (typeof (cfg as any)?.perDeviceCooldownMs === 'number') this.perDeviceCooldownMs = (cfg as any).perDeviceCooldownMs
44
31
  if (typeof (cfg as any)?.writeDebounceMs === 'number') this.writeDebounceMs = (cfg as any).writeDebounceMs
45
32
  }
46
33
 
47
- // pending writes coalescing: deviceId -> { timer, body, resolvers }
48
- private pendingWrites: Map<string, { timer: any; body: any; resolvers: Array<{ resolve: (v:any)=>void; reject: (e:any)=>void }>; }> = new Map()
49
-
50
34
  async init(): Promise<void> {
51
- // Try to dynamically import the new node-switchbot package. If it exposes
52
- // a hybrid client we will use it. Otherwise the consumer can implement
53
- // OpenAPI fallback logic here.
54
35
  try {
55
- const mod = await import('node-switchbot')
56
- // The new library may export a default or named client. Try common names.
57
- const m: any = mod as any
58
- this.client = m?.default ?? m?.SwitchBot ?? m
59
- if (typeof this.client === 'function') {
60
- // some builds export a constructor
61
- this.client = new this.client({ token: this.cfg.openApiToken })
62
- }
36
+ // Dynamic import of node-switchbot v4 with native resilience features
37
+ const { SwitchBot } = await import('node-switchbot')
38
+ this.client = new SwitchBot({
39
+ token: this.cfg.openApiToken,
40
+ secret: this.cfg.openApiSecret,
41
+ // Enable all built-in resilience features from node-switchbot v4
42
+ enableFallback: true, // Auto-fallback from BLE to API
43
+ enableRetry: true, // Retry with exponential backoff
44
+ enableCircuitBreaker: true, // Circuit breaker per connection type
45
+ enableMetrics: true, // Connection tracking and statistics
46
+ ...(typeof (this.cfg as any)?.nodeClientConfig === 'object' && (this.cfg as any).nodeClientConfig),
47
+ })
48
+ this.logger?.info?.('SwitchBot client initialized with native resilience features')
63
49
  } catch (e) {
64
- // If import fails, leave client null and rely on OpenAPI code paths later.
65
- this.logger?.warn?.('node-switchbot import failed; falling back to OpenAPI when token available')
50
+ this.logger?.warn?.('Failed to load node-switchbot; will use OpenAPI fallback:', e)
66
51
  this.client = null
67
52
  }
68
53
  }
69
54
 
70
- private async fetchWithTimeoutAndRetry(url: string, opts: any = {}, timeoutMs?: number, retries?: number, deviceId?: string) {
55
+ private async fetchWithTimeoutAndRetry(url: string, opts: any = {}, timeoutMs?: number, retries?: number) {
71
56
  const to = typeof timeoutMs === 'number' ? timeoutMs : this.requestTimeout
72
57
  const max = typeof retries === 'number' ? retries : this.maxRetries
73
58
 
74
- // Check per-device cooldown
75
- if (deviceId) {
76
- const until = this.perDeviceCooldownUntil.get(deviceId) ?? 0
77
- if (Date.now() < until) {
78
- const ms = until - Date.now()
79
- this.logger?.warn?.('device in cooldown, aborting fetch', { deviceId, cooldownMs: ms })
80
- throw new Error(`device ${deviceId} in cooldown for ${ms}ms`)
81
- }
82
- }
83
-
84
59
  let attempt = 0
85
60
  while (true) {
86
61
  attempt += 1
87
- // AbortController-based timeout
88
62
  const controller = new AbortController()
89
63
  const timer = setTimeout(() => controller.abort(), to)
90
64
  try {
91
- this.logger?.debug?.('fetch', { url, attempt, deviceId })
92
65
  const fetchOpts = Object.assign({}, opts)
93
66
  try {
94
67
  Object.defineProperty(fetchOpts, 'signal', { value: controller.signal, enumerable: false, configurable: true })
95
68
  } catch (_e) {
96
- // ignore environments where defineProperty may fail
69
+ // ignore
97
70
  }
98
71
  const resp = await fetch(url, fetchOpts)
99
72
  clearTimeout(timer)
100
-
101
- // helper to safely read response body when available
102
- const safeReadBody = async (r: any) => {
103
- try {
104
- if (!r) return ''
105
- if (typeof r.text === 'function') return await r.text()
106
- if (typeof r.json === 'function') {
107
- const j = await r.json().catch(() => null)
108
- return j ? JSON.stringify(j) : ''
109
- }
110
- return String(r)
111
- } catch (_e) {
112
- return ''
113
- }
114
- }
115
-
116
- // Some tests/mocks provide response-like objects without numeric `status`.
117
- // In those cases attempt to parse the body and return a Response-like
118
- // object so callers (resp.json()) continue to work instead of forcing
119
- // a retry path.
120
- if (typeof (resp as any).status !== 'number') {
121
- const bodyStr = await safeReadBody(resp)
122
- try {
123
- const parsed = bodyStr ? JSON.parse(bodyStr) : {}
124
- if (deviceId) this.perDeviceRetries.set(deviceId, 0)
125
- return { ok: true, status: 200, json: async () => parsed }
126
- } catch (_e) {
127
- if (deviceId) this.perDeviceRetries.set(deviceId, 0)
128
- return { ok: true, status: 200, json: async () => ({ body: bodyStr }) }
129
- }
130
- }
131
-
132
- // classify response
133
- if (resp.ok) {
134
- if (deviceId) this.perDeviceRetries.set(deviceId, 0)
135
- return resp
136
- }
137
-
138
- // Do not retry on client errors (4xx), except 429 (rate limit)
139
- if (resp.status >= 400 && resp.status < 500 && resp.status !== 429) {
140
- const body = await safeReadBody(resp)
141
- this.logger?.error?.('fetch non-retriable response', { url, status: resp.status, body })
142
- const err = new Error(`HTTP ${resp.status}`)
143
- ;(err as any).status = resp.status
144
- throw err
73
+ if (resp.ok) return resp
74
+ // server error / rate limit: retry
75
+ if (resp.status >= 500 || resp.status === 429) {
76
+ throw new Error(`retriable response: ${resp.status}`)
145
77
  }
146
-
147
- // server error / rate limit: treat as retriable
148
- const body = await safeReadBody(resp)
149
- this.logger?.warn?.('fetch retriable response', { url, status: resp.status, body })
150
- // throw to enter retry/catch logic below and apply backoff
151
- throw new Error(`retriable response: ${resp.status || 'unknown'}`)
78
+ return resp
152
79
  } catch (err: any) {
153
80
  clearTimeout(timer)
154
- const isAbort = err?.name === 'AbortError' || err?.message === 'The user aborted a request.' || err?.message === 'timeout'
155
- const reason = isAbort ? 'timeout' : err?.message ?? String(err)
156
- this.logger?.warn?.('fetch failed', { url, attempt, deviceId, reason })
157
-
158
- // per-device retry guard
159
- if (deviceId) {
160
- const prev = this.perDeviceRetries.get(deviceId) ?? 0
161
- const next = prev + 1
162
- this.perDeviceRetries.set(deviceId, next)
163
- if (next > this.perDeviceMaxRetries) {
164
- this.logger?.error?.('per-device retry limit exceeded, entering cooldown', { deviceId, attempts: next })
165
- this.perDeviceCooldownUntil.set(deviceId, Date.now() + this.perDeviceCooldownMs)
166
- throw new Error(`per-device retry limit exceeded for ${deviceId}`)
167
- }
168
- }
169
-
170
81
  if (attempt > max) throw err
171
-
172
- // exponential backoff with optional jitter
173
- let backoff = Math.min(this.baseBackoffMs * Math.pow(this.backoffFactor, attempt - 1), this.maxBackoffMs)
174
- if (this.jitter) {
175
- const jitterVal = Math.floor(Math.random() * backoff)
176
- backoff = Math.floor(backoff / 2) + jitterVal
177
- }
178
- this.logger?.debug?.('backoff before retry', { url, attempt, backoff })
82
+ // exponential backoff
83
+ const backoff = Math.min(100 * Math.pow(2, attempt - 1), 2000)
179
84
  await new Promise((r) => setTimeout(r, backoff))
180
85
  }
181
86
  }
182
87
  }
183
88
 
184
89
  async getDevice(id: string): Promise<any> {
185
- if (this.client && typeof this.client.getDevice === 'function') {
186
- return this.client.getDevice(id)
90
+ // Try client API first (with node-switchbot's smart fallback and retry)
91
+ if (this.client?.getDevice) {
92
+ try {
93
+ return await this.client.getDevice(id)
94
+ } catch (e) {
95
+ this.logger?.warn?.(`Client getDevice failed for ${id}, trying OpenAPI fallback:`, e)
96
+ }
187
97
  }
188
- // Fallback: call OpenAPI via HTTP using token if available.
98
+ // Fallback: call OpenAPI via HTTP
189
99
  if (this.cfg.openApiToken) {
190
100
  const url = `${this.baseUrl}/devices/${id}`
191
101
  const opts = { headers: { Authorization: this.cfg.openApiToken } }
192
- const resp = await this.fetchWithTimeoutAndRetry(url, opts, undefined, undefined, id)
102
+ const resp = await this.fetchWithTimeoutAndRetry(url, opts)
193
103
  return resp.json()
194
104
  }
195
105
  throw new Error('No SwitchBot client available')
196
106
  }
197
107
 
108
+ async getDevices(): Promise<any[]> {
109
+ // Try client API first
110
+ if (this.client?.getDevices) {
111
+ try {
112
+ return await this.client.getDevices()
113
+ } catch (e) {
114
+ this.logger?.warn?.('Client getDevices failed, trying OpenAPI fallback:', e)
115
+ }
116
+ }
117
+ // Fallback: call OpenAPI
118
+ if (this.cfg.openApiToken) {
119
+ const url = `${this.baseUrl}/devices`
120
+ const opts = { headers: { Authorization: this.cfg.openApiToken } }
121
+ const resp = await this.fetchWithTimeoutAndRetry(url, opts)
122
+ const data = await resp.json()
123
+ return (data?.body || data) as any[]
124
+ }
125
+ return []
126
+ }
127
+
198
128
  async setDeviceState(id: string, body: any): Promise<any> {
199
- // If debounce is disabled do a direct write path
129
+ // Plugin-level debounce: coalesce rapid writes per device
200
130
  if (!this.writeDebounceMs || this.writeDebounceMs <= 0) {
201
131
  return this._doSetDeviceState(id, body)
202
132
  }
203
133
 
204
- // Coalesce writes per-device within debounce window. Last write wins.
205
134
  return new Promise((resolve, reject) => {
206
135
  const existing = this.pendingWrites.get(id)
207
136
  if (existing) {
208
137
  existing.body = body
209
138
  existing.resolvers.push({ resolve, reject })
210
- // timer already scheduled
211
139
  return
212
140
  }
213
141
 
@@ -229,14 +157,22 @@ export class SwitchBotClient implements ISwitchBotClient {
229
157
  }
230
158
 
231
159
  private async _doSetDeviceState(id: string, body: any): Promise<any> {
232
- // Prefer client API
233
- if (this.client && typeof this.client.setDeviceState === 'function') {
234
- return this.client.setDeviceState(id, body)
160
+ // Prefer client API (which has node-switchbot's native resilience)
161
+ if (this.client?.setDeviceState) {
162
+ try {
163
+ return await this.client.setDeviceState(id, body)
164
+ } catch (e) {
165
+ this.logger?.warn?.(`Client setDeviceState failed for ${id}, trying fallback:`, e)
166
+ }
235
167
  }
236
168
 
237
- // Try generic command methods
238
- if (this.client && typeof this.client.sendCommand === 'function') {
239
- return this.client.sendCommand(id, body)
169
+ // Try generic sendCommand if available
170
+ if (this.client?.sendCommand) {
171
+ try {
172
+ return await this.client.sendCommand(id, body)
173
+ } catch (e) {
174
+ this.logger?.warn?.(`Client sendCommand failed for ${id}, trying OpenAPI:`, e)
175
+ }
240
176
  }
241
177
 
242
178
  // OpenAPI fallback
@@ -250,7 +186,7 @@ export class SwitchBotClient implements ISwitchBotClient {
250
186
  },
251
187
  body: JSON.stringify(body),
252
188
  }
253
- const resp = await this.fetchWithTimeoutAndRetry(url, opts, undefined, undefined, id)
189
+ const resp = await this.fetchWithTimeoutAndRetry(url, opts)
254
190
  return resp.json()
255
191
  }
256
192
 
@@ -258,9 +194,10 @@ export class SwitchBotClient implements ISwitchBotClient {
258
194
  }
259
195
 
260
196
  async destroy(): Promise<void> {
261
- if (this.client && typeof this.client.destroy === 'function') {
197
+ if (this.client?.destroy) {
262
198
  await this.client.destroy()
263
199
  }
264
200
  this.client = null
265
201
  }
266
202
  }
203
+