@switchbot/homebridge-switchbot 5.0.0-beta.77 → 5.0.0-beta.78

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.
@@ -48,6 +48,17 @@
48
48
  <div id="saveStatus"></div>
49
49
  </div>
50
50
 
51
+ <div class="card">
52
+ <h2>Discover Devices</h2>
53
+ <p>Click "Discover" to find all devices available in your SwitchBot account and add them to your configuration.</p>
54
+ <button id="discoverBtn" onclick="discoverDevices()" style="margin-bottom: 16px">🔍 Discover Devices</button>
55
+ <div id="discoverStatus" style="margin-bottom: 16px"></div>
56
+ <div id="discoveredList" style="display: none">
57
+ <h3 style="margin-top: 0">Available Devices</h3>
58
+ <ul id="discoveredDevices" style="max-height: 300px; overflow-y: auto"></ul>
59
+ </div>
60
+ </div>
61
+
51
62
  <div class="card">
52
63
  <h2>Configured Devices</h2>
53
64
  <p>This page lists devices found in your Homebridge config for the SwitchBot platform. Use the copy button to insert device IDs into the plugin configuration. Connection preference (BLE/OpenAPI) is shown when available.</p>
@@ -144,7 +155,7 @@
144
155
  async function fetchDevices() {
145
156
  try {
146
157
  const resp = await homebridge.request('/devices', {})
147
- if (!resp || !resp.success) throw new Error(resp?.data?.message || 'request failed')
158
+ if (!resp || resp.success === false) throw new Error(resp?.data?.message || 'request failed')
148
159
  return resp.data || []
149
160
  } catch (e) {
150
161
  console.error(e)
@@ -152,6 +163,124 @@
152
163
  }
153
164
  }
154
165
 
