@switchbot/homebridge-switchbot 5.0.0-beta.110 → 5.0.0-beta.112
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/copilot-instructions.md +39 -0
- package/dist/homebridge-ui/endpoints/devices.d.ts.map +1 -1
- package/dist/homebridge-ui/endpoints/devices.js +10 -1
- package/dist/homebridge-ui/endpoints/devices.js.map +1 -1
- package/dist/homebridge-ui/public/css/styles.css +111 -16
- package/dist/homebridge-ui/public/index.html +34 -60
- package/dist/homebridge-ui/public/js/api.d.ts +5 -1
- package/dist/homebridge-ui/public/js/api.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/api.js +13 -3
- package/dist/homebridge-ui/public/js/api.js.map +1 -1
- package/dist/homebridge-ui/public/js/api.ts +13 -3
- package/dist/homebridge-ui/public/js/app.js +338 -42
- package/dist/homebridge-ui/public/js/app.js.map +3 -3
- package/dist/homebridge-ui/public/js/devices.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/devices.js +12 -3
- package/dist/homebridge-ui/public/js/devices.js.map +1 -1
- package/dist/homebridge-ui/public/js/devices.ts +13 -3
- package/dist/homebridge-ui/public/js/discovery.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/discovery.js +37 -3
- package/dist/homebridge-ui/public/js/discovery.js.map +1 -1
- package/dist/homebridge-ui/public/js/discovery.ts +39 -2
- package/dist/homebridge-ui/public/js/modals.d.ts +5 -0
- package/dist/homebridge-ui/public/js/modals.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/modals.js +146 -0
- package/dist/homebridge-ui/public/js/modals.js.map +1 -1
- package/dist/homebridge-ui/public/js/modals.ts +167 -0
- package/dist/homebridge-ui/public/js/render.d.ts +3 -1
- package/dist/homebridge-ui/public/js/render.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/render.js +135 -32
- package/dist/homebridge-ui/public/js/render.js.map +1 -1
- package/dist/homebridge-ui/public/js/render.ts +145 -32
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +64 -109
- package/dist/platform.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +2 -1
- package/dist/utils.js.map +1 -1
- package/docs/variables/default.html +1 -1
- package/package.json +9 -8
- package/src/homebridge-ui/endpoints/devices.ts +5 -2
- package/src/homebridge-ui/public/css/styles.css +111 -16
- package/src/homebridge-ui/public/index.html +34 -60
- package/src/homebridge-ui/public/js/api.ts +13 -3
- package/src/homebridge-ui/public/js/devices.ts +13 -3
- package/src/homebridge-ui/public/js/discovery.ts +39 -2
- package/src/homebridge-ui/public/js/modals.ts +167 -0
- package/src/homebridge-ui/public/js/render.ts +145 -32
- package/src/platform.ts +77 -109
- package/src/utils.ts +2 -1
- package/test/e2e/run-e2e.spec.ts +12 -10
- package/test/helpers/matter-harness.ts +2 -2
- package/test/matter-childbridge.spec.ts +5 -1
- package/test/matter-integration.spec.ts +41 -0
- package/test/utils.spec.ts +18 -2
- package/todo/COMPLETED.md +53 -0
- package/todo/TODO.md +13 -42
- package/switchbot-homebridge-switchbot-5.0.0.tgz +0 -0
|
@@ -9,65 +9,45 @@
|
|
|
9
9
|
<body>
|
|
10
10
|
<h1>🤖 SwitchBot Configuration</h1>
|
|
11
11
|
|
|
12
|
-
<div class="card">
|
|
12
|
+
<div class="card compact">
|
|
13
13
|
<h2>API Credentials</h2>
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
type="password"
|
|
23
|
-
id="
|
|
24
|
-
|
|
25
|
-
/>
|
|
26
|
-
<div class="status" id="tokenStatus"></div>
|
|
27
|
-
</div>
|
|
28
|
-
|
|
29
|
-
<div class="form-group">
|
|
30
|
-
<label for="secret">API Secret:</label>
|
|
31
|
-
<input
|
|
32
|
-
type="password"
|
|
33
|
-
id="secret"
|
|
34
|
-
placeholder="Enter your SwitchBot API secret"
|
|
35
|
-
/>
|
|
36
|
-
<div class="status" id="secretStatus"></div>
|
|
14
|
+
<div class="credentials-row">
|
|
15
|
+
<div class="form-group">
|
|
16
|
+
<label for="token">Token:</label>
|
|
17
|
+
<input type="password" id="token" placeholder="Enter token" />
|
|
18
|
+
<div class="status" id="tokenStatus"></div>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="form-group">
|
|
21
|
+
<label for="secret">Secret:</label>
|
|
22
|
+
<input type="password" id="secret" placeholder="Enter secret" />
|
|
23
|
+
<div class="status" id="secretStatus"></div>
|
|
24
|
+
</div>
|
|
37
25
|
</div>
|
|
38
|
-
|
|
39
|
-
<button id="saveBtn" onclick="saveCredentials()">Save Credentials</button>
|
|
26
|
+
<button id="saveBtn" onclick="saveCredentials()">Save</button>
|
|
40
27
|
<div id="saveStatus"></div>
|
|
41
28
|
</div>
|
|
42
29
|
|
|
43
|
-
<div class="card">
|
|
30
|
+
<div class="card compact">
|
|
44
31
|
<h2>Discover Devices</h2>
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<input
|
|
54
|
-
type="checkbox"
|
|
55
|
-
id="autoAddAllCheckbox"
|
|
56
|
-
style="width: auto; margin: 0"
|
|
57
|
-
/>
|
|
58
|
-
<span>Auto-add all discovered devices</span>
|
|
32
|
+
<div style="margin-bottom: 6px">
|
|
33
|
+
<label style="display: flex; align-items: center; gap: 4px; cursor: pointer; font-size: 11px">
|
|
34
|
+
<input type="checkbox" id="autoAddAllCheckbox" style="width: auto; margin: 0" />
|
|
35
|
+
<span>Auto-add all</span>
|
|
36
|
+
<span
|
|
37
|
+
title="When enabled, Discover immediately adds all found devices to your config. Existing devices are skipped automatically."
|
|
38
|
+
style="font-size: 11px; opacity: 0.7"
|
|
39
|
+
>ⓘ</span>
|
|
59
40
|
</label>
|
|
41
|
+
<div style="margin-top: 2px; margin-left: 22px; font-size: 10px; opacity: 0.75; line-height: 1.25">
|
|
42
|
+
Adds all discovered devices automatically; already configured devices are skipped.
|
|
43
|
+
</div>
|
|
60
44
|
</div>
|
|
61
|
-
<button
|
|
62
|
-
|
|
63
|
-
onclick="discoverDevices()"
|
|
64
|
-
style="margin-bottom: 16px"
|
|
65
|
-
>
|
|
66
|
-
🔍 Discover Devices
|
|
45
|
+
<button id="discoverBtn" onclick="discoverDevices()" style="margin-bottom: 8px">
|
|
46
|
+
🔍 Discover
|
|
67
47
|
</button>
|
|
68
|
-
<div id="discoverStatus" style="margin-bottom:
|
|
48
|
+
<div id="discoverStatus" style="margin-bottom: 6px; font-size: 11px"></div>
|
|
69
49
|
<div id="discoveredList" style="display: none">
|
|
70
|
-
<h3 style="margin-top: 0">Available Devices</h3>
|
|
50
|
+
<h3 style="margin-top: 0; font-size: 12px; margin-bottom: 6px; font-weight: 600">Available Devices</h3>
|
|
71
51
|
<ul
|
|
72
52
|
id="discoveredDevices"
|
|
73
53
|
style="max-height: 300px; overflow-y: auto"
|
|
@@ -75,22 +55,16 @@
|
|
|
75
55
|
</div>
|
|
76
56
|
</div>
|
|
77
57
|
|
|
78
|
-
<div class="card">
|
|
58
|
+
<div class="card compact">
|
|
79
59
|
<h2>Configured Devices</h2>
|
|
80
|
-
<p>
|
|
81
|
-
This page lists devices found in your Homebridge config for the
|
|
82
|
-
SwitchBot platform. Use the copy button to insert device IDs into the
|
|
83
|
-
plugin configuration. Connection preference (BLE/OpenAPI) is shown when
|
|
84
|
-
available.
|
|
85
|
-
</p>
|
|
86
60
|
<div id="status">Loading…</div>
|
|
87
61
|
<ul id="devices"></ul>
|
|
88
62
|
<div
|
|
89
63
|
id="removeAllContainer"
|
|
90
64
|
style="
|
|
91
65
|
display: none;
|
|
92
|
-
margin-top:
|
|
93
|
-
padding-top:
|
|
66
|
+
margin-top: 12px;
|
|
67
|
+
padding-top: 10px;
|
|
94
68
|
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
|
95
69
|
"
|
|
96
70
|
>
|
|
@@ -99,8 +73,8 @@
|
|
|
99
73
|
style="
|
|
100
74
|
background: #dc2626;
|
|
101
75
|
width: 100%;
|
|
102
|
-
padding:
|
|
103
|
-
font-size:
|
|
76
|
+
padding: 7px 12px;
|
|
77
|
+
font-size: 12px;
|
|
104
78
|
font-weight: 500;
|
|
105
79
|
"
|
|
106
80
|
>
|
|
@@ -115,10 +115,20 @@ export async function discoverDevices(): Promise<any[]> {
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
export async function addDevice(
|
|
118
|
+
export async function addDevice(
|
|
119
|
+
deviceId: string,
|
|
120
|
+
name: string,
|
|
121
|
+
type: string,
|
|
122
|
+
options?: { address?: string, model?: string, rssi?: number },
|
|
123
|
+
): Promise<any> {
|
|
119
124
|
try {
|
|
120
|
-
|
|
121
|
-
|
|
125
|
+
const payload: any = { deviceId, name, type }
|
|
126
|
+
if (options?.address) { payload.address = options.address }
|
|
127
|
+
if (options?.model) { payload.model = options.model }
|
|
128
|
+
if (options?.rssi !== undefined && options?.rssi !== null && options?.rssi !== 0) { payload.rssi = options.rssi }
|
|
129
|
+
|
|
130
|
+
uiLog.info('Adding device to config:', payload)
|
|
131
|
+
const resp = await homebridge.request('/add-device', payload)
|
|
122
132
|
uiLog.info('Add device response:', resp)
|
|
123
133
|
|
|
124
134
|
if (!resp || resp.success === false) {
|
|
@@ -8,9 +8,19 @@ export async function addDeviceToConfig(device: any, options: { refresh?: boolea
|
|
|
8
8
|
const { refresh = true, showStatus = true } = options
|
|
9
9
|
|
|
10
10
|
try {
|
|
11
|
+
const { importDiscoveredDevice } = await import('./modals.js')
|
|
12
|
+
const importValues = await importDiscoveredDevice(device)
|
|
13
|
+
if (!importValues) {
|
|
14
|
+
return { added: false }
|
|
15
|
+
}
|
|
16
|
+
|
|
11
17
|
showBusyUi()
|
|
12
18
|
uiLog.info('Adding device to config:', device)
|
|
13
|
-
const resp = await addDevice(device.id,
|
|
19
|
+
const resp = await addDevice(device.id, importValues.configDeviceName, importValues.configDeviceType, {
|
|
20
|
+
address: importValues.address,
|
|
21
|
+
model: device.model,
|
|
22
|
+
rssi: device.rssi,
|
|
23
|
+
})
|
|
14
24
|
uiLog.info('Add device response:', resp)
|
|
15
25
|
|
|
16
26
|
if (!resp || resp.success === false) {
|
|
@@ -21,8 +31,8 @@ export async function addDeviceToConfig(device: any, options: { refresh?: boolea
|
|
|
21
31
|
const message
|
|
22
32
|
= resp?.message
|
|
23
33
|
|| (alreadyExists
|
|
24
|
-
? `Device "${
|
|
25
|
-
: `Device "${
|
|
34
|
+
? `Device "${importValues.configDeviceName}" already in config`
|
|
35
|
+
: `Device "${importValues.configDeviceName}" added successfully!`)
|
|
26
36
|
|
|
27
37
|
if (alreadyExists) {
|
|
28
38
|
toastInfo(message)
|
|
@@ -10,6 +10,10 @@ import { hideBusyUi, showBusyUi } from './modal.js'
|
|
|
10
10
|
import { renderDiscoveredDevices, filterDevices, sortDevices, getDiscoveryPreferences, setDiscoveryPreferences } from './render.js'
|
|
11
11
|
import { toastError, toastInfo, toastSuccess, toastWarning } from './toast.js'
|
|
12
12
|
|
|
13
|
+
function normalizeId(value: any): string {
|
|
14
|
+
return String(value ?? '').trim().toLowerCase()
|
|
15
|
+
}
|
|
16
|
+
|
|
13
17
|
export async function discoverDevices(): Promise<void> {
|
|
14
18
|
const btn = document.getElementById('discoverBtn') as HTMLButtonElement
|
|
15
19
|
const status = document.getElementById('discoverStatus')
|
|
@@ -138,6 +142,7 @@ export async function discoverDevices(): Promise<void> {
|
|
|
138
142
|
btn.style.cursor = 'pointer'
|
|
139
143
|
btn.style.border = preferences.connectionType === option.value ? '2px solid #007AFF' : '1px solid #ccc'
|
|
140
144
|
btn.style.backgroundColor = preferences.connectionType === option.value ? '#f0f7ff' : '#fff'
|
|
145
|
+
btn.style.color = preferences.connectionType === option.value ? '#1d4ed8' : '#374151'
|
|
141
146
|
|
|
142
147
|
btn.onclick = () => {
|
|
143
148
|
preferences.connectionType = option.value
|
|
@@ -147,9 +152,11 @@ export async function discoverDevices(): Promise<void> {
|
|
|
147
152
|
for (const b of Array.from(filterGroup.querySelectorAll('button'))) {
|
|
148
153
|
;(b as HTMLButtonElement).style.border = '1px solid #ccc'
|
|
149
154
|
;(b as HTMLButtonElement).style.backgroundColor = '#fff'
|
|
155
|
+
;(b as HTMLButtonElement).style.color = '#374151'
|
|
150
156
|
}
|
|
151
157
|
btn.style.border = '2px solid #007AFF'
|
|
152
158
|
btn.style.backgroundColor = '#f0f7ff'
|
|
159
|
+
btn.style.color = '#1d4ed8'
|
|
153
160
|
}
|
|
154
161
|
|
|
155
162
|
filterGroup.appendChild(btn)
|
|
@@ -222,6 +229,19 @@ export async function discoverDevices(): Promise<void> {
|
|
|
222
229
|
list.innerHTML = ''
|
|
223
230
|
list.appendChild(controlsDiv)
|
|
224
231
|
|
|
232
|
+
// Create or get the device list container
|
|
233
|
+
let deviceListContainer = document.getElementById('discoveredDevices')
|
|
234
|
+
if (!deviceListContainer) {
|
|
235
|
+
deviceListContainer = document.createElement('ul')
|
|
236
|
+
deviceListContainer.id = 'discoveredDevices'
|
|
237
|
+
deviceListContainer.style.maxHeight = '400px'
|
|
238
|
+
deviceListContainer.style.overflowY = 'auto'
|
|
239
|
+
deviceListContainer.style.marginTop = '12px'
|
|
240
|
+
deviceListContainer.style.padding = '0'
|
|
241
|
+
deviceListContainer.style.listStyle = 'none'
|
|
242
|
+
list.appendChild(deviceListContainer)
|
|
243
|
+
}
|
|
244
|
+
|
|
225
245
|
// Display discovery list
|
|
226
246
|
list.style.display = 'block'
|
|
227
247
|
|
|
@@ -255,9 +275,26 @@ async function updateDiscoveryView(
|
|
|
255
275
|
const filtered = filterDevices(allDevices, preferences.connectionType, preferences.searchQuery)
|
|
256
276
|
const sorted = sortDevices(filtered, preferences.sortBy)
|
|
257
277
|
|
|
278
|
+
// Build configured device lookup for duplicate detection
|
|
279
|
+
const configured = await fetchDevices()
|
|
280
|
+
const configuredIds = new Set((configured || []).map(d => normalizeId(d?.id || d?.deviceId)))
|
|
281
|
+
|
|
258
282
|
// Render updated list
|
|
259
|
-
const ul = await renderDiscoveredDevices(sorted
|
|
260
|
-
|
|
283
|
+
const ul = await renderDiscoveredDevices(sorted, {
|
|
284
|
+
configuredIds,
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
// Replace or append the rendered list
|
|
288
|
+
const existingList = document.getElementById('discoveredDevices')
|
|
289
|
+
if (existingList && existingList.parentNode) {
|
|
290
|
+
existingList.replaceWith(ul)
|
|
291
|
+
} else {
|
|
292
|
+
// Fallback: append to discovery list container
|
|
293
|
+
const listContainer = document.getElementById('discoveredList')
|
|
294
|
+
if (listContainer) {
|
|
295
|
+
listContainer.appendChild(ul)
|
|
296
|
+
}
|
|
297
|
+
}
|
|
261
298
|
ul.id = 'discoveredDevices'
|
|
262
299
|
|
|
263
300
|
// Update status with count
|
|
@@ -1,6 +1,173 @@
|
|
|
1
1
|
import { DEVICE_TYPES } from './constants.js'
|
|
2
2
|
import { uiLog } from './logger.js'
|
|
3
3
|
|
|
4
|
+
export async function importDiscoveredDevice(device: any): Promise<{ configDeviceName: string, configDeviceType: string, address?: string } | null> {
|
|
5
|
+
return new Promise((resolve) => {
|
|
6
|
+
const div = document.createElement('div')
|
|
7
|
+
div.style.position = 'fixed'
|
|
8
|
+
div.style.top = '0'
|
|
9
|
+
div.style.left = '0'
|
|
10
|
+
div.style.width = '100%'
|
|
11
|
+
div.style.height = '100%'
|
|
12
|
+
div.style.background = 'rgba(0,0,0,0.7)'
|
|
13
|
+
div.style.display = 'flex'
|
|
14
|
+
div.style.alignItems = 'center'
|
|
15
|
+
div.style.justifyContent = 'center'
|
|
16
|
+
div.style.zIndex = '9999'
|
|
17
|
+
|
|
18
|
+
const modal = document.createElement('div')
|
|
19
|
+
modal.style.background = getComputedStyle(document.body).backgroundColor
|
|
20
|
+
modal.style.color = getComputedStyle(document.body).color
|
|
21
|
+
modal.style.padding = '0'
|
|
22
|
+
modal.style.borderRadius = '10px'
|
|
23
|
+
modal.style.minWidth = '440px'
|
|
24
|
+
modal.style.maxWidth = '90vw'
|
|
25
|
+
modal.style.boxShadow = '0 8px 32px rgba(0,0,0,0.35)'
|
|
26
|
+
modal.style.overflow = 'hidden'
|
|
27
|
+
modal.style.borderTop = '3px solid var(--switchbot-red, #ef4444)'
|
|
28
|
+
|
|
29
|
+
const title = document.createElement('h3')
|
|
30
|
+
title.textContent = 'Import Discovered Device'
|
|
31
|
+
title.style.marginTop = '0'
|
|
32
|
+
title.style.marginBottom = '16px'
|
|
33
|
+
title.style.padding = '20px 20px 0'
|
|
34
|
+
title.style.fontSize = '18px'
|
|
35
|
+
title.style.fontWeight = '600'
|
|
36
|
+
title.style.color = 'var(--switchbot-red, #ef4444)'
|
|
37
|
+
title.style.letterSpacing = '-0.02em'
|
|
38
|
+
|
|
39
|
+
const contentDiv = document.createElement('div')
|
|
40
|
+
contentDiv.style.padding = '0 20px 20px'
|
|
41
|
+
|
|
42
|
+
const nameLabel = document.createElement('label')
|
|
43
|
+
nameLabel.textContent = 'Device Name'
|
|
44
|
+
nameLabel.style.display = 'block'
|
|
45
|
+
nameLabel.style.marginBottom = '6px'
|
|
46
|
+
nameLabel.style.fontWeight = '500'
|
|
47
|
+
nameLabel.style.fontSize = '12px'
|
|
48
|
+
nameLabel.style.color = '#6b7280'
|
|
49
|
+
|
|
50
|
+
const nameInput = document.createElement('input')
|
|
51
|
+
nameInput.type = 'text'
|
|
52
|
+
nameInput.value = device.name || device.id || ''
|
|
53
|
+
nameInput.style.width = '100%'
|
|
54
|
+
nameInput.style.marginBottom = '12px'
|
|
55
|
+
nameInput.style.padding = '8px 10px'
|
|
56
|
+
nameInput.style.borderRadius = '6px'
|
|
57
|
+
nameInput.style.fontSize = '14px'
|
|
58
|
+
nameInput.style.boxSizing = 'border-box'
|
|
59
|
+
|
|
60
|
+
const typeLabel = document.createElement('label')
|
|
61
|
+
typeLabel.textContent = 'Config Device Type'
|
|
62
|
+
typeLabel.style.display = 'block'
|
|
63
|
+
typeLabel.style.marginBottom = '6px'
|
|
64
|
+
typeLabel.style.fontWeight = '500'
|
|
65
|
+
typeLabel.style.fontSize = '12px'
|
|
66
|
+
typeLabel.style.color = '#6b7280'
|
|
67
|
+
|
|
68
|
+
const typeSelect = document.createElement('select')
|
|
69
|
+
typeSelect.style.width = '100%'
|
|
70
|
+
typeSelect.style.padding = '8px 10px'
|
|
71
|
+
typeSelect.style.marginBottom = '12px'
|
|
72
|
+
typeSelect.style.borderRadius = '6px'
|
|
73
|
+
typeSelect.style.fontSize = '14px'
|
|
74
|
+
typeSelect.style.background = getComputedStyle(nameInput).background
|
|
75
|
+
typeSelect.style.color = getComputedStyle(nameInput).color
|
|
76
|
+
typeSelect.style.border = getComputedStyle(nameInput).border
|
|
77
|
+
typeSelect.style.boxSizing = 'border-box'
|
|
78
|
+
|
|
79
|
+
Object.keys(DEVICE_TYPES).forEach((categoryName) => {
|
|
80
|
+
const optgroup = document.createElement('optgroup')
|
|
81
|
+
optgroup.label = categoryName
|
|
82
|
+
|
|
83
|
+
DEVICE_TYPES[categoryName as keyof typeof DEVICE_TYPES].forEach((deviceType) => {
|
|
84
|
+
const opt = document.createElement('option')
|
|
85
|
+
opt.value = deviceType
|
|
86
|
+
opt.text = deviceType
|
|
87
|
+
const detectedType = (device.type || '').toLowerCase()
|
|
88
|
+
opt.selected = deviceType.toLowerCase() === detectedType
|
|
89
|
+
optgroup.appendChild(opt)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
typeSelect.appendChild(optgroup)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
const macLabel = document.createElement('label')
|
|
96
|
+
macLabel.textContent = 'BLE MAC Address (optional)'
|
|
97
|
+
macLabel.style.display = 'block'
|
|
98
|
+
macLabel.style.marginBottom = '6px'
|
|
99
|
+
macLabel.style.fontWeight = '500'
|
|
100
|
+
macLabel.style.fontSize = '12px'
|
|
101
|
+
macLabel.style.color = '#6b7280'
|
|
102
|
+
|
|
103
|
+
const macInput = document.createElement('input')
|
|
104
|
+
macInput.type = 'text'
|
|
105
|
+
macInput.value = device.address || ''
|
|
106
|
+
macInput.placeholder = 'AA:BB:CC:DD:EE:FF'
|
|
107
|
+
macInput.style.width = '100%'
|
|
108
|
+
macInput.style.marginBottom = '12px'
|
|
109
|
+
macInput.style.padding = '8px 10px'
|
|
110
|
+
macInput.style.borderRadius = '6px'
|
|
111
|
+
macInput.style.fontSize = '14px'
|
|
112
|
+
macInput.style.boxSizing = 'border-box'
|
|
113
|
+
|
|
114
|
+
const buttons = document.createElement('div')
|
|
115
|
+
buttons.style.display = 'flex'
|
|
116
|
+
buttons.style.gap = '10px'
|
|
117
|
+
buttons.style.justifyContent = 'flex-end'
|
|
118
|
+
buttons.style.marginTop = '18px'
|
|
119
|
+
buttons.style.paddingTop = '18px'
|
|
120
|
+
buttons.style.borderTop = '1px solid rgba(0, 0, 0, 0.08)'
|
|
121
|
+
|
|
122
|
+
const cancelBtn = document.createElement('button')
|
|
123
|
+
cancelBtn.textContent = 'Cancel'
|
|
124
|
+
cancelBtn.className = 'secondary'
|
|
125
|
+
cancelBtn.style.background = '#6b7280'
|
|
126
|
+
cancelBtn.style.padding = '8px 16px'
|
|
127
|
+
cancelBtn.style.fontSize = '13px'
|
|
128
|
+
|
|
129
|
+
const importBtn = document.createElement('button')
|
|
130
|
+
importBtn.textContent = 'Add to Config'
|
|
131
|
+
importBtn.style.background = 'var(--switchbot-red, #ef4444)'
|
|
132
|
+
importBtn.style.padding = '8px 20px'
|
|
133
|
+
importBtn.style.fontSize = '13px'
|
|
134
|
+
|
|
135
|
+
const cleanup = (result: { configDeviceName: string, configDeviceType: string, address?: string } | null) => {
|
|
136
|
+
div.remove()
|
|
137
|
+
resolve(result)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
cancelBtn.onclick = () => cleanup(null)
|
|
141
|
+
importBtn.onclick = () => cleanup({
|
|
142
|
+
configDeviceName: nameInput.value || device.name || device.id,
|
|
143
|
+
configDeviceType: typeSelect.value || device.type,
|
|
144
|
+
address: macInput.value || undefined,
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
div.addEventListener('click', (event) => {
|
|
148
|
+
if (event.target === div) {
|
|
149
|
+
cleanup(null)
|
|
150
|
+
}
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
buttons.appendChild(cancelBtn)
|
|
154
|
+
buttons.appendChild(importBtn)
|
|
155
|
+
contentDiv.appendChild(nameLabel)
|
|
156
|
+
contentDiv.appendChild(nameInput)
|
|
157
|
+
contentDiv.appendChild(typeLabel)
|
|
158
|
+
contentDiv.appendChild(typeSelect)
|
|
159
|
+
contentDiv.appendChild(macLabel)
|
|
160
|
+
contentDiv.appendChild(macInput)
|
|
161
|
+
contentDiv.appendChild(buttons)
|
|
162
|
+
|
|
163
|
+
modal.appendChild(title)
|
|
164
|
+
modal.appendChild(contentDiv)
|
|
165
|
+
div.appendChild(modal)
|
|
166
|
+
document.body.appendChild(div)
|
|
167
|
+
nameInput.focus()
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
|
|
4
171
|
export async function editDevice(device: any): Promise<void> {
|
|
5
172
|
// Create modal dialog for editing
|
|
6
173
|
const div = document.createElement('div')
|