@switchbot/homebridge-switchbot 5.0.0-beta.115 → 5.0.0-beta.117

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.
@@ -8,6 +8,251 @@ var __export = (target, all) => {
8
8
  __defProp(target, name, { get: all[name], enumerable: true });
9
9
  };
10
10
 
11
+ // src/device-types.ts
12
+ function getValidDeviceTypes() {
13
+ const validTypes = /* @__PURE__ */ new Set();
14
+ for (const category of Object.values(DEVICE_TYPES)) {
15
+ for (const type of category) {
16
+ validTypes.add(type);
17
+ }
18
+ }
19
+ return validTypes;
20
+ }
21
+ function normalizeDeviceType(deviceType) {
22
+ if (!deviceType || typeof deviceType !== "string") {
23
+ return null;
24
+ }
25
+ const trimmed = deviceType.trim();
26
+ const lowercase = trimmed.toLowerCase();
27
+ const validTypes = getValidDeviceTypes();
28
+ if (validTypes.has(trimmed)) {
29
+ return trimmed;
30
+ }
31
+ const normalized = DEVICE_TYPE_NORMALIZATION_MAP[lowercase];
32
+ if (normalized && validTypes.has(normalized)) {
33
+ return normalized;
34
+ }
35
+ return null;
36
+ }
37
+ function isValidDeviceType(deviceType) {
38
+ if (!deviceType || typeof deviceType !== "string") {
39
+ return false;
40
+ }
41
+ const validTypes = getValidDeviceTypes();
42
+ return validTypes.has(deviceType.trim());
43
+ }
44
+ var DEVICE_TYPES, DEVICE_TYPE_NORMALIZATION_MAP;
45
+ var init_device_types = __esm({
46
+ "src/device-types.ts"() {
47
+ "use strict";
48
+ DEVICE_TYPES = {
49
+ "Window Coverings": ["Blind Tilt", "Curtain", "Curtain3", "Roller Shade"],
50
+ "Locks & Access": [
51
+ "Keypad",
52
+ "Keypad Touch",
53
+ "Keypad Vision",
54
+ "Keypad Vision Pro",
55
+ "Lock Vision Pro",
56
+ "Lock Lite",
57
+ "Smart Lock",
58
+ "Smart Lock Pro",
59
+ "Smart Lock Ultra",
60
+ "Video Doorbell"
61
+ ],
62
+ "Sensors": ["Contact Sensor", "Motion Sensor", "Presence Sensor", "Water Detector"],
63
+ "Lighting": [
64
+ "Candle Warmer Lamp",
65
+ "Ceiling Light",
66
+ "Ceiling Light Pro",
67
+ "Color Bulb",
68
+ "Floor Lamp",
69
+ "RGBIC Neon Rope Light",
70
+ "RGBIC Neon Wire Rope Light",
71
+ "RGBICWW Floor Lamp",
72
+ "RGBICWW Strip Light",
73
+ "Strip Light",
74
+ "Strip Light 3"
75
+ ],
76
+ "Climate Control": [
77
+ "Air Purifier PM2.5",
78
+ "Air Purifier Table PM2.5",
79
+ "Air Purifier VOC",
80
+ "Air Purifier Table VOC",
81
+ "Battery Circulator Fan",
82
+ "Circulator Fan",
83
+ "Humidifier",
84
+ "Humidifier2",
85
+ "Meter",
86
+ "MeterPlus",
87
+ "Meter Plus",
88
+ "MeterPro",
89
+ "Meter Pro",
90
+ "MeterPro(CO2)",
91
+ "Meter Pro (CO2)",
92
+ "Smart Radiator Thermostat",
93
+ "Standing Circulator Fan",
94
+ "WoIOSensor"
95
+ ],
96
+ "Plugs & Switches": [
97
+ "Garage Door Opener",
98
+ "Plug",
99
+ "Plug Mini (EU)",
100
+ "Plug Mini (JP)",
101
+ "Plug Mini (US)",
102
+ "Relay Switch 1",
103
+ "Relay Switch 1PM",
104
+ "Relay Switch 2PM"
105
+ ],
106
+ "Robot Vacuums": [
107
+ "K10+",
108
+ "K10+ Pro",
109
+ "Robot Vacuum Cleaner K10+ Pro Combo",
110
+ "Robot Vacuum Cleaner K11+",
111
+ "Robot Vacuum Cleaner K20 Plus Pro",
112
+ "Robot Vacuum Cleaner S1",
113
+ "Robot Vacuum Cleaner S1 Plus",
114
+ "Robot Vacuum Cleaner S10",
115
+ "Robot Vacuum Cleaner S20"
116
+ ],
117
+ "Hubs": ["AI Hub", "Hub", "Hub 2", "Hub 3", "Hub Mini", "Hub Plus"],
118
+ "Cameras": [
119
+ "Indoor Cam",
120
+ "Pan/Tilt Cam",
121
+ "Pan/Tilt Cam 2K",
122
+ "Pan/Tilt Cam Plus 2K",
123
+ "Pan/Tilt Cam Plus 3K"
124
+ ],
125
+ "IR Devices": [
126
+ "Air Conditioner",
127
+ "Air Purifier",
128
+ "Camera",
129
+ "DVD",
130
+ "Fan",
131
+ "Light",
132
+ "Others",
133
+ "Projector",
134
+ "Set Top Box",
135
+ "Speaker",
136
+ "Streamer",
137
+ "TV",
138
+ "Vacuum Cleaner",
139
+ "Water Heater"
140
+ ],
141
+ "Other Devices": ["AI Art Frame", "Bot", "Home Climate Panel", "Remote", "remote with screen"]
142
+ };
143
+ DEVICE_TYPE_NORMALIZATION_MAP = {
144
+ // --- node-switchbot beta new types normalization additions ---
145
+ "hub mini": "Hub Mini",
146
+ "hub 3": "Hub 3",
147
+ "keypad": "Keypad",
148
+ // node-switchbot beta
149
+ "plug mini": "Plug Mini (US)",
150
+ // fallback to US if region not specified
151
+ "art frame": "AI Art Frame",
152
+ "rgbicww": "RGBICWW Strip Light",
153
+ "lock vision": "Lock Vision Pro",
154
+ // alias for new lock vision
155
+ "lock pro": "Smart Lock Pro",
156
+ "lock lite": "Lock Lite",
157
+ // node-switchbot beta
158
+ "circulator fan": "Circulator Fan",
159
+ // node-switchbot beta
160
+ "smart thermostat radiator": "Smart Radiator Thermostat",
161
+ "climate panel": "Home Climate Panel",
162
+ "evaporative humidifier": "Humidifier",
163
+ // --- end node-switchbot beta additions ---
164
+ // Only keep the last occurrence for each key, all values canonical
165
+ "air purifier pm2.5": "Air Purifier PM2.5",
166
+ "pan/tilt cam plus 3k": "Pan/Tilt Cam Plus 3K",
167
+ "remote with screen": "Remote with Screen",
168
+ "ai hub": "AI Hub",
169
+ "water detector": "Water Detector",
170
+ "video doorbell": "Video Doorbell",
171
+ "smart radiator thermostat": "Smart Radiator Thermostat",
172
+ "woiosensor": "WoIOSensor",
173
+ "garage door opener": "Garage Door Opener",
174
+ "air purifier table pm2.5": "Air Purifier Table PM2.5",
175
+ "air purifier voc": "Air Purifier VOC",
176
+ "air purifier table voc": "Air Purifier Table VOC",
177
+ "plug mini (eu)": "Plug Mini (EU)",
178
+ // Only last occurrence for each key is kept above. Removed duplicates here.
179
+ // Climate control conversions
180
+ "humidifier2": "Humidifier2",
181
+ "battery circulator fan": "Battery Circulator Fan",
182
+ "standing circulator fan": "Standing Circulator Fan",
183
+ // Lock/keypad conversions
184
+ "smart lock": "Smart Lock",
185
+ "smart lock pro": "Smart Lock Pro",
186
+ "smart lock ultra": "Smart Lock Ultra",
187
+ "keypad touch": "Keypad Touch",
188
+ "keypad vision": "Keypad Vision",
189
+ "keypad vision pro": "Keypad Vision Pro",
190
+ // Light conversions
191
+ "color bulb": "Color Bulb",
192
+ "ceiling light": "Ceiling Light",
193
+ "ceiling light pro": "Ceiling Light Pro",
194
+ "candle warmer lamp": "Candle Warmer Lamp",
195
+ "floor lamp": "Floor Lamp",
196
+ "rgbic neon rope light": "RGBIC Neon Rope Light",
197
+ "rgbic neon wire rope light": "RGBIC Neon Wire Rope Light",
198
+ "rgbicww floor lamp": "RGBICWW Floor Lamp",
199
+ "rgbicww strip light": "RGBICWW Strip Light",
200
+ "strip light": "Strip Light",
201
+ "strip light 3": "Strip Light 3",
202
+ // node-switchbot beta
203
+ // Vacuum conversions
204
+ "robot vacuum cleaner s1": "Robot Vacuum Cleaner S1",
205
+ "robot vacuum cleaner s1 plus": "Robot Vacuum Cleaner S1 Plus",
206
+ "robot vacuum cleaner s10": "Robot Vacuum Cleaner S10",
207
+ "robot vacuum cleaner s20": "Robot Vacuum Cleaner S20",
208
+ "robot vacuum cleaner k10+ pro combo": "Robot Vacuum Cleaner K10+ Pro Combo",
209
+ "robot vacuum cleaner k11+": "Robot Vacuum Cleaner K11+",
210
+ "robot vacuum cleaner k20 plus pro": "Robot Vacuum Cleaner K20 Plus Pro",
211
+ // Exact device type mappings (API format → canonical format)
212
+ "relay switch 1": "Relay Switch 1",
213
+ "blind tilt": "Blind Tilt",
214
+ "roller shade": "Roller Shade",
215
+ "curtain3": "Curtain3",
216
+ "hub 2": "Hub 2",
217
+ "meterplus": "MeterPlus",
218
+ "meterpro": "MeterPro",
219
+ "meterpro(co2)": "MeterPro(CO2)",
220
+ "walletfinder": "WalletFinder",
221
+ "k10+": "K10+",
222
+ "k10+ pro (wosweeperminipro)": "K10+ Pro (wosweeperminipro)",
223
+ // Handle spaced variants from config files (normalize back to canonical type)
224
+ "meter pro": "Meter Pro",
225
+ "meter pro (co2)": "Meter Pro (CO2)",
226
+ "meter plus": "Meter Plus",
227
+ "relay switch 1 pm": "Relay Switch 1PM",
228
+ "relay switch 2 pm": "Relay Switch 2PM",
229
+ "plug mini eu": "Plug Mini (EU)",
230
+ "plug mini jp": "Plug Mini (JP)",
231
+ "plug mini us": "Plug Mini (US)",
232
+ // Migration mappings for invalid/legacy device types
233
+ "lock vision pro": "Lock Vision Pro",
234
+ // Valid alias; map to canonical
235
+ // 'lock vision': 'Keypad Vision', // Invalid type (removed, now node-switchbot beta alias above)
236
+ "lock touch": "Keypad Touch",
237
+ // Invalid type
238
+ // Additional normalization for new/unknown types from logs
239
+ "woplugus": "Plug Mini (US)"
240
+ // Removed duplicate keys below, only last occurrence kept
241
+ // 'plug mini us': 'plug mini (us)', // duplicate, removed
242
+ // 'plug us': 'plug mini (us)', // duplicate, removed
243
+ // 'plug': 'plug', // duplicate, removed
244
+ // 'air purifier pm2.5': 'air purifier pm2.5', // duplicate, removed
245
+ // 'rgbic neon wire rope light': 'rgbic neon wire rope light', // duplicate, removed
246
+ // 'candle warmer lamp': 'candle warmer lamp', // duplicate, removed
247
+ // 'pan/tilt cam plus 3k': 'pan/tilt cam plus 3k', // duplicate, removed
248
+ // 'remote with screen': 'remote with screen', // duplicate, removed
249
+ // 'ai hub': 'ai hub', // duplicate, removed
250
+ // 'lock vision pro': 'lock vision pro', // duplicate, remove this line
251
+ // Add any other device types from logs as needed
252
+ };
253
+ }
254
+ });
255
+
11
256
  // src/homebridge-ui/public/js/types.ts