166
+ async function discoverDevices() {
167
+ const btn = document.getElementById('discoverBtn')
168
+ const status = document.getElementById('discoverStatus')
169
+ const list = document.getElementById('discoveredList')
170
+ const ul = document.getElementById('discoveredDevices')
171
+
172
+ try {
173
+ btn.disabled = true
174
+ btn.textContent = '🔍 Discovering...'
175
+ status.textContent = 'Searching SwitchBot account...'
176
+ status.classList.remove('error')
177
+
178
+ const resp = await homebridge.request('/discover', {})
179
+ console.log('Discover response:', resp)
180
+
181
+ if (!resp || resp.success === false) {
182
+ throw new Error(resp?.data?.message || 'Discovery failed')
183
+ }
184
+
185
+ const devices = resp.data || []
186
+
187
+ if (!devices.length) {
188
+ status.textContent = 'No devices found in your SwitchBot account'
189
+ list.style.display = 'none'
190
+ return
191
+ }
192
+
193
+ status.textContent = `Found ${devices.length} device(s)`
194
+ ul.innerHTML = ''
195
+
196
+ for (const d of devices) {
197
+ const li = document.createElement('li')
198
+ li.style.display = 'flex'
199
+ li.style.gap = '8px'
200
+ li.style.alignItems = 'center'
201
+ li.style.justifyContent = 'space-between'
202
+ li.style.padding = '8px'
203
+ li.style.background = '#333'
204
+ li.style.borderRadius = '4px'
205
+ li.style.marginBottom = '8px'
206
+
207
+ const info = document.createElement('div')
208
+ info.style.flex = '1'
209
+ const name = document.createElement('div')
210
+ name.style.fontWeight = '500'
211
+ name.textContent = d.name || d.id
212
+ const details = document.createElement('div')
213
+ details.style.fontSize = '12px'
214
+ details.style.opacity = '0.75'
215
+ details.textContent = `ID: ${d.id} | Type: ${d.type} | Model: ${d.model || 'N/A'}`
216
+ info.appendChild(name)
217
+ info.appendChild(details)
218
+
219
+ const addBtn = document.createElement('button')
220
+ addBtn.textContent = '+ Add'
221
+ addBtn.style.marginLeft = '8px'
222
+ addBtn.onclick = async () => {
223
+ await addDeviceToConfig(d)
224
+ }
225
+
226
+ li.appendChild(info)
227
+ li.appendChild(addBtn)
228
+ ul.appendChild(li)
229
+ }
230
+
231
+ list.style.display = 'block'
232
+ } catch (e) {
233
+ console.error('Discovery error:', e)
234
+ status.textContent = 'Error: ' + (e?.message || 'Discovery failed')
235
+ status.classList.add('error')
236
+ list.style.display = 'none'
237
+ } finally {
238
+ btn.disabled = false
239
+ btn.textContent = '🔍 Discover Devices'
240
+ }
241
+ }
242
+
243
+ async function addDeviceToConfig(device) {
244
+ try {
245
+ console.log('Adding device to config:', device)
246
+
247
+ const resp = await homebridge.request('/add-device', {
248
+ deviceId: device.id,
249
+ name: device.name,
250
+ type: device.type,
251
+ })
252
+
253
+ console.log('Add device response:', resp)
254
+
255
+ if (!resp || resp.success === false) {
256
+ throw new Error(resp?.data?.message || 'Failed to add device')
257
+ }
258
+
259
+ // Show success message
260
+ const message = resp.data?.message || `Device "${device.name}" added successfully!`
261
+ const status = document.getElementById('discoverStatus')
262
+ status.textContent = '✓ ' + message
263
+ status.classList.remove('error')
264
+ status.classList.add('success-msg')
265
+
266
+ // Refresh lists after 1 second
267
+ setTimeout(async () => {
268
+ await loadConfiguredDevices()
269
+ await discoverDevices()
270
+ }, 1000)
271
+ } catch (e) {
272
+ console.error('Add device error:', e)
273
+ const status = document.getElementById('discoverStatus')
274
+ status.textContent = '✗ Error: ' + (e?.message || 'Failed to add device')
275
+ status.classList.add('error')
276
+ }
277
+ }
278
+
279
+ async function loadConfiguredDevices() {
280
+ const list = await fetchDevices()
281
+ render(list)
282
+ }
283
+
155
284
  function render(list) {
156
285
  const ul = document.getElementById('devices')
157
286
  const status = document.getElementById('status')
@@ -1 +1 @@
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;AA6H7C,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;AAmN7C,eAAe,MAAM,CAAA"}
@@ -65,6 +65,76 @@ server.onRequest('/devices', async () => {
65
65
  throw new RequestError('Failed to read Homebridge config', e);
66
66
  }
67
67
  });
