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

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.
@@ -1,106 +1,106 @@
1
- {
2
- "pluginAlias": "SwitchBot",
3
- "pluginType": "platform",
4
- "singular": true,
5
- "customUi": true,
6
- "customUiPath": "./dist/homebridge-ui",
7
- "headerDisplay": "<p align='center'><img width='100px' src='https://user-images.githubusercontent.com/9875439/102681754-ac2eb800-4189-11eb-8e17-7c47c804484b.png'></p>\n\nThe **SwitchBot** plugin allows you to control SwitchBot Devices. Use the hybrid client (BLE + OpenAPI) or only OpenAPI by supplying an Open Token.",
8
- "footerDisplay": "Please raise any issues on our [project page](https://github.com/OpenWonderLabs/homebridge-switchbot/issues).",
9
- "schema": {
10
- "type": "object",
11
- "required": [],
12
- "properties": {
13
- "name": {
14
- "type": "string",
15
- "title": "Name",
16
- "default": "SwitchBot"
17
- },
18
- "openApiToken": {
19
- "type": "string",
20
- "title": "OpenAPI Token",
21
- "description": "SwitchBot OpenAPI token used as a fallback when BLE isn't available. Keep this secret.",
22
- "format": "password"
23
- },
24
- "openApiSecret": {
25
- "type": "string",
26
- "title": "OpenAPI Secret (optional)",
27
- "description": "Optional OpenAPI secret for certain account setups.",
28
- "format": "password"
29
- },
30
- "enableMatter": {
31
- "type": "boolean",
32
- "title": "Enable Matter Support",
33
- "default": true,
34
- "description": "Allow the plugin to register Matter accessories when Homebridge Matter is available."
35
- },
36
- "preferMatter": {
37
- "type": "boolean",
38
- "title": "Prefer Matter when available",
39
- "default": true,
40
- "description": "If enabled and Matter is available, device will be presented as Matter accessories where supported."
41
- },
42
- "devices": {
43
- "type": "array",
44
- "title": "Devices",
45
- "description": "List of SwitchBot devices to manage. Use the UI to add devices and copy device IDs.",
46
- "items": {
47
- "type": "object",
48
- "required": ["deviceId", "configDeviceType"],
49
- "properties": {
50
- "configDeviceName": {
51
- "type": "string",
52
- "title": "Name",
53
- "description": "Friendly name for this device as shown in HomeKit/Matter."
54
- },
55
- "deviceId": {
56
- "type": "string",
57
- "title": "Device ID",
58
- "description": "The SwitchBot device identifier (MAC or SwitchBot Cloud deviceId). Use the plugin UI to copy this."
59
- },
60
- "configDeviceType": {
61
- "type": "string",
62
- "title": "Device Type",
63
- "description": "Select the device type so the plugin can expose the correct HomeKit/Matter services and characteristics.",
64
- "enum": [
65
- "bot",
66
- "curtain",
67
- "fan",
68
- "light",
69
- "lightstrip",
70
- "motion",
71
- "contact",
72
- "vacuum",
73
- "lock",
74
- "humidifier",
75
- "temperature",
76
- "relay",
77
- "plug",
78
- "wosweeper",
79
- "blindtilt",
80
- "curtain3",
81
- "rollershade",
82
- "meter",
83
- "waterdetector",
84
- "walletfinder",
85
- "unknown"
86
- ],
87
- "default": "unknown"
88
- },
89
- "connectionPreference": {
90
- "type": "string",
91
- "title": "Connection Preference",
92
- "description": "Select whether this device should prefer BLE, OpenAPI, or auto-detect (hybrid).",
93
- "enum": ["auto", "ble", "openapi"],
94
- "default": "auto"
95
- },
96
- "room": {
97
- "type": "string",
98
- "title": "Room",
99
- "description": "Optional room/location metadata for the device."
100
- }
1
+ {
2
+ "pluginAlias": "SwitchBot",
3
+ "pluginType": "platform",
4
+ "singular": true,
5
+ "customUi": true,
6
+ "customUiPath": "./dist/homebridge-ui",
7
+ "headerDisplay": "<p align='center'><img width='100px' src='https://user-images.githubusercontent.com/9875439/102681754-ac2eb800-4189-11eb-8e17-7c47c804484b.png'></p>\n\nThe **SwitchBot** plugin allows you to control SwitchBot Devices. Use the hybrid client (BLE + OpenAPI) or only OpenAPI by supplying an Open Token.",
8
+ "footerDisplay": "Please raise any issues on our [project page](https://github.com/OpenWonderLabs/homebridge-switchbot/issues).",
9
+ "schema": {
10
+ "type": "object",
11
+ "required": [],
12
+ "properties": {
13
+ "name": {
14
+ "type": "string",
15
+ "title": "Name",
16
+ "default": "SwitchBot"
17
+ },
18
+ "openApiToken": {
19
+ "type": "string",
20
+ "title": "OpenAPI Token",
21
+ "description": "SwitchBot OpenAPI token used as a fallback when BLE isn't available. Keep this secret.",
22
+ "format": "password"
23
+ },
24
+ "openApiSecret": {
25
+ "type": "string",
26
+ "title": "OpenAPI Secret (optional)",
27
+ "description": "Optional OpenAPI secret for certain account setups.",
28
+ "format": "password"
29
+ },
30
+ "enableMatter": {
31
+ "type": "boolean",
32
+ "title": "Enable Matter Support",
33
+ "default": true,
34
+ "description": "Allow the plugin to register Matter accessories when Homebridge Matter is available."
35
+ },
36
+ "preferMatter": {
37
+ "type": "boolean",
38
+ "title": "Prefer Matter when available",
39
+ "default": true,
40
+ "description": "If enabled and Matter is available, device will be presented as Matter accessories where supported."
41
+ },
42
+ "devices": {
43
+ "type": "array",
44
+ "title": "Devices",
45
+ "description": "List of SwitchBot devices to manage. Use the UI to add devices and copy device IDs.",
46
+ "items": {
47
+ "type": "object",
48
+ "required": ["deviceId", "configDeviceType"],
49
+ "properties": {
50
+ "configDeviceName": {
51
+ "type": "string",
52
+ "title": "Name",
53
+ "description": "Friendly name for this device as shown in HomeKit/Matter."
54
+ },
55
+ "deviceId": {
56
+ "type": "string",
57
+ "title": "Device ID",
58
+ "description": "The SwitchBot device identifier (MAC or SwitchBot Cloud deviceId). Use the plugin UI to copy this."
59
+ },
60
+ "configDeviceType": {
61
+ "type": "string",
62
+ "title": "Device Type",
63
+ "description": "Select the device type so the plugin can expose the correct HomeKit/Matter services and characteristics.",
64
+ "enum": [
65
+ "bot",
66
+ "curtain",
67
+ "fan",
68
+ "light",
69
+ "lightstrip",
70
+ "motion",
71
+ "contact",
72
+ "vacuum",
73
+ "lock",
74
+ "humidifier",
75
+ "temperature",
76
+ "relay",
77
+ "plug",
78
+ "wosweeper",
79
+ "blindtilt",
80
+ "curtain3",
81
+ "rollershade",
82
+ "meter",
83
+ "waterdetector",
84
+ "walletfinder",
85
+ "unknown"
86
+ ],
87
+ "default": "unknown"
88
+ },
89
+ "connectionPreference": {
90
+ "type": "string",
91
+ "title": "Connection Preference",
92
+ "description": "Select whether this device should prefer BLE, OpenAPI, or auto-detect (hybrid).",
93
+ "enum": ["auto", "ble", "openapi"],
94
+ "default": "auto"
95
+ },
96
+ "room": {
97
+ "type": "string",
98
+ "title": "Room",
99
+ "description": "Optional room/location metadata for the device."
101
100
  }
102
101
  }
103
102
  }
104
103
  }
105
104
  }
106
105
  }
106
+ }
@@ -1,4 +1,4 @@
1
- import type { SwitchBotPluginConfig } from '../settings';
1
+ import type { SwitchBotPluginConfig } from '../settings.js';
2
2
  export interface DeviceOptions {
3
3
  id: string;
4
4
  type: string;
@@ -1 +1 @@
1
- {"version":3,"file":"deviceBase.d.ts","sourceRoot":"","sources":["../../src/devices/deviceBase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAIxD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB;AAED,8BAAsB,UAAU;IAC9B,SAAS,CAAC,IAAI,EAAE,aAAa,CAAA;IAC7B,SAAS,CAAC,GAAG,EAAE,qBAAqB,CAAA;IACpC,SAAS,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI,CAAA;gBAEhB,IAAI,EAAE,aAAa,EAAE,GAAG,EAAE,qBAAqB;IAOrD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAG3B,QAAQ,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC;IAGjC,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IAE5C;;;;;;;;;;;;;;;;;;;OAmBG;IACH,kBAAkB,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG;IAmCjC;;;;;;;;;OASG;IACH,qBAAqB,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG;IA2C9B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAC/B"}
1
+ {"version":3,"file":"deviceBase.d.ts","sourceRoot":"","sources":["../../src/devices/deviceBase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AAI3D,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB;AAED,8BAAsB,UAAU;IAC9B,SAAS,CAAC,IAAI,EAAE,aAAa,CAAA;IAC7B,SAAS,CAAC,GAAG,EAAE,qBAAqB,CAAA;IACpC,SAAS,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI,CAAA;gBAEhB,IAAI,EAAE,aAAa,EAAE,GAAG,EAAE,qBAAqB;IAOrD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAG3B,QAAQ,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC;IAGjC,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IAE5C;;;;;;;;;;;;;;;;;;;OAmBG;IACH,kBAAkB,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG;IAmCjC;;;;;;;;;OASG;IACH,qBAAqB,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG;IA2C9B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAC/B"}
@@ -1,4 +1,4 @@
1
- import { MATTER_ATTRIBUTE_IDS, MATTER_CLUSTER_IDS } from '../utils';
1
+ import { MATTER_ATTRIBUTE_IDS, MATTER_CLUSTER_IDS } from '../utils.js';
2
2
  export class DeviceBase {
3
3
  opts;
4
4
  cfg;
@@ -1 +1 @@
1
- {"version":3,"file":"deviceBase.js","sourceRoot":"","sources":["../../src/devices/deviceBase.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"deviceBase.js","sourceRoot":"","sources":["../../src/devices/deviceBase.ts"],"names":[],"mappings":"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"}
@@ -5,21 +5,135 @@
5
5
  <meta name="viewport" content="width=device-width,initial-scale=1" />
6
6
  <title>SwitchBot Plugin UI</title>
7
7
  <style>
8
- body { font-family: system-ui, -apple-system, Arial; padding: 16px; }
9
- h1 { font-size: 18px }
8
+ body { font-family: system-ui, -apple-system, Arial; padding: 16px; background:#1a1a1a; color:#fff }
9
+ h1 { font-size: 24px; margin-top:0 }
10
+ h2 { font-size: 16px; margin-top: 24px; margin-bottom: 12px; border-bottom:1px solid #444; padding-bottom:8px }
10
11
  ul { padding-left: 0; list-style: none }
11
12
  li { margin: 8px 0; display:flex; gap:8px; align-items:center }
12
- button { padding: 6px 10px }
13
- code { background:#f3f3f3; padding:4px 6px; border-radius:4px }
13
+ button { padding: 8px 16px; background:#6366f1; color:#fff; border:none; border-radius:4px; cursor:pointer }
14
+ button:hover { background:#4f46e5 }
15
+ button.success { background:#10b981 }
16
+ code { background:#333; padding:4px 6px; border-radius:4px; color:#fff }
17
+ .form-group { margin-bottom:16px }
18
+ label { display:block; margin-bottom:6px; font-weight:500 }
19
+ input { width:100%; max-width:400px; padding:8px; background:#2a2a2a; border:1px solid #444; border-radius:4px; color:#fff; font-family:monospace }
20
+ input:focus { outline:none; border-color:#6366f1 }
21
+ .status { font-size:14px; color:#888; margin-top:4px }
22
+ .status.ok { color:#10b981 }
23
+ .error { color:#ef4444 }
24
+ .success-msg { color:#10b981; margin-top:8px }
25
+ .card { background:#2a2a2a; padding:16px; border-radius:8px; margin-bottom:16px }
14
26
  </style>
15
27
  </head>
16
28
  <body>
17
- <h1>SwitchBot: Configured Devices</h1>
18
- <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>
19
- <div id="status">Loading…</div>
20
- <ul id="devices"></ul>
29
+ <h1>🤖 SwitchBot Configuration</h1>
30
+
31
+ <div class="card">
32
+ <h2>API Credentials</h2>
33
+ <p>Configure your SwitchBot API token and secret to enable device discovery and control.</p>
34
+
35
+ <div class="form-group">
36
+ <label for="token">API Token:</label>
37
+ <input type="password" id="token" placeholder="Enter your SwitchBot API token" />
38
+ <div class="status" id="tokenStatus"></div>
39
+ </div>
40
+
41
+ <div class="form-group">
42
+ <label for="secret">API Secret:</label>
43
+ <input type="password" id="secret" placeholder="Enter your SwitchBot API secret" />
44
+ <div class="status" id="secretStatus"></div>
45
+ </div>
46
+
47
+ <button id="saveBtn" onclick="saveCredentials()">Save Credentials</button>
48
+ <div id="saveStatus"></div>
49
+ </div>
50
+
51
+ <div class="card">
52
+ <h2>Configured Devices</h2>
53
+ <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>
54
+ <div id="status">Loading…</div>
55
+ <ul id="devices"></ul>
56
+ </div>
21
57
 
22
58
  <script>
59
+ async function loadCredentialStatus() {
60
+ try {
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
+ }
67
+ }
68
+
69
+ const creds = resp.data || {}
70
+ const tokenStatus = document.getElementById('tokenStatus')
71
+ const secretStatus = document.getElementById('secretStatus')
72
+
73
+ if (creds.hasToken) {
74
+ tokenStatus.textContent = `✓ Configured (${creds.tokenLength} characters)`
75
+ tokenStatus.classList.add('ok')
76
+ } else {
77
+ tokenStatus.textContent = 'Not configured'
78
+ }
79
+
80
+ if (creds.hasSecret) {
81
+ secretStatus.textContent = `✓ Configured (${creds.secretLength} characters)`
82
+ secretStatus.classList.add('ok')
83
+ } else {
84
+ secretStatus.textContent = 'Not configured'
85
+ }
86
+ } catch (e) {
87
+ console.error('Error loading credentials:', e)
88
+ }
89
+ }
90
+
91
+ async function saveCredentials() {
92
+ const token = document.getElementById('token').value
93
+ const secret = document.getElementById('secret').value
94
+ const saveStatus = document.getElementById('saveStatus')
95
+ const saveBtn = document.getElementById('saveBtn')
96
+
97
+ if (!token || !secret) {
98
+ saveStatus.textContent = 'Please enter both token and secret'
99
+ saveStatus.classList.add('error')
100
+ return
101
+ }
102
+
103
+ try {
104
+ saveBtn.disabled = true
105
+ saveBtn.textContent = 'Saving...'
106
+
107
+ const resp = await homebridge.request('/credentials', { token, secret })
108
+ if (!resp || resp.success === false) {
109
+ throw new Error(resp?.data?.message || 'Save failed')
110
+ }
111
+
112
+ saveStatus.textContent = '✓ ' + (resp.data?.message || 'Credentials saved')
113
+ saveStatus.classList.remove('error')
114
+ saveStatus.classList.add('success-msg')
115
+
116
+ // Clear inputs after successful save
117
+ document.getElementById('token').value = ''
118
+ document.getElementById('secret').value = ''
119
+
120
+ // Reload status
121
+ setTimeout(() => loadCredentialStatus(), 1000)
122
+
123
+ // Clear status message
124
+ setTimeout(() => {
125
+ saveStatus.textContent = ''
126
+ saveStatus.classList.remove('success-msg')
127
+ }, 3000)
128
+ } catch (e) {
129
+ saveStatus.textContent = 'Error: ' + (e?.message || 'Failed to save')
130
+ saveStatus.classList.add('error')
131
+ } finally {
132
+ saveBtn.disabled = false
133
+ saveBtn.textContent = 'Save Credentials'
134
+ }
135
+ }
136
+
23
137
  async function fetchDevices() {
24
138
  try {
25
139
  const resp = await homebridge.request('/devices', {})
@@ -59,7 +173,11 @@
59
173
  try {
60
174
  await navigator.clipboard.writeText(d.id)
61
175
  btn.textContent = 'Copied'
62
- setTimeout(() => (btn.textContent = 'Copy ID'), 1200)
176
+ btn.classList.add('success')
177
+ setTimeout(() => {
178
+ btn.textContent = 'Copy ID'
179
+ btn.classList.remove('success')
180
+ }, 1200)
63
181
  } catch (e) {
64
182
  alert('Failed to copy')
65
183
  }
@@ -73,9 +191,10 @@
73
191
  }
74
192
 
75
193
  (async () => {
194
+ await loadCredentialStatus()
76
195
  const list = await fetchDevices()
77
196
  render(list)
78
197
  })()
79
198
  </script>
80
199
  </body>
81
- </html>
200
+ </html>
@@ -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;AAmD7C,eAAe,MAAM,CAAA"}
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,6 +1,24 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import { HomebridgePluginUiServer, RequestError } from '@homebridge/plugin-ui-utils';
3
3
  const server = new HomebridgePluginUiServer();
4
+ // Helper function to find the SwitchBot platform config
5
+ async function getSwitchBotPlatformConfig() {
6
+ const cfgPath = server.homebridgeConfigPath;
7
+ if (!cfgPath) {
8
+ throw new Error('HOMEBRIDGE_CONFIG_PATH not set');
9
+ }
10
+ const raw = await fs.readFile(cfgPath, 'utf8');
11
+ const cfg = JSON.parse(raw);
12
+ const platforms = Array.isArray(cfg.platforms) ? cfg.platforms : [];
13
+ for (const p of platforms) {
14
+ const platformName = p.platform || p.name || '';
15
+ if (!platformName || !/switchbot/i.test(String(platformName))) {
16
+ continue;
17
+ }
18
+ return { config: cfg, platform: p, cfgPath };
19
+ }
20
+ throw new Error('SwitchBot platform not found in config');
21
+ }
4
22
  server.onRequest('/devices', async () => {
5
23
  try {
6
24
  const cfgPath = server.homebridgeConfigPath;
@@ -44,6 +62,41 @@ server.onRequest('/devices', async () => {
44
62
  throw new RequestError('Failed to read Homebridge config', e);
45
63
  }
46
64
  });
65
+ server.onRequest('/credentials', async (body) => {
66
+ try {
67
+ // Handle both GET and POST requests
68
+ if (!body || Object.keys(body).length === 0) {
69
+ // GET request - return current status
70
+ const { platform } = await getSwitchBotPlatformConfig();
71
+ return {
72
+ hasToken: !!platform.openApiToken,
73
+ hasSecret: !!platform.openApiSecret,
74
+ tokenLength: platform.openApiToken ? String(platform.openApiToken).length : 0,
75
+ secretLength: platform.openApiSecret ? String(platform.openApiSecret).length : 0,
76
+ };
77
+ }
78
+ else {
79
+ // POST request - save credentials
80
+ const { token, secret } = body;
81
+ if (!token || !secret) {
82
+ throw new Error('Token and secret are required');
83
+ }
84
+ const { config, platform, cfgPath } = await getSwitchBotPlatformConfig();
85
+ // Save token and secret directly on the platform config
86
+ platform.openApiToken = token;
87
+ platform.openApiSecret = secret;
88
+ // Write back to config file
89
+ await fs.writeFile(cfgPath, JSON.stringify(config, null, 2), 'utf8');
90
+ return {
91
+ success: true,
92
+ message: 'Credentials saved successfully',
93
+ };
94
+ }
95
+ }
96
+ catch (e) {
97
+ throw new RequestError('Failed to handle credentials request', e);
98
+ }
99
+ });
47
100
  server.ready();
48
101
  export default server;
49
102
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/homebridge-ui/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAEjC,OAAO,EAAE,wBAAwB,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAEpF,MAAM,MAAM,GAAG,IAAI,wBAAwB,EAAE,CAAA;AAE7C,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,KAAK,CAAA;IACd,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,YAAY,CAAC,kCAAkC,EAAE,CAAC,CAAC,CAAA;IAC/D,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/c94a256343c2ebc4d7ccd446bf98eb0e2958061a/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/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>
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.72",
5
+ "version": "5.0.0-beta.74",
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": [
@@ -1,6 +1,6 @@
1
- import type { SwitchBotPluginConfig } from '../settings'
1
+ import type { SwitchBotPluginConfig } from '../settings.js'
2
2
 
3
- import { MATTER_ATTRIBUTE_IDS, MATTER_CLUSTER_IDS } from '../utils'
3
+ import { MATTER_ATTRIBUTE_IDS, MATTER_CLUSTER_IDS } from '../utils.js'
4
4
 
5
5
  export interface DeviceOptions {
6
6
  id: string
@@ -5,21 +5,135 @@
5
5
  <meta name="viewport" content="width=device-width,initial-scale=1" />
6
6
  <title>SwitchBot Plugin UI</title>
7
7
  <style>
8
- body { font-family: system-ui, -apple-system, Arial; padding: 16px; }
9
- h1 { font-size: 18px }
8
+ body { font-family: system-ui, -apple-system, Arial; padding: 16px; background:#1a1a1a; color:#fff }
9
+ h1 { font-size: 24px; margin-top:0 }
10
+ h2 { font-size: 16px; margin-top: 24px; margin-bottom: 12px; border-bottom:1px solid #444; padding-bottom:8px }
10
11
  ul { padding-left: 0; list-style: none }
11
12
  li { margin: 8px 0; display:flex; gap:8px; align-items:center }
12
- button { padding: 6px 10px }
13
- code { background:#f3f3f3; padding:4px 6px; border-radius:4px }
13
+ button { padding: 8px 16px; background:#6366f1; color:#fff; border:none; border-radius:4px; cursor:pointer }
14
+ button:hover { background:#4f46e5 }
15
+ button.success { background:#10b981 }
16
+ code { background:#333; padding:4px 6px; border-radius:4px; color:#fff }
17
+ .form-group { margin-bottom:16px }
18
+ label { display:block; margin-bottom:6px; font-weight:500 }
19
+ input { width:100%; max-width:400px; padding:8px; background:#2a2a2a; border:1px solid #444; border-radius:4px; color:#fff; font-family:monospace }
20
+ input:focus { outline:none; border-color:#6366f1 }
21
+ .status { font-size:14px; color:#888; margin-top:4px }
22
+ .status.ok { color:#10b981 }
23
+ .error { color:#ef4444 }
24
+ .success-msg { color:#10b981; margin-top:8px }
25
+ .card { background:#2a2a2a; padding:16px; border-radius:8px; margin-bottom:16px }
14
26
  </style>
15
27
  </head>
16
28
  <body>
17
- <h1>SwitchBot: Configured Devices</h1>
18
- <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>
19
- <div id="status">Loading…</div>
20
- <ul id="devices"></ul>
29
+ <h1>🤖 SwitchBot Configuration</h1>
30
+
31
+ <div class="card">
32
+ <h2>API Credentials</h2>
33
+ <p>Configure your SwitchBot API token and secret to enable device discovery and control.</p>
34
+
35
+ <div class="form-group">
36
+ <label for="token">API Token:</label>
37
+ <input type="password" id="token" placeholder="Enter your SwitchBot API token" />
38
+ <div class="status" id="tokenStatus"></div>
39
+ </div>
40
+
41
+ <div class="form-group">
42
+ <label for="secret">API Secret:</label>
43
+ <input type="password" id="secret" placeholder="Enter your SwitchBot API secret" />
44
+ <div class="status" id="secretStatus"></div>
45
+ </div>
46
+
47
+ <button id="saveBtn" onclick="saveCredentials()">Save Credentials</button>
48
+ <div id="saveStatus"></div>
49
+ </div>
50
+
51
+ <div class="card">
52
+ <h2>Configured Devices</h2>
53
+ <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>
54
+ <div id="status">Loading…</div>
55
+ <ul id="devices"></ul>
56
+ </div>
21
57
 
22
58
  <script>
59
+ async function loadCredentialStatus() {
60
+ try {
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
+ }
67
+ }
68
+
69
+ const creds = resp.data || {}
70
+ const tokenStatus = document.getElementById('tokenStatus')
71
+ const secretStatus = document.getElementById('secretStatus')
72
+
73
+ if (creds.hasToken) {
74
+ tokenStatus.textContent = `✓ Configured (${creds.tokenLength} characters)`
75
+ tokenStatus.classList.add('ok')
76
+ } else {
77
+ tokenStatus.textContent = 'Not configured'
78
+ }
79
+
80
+ if (creds.hasSecret) {
81
+ secretStatus.textContent = `✓ Configured (${creds.secretLength} characters)`
82
+ secretStatus.classList.add('ok')
83
+ } else {
84
+ secretStatus.textContent = 'Not configured'
85
+ }
86
+ } catch (e) {
87
+ console.error('Error loading credentials:', e)
88
+ }
89
+ }
90
+
91
+ async function saveCredentials() {
92
+ const token = document.getElementById('token').value
93
+ const secret = document.getElementById('secret').value
94
+ const saveStatus = document.getElementById('saveStatus')
95
+ const saveBtn = document.getElementById('saveBtn')
96
+
97
+ if (!token || !secret) {
98
+ saveStatus.textContent = 'Please enter both token and secret'
99
+ saveStatus.classList.add('error')
100
+ return
101
+ }
102
+
103
+ try {
104
+ saveBtn.disabled = true
105
+ saveBtn.textContent = 'Saving...'
106
+
107
+ const resp = await homebridge.request('/credentials', { token, secret })
108
+ if (!resp || resp.success === false) {
109
+ throw new Error(resp?.data?.message || 'Save failed')
110
+ }
111
+
112
+ saveStatus.textContent = '✓ ' + (resp.data?.message || 'Credentials saved')
113
+ saveStatus.classList.remove('error')
114
+ saveStatus.classList.add('success-msg')
115
+
116
+ // Clear inputs after successful save
117
+ document.getElementById('token').value = ''
118
+ document.getElementById('secret').value = ''
119
+
120
+ // Reload status
121
+ setTimeout(() => loadCredentialStatus(), 1000)
122
+
123
+ // Clear status message
124
+ setTimeout(() => {
125
+ saveStatus.textContent = ''
126
+ saveStatus.classList.remove('success-msg')
127
+ }, 3000)
128
+ } catch (e) {
129
+ saveStatus.textContent = 'Error: ' + (e?.message || 'Failed to save')
130
+ saveStatus.classList.add('error')
131
+ } finally {
132
+ saveBtn.disabled = false
133
+ saveBtn.textContent = 'Save Credentials'
134
+ }
135
+ }
136
+
23
137
  async function fetchDevices() {
24
138
  try {
25
139
  const resp = await homebridge.request('/devices', {})
@@ -59,7 +173,11 @@
59
173
  try {
60
174
  await navigator.clipboard.writeText(d.id)
61
175
  btn.textContent = 'Copied'
62
- setTimeout(() => (btn.textContent = 'Copy ID'), 1200)
176
+ btn.classList.add('success')
177
+ setTimeout(() => {
178
+ btn.textContent = 'Copy ID'
179
+ btn.classList.remove('success')
180
+ }, 1200)
63
181
  } catch (e) {
64
182
  alert('Failed to copy')
65
183
  }
@@ -73,9 +191,10 @@
73
191
  }
74
192
 
75
193
  (async () => {
194
+ await loadCredentialStatus()
76
195
  const list = await fetchDevices()
77
196
  render(list)
78
197
  })()
79
198
  </script>
80
199
  </body>
81
- </html>
200
+ </html>
@@ -4,6 +4,28 @@ import { HomebridgePluginUiServer, RequestError } from '@homebridge/plugin-ui-ut
4
4
 
5
5
  const server = new HomebridgePluginUiServer()
6
6
 
7
+ // Helper function to find the SwitchBot platform config
8
+ async function getSwitchBotPlatformConfig() {
9
+ const cfgPath = server.homebridgeConfigPath
10
+ if (!cfgPath) {
11
+ throw new Error('HOMEBRIDGE_CONFIG_PATH not set')
12
+ }
13
+
14
+ const raw = await fs.readFile(cfgPath, 'utf8')
15
+ const cfg = JSON.parse(raw)
16
+ const platforms = Array.isArray(cfg.platforms) ? cfg.platforms : []
17
+
18
+ for (const p of platforms) {
19
+ const platformName = p.platform || p.name || ''
20
+ if (!platformName || !/switchbot/i.test(String(platformName))) {
21
+ continue
22
+ }
23
+ return { config: cfg, platform: p, cfgPath }
24
+ }
25
+
26
+ throw new Error('SwitchBot platform not found in config')
27
+ }
28
+
7
29
  server.onRequest('/devices', async () => {
8
30
  try {
9
31
  const cfgPath = server.homebridgeConfigPath
@@ -51,6 +73,46 @@ server.onRequest('/devices', async () => {
51
73
  }
52
74
  })
53
75
 
76
+ server.onRequest('/credentials', async (body: any) => {
77
+ try {
78
+ // Handle both GET and POST requests
79
+ if (!body || Object.keys(body).length === 0) {
80
+ // GET request - return current status
81
+ const { platform } = await getSwitchBotPlatformConfig()
82
+
83
+ return {
84
+ hasToken: !!platform.openApiToken,
85
+ hasSecret: !!platform.openApiSecret,
86
+ tokenLength: platform.openApiToken ? String(platform.openApiToken).length : 0,
87
+ secretLength: platform.openApiSecret ? String(platform.openApiSecret).length : 0,
88
+ }
89
+ } else {
90
+ // POST request - save credentials
91
+ const { token, secret } = body
92
+
93
+ if (!token || !secret) {
94
+ throw new Error('Token and secret are required')
95
+ }
96
+
97
+ const { config, platform, cfgPath } = await getSwitchBotPlatformConfig()
98
+
99
+ // Save token and secret directly on the platform config
100
+ platform.openApiToken = token
101
+ platform.openApiSecret = secret
102
+
103
+ // Write back to config file
104
+ await fs.writeFile(cfgPath, JSON.stringify(config, null, 2), 'utf8')
105
+
106
+ return {
107
+ success: true,
108
+ message: 'Credentials saved successfully',
109
+ }
110
+ }
111
+ } catch (e) {
112
+ throw new RequestError('Failed to handle credentials request', e)
113
+ }
114
+ })
115
+
54
116
  server.ready()
55
117
 
56
118
  export default server