12
257
  var init_types = __esm({
13
258
  "src/homebridge-ui/public/js/types.ts"() {
@@ -48,8 +293,27 @@ __export(api_exports, {
48
293
  saveCredentials: () => saveCredentials,
49
294
  syncParentPluginConfigFromDisk: () => syncParentPluginConfigFromDisk,
50
295
  testDeviceConnection: () => testDeviceConnection,
51
- updateDevice: () => updateDevice
296
+ updateDevice: () => updateDevice,
297
+ validateAndFixDeviceTypes: () => validateAndFixDeviceTypes
52
298
  });
299
+ function validateAndFixDeviceTypes(devices) {
300
+ const errors = [];
301
+ for (const d of devices) {
302
+ if (!isValidDeviceType(d.configDeviceType)) {
303
+ const fixed = normalizeDeviceType(d.configDeviceType);
304
+ if (fixed) {
305
+ d.configDeviceType = fixed;
306
+ } else {
307
+ errors.push({
308
+ deviceId: d.deviceId,
309
+ name: d.configDeviceName,
310
+ type: d.configDeviceType
311
+ });
312
+ }
313
+ }
314
+ }
315
+ return errors;
316
+ }
53
317
  function isSwitchBotPlatformConfig(block) {
54
318
  const platformName = String(block?.platform || block?.name || "").toLowerCase();
55
319
  return platformName === "switchbot" || platformName === "@switchbot/homebridge-switchbot" || platformName.includes("switchbot");
@@ -75,6 +339,11 @@ async function syncParentPluginConfigFromDisk(autoSave = false) {
75
339
  uiLog.warn("SwitchBot platform block not found in Homebridge plugin config");
76
340
  return false;
77
341
  }
342
+ const errors = validateAndFixDeviceTypes(diskResp.data.devices || []);
343
+ if (errors.length > 0) {
344
+ homebridge.toast?.error?.(`Invalid device types found: ${errors.map((e) => `${e.name} (${e.type})`).join(", ")}`);
345
+ return false;
346
+ }
78
347
  pluginConfigBlocks[index] = diskResp.data;
79
348
  await homebridge.updatePluginConfig(pluginConfigBlocks);
80
349
  if (autoSave && typeof homebridge.savePluginConfig === "function") {
@@ -103,41 +372,35 @@ async function fetchCredentialStatus() {
103
372
  }
104
373
  }
105
374
  async function saveCredentials(token, secret) {
106
- try {
107
- uiLog.info("Saving credentials...");
108
- const resp = await homebridge.request("/credentials", { token, secret });
109
- uiLog.info("Save response:", resp);
110
- if (!resp || resp.success === false) {
111
- throw new Error(resp?.message || "Save failed");
112
- }
113
- return resp.data || resp;
114
- } catch (e) {
115
- throw e;
375
+ uiLog.info("Saving credentials...");
376
+ const resp = await homebridge.request("/credentials", { token, secret });
377
+ uiLog.info("Save response:", resp);
378
+ if (!resp || resp.success === false) {
379
+ throw new Error(resp?.message || "Save failed");
116
380
  }
381
+ return resp.data || resp;
117
382
  }
118
383
  async function fetchDevices() {
119
384
  try {
120
385
  const resp = await homebridge.request("/devices", {});
121
386
  if (!resp || resp.success === false) {
122
- throw new Error(resp?.data?.message || "request failed");
387
+ const backendMsg = resp?.data?.message || resp?.message;
388
+ throw new Error(backendMsg || "request failed");
123
389
  }
124
390
  return resp.data || [];
125
391
  } catch (e) {
126
- uiLog.error("Error fetching devices:", e);
392
+ const msg = e instanceof Error ? e.message : String(e);
393
+ uiLog.error("Error fetching devices:", msg);
127
394
  return [];
128
395
  }
129
396
  }
130
397
  async function discoverDevices(mode = "all", options) {
131
- try {
132
- const resp = await homebridge.request("/discover", { mode, ...options });
133
- uiLog.info("Discover response:", resp);
134
- if (!resp || resp.success === false) {
135
- throw new Error(resp?.data?.message || "Discovery failed");
136
- }
137
- return resp.data || [];
138
- } catch (e) {
139
- throw e;
398
+ const resp = await homebridge.request("/discover", { mode, ...options });
399
+ uiLog.info("Discover response:", resp);
400
+ if (!resp || resp.success === false) {
401
+ throw new Error(resp?.data?.message || "Discovery failed");
140
402
  }
403
+ return resp.data || [];
141
404
  }
142
405
  async function fetchBluetoothStatus() {
143
406
  try {
@@ -164,95 +427,76 @@ async function testDeviceConnection(payload) {
164
427
  };
165
428
  }
166
429
  async function addDevice(deviceId, name, type, options) {
167
- try {
168
- const payload = { deviceId, name, type };
169
- if (options?.address) {
170
- payload.address = options.address;
171
- }
172
- if (options?.model) {
173
- payload.model = options.model;
174
- }
175
- if (options?.rssi !== void 0 && options?.rssi !== null && options?.rssi !== 0) {
176
- payload.rssi = options.rssi;
177
- }
178
- if (options?.encryptionKey) {
179
- payload.encryptionKey = options.encryptionKey;
180
- }
181
- if (options?.keyId) {
182
- payload.keyId = options.keyId;
183
- }
184
- uiLog.info("Adding device to config:", payload);
185
- const resp = await homebridge.request("/add-device", payload);
186
- uiLog.info("Add device response:", resp);
187
- if (!resp || resp.success === false) {
188
- throw new Error(resp?.data?.message || "Failed to add device");
189
- }
190
- return resp.data || resp;
191
- } catch (e) {
192
- throw e;
430
+ const payload = { deviceId, name, type };
431
+ if (options?.address) {
432
+ payload.address = options.address;
433
+ }
434
+ if (options?.model) {
435
+ payload.model = options.model;
436
+ }
437
+ if (options?.rssi !== void 0 && options?.rssi !== null && options?.rssi !== 0) {
438
+ payload.rssi = options.rssi;
439
+ }
440
+ if (options?.encryptionKey) {
441
+ payload.encryptionKey = options.encryptionKey;
442
+ }
443
+ if (options?.keyId) {
444
+ payload.keyId = options.keyId;
445
+ }
446
+ uiLog.info("Adding device to config:", payload);
447
+ const resp = await homebridge.request("/add-device", payload);
448
+ uiLog.info("Add device response:", resp);
449
+ if (!resp || resp.success === false) {
450
+ throw new Error(resp?.data?.message || "Failed to add device");
193
451
  }
452
+ return resp.data || resp;
194
453
  }
195
454
  async function addDevicesInBulk(devices) {
196
- try {
197
- const resp = await homebridge.request("/add-devices", { devices });
198
- uiLog.info("Bulk add response:", resp);
199
- if (!resp || resp.success === false) {
200
- throw new Error(resp?.data?.message || "Bulk add failed");
201
- }
202
- return resp.data || resp;
203
- } catch (e) {
204
- throw e;
455
+ const resp = await homebridge.request("/add-devices", { devices });
456
+ uiLog.info("Bulk add response:", resp);
457
+ if (!resp || resp.success === false) {
458
+ throw new Error(resp?.data?.message || "Bulk add failed");
205
459
  }
460
+ return resp.data || resp;
206
461
  }
207
462
  async function updateDevice(deviceId, configDeviceName, configDeviceType) {
208
- try {
209
- const params = { deviceId };
210
- if (configDeviceName) {
211
- params.configDeviceName = configDeviceName;
212
- }
213
- if (configDeviceType) {
214
- params.configDeviceType = configDeviceType;
215
- }
216
- uiLog.info("[Update Device] Sending update request with params:", params);
217
- const resp = await homebridge.request("/update-device", params);
218
- uiLog.info("[Update Device] Update response:", resp);
219
- if (!resp || resp.success === false) {
220
- throw new Error(resp?.data?.message || "Failed to update device");
221
- }
222
- return resp.data || resp;
223
- } catch (e) {
224
- throw e;
463
+ const params = { deviceId };
464
+ if (configDeviceName) {
465
+ params.configDeviceName = configDeviceName;
466
+ }
467
+ if (configDeviceType) {
468
+ params.configDeviceType = configDeviceType;
469
+ }
470
+ uiLog.info("[Update Device] Sending update request with params:", params);
471
+ const resp = await homebridge.request("/update-device", params);
472
+ uiLog.info("[Update Device] Update response:", resp);
473
+ if (!resp || resp.success === false) {
474
+ throw new Error(resp?.data?.message || "Failed to update device");
225
475
  }
476
+ return resp.data || resp;
226
477
  }
227
478
  async function deleteDevice(deviceId) {
228
- try {
229
- uiLog.info("Sending delete request for deviceId:", deviceId);
230
- const resp = await homebridge.request("/delete-device", { deviceId });
231
- uiLog.info("Delete response:", resp);
232
- if (!resp || resp.success === false) {
233
- throw new Error(resp?.data?.message || "Failed to delete device");
234
- }
235
- return resp.data || resp;
236
- } catch (e) {
237
- throw e;
479
+ uiLog.info("Sending delete request for deviceId:", deviceId);
480
+ const resp = await homebridge.request("/delete-device", { deviceId });
481
+ uiLog.info("Delete response:", resp);
482
+ if (!resp || resp.success === false) {
483
+ throw new Error(resp?.data?.message || "Failed to delete device");
238
484
  }
485
+ return resp.data || resp;
239
486
  }
240
487
  async function deleteAllDevices() {
241
- try {
242
- uiLog.info("Sending delete all devices request");
243
- const resp = await homebridge.request("/delete-all-devices", {});
244
- uiLog.info("Delete all response:", resp);
245
- if (!resp || resp.success === false) {
246
- throw new Error(resp?.data?.message || "Failed to delete all devices");
247
- }
248
- return resp.data || resp;
249
- } catch (e) {
250
- throw e;
488
+ uiLog.info("Sending delete all devices request");
489
+ const resp = await homebridge.request("/delete-all-devices", {});
490
+ uiLog.info("Delete all response:", resp);
491
+ if (!resp || resp.success === false) {
492
+ throw new Error(resp?.data?.message || "Failed to delete all devices");
251
493
  }
494
+ return resp.data || resp;
252
495
  }
253
496
  var init_api = __esm({
254
497
  "src/homebridge-ui/public/js/api.ts"() {
255
498
  "use strict";
499
+ init_device_types();
256
500
  init_types();
257
501
  init_logger();
258
502
  }
@@ -1581,109 +1825,6 @@ var init_discovery = __esm({
1581
1825
  }
1582
1826
  });
1583
1827
 
1584
- // src/device-types.ts
1585
- var DEVICE_TYPES;
1586
- var init_device_types = __esm({
1587
- "src/device-types.ts"() {
1588
- "use strict";
1589
- DEVICE_TYPES = {
1590
- "Window Coverings": ["Blind Tilt", "Curtain", "Curtain3", "Roller Shade"],
1591
- "Locks & Access": [
1592
- "Keypad",
1593
- "Keypad Touch",
1594
- "Keypad Vision",
1595
- "Keypad Vision Pro",
1596
- "Lock Vision Pro",
1597
- "Lock Lite",
1598
- "Smart Lock",
1599
- "Smart Lock Pro",
1600
- "Smart Lock Ultra",
1601
- "Video Doorbell"
1602
- ],
1603
- "Sensors": ["Contact Sensor", "Motion Sensor", "Presence Sensor", "Water Detector"],
1604
- "Lighting": [
1605
- "Candle Warmer Lamp",
1606
- "Ceiling Light",
1607
- "Ceiling Light Pro",
1608
- "Color Bulb",
1609
- "Floor Lamp",
1610
- "RGBIC Neon Rope Light",
1611
- "RGBIC Neon Wire Rope Light",
1612
- "RGBICWW Floor Lamp",
1613
- "RGBICWW Strip Light",
1614
- "Strip Light",
1615
- "Strip Light 3"
1616
- ],
1617
- "Climate Control": [
1618
- "Air Purifier PM2.5",
1619
- "Air Purifier Table PM2.5",
1620
- "Air Purifier VOC",
1621
- "Air Purifier Table VOC",
1622
- "Battery Circulator Fan",
1623
- "Circulator Fan",
1624
- "Humidifier",
1625
- "Humidifier2",
1626
- "Meter",
1627
- "MeterPlus",
1628
- "Meter Plus",
1629
- "MeterPro",
1630
- "Meter Pro",
1631
- "MeterPro(CO2)",
1632
- "Meter Pro (CO2)",
1633
- "Smart Radiator Thermostat",
1634
- "Standing Circulator Fan",
1635
- "WoIOSensor"
1636
- ],
1637
- "Plugs & Switches": [
1638
- "Garage Door Opener",
1639
- "Plug",
1640
- "Plug Mini (EU)",
1641
- "Plug Mini (JP)",
1642
- "Plug Mini (US)",
1643
- "Relay Switch 1",
1644
- "Relay Switch 1PM",
1645
- "Relay Switch 2PM"
1646
- ],
1647
- "Robot Vacuums": [
1648
- "K10+",
1649
- "K10+ Pro",
1650
- "Robot Vacuum Cleaner K10+ Pro Combo",
1651
- "Robot Vacuum Cleaner K11+",
1652
- "Robot Vacuum Cleaner K20 Plus Pro",
1653
- "Robot Vacuum Cleaner S1",
1654
- "Robot Vacuum Cleaner S1 Plus",
1655
- "Robot Vacuum Cleaner S10",
1656
- "Robot Vacuum Cleaner S20"
1657
- ],
1658
- "Hubs": ["AI Hub", "Hub", "Hub 2", "Hub 3", "Hub Mini", "Hub Plus"],
1659
- "Cameras": [
1660
- "Indoor Cam",
1661
- "Pan/Tilt Cam",
1662
- "Pan/Tilt Cam 2K",
1663
- "Pan/Tilt Cam Plus 2K",
1664
- "Pan/Tilt Cam Plus 3K"
1665
- ],
1666
- "IR Devices": [
1667
- "Air Conditioner",
1668
- "Air Purifier",
1669
- "Camera",
1670
- "DVD",
1671
- "Fan",
1672
- "Light",
1673
- "Others",
1674
- "Projector",
1675
- "Set Top Box",
1676
- "Speaker",
1677
- "Streamer",
1678
- "TV",
1679
- "Vacuum Cleaner",
1680
- "Water Heater"
1681
- ],
1682
- "Other Devices": ["AI Art Frame", "Bot", "Home Climate Panel", "Remote", "remote with screen"]
1683
- };
1684
- }
1685
- });
1686
-
1687
1828
  // src/homebridge-ui/public/js/constants.ts
1688
1829
  var init_constants = __esm({
1689
1830
  "src/homebridge-ui/public/js/constants.ts"() {
@@ -3188,13 +3329,14 @@ async function addDeviceToConfig2(device, options = {}) {
3188
3329
  }
3189
3330
  return { added: !alreadyExists };
3190
3331
  } catch (e) {
3191
- uiLog.error("Add device error:", e);
3332
+ const msg = e instanceof Error ? e.message : String(e);
3333
+ uiLog.error("Add device error:", msg);
3192
3334
  const status = document.getElementById("discoverStatus");
3193
3335
  if (status) {
3194
- status.textContent = `\u2717 Error: ${e instanceof Error ? e.message : "Failed to add device"}`;
3336
+ status.textContent = `\u2717 Error: ${msg}`;
3195
3337
  status.classList.add("error");
3196
3338
  }
3197
- toastError(e instanceof Error ? e.message : "Failed to add device");
3339
+ toastError(msg);
3198
3340
  return { added: false };
3199
3341
  } finally {
3200
3342
  hideBusyUi();