68
+ server.onRequest('/discover', async () => {
69
+ try {
70
+ const { platform } = await getSwitchBotPlatformConfig();
71
+ if (!platform.openApiToken) {
72
+ throw new Error('OpenAPI token not configured');
73
+ }
74
+ console.log('[SwitchBot UI] GET /discover - Fetching devices from SwitchBot API');
75
+ const url = 'https://api.switch-bot.com/v1.0/devices';
76
+ const opts = {
77
+ headers: {
78
+ Authorization: platform.openApiToken,
79
+ },
80
+ };
81
+ const resp = await fetch(url, opts);
82
+ if (!resp.ok) {
83
+ throw new Error(`SwitchBot API returned ${resp.status}`);
84
+ }
85
+ const data = await resp.json();
86
+ const devices = data.body || [];
87
+ const discovered = devices.map((d) => ({
88
+ id: d.deviceId || d.id,
89
+ name: d.deviceName || d.name,
90
+ type: d.deviceType || d.type || 'unknown',
91
+ model: d.deviceModel || d.model,
92
+ enabled: d.enableCloudService !== false,
93
+ }));
94
+ console.log('[SwitchBot UI] GET /discover - Found', discovered.length, 'devices from API');
95
+ return { success: true, data: discovered };
96
+ }
97
+ catch (e) {
98
+ console.error('[SwitchBot UI] Error in /discover:', e);
99
+ throw new RequestError('Failed to discover devices', e);
100
+ }
101
+ });
102
+ server.onRequest('/add-device', async (body) => {
103
+ try {
104
+ const { deviceId, name, type } = body;
105
+ if (!deviceId) {
106
+ throw new Error('Device ID is required');
107
+ }
108
+ const { config, platform, cfgPath } = await getSwitchBotPlatformConfig();
109
+ console.log('[SwitchBot UI] POST /add-device - Adding device:', deviceId);
110
+ // Initialize devices array if needed
111
+ if (!Array.isArray(platform.devices)) {
112
+ platform.devices = [];
113
+ }
114
+ // Check if already exists
115
+ const exists = platform.devices.some((d) => (d.deviceId ?? d.id) === deviceId);
116
+ if (exists) {
117
+ return { success: false, data: { message: 'Device already in config' } };
118
+ }
119
+ // Add device
120
+ platform.devices.push({
121
+ deviceId,
122
+ configDeviceName: name,
123
+ configDeviceType: type,
124
+ });
125
+ // Save config
126
+ await fs.writeFile(cfgPath, JSON.stringify(config, null, 2), 'utf8');
127
+ console.log('[SwitchBot UI] Device added successfully:', deviceId);
128
+ return {
129
+ success: true,
130
+ data: { message: `Device "${name}" added successfully` },
131
+ };
132
+ }
133
+ catch (e) {
134
+ console.error('[SwitchBot UI] Error in /add-device:', e);
135
+ throw new RequestError('Failed to add device', e);
136
+ }
137
+ });
68
138
  server.onRequest('/credentials', async (body) => {
69
139
  try {
70
140
  // Handle both GET and POST requests
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/homebridge-ui/server.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,OAAO,EAAE,wBAAwB,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AACpF,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAEjC,MAAM,MAAM,GAAG,IAAI,wBAAwB,EAAE,CAAA;AAE7C,wDAAwD;AACxD,KAAK,UAAU,0BAA0B;IACvC,MAAM,OAAO,GAAG,MAAM,CAAC,oBAAoB,CAAA;IAC3C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAA;IACnD,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IAC9C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC3B,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAA;IAEnE,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,MAAM,YAAY,GAAG,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,IAAI,IAAI,EAAE,CAAA;QAC/C,IAAI,CAAC,YAAY,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC;YAC9D,SAAQ;QACV,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,CAAA;IAC9C,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAA;AAC3D,CAAC;AAED,MAAM,CAAC,SAAS,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;IACtC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,CAAC,oBAAoB,CAAA;QAC3C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAA;QACnD,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QAC9C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC3B,MAAM,KAAK,GAAsG,EAAE,CAAA;QAEnH,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAA;QACnE,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;YAC1B,IAAI,CAAC;gBACH,MAAM,YAAY,GAAG,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,IAAI,IAAI,EAAE,CAAA;gBAC/C,6CAA6C;gBAC7C,IAAI,CAAC,YAAY,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC;oBAC9D,SAAQ;gBACV,CAAC;gBAED,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,IAAI,CAAC,CAAA;gBAC9B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAA;gBACrE,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;oBACxB,MAAM,EAAE,GAAG,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,EAAE,CAAA;oBAC7B,IAAI,CAAC,EAAE,EAAE,CAAC;wBACR,SAAQ;oBACV,CAAC;oBAED,KAAK,CAAC,IAAI,CAAC;wBACT,EAAE;wBACF,IAAI,EAAE,CAAC,CAAC,gBAAgB,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,UAAU;wBAClD,IAAI,EAAE,CAAC,CAAC,gBAAgB,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,UAAU;wBAClD,oBAAoB,EAAE,CAAC,CAAC,oBAAoB,IAAI,CAAC,CAAC,UAAU,IAAI,MAAM;wBACtE,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,QAAQ,IAAI,SAAS;qBACxC,CAAC,CAAA;gBACJ,CAAC;YACH,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,oCAAoC;YACtC,CAAC;QACH,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,qCAAqC,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;QAC3E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAA;IACvC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,CAAC,CAAC,CAAA;QACrD,MAAM,IAAI,YAAY,CAAC,kCAAkC,EAAE,CAAC,CAAC,CAAA;IAC/D,CAAC;AACH,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,SAAS,CAAC,cAAc,EAAE,KAAK,EAAE,IAAS,EAAE,EAAE;IACnD,IAAI,CAAC;QACH,oCAAoC;QACpC,IAAI,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5C,sCAAsC;YACtC,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,0BAA0B,EAAE,CAAA;YAEvD,MAAM,MAAM,GAAG;gBACb,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,YAAY;gBACjC,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,aAAa;gBACnC,WAAW,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBAC7E,YAAY,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;aACjF,CAAA;YAED,OAAO,CAAC,GAAG,CAAC,2CAA2C,EAAE,MAAM,CAAC,CAAA;YAChE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;QACxC,CAAC;aAAM,CAAC;YACN,kCAAkC;YAClC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;YAE9B,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;gBACtB,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAA;YAClD,CAAC;YAED,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,0BAA0B,EAAE,CAAA;YAExE,OAAO,CAAC,GAAG,CAAC,wDAAwD,EAAE,QAAQ,CAAC,QAAQ,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAA;YACzG,OAAO,CAAC,GAAG,CAAC,6BAA6B,EAAE,OAAO,CAAC,CAAA;YACnD,OAAO,CAAC,GAAG,CAAC,8BAA8B,EAAE,KAAK,CAAC,MAAM,EAAE,gBAAgB,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;YAE1F,wDAAwD;YACxD,QAAQ,CAAC,YAAY,GAAG,KAAK,CAAA;YAC7B,QAAQ,CAAC,aAAa,GAAG,MAAM,CAAA;YAE/B,4BAA4B;YAC5B,MAAM,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;YAEpE,OAAO,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAA;YAE5D,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,IAAI,EAAE,EAAE,OAAO,EAAE,gCAAgC,EAAE;aACpD,CAAA;QACH,CAAC;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,CAAC,CAAC,CAAA;QACzD,MAAM,IAAI,YAAY,CAAC,sCAAsC,EAAE,CAAC,CAAC,CAAA;IACnE,CAAC;AACH,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,KAAK,EAAE,CAAA;AAEd,eAAe,MAAM,CAAA"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/homebridge-ui/server.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/fdf94c065018b63f189137635c0d74689aa57769/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/3f14ea4c3a85b6681916260e6148ec2b9d2de19c/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/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.77",
5
+ "version": "5.0.0-beta.78",
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": [
@@ -48,6 +48,17 @@
48
48
  <div id="saveStatus"></div>
49
49
  </div>
50
50
 
51
+ <div class="card">
52
+ <h2>Discover Devices</h2>
53
+ <p>Click "Discover" to find all devices available in your SwitchBot account and add them to your configuration.</p>
54
+ <button id="discoverBtn" onclick="discoverDevices()" style="margin-bottom: 16px">🔍 Discover Devices</button>
55
+ <div id="discoverStatus" style="margin-bottom: 16px"></div>
56
+ <div id="discoveredList" style="display: none">
57
+ <h3 style="margin-top: 0">Available Devices</h3>
58
+ <ul id="discoveredDevices" style="max-height: 300px; overflow-y: auto"></ul>
59
+ </div>
60
+ </div>
61
+
51
62
  <div class="card">
52
63
  <h2>Configured Devices</h2>
53
64
  <p>This page lists devices found in your Homebridge config for the SwitchBot platform. Use the copy button to insert device IDs into the plugin configuration. Connection preference (BLE/OpenAPI) is shown when available.</p>
@@ -144,7 +155,7 @@
144
155
  async function fetchDevices() {
145
156
  try {
146
157
  const resp = await homebridge.request('/devices', {})
147
- if (!resp || !resp.success) throw new Error(resp?.data?.message || 'request failed')
158
+ if (!resp || resp.success === false) throw new Error(resp?.data?.message || 'request failed')
148
159
  return resp.data || []
149
160
  } catch (e) {
150
161
  console.error(e)
@@ -152,6 +163,124 @@
152
163
  }
153
164
  }
154
165
 
166
+ async function discoverDevices() {
167
+ const btn = document.getElementById('discoverBtn')
168
+ const status = document.getElementById('discoverStatus')
169
+ const list = document.getElementById('discoveredList')
170
+ const ul = document.getElementById('discoveredDevices')
171
+
172
+ try {
173
+ btn.disabled = true
174
+ btn.textContent = '🔍 Discovering...'
175
+ status.textContent = 'Searching SwitchBot account...'
176
+ status.classList.remove('error')
177
+
178
+ const resp = await homebridge.request('/discover', {})
179
+ console.log('Discover response:', resp)
180
+
181
+ if (!resp || resp.success === false) {
182
+ throw new Error(resp?.data?.message || 'Discovery failed')
183
+ }
184
+
185
+ const devices = resp.data || []
186
+
187
+ if (!devices.length) {
188
+ status.textContent = 'No devices found in your SwitchBot account'
189
+ list.style.display = 'none'
190
+ return
191
+ }
192
+
193
+ status.textContent = `Found ${devices.length} device(s)`
194
+ ul.innerHTML = ''
195
+
196
+ for (const d of devices) {
197
+ const li = document.createElement('li')
198
+ li.style.display = 'flex'
199
+ li.style.gap = '8px'
200
+ li.style.alignItems = 'center'
201
+ li.style.justifyContent = 'space-between'
202
+ li.style.padding = '8px'
203
+ li.style.background = '#333'
204
+ li.style.borderRadius = '4px'
205
+ li.style.marginBottom = '8px'
206
+
207
+ const info = document.createElement('div')
208
+ info.style.flex = '1'
209
+ const name = document.createElement('div')
210
+ name.style.fontWeight = '500'
211
+ name.textContent = d.name || d.id
212
+ const details = document.createElement('div')
213
+ details.style.fontSize = '12px'
214
+ details.style.opacity = '0.75'
215
+ details.textContent = `ID: ${d.id} | Type: ${d.type} | Model: ${d.model || 'N/A'}`
216
+ info.appendChild(name)
217
+ info.appendChild(details)
218
+
219
+ const addBtn = document.createElement('button')
220
+ addBtn.textContent = '+ Add'
221
+ addBtn.style.marginLeft = '8px'
222
+ addBtn.onclick = async () => {
223
+ await addDeviceToConfig(d)
224
+ }
225
+
226
+ li.appendChild(info)
227
+ li.appendChild(addBtn)
228
+ ul.appendChild(li)
229
+ }
230
+
231
+ list.style.display = 'block'
232
+ } catch (e) {
233
+ console.error('Discovery error:', e)
234
+ status.textContent = 'Error: ' + (e?.message || 'Discovery failed')
235
+ status.classList.add('error')
236
+ list.style.display = 'none'
237
+ } finally {
238
+ btn.disabled = false
239
+ btn.textContent = '🔍 Discover Devices'
240
+ }
241
+ }
242
+
243
+ async function addDeviceToConfig(device) {
244
+ try {
245
+ console.log('Adding device to config:', device)
246
+
247
+ const resp = await homebridge.request('/add-device', {
248
+ deviceId: device.id,
249
+ name: device.name,
250
+ type: device.type,
251
+ })
252
+
253
+ console.log('Add device response:', resp)
254
+
255
+ if (!resp || resp.success === false) {
256
+ throw new Error(resp?.data?.message || 'Failed to add device')
257
+ }
258
+
259
+ // Show success message
260
+ const message = resp.data?.message || `Device "${device.name}" added successfully!`
261
+ const status = document.getElementById('discoverStatus')
262
+ status.textContent = '✓ ' + message
263
+ status.classList.remove('error')
264
+ status.classList.add('success-msg')
265
+
266
+ // Refresh lists after 1 second
267
+ setTimeout(async () => {
268
+ await loadConfiguredDevices()
269
+ await discoverDevices()
270
+ }, 1000)
271
+ } catch (e) {
272
+ console.error('Add device error:', e)
273
+ const status = document.getElementById('discoverStatus')
274
+ status.textContent = '✗ Error: ' + (e?.message || 'Failed to add device')
275
+ status.classList.add('error')
276
+ }
277
+ }
278
+
279
+ async function loadConfiguredDevices() {
280
+ const list = await fetchDevices()
281
+ render(list)
282
+ }
283
+
155
284
  function render(list) {
156
285
  const ul = document.getElementById('devices')
157
286
  const status = document.getElementById('status')
@@ -75,6 +75,92 @@ server.onRequest('/devices', async () => {
75
75
  }
76
76
  })
77
77
 
78
+ server.onRequest('/discover', async () => {
79
+ try {
80
+ const { platform } = await getSwitchBotPlatformConfig()
81
+
82
+ if (!platform.openApiToken) {
83
+ throw new Error('OpenAPI token not configured')
84
+ }
85
+
86
+ console.log('[SwitchBot UI] GET /discover - Fetching devices from SwitchBot API')
87
+
88
+ const url = 'https://api.switch-bot.com/v1.0/devices'
89
+ const opts = {
90
+ headers: {
91
+ Authorization: platform.openApiToken,
92
+ },
93
+ }
94
+
95
+ const resp = await fetch(url, opts)
96
+ if (!resp.ok) {
97
+ throw new Error(`SwitchBot API returned ${resp.status}`)
98
+ }
99
+
100
+ const data = await resp.json()
101
+ const devices = data.body || []
102
+
103
+ const discovered = devices.map((d: any) => ({
104
+ id: d.deviceId || d.id,
105
+ name: d.deviceName || d.name,
106
+ type: d.deviceType || d.type || 'unknown',
107
+ model: d.deviceModel || d.model,
108
+ enabled: d.enableCloudService !== false,
109
+ }))
110
+
111
+ console.log('[SwitchBot UI] GET /discover - Found', discovered.length, 'devices from API')
112
+ return { success: true, data: discovered }
113
+ } catch (e) {
114
+ console.error('[SwitchBot UI] Error in /discover:', e)
115
+ throw new RequestError('Failed to discover devices', e)
116
+ }
117
+ })
118
+
119
+ server.onRequest('/add-device', async (body: any) => {
120
+ try {
121
+ const { deviceId, name, type } = body
122
+
123
+ if (!deviceId) {
124
+ throw new Error('Device ID is required')
125
+ }
126
+
127
+ const { config, platform, cfgPath } = await getSwitchBotPlatformConfig()
128
+
129
+ console.log('[SwitchBot UI] POST /add-device - Adding device:', deviceId)
130
+
131
+ // Initialize devices array if needed
132
+ if (!Array.isArray(platform.devices)) {
133
+ platform.devices = []
134
+ }
135
+
136
+ // Check if already exists
137
+ const exists = platform.devices.some((d: any) => (d.deviceId ?? d.id) === deviceId)
138
+ if (exists) {
139
+ return { success: false, data: { message: 'Device already in config' } }
140
+ }
141
+
142
+ // Add device
143
+ platform.devices.push({
144
+ deviceId,
145
+ configDeviceName: name,
146
+ configDeviceType: type,
147
+ })
148
+
149
+ // Save config
150
+ await fs.writeFile(cfgPath, JSON.stringify(config, null, 2), 'utf8')
151
+
152
+ console.log('[SwitchBot UI] Device added successfully:', deviceId)
153
+
154
+ return {
155
+ success: true,
156
+ data: { message: `Device "${name}" added successfully` },
157
+ }
158
+ } catch (e) {
159
+ console.error('[SwitchBot UI] Error in /add-device:', e)
160
+ throw new RequestError('Failed to add device', e)
161
+ }
162
+ })
163
+
78
164
  server.onRequest('/credentials', async (body: any) => {
79
165
  try {
80
166
  // Handle both GET and POST